1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et tw=80 : */
3 :
4 : /* ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is mozilla.org code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * The Mozilla Foundation.
21 : * Portions created by the Initial Developer are Copyright (C) 2010
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Daniel Witte <dwitte@mozilla.com>
26 : * Frederic Plourde <bugzillaFred@gmail.com>
27 : * Jason Duell <jduell.mcbugs@gmail.com>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either the GNU General Public License Version 2 or later (the "GPL"), or
31 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 : #include "mozilla/net/HttpBaseChannel.h"
44 :
45 : #include "nsHttpHandler.h"
46 : #include "nsMimeTypes.h"
47 : #include "nsNetUtil.h"
48 :
49 : #include "nsICachingChannel.h"
50 : #include "nsISeekableStream.h"
51 : #include "nsITimedChannel.h"
52 : #include "nsIEncodedChannel.h"
53 : #include "nsIResumableChannel.h"
54 : #include "nsIApplicationCacheChannel.h"
55 : #include "nsEscape.h"
56 :
57 : #include "prnetdb.h"
58 :
59 : namespace mozilla {
60 : namespace net {
61 :
62 3514 : HttpBaseChannel::HttpBaseChannel()
63 : : mStartPos(LL_MAXUINT)
64 : , mStatus(NS_OK)
65 : , mLoadFlags(LOAD_NORMAL)
66 : , mPriority(PRIORITY_NORMAL)
67 : , mCaps(0)
68 3514 : , mRedirectionLimit(gHttpHandler->RedirectionLimit())
69 : , mApplyConversion(true)
70 : , mCanceled(false)
71 : , mIsPending(false)
72 : , mWasOpened(false)
73 : , mResponseHeadersModified(false)
74 : , mAllowPipelining(true)
75 : , mForceAllowThirdPartyCookie(false)
76 : , mUploadStreamHasHeaders(false)
77 : , mInheritApplicationCache(true)
78 : , mChooseApplicationCache(false)
79 : , mLoadedFromApplicationCache(false)
80 : , mChannelIsForDownload(false)
81 : , mTracingEnabled(true)
82 : , mTimingEnabled(false)
83 : , mAllowSpdy(true)
84 : , mSuspendCount(0)
85 7028 : , mRedirectedCachekeys(nsnull)
86 : {
87 3514 : LOG(("Creating HttpBaseChannel @%x\n", this));
88 :
89 : // grab a reference to the handler to ensure that it doesn't go away.
90 3514 : NS_ADDREF(gHttpHandler);
91 :
92 : // Subfields of unions cannot be targeted in an initializer list
93 3514 : mSelfAddr.raw.family = PR_AF_UNSPEC;
94 3514 : mPeerAddr.raw.family = PR_AF_UNSPEC;
95 3514 : }
96 :
97 6480 : HttpBaseChannel::~HttpBaseChannel()
98 : {
99 3240 : LOG(("Destroying HttpBaseChannel @%x\n", this));
100 :
101 : // Make sure we don't leak
102 3240 : CleanRedirectCacheChainIfNecessary();
103 :
104 3240 : gHttpHandler->Release();
105 6480 : }
106 :
107 : nsresult
108 3514 : HttpBaseChannel::Init(nsIURI *aURI,
109 : PRUint8 aCaps,
110 : nsProxyInfo *aProxyInfo)
111 : {
112 3514 : LOG(("HttpBaseChannel::Init [this=%p]\n", this));
113 :
114 3514 : NS_PRECONDITION(aURI, "null uri");
115 :
116 3514 : nsresult rv = nsHashPropertyBag::Init();
117 3514 : if (NS_FAILED(rv)) return rv;
118 :
119 3514 : mURI = aURI;
120 3514 : mOriginalURI = aURI;
121 3514 : mDocumentURI = nsnull;
122 3514 : mCaps = aCaps;
123 :
124 : // Construct connection info object
125 7028 : nsCAutoString host;
126 3514 : PRInt32 port = -1;
127 3514 : bool usingSSL = false;
128 :
129 3514 : rv = mURI->SchemeIs("https", &usingSSL);
130 3514 : if (NS_FAILED(rv)) return rv;
131 :
132 3514 : rv = mURI->GetAsciiHost(host);
133 3514 : if (NS_FAILED(rv)) return rv;
134 :
135 : // Reject the URL if it doesn't specify a host
136 3514 : if (host.IsEmpty())
137 0 : return NS_ERROR_MALFORMED_URI;
138 :
139 3514 : rv = mURI->GetPort(&port);
140 3514 : if (NS_FAILED(rv)) return rv;
141 :
142 3514 : LOG(("host=%s port=%d\n", host.get(), port));
143 :
144 3514 : rv = mURI->GetAsciiSpec(mSpec);
145 3514 : if (NS_FAILED(rv)) return rv;
146 3514 : LOG(("uri=%s\n", mSpec.get()));
147 :
148 : mConnectionInfo = new nsHttpConnectionInfo(host, port,
149 3514 : aProxyInfo, usingSSL);
150 3514 : if (!mConnectionInfo)
151 0 : return NS_ERROR_OUT_OF_MEMORY;
152 :
153 : // Set default request method
154 3514 : mRequestHead.SetMethod(nsHttp::Get);
155 :
156 : // Set request headers
157 7028 : nsCAutoString hostLine;
158 3514 : rv = nsHttpHandler::GenerateHostPort(host, port, hostLine);
159 3514 : if (NS_FAILED(rv)) return rv;
160 :
161 3514 : rv = mRequestHead.SetHeader(nsHttp::Host, hostLine);
162 3514 : if (NS_FAILED(rv)) return rv;
163 :
164 : rv = gHttpHandler->
165 3514 : AddStandardRequestHeaders(&mRequestHead.Headers(), aCaps,
166 3514 : !mConnectionInfo->UsingSSL() &&
167 7028 : mConnectionInfo->UsingHttpProxy());
168 :
169 3514 : return rv;
170 : }
171 :
172 : //-----------------------------------------------------------------------------
173 : // HttpBaseChannel::nsISupports
174 : //-----------------------------------------------------------------------------
175 :
176 274081 : NS_IMPL_ISUPPORTS_INHERITED9(HttpBaseChannel,
177 : nsHashPropertyBag,
178 : nsIRequest,
179 : nsIChannel,
180 : nsIEncodedChannel,
181 : nsIHttpChannel,
182 : nsIHttpChannelInternal,
183 : nsIUploadChannel,
184 : nsIUploadChannel2,
185 : nsISupportsPriority,
186 : nsITraceableChannel)
187 :
188 : //-----------------------------------------------------------------------------
189 : // HttpBaseChannel::nsIRequest
190 : //-----------------------------------------------------------------------------
191 :
192 : NS_IMETHODIMP
193 8 : HttpBaseChannel::GetName(nsACString& aName)
194 : {
195 8 : aName = mSpec;
196 8 : return NS_OK;
197 : }
198 :
199 : NS_IMETHODIMP
200 374 : HttpBaseChannel::IsPending(bool *aIsPending)
201 : {
202 374 : NS_ENSURE_ARG_POINTER(aIsPending);
203 374 : *aIsPending = mIsPending;
204 374 : return NS_OK;
205 : }
206 :
207 : NS_IMETHODIMP
208 1714 : HttpBaseChannel::GetStatus(nsresult *aStatus)
209 : {
210 1714 : NS_ENSURE_ARG_POINTER(aStatus);
211 1714 : *aStatus = mStatus;
212 1714 : return NS_OK;
213 : }
214 :
215 : NS_IMETHODIMP
216 784 : HttpBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
217 : {
218 784 : NS_ENSURE_ARG_POINTER(aLoadGroup);
219 784 : *aLoadGroup = mLoadGroup;
220 784 : NS_IF_ADDREF(*aLoadGroup);
221 784 : return NS_OK;
222 : }
223 :
224 : NS_IMETHODIMP
225 167 : HttpBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
226 : {
227 167 : mLoadGroup = aLoadGroup;
228 167 : mProgressSink = nsnull;
229 167 : return NS_OK;
230 : }
231 :
232 : NS_IMETHODIMP
233 12499 : HttpBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
234 : {
235 12499 : NS_ENSURE_ARG_POINTER(aLoadFlags);
236 12499 : *aLoadFlags = mLoadFlags;
237 12499 : return NS_OK;
238 : }
239 :
240 : NS_IMETHODIMP
241 4439 : HttpBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
242 : {
243 4439 : mLoadFlags = aLoadFlags;
244 4439 : return NS_OK;
245 : }
246 :
247 : //-----------------------------------------------------------------------------
248 : // HttpBaseChannel::nsIChannel
249 : //-----------------------------------------------------------------------------
250 :
251 : NS_IMETHODIMP
252 1333 : HttpBaseChannel::GetOriginalURI(nsIURI **aOriginalURI)
253 : {
254 1333 : NS_ENSURE_ARG_POINTER(aOriginalURI);
255 1333 : *aOriginalURI = mOriginalURI;
256 1333 : NS_ADDREF(*aOriginalURI);
257 1333 : return NS_OK;
258 : }
259 :
260 : NS_IMETHODIMP
261 143 : HttpBaseChannel::SetOriginalURI(nsIURI *aOriginalURI)
262 : {
263 143 : ENSURE_CALLED_BEFORE_ASYNC_OPEN();
264 :
265 143 : NS_ENSURE_ARG_POINTER(aOriginalURI);
266 143 : mOriginalURI = aOriginalURI;
267 143 : return NS_OK;
268 : }
269 :
270 : NS_IMETHODIMP
271 11346 : HttpBaseChannel::GetURI(nsIURI **aURI)
272 : {
273 11346 : NS_ENSURE_ARG_POINTER(aURI);
274 11346 : *aURI = mURI;
275 11346 : NS_ADDREF(*aURI);
276 11346 : return NS_OK;
277 : }
278 :
279 : NS_IMETHODIMP
280 628 : HttpBaseChannel::GetOwner(nsISupports **aOwner)
281 : {
282 628 : NS_ENSURE_ARG_POINTER(aOwner);
283 628 : *aOwner = mOwner;
284 628 : NS_IF_ADDREF(*aOwner);
285 628 : return NS_OK;
286 : }
287 :
288 : NS_IMETHODIMP
289 583 : HttpBaseChannel::SetOwner(nsISupports *aOwner)
290 : {
291 583 : mOwner = aOwner;
292 583 : return NS_OK;
293 : }
294 :
295 : NS_IMETHODIMP
296 955 : HttpBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
297 : {
298 955 : *aCallbacks = mCallbacks;
299 955 : NS_IF_ADDREF(*aCallbacks);
300 955 : return NS_OK;
301 : }
302 :
303 : NS_IMETHODIMP
304 3926 : HttpBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
305 : {
306 3926 : mCallbacks = aCallbacks;
307 3926 : mProgressSink = nsnull;
308 3926 : return NS_OK;
309 : }
310 :
311 : NS_IMETHODIMP
312 1502 : HttpBaseChannel::GetContentType(nsACString& aContentType)
313 : {
314 1502 : if (!mResponseHead) {
315 0 : aContentType.Truncate();
316 0 : return NS_ERROR_NOT_AVAILABLE;
317 : }
318 :
319 1502 : if (!mResponseHead->ContentType().IsEmpty()) {
320 1502 : aContentType = mResponseHead->ContentType();
321 1502 : return NS_OK;
322 : }
323 :
324 0 : aContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
325 0 : return NS_OK;
326 : }
327 :
328 : NS_IMETHODIMP
329 2014 : HttpBaseChannel::SetContentType(const nsACString& aContentType)
330 : {
331 2014 : if (mListener || mWasOpened) {
332 1419 : if (!mResponseHead)
333 69 : return NS_ERROR_NOT_AVAILABLE;
334 :
335 2700 : nsCAutoString contentTypeBuf, charsetBuf;
336 : bool hadCharset;
337 1350 : net_ParseContentType(aContentType, contentTypeBuf, charsetBuf, &hadCharset);
338 :
339 1350 : mResponseHead->SetContentType(contentTypeBuf);
340 :
341 : // take care not to stomp on an existing charset
342 1350 : if (hadCharset)
343 0 : mResponseHead->SetContentCharset(charsetBuf);
344 :
345 : } else {
346 : // We are being given a content-type hint.
347 : bool dummy;
348 : net_ParseContentType(aContentType, mContentTypeHint, mContentCharsetHint,
349 595 : &dummy);
350 : }
351 :
352 1945 : return NS_OK;
353 : }
354 :
355 : NS_IMETHODIMP
356 1051 : HttpBaseChannel::GetContentCharset(nsACString& aContentCharset)
357 : {
358 1051 : if (!mResponseHead)
359 71 : return NS_ERROR_NOT_AVAILABLE;
360 :
361 980 : aContentCharset = mResponseHead->ContentCharset();
362 980 : return NS_OK;
363 : }
364 :
365 : NS_IMETHODIMP
366 1 : HttpBaseChannel::SetContentCharset(const nsACString& aContentCharset)
367 : {
368 1 : if (mListener) {
369 1 : if (!mResponseHead)
370 0 : return NS_ERROR_NOT_AVAILABLE;
371 :
372 1 : mResponseHead->SetContentCharset(aContentCharset);
373 : } else {
374 : // Charset hint
375 0 : mContentCharsetHint = aContentCharset;
376 : }
377 1 : return NS_OK;
378 : }
379 :
380 : NS_IMETHODIMP
381 17 : HttpBaseChannel::GetContentDisposition(PRUint32 *aContentDisposition)
382 : {
383 : nsresult rv;
384 34 : nsCString header;
385 :
386 17 : rv = GetContentDispositionHeader(header);
387 17 : if (NS_FAILED(rv))
388 0 : return rv;
389 :
390 17 : *aContentDisposition = NS_GetContentDispositionFromHeader(header, this);
391 :
392 17 : return NS_OK;
393 : }
394 :
395 : NS_IMETHODIMP
396 11 : HttpBaseChannel::GetContentDispositionFilename(nsAString& aContentDispositionFilename)
397 : {
398 11 : aContentDispositionFilename.Truncate();
399 :
400 : nsresult rv;
401 22 : nsCString header;
402 :
403 11 : rv = GetContentDispositionHeader(header);
404 11 : if (NS_FAILED(rv))
405 0 : return rv;
406 :
407 : return NS_GetFilenameFromDisposition(aContentDispositionFilename,
408 11 : header, mURI);
409 : }
410 :
411 : NS_IMETHODIMP
412 37 : HttpBaseChannel::GetContentDispositionHeader(nsACString& aContentDispositionHeader)
413 : {
414 37 : if (!mResponseHead)
415 0 : return NS_ERROR_NOT_AVAILABLE;
416 :
417 : nsresult rv = mResponseHead->GetHeader(nsHttp::Content_Disposition,
418 37 : aContentDispositionHeader);
419 37 : if (NS_FAILED(rv) || aContentDispositionHeader.IsEmpty())
420 4 : return NS_ERROR_NOT_AVAILABLE;
421 :
422 33 : return NS_OK;
423 : }
424 :
425 : NS_IMETHODIMP
426 477 : HttpBaseChannel::GetContentLength(PRInt32 *aContentLength)
427 : {
428 477 : NS_ENSURE_ARG_POINTER(aContentLength);
429 :
430 477 : if (!mResponseHead)
431 16 : return NS_ERROR_NOT_AVAILABLE;
432 :
433 : // XXX truncates to 32 bit
434 461 : *aContentLength = mResponseHead->ContentLength();
435 461 : return NS_OK;
436 : }
437 :
438 : NS_IMETHODIMP
439 0 : HttpBaseChannel::SetContentLength(PRInt32 value)
440 : {
441 0 : NS_NOTYETIMPLEMENTED("HttpBaseChannel::SetContentLength");
442 0 : return NS_ERROR_NOT_IMPLEMENTED;
443 : }
444 :
445 : NS_IMETHODIMP
446 3 : HttpBaseChannel::Open(nsIInputStream **aResult)
447 : {
448 3 : NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS);
449 1 : return NS_ImplementChannelOpen(this, aResult);
450 : }
451 :
452 : //-----------------------------------------------------------------------------
453 : // HttpBaseChannel::nsIUploadChannel
454 : //-----------------------------------------------------------------------------
455 :
456 : NS_IMETHODIMP
457 0 : HttpBaseChannel::GetUploadStream(nsIInputStream **stream)
458 : {
459 0 : NS_ENSURE_ARG_POINTER(stream);
460 0 : *stream = mUploadStream;
461 0 : NS_IF_ADDREF(*stream);
462 0 : return NS_OK;
463 : }
464 :
465 : NS_IMETHODIMP
466 379 : HttpBaseChannel::SetUploadStream(nsIInputStream *stream,
467 : const nsACString &contentType,
468 : PRInt32 contentLength)
469 : {
470 : // NOTE: for backwards compatibility and for compatibility with old style
471 : // plugins, |stream| may include headers, specifically Content-Type and
472 : // Content-Length headers. in this case, |contentType| and |contentLength|
473 : // would be unspecified. this is traditionally the case of a POST request,
474 : // and so we select POST as the request method if contentType and
475 : // contentLength are unspecified.
476 :
477 379 : if (stream) {
478 379 : if (contentType.IsEmpty()) {
479 2 : mUploadStreamHasHeaders = true;
480 2 : mRequestHead.SetMethod(nsHttp::Post); // POST request
481 : } else {
482 377 : if (contentLength < 0) {
483 : // Not really kosher to assume Available == total length of
484 : // stream, but apparently works for the streams we see here.
485 7 : stream->Available((PRUint32 *) &contentLength);
486 7 : if (contentLength < 0) {
487 0 : NS_ERROR("unable to determine content length");
488 0 : return NS_ERROR_FAILURE;
489 : }
490 : }
491 : // SetRequestHeader propagates headers to chrome if HttpChannelChild
492 754 : nsCAutoString contentLengthStr;
493 377 : contentLengthStr.AppendInt(PRInt64(contentLength));
494 377 : SetRequestHeader(NS_LITERAL_CSTRING("Content-Length"), contentLengthStr,
495 377 : false);
496 377 : SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), contentType,
497 377 : false);
498 377 : mUploadStreamHasHeaders = false;
499 377 : mRequestHead.SetMethod(nsHttp::Put); // PUT request
500 : }
501 : } else {
502 0 : mUploadStreamHasHeaders = false;
503 0 : mRequestHead.SetMethod(nsHttp::Get); // revert to GET request
504 : }
505 379 : mUploadStream = stream;
506 379 : return NS_OK;
507 : }
508 :
509 : //-----------------------------------------------------------------------------
510 : // HttpBaseChannel::nsIUploadChannel2
511 : //-----------------------------------------------------------------------------
512 :
513 : NS_IMETHODIMP
514 6 : HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream *aStream,
515 : const nsACString &aContentType,
516 : PRInt64 aContentLength,
517 : const nsACString &aMethod,
518 : bool aStreamHasHeaders)
519 : {
520 : // Ensure stream is set and method is valid
521 6 : NS_ENSURE_TRUE(aStream, NS_ERROR_FAILURE);
522 :
523 6 : if (aContentLength < 0 && !aStreamHasHeaders) {
524 : PRUint32 streamLength;
525 4 : aStream->Available(&streamLength);
526 4 : aContentLength = streamLength;
527 4 : if (aContentLength < 0) {
528 0 : NS_ERROR("unable to determine content length");
529 0 : return NS_ERROR_FAILURE;
530 : }
531 : }
532 :
533 6 : nsresult rv = SetRequestMethod(aMethod);
534 6 : NS_ENSURE_SUCCESS(rv, rv);
535 :
536 6 : if (!aStreamHasHeaders) {
537 : // SetRequestHeader propagates headers to chrome if HttpChannelChild
538 10 : nsCAutoString contentLengthStr;
539 5 : contentLengthStr.AppendInt(aContentLength);
540 5 : SetRequestHeader(NS_LITERAL_CSTRING("Content-Length"), contentLengthStr,
541 5 : false);
542 5 : SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), aContentType,
543 5 : false);
544 : }
545 :
546 6 : mUploadStreamHasHeaders = aStreamHasHeaders;
547 6 : mUploadStream = aStream;
548 6 : return NS_OK;
549 : }
550 :
551 : NS_IMETHODIMP
552 0 : HttpBaseChannel::GetUploadStreamHasHeaders(bool *hasHeaders)
553 : {
554 0 : NS_ENSURE_ARG(hasHeaders);
555 :
556 0 : *hasHeaders = mUploadStreamHasHeaders;
557 0 : return NS_OK;
558 : }
559 :
560 : //-----------------------------------------------------------------------------
561 : // HttpBaseChannel::nsIEncodedChannel
562 : //-----------------------------------------------------------------------------
563 :
564 : NS_IMETHODIMP
565 0 : HttpBaseChannel::GetApplyConversion(bool *value)
566 : {
567 0 : *value = mApplyConversion;
568 0 : return NS_OK;
569 : }
570 :
571 : NS_IMETHODIMP
572 234 : HttpBaseChannel::SetApplyConversion(bool value)
573 : {
574 234 : LOG(("HttpBaseChannel::SetApplyConversion [this=%p value=%d]\n", this, value));
575 234 : mApplyConversion = value;
576 234 : return NS_OK;
577 : }
578 :
579 : nsresult
580 3021 : HttpBaseChannel::ApplyContentConversions()
581 : {
582 3021 : if (!mResponseHead)
583 150 : return NS_OK;
584 :
585 2871 : LOG(("HttpBaseChannel::ApplyContentConversions [this=%p]\n", this));
586 :
587 2871 : if (!mApplyConversion) {
588 24 : LOG(("not applying conversion per mApplyConversion\n"));
589 24 : return NS_OK;
590 : }
591 :
592 5694 : nsCAutoString contentEncoding;
593 : char *cePtr, *val;
594 : nsresult rv;
595 :
596 2847 : rv = mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
597 2847 : if (NS_FAILED(rv) || contentEncoding.IsEmpty())
598 2844 : return NS_OK;
599 :
600 : // The encodings are listed in the order they were applied
601 : // (see rfc 2616 section 14.11), so they need to removed in reverse
602 : // order. This is accomplished because the converter chain ends up
603 : // being a stack with the last converter created being the first one
604 : // to accept the raw network data.
605 :
606 3 : cePtr = contentEncoding.BeginWriting();
607 3 : PRUint32 count = 0;
608 10 : while ((val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr))) {
609 4 : if (++count > 16) {
610 : // That's ridiculous. We only understand 2 different ones :)
611 : // but for compatibility with old code, we will just carry on without
612 : // removing the encodings
613 0 : LOG(("Too many Content-Encodings. Ignoring remainder.\n"));
614 0 : break;
615 : }
616 :
617 4 : if (gHttpHandler->IsAcceptableEncoding(val)) {
618 8 : nsCOMPtr<nsIStreamConverterService> serv;
619 4 : rv = gHttpHandler->GetStreamConverterService(getter_AddRefs(serv));
620 :
621 : // we won't fail to load the page just because we couldn't load the
622 : // stream converter service.. carry on..
623 4 : if (NS_FAILED(rv)) {
624 0 : if (val)
625 0 : LOG(("Unknown content encoding '%s', ignoring\n", val));
626 0 : continue;
627 : }
628 :
629 8 : nsCOMPtr<nsIStreamListener> converter;
630 8 : nsCAutoString from(val);
631 4 : ToLowerCase(from);
632 4 : rv = serv->AsyncConvertData(from.get(),
633 : "uncompressed",
634 : mListener,
635 : mListenerContext,
636 4 : getter_AddRefs(converter));
637 4 : if (NS_FAILED(rv)) {
638 0 : LOG(("Unexpected failure of AsyncConvertData %s\n", val));
639 0 : return rv;
640 : }
641 :
642 4 : LOG(("converter removed '%s' content-encoding\n", val));
643 8 : mListener = converter;
644 : }
645 : else {
646 0 : if (val)
647 0 : LOG(("Unknown content encoding '%s', ignoring\n", val));
648 : }
649 : }
650 :
651 3 : return NS_OK;
652 : }
653 :
654 : NS_IMETHODIMP
655 81 : HttpBaseChannel::GetContentEncodings(nsIUTF8StringEnumerator** aEncodings)
656 : {
657 81 : if (!mResponseHead) {
658 51 : *aEncodings = nsnull;
659 51 : return NS_OK;
660 : }
661 :
662 30 : const char *encoding = mResponseHead->PeekHeader(nsHttp::Content_Encoding);
663 30 : if (!encoding) {
664 30 : *aEncodings = nsnull;
665 30 : return NS_OK;
666 : }
667 0 : nsContentEncodings* enumerator = new nsContentEncodings(this, encoding);
668 0 : NS_ADDREF(*aEncodings = enumerator);
669 0 : return NS_OK;
670 : }
671 :
672 : //-----------------------------------------------------------------------------
673 : // HttpBaseChannel::nsContentEncodings <public>
674 : //-----------------------------------------------------------------------------
675 :
676 0 : HttpBaseChannel::nsContentEncodings::nsContentEncodings(nsIHttpChannel* aChannel,
677 : const char* aEncodingHeader)
678 : : mEncodingHeader(aEncodingHeader)
679 : , mChannel(aChannel)
680 0 : , mReady(false)
681 : {
682 0 : mCurEnd = aEncodingHeader + strlen(aEncodingHeader);
683 0 : mCurStart = mCurEnd;
684 0 : }
685 :
686 0 : HttpBaseChannel::nsContentEncodings::~nsContentEncodings()
687 : {
688 0 : }
689 :
690 : //-----------------------------------------------------------------------------
691 : // HttpBaseChannel::nsContentEncodings::nsISimpleEnumerator
692 : //-----------------------------------------------------------------------------
693 :
694 : NS_IMETHODIMP
695 0 : HttpBaseChannel::nsContentEncodings::HasMore(bool* aMoreEncodings)
696 : {
697 0 : if (mReady) {
698 0 : *aMoreEncodings = true;
699 0 : return NS_OK;
700 : }
701 :
702 0 : nsresult rv = PrepareForNext();
703 0 : *aMoreEncodings = NS_SUCCEEDED(rv);
704 0 : return NS_OK;
705 : }
706 :
707 : NS_IMETHODIMP
708 0 : HttpBaseChannel::nsContentEncodings::GetNext(nsACString& aNextEncoding)
709 : {
710 0 : aNextEncoding.Truncate();
711 0 : if (!mReady) {
712 0 : nsresult rv = PrepareForNext();
713 0 : if (NS_FAILED(rv)) {
714 0 : return NS_ERROR_FAILURE;
715 : }
716 : }
717 :
718 0 : const nsACString & encoding = Substring(mCurStart, mCurEnd);
719 :
720 0 : nsACString::const_iterator start, end;
721 0 : encoding.BeginReading(start);
722 0 : encoding.EndReading(end);
723 :
724 0 : bool haveType = false;
725 0 : if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("gzip"), start, end)) {
726 0 : aNextEncoding.AssignLiteral(APPLICATION_GZIP);
727 0 : haveType = true;
728 : }
729 :
730 0 : if (!haveType) {
731 0 : encoding.BeginReading(start);
732 0 : if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("compress"), start, end)) {
733 0 : aNextEncoding.AssignLiteral(APPLICATION_COMPRESS);
734 0 : haveType = true;
735 : }
736 : }
737 :
738 0 : if (!haveType) {
739 0 : encoding.BeginReading(start);
740 0 : if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("deflate"), start, end)) {
741 0 : aNextEncoding.AssignLiteral(APPLICATION_ZIP);
742 0 : haveType = true;
743 : }
744 : }
745 :
746 : // Prepare to fetch the next encoding
747 0 : mCurEnd = mCurStart;
748 0 : mReady = false;
749 :
750 0 : if (haveType)
751 0 : return NS_OK;
752 :
753 0 : NS_WARNING("Unknown encoding type");
754 0 : return NS_ERROR_FAILURE;
755 : }
756 :
757 : //-----------------------------------------------------------------------------
758 : // HttpBaseChannel::nsContentEncodings::nsISupports
759 : //-----------------------------------------------------------------------------
760 :
761 0 : NS_IMPL_ISUPPORTS1(HttpBaseChannel::nsContentEncodings, nsIUTF8StringEnumerator)
762 :
763 : //-----------------------------------------------------------------------------
764 : // HttpBaseChannel::nsContentEncodings <private>
765 : //-----------------------------------------------------------------------------
766 :
767 : nsresult
768 0 : HttpBaseChannel::nsContentEncodings::PrepareForNext(void)
769 : {
770 0 : NS_ASSERTION(mCurStart == mCurEnd, "Indeterminate state");
771 :
772 : // At this point both mCurStart and mCurEnd point to somewhere
773 : // past the end of the next thing we want to return
774 :
775 0 : while (mCurEnd != mEncodingHeader) {
776 0 : --mCurEnd;
777 0 : if (*mCurEnd != ',' && !nsCRT::IsAsciiSpace(*mCurEnd))
778 0 : break;
779 : }
780 0 : if (mCurEnd == mEncodingHeader)
781 0 : return NS_ERROR_NOT_AVAILABLE; // no more encodings
782 0 : ++mCurEnd;
783 :
784 : // At this point mCurEnd points to the first char _after_ the
785 : // header we want. Furthermore, mCurEnd - 1 != mEncodingHeader
786 :
787 0 : mCurStart = mCurEnd - 1;
788 0 : while (mCurStart != mEncodingHeader &&
789 0 : *mCurStart != ',' && !nsCRT::IsAsciiSpace(*mCurStart))
790 0 : --mCurStart;
791 0 : if (*mCurStart == ',' || nsCRT::IsAsciiSpace(*mCurStart))
792 0 : ++mCurStart; // we stopped because of a weird char, so move up one
793 :
794 : // At this point mCurStart and mCurEnd bracket the encoding string
795 : // we want. Check that it's not "identity"
796 0 : if (Substring(mCurStart, mCurEnd).Equals("identity",
797 0 : nsCaseInsensitiveCStringComparator())) {
798 0 : mCurEnd = mCurStart;
799 0 : return PrepareForNext();
800 : }
801 :
802 0 : mReady = true;
803 0 : return NS_OK;
804 : }
805 :
806 :
807 : //-----------------------------------------------------------------------------
808 : // HttpBaseChannel::nsIHttpChannel
809 : //-----------------------------------------------------------------------------
810 :
811 : NS_IMETHODIMP
812 5400 : HttpBaseChannel::GetRequestMethod(nsACString& aMethod)
813 : {
814 5400 : aMethod = mRequestHead.Method();
815 5400 : return NS_OK;
816 : }
817 :
818 : NS_IMETHODIMP
819 2266 : HttpBaseChannel::SetRequestMethod(const nsACString& aMethod)
820 : {
821 2266 : ENSURE_CALLED_BEFORE_ASYNC_OPEN();
822 :
823 4532 : const nsCString& flatMethod = PromiseFlatCString(aMethod);
824 :
825 : // Method names are restricted to valid HTTP tokens.
826 2266 : if (!nsHttp::IsValidToken(flatMethod))
827 0 : return NS_ERROR_INVALID_ARG;
828 :
829 2266 : nsHttpAtom atom = nsHttp::ResolveAtom(flatMethod.get());
830 2266 : if (!atom)
831 0 : return NS_ERROR_FAILURE;
832 :
833 2266 : mRequestHead.SetMethod(atom);
834 2266 : return NS_OK;
835 : }
836 :
837 : NS_IMETHODIMP
838 24 : HttpBaseChannel::GetReferrer(nsIURI **referrer)
839 : {
840 24 : NS_ENSURE_ARG_POINTER(referrer);
841 24 : *referrer = mReferrer;
842 24 : NS_IF_ADDREF(*referrer);
843 24 : return NS_OK;
844 : }
845 :
846 : NS_IMETHODIMP
847 36 : HttpBaseChannel::SetReferrer(nsIURI *referrer)
848 : {
849 36 : ENSURE_CALLED_BEFORE_ASYNC_OPEN();
850 :
851 : // clear existing referrer, if any
852 36 : mReferrer = nsnull;
853 36 : mRequestHead.ClearHeader(nsHttp::Referer);
854 :
855 36 : if (!referrer)
856 2 : return NS_OK;
857 :
858 : // check referrer blocking pref
859 : PRUint32 referrerLevel;
860 34 : if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI)
861 0 : referrerLevel = 1; // user action
862 : else
863 34 : referrerLevel = 2; // inline content
864 34 : if (gHttpHandler->ReferrerLevel() < referrerLevel)
865 0 : return NS_OK;
866 :
867 68 : nsCOMPtr<nsIURI> referrerGrip;
868 : nsresult rv;
869 : bool match;
870 :
871 : //
872 : // Strip off "wyciwyg://123/" from wyciwyg referrers.
873 : //
874 : // XXX this really belongs elsewhere since wyciwyg URLs aren't part of necko.
875 : // perhaps some sort of generic nsINestedURI could be used. then, if an URI
876 : // fails the whitelist test, then we could check for an inner URI and try
877 : // that instead. though, that might be too automatic.
878 : //
879 34 : rv = referrer->SchemeIs("wyciwyg", &match);
880 34 : if (NS_FAILED(rv)) return rv;
881 34 : if (match) {
882 0 : nsCAutoString path;
883 0 : rv = referrer->GetPath(path);
884 0 : if (NS_FAILED(rv)) return rv;
885 :
886 0 : PRUint32 pathLength = path.Length();
887 0 : if (pathLength <= 2) return NS_ERROR_FAILURE;
888 :
889 : // Path is of the form "//123/http://foo/bar", with a variable number of digits.
890 : // To figure out where the "real" URL starts, search path for a '/', starting at
891 : // the third character.
892 0 : PRInt32 slashIndex = path.FindChar('/', 2);
893 0 : if (slashIndex == kNotFound) return NS_ERROR_FAILURE;
894 :
895 : // Get the charset of the original URI so we can pass it to our fixed up URI.
896 0 : nsCAutoString charset;
897 0 : referrer->GetOriginCharset(charset);
898 :
899 : // Replace |referrer| with a URI without wyciwyg://123/.
900 0 : rv = NS_NewURI(getter_AddRefs(referrerGrip),
901 0 : Substring(path, slashIndex + 1, pathLength - slashIndex - 1),
902 0 : charset.get());
903 0 : if (NS_FAILED(rv)) return rv;
904 :
905 0 : referrer = referrerGrip.get();
906 : }
907 :
908 : //
909 : // block referrer if not on our white list...
910 : //
911 : static const char *const referrerWhiteList[] = {
912 : "http",
913 : "https",
914 : "ftp",
915 : "gopher",
916 : nsnull
917 : };
918 34 : match = false;
919 34 : const char *const *scheme = referrerWhiteList;
920 68 : for (; *scheme && !match; ++scheme) {
921 34 : rv = referrer->SchemeIs(*scheme, &match);
922 34 : if (NS_FAILED(rv)) return rv;
923 : }
924 34 : if (!match)
925 0 : return NS_OK; // kick out....
926 :
927 : //
928 : // Handle secure referrals.
929 : //
930 : // Support referrals from a secure server if this is a secure site
931 : // and (optionally) if the host names are the same.
932 : //
933 34 : rv = referrer->SchemeIs("https", &match);
934 34 : if (NS_FAILED(rv)) return rv;
935 34 : if (match) {
936 0 : rv = mURI->SchemeIs("https", &match);
937 0 : if (NS_FAILED(rv)) return rv;
938 0 : if (!match)
939 0 : return NS_OK;
940 :
941 0 : if (!gHttpHandler->SendSecureXSiteReferrer()) {
942 0 : nsCAutoString referrerHost;
943 0 : nsCAutoString host;
944 :
945 0 : rv = referrer->GetAsciiHost(referrerHost);
946 0 : if (NS_FAILED(rv)) return rv;
947 :
948 0 : rv = mURI->GetAsciiHost(host);
949 0 : if (NS_FAILED(rv)) return rv;
950 :
951 : // GetAsciiHost returns lowercase hostname.
952 0 : if (!referrerHost.Equals(host))
953 0 : return NS_OK;
954 : }
955 : }
956 :
957 68 : nsCOMPtr<nsIURI> clone;
958 : //
959 : // we need to clone the referrer, so we can:
960 : // (1) modify it
961 : // (2) keep a reference to it after returning from this function
962 : //
963 : // Use CloneIgnoringRef to strip away any fragment per RFC 2616 section 14.36
964 34 : rv = referrer->CloneIgnoringRef(getter_AddRefs(clone));
965 34 : if (NS_FAILED(rv)) return rv;
966 :
967 : // strip away any userpass; we don't want to be giving out passwords ;-)
968 34 : rv = clone->SetUserPass(EmptyCString());
969 34 : if (NS_FAILED(rv)) return rv;
970 :
971 68 : nsCAutoString spec;
972 34 : rv = clone->GetAsciiSpec(spec);
973 34 : if (NS_FAILED(rv)) return rv;
974 :
975 : // finally, remember the referrer URI and set the Referer header.
976 34 : mReferrer = clone;
977 34 : mRequestHead.SetHeader(nsHttp::Referer, spec);
978 34 : return NS_OK;
979 : }
980 :
981 : NS_IMETHODIMP
982 1178 : HttpBaseChannel::GetRequestHeader(const nsACString& aHeader,
983 : nsACString& aValue)
984 : {
985 : // XXX might be better to search the header list directly instead of
986 : // hitting the http atom hash table.
987 1178 : nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
988 1178 : if (!atom)
989 0 : return NS_ERROR_NOT_AVAILABLE;
990 :
991 1178 : return mRequestHead.GetHeader(atom, aValue);
992 : }
993 :
994 : NS_IMETHODIMP
995 7628 : HttpBaseChannel::SetRequestHeader(const nsACString& aHeader,
996 : const nsACString& aValue,
997 : bool aMerge)
998 : {
999 15256 : const nsCString &flatHeader = PromiseFlatCString(aHeader);
1000 15256 : const nsCString &flatValue = PromiseFlatCString(aValue);
1001 :
1002 7628 : LOG(("HttpBaseChannel::SetRequestHeader [this=%p header=\"%s\" value=\"%s\" merge=%u]\n",
1003 : this, flatHeader.get(), flatValue.get(), aMerge));
1004 :
1005 : // Header names are restricted to valid HTTP tokens.
1006 7628 : if (!nsHttp::IsValidToken(flatHeader))
1007 2 : return NS_ERROR_INVALID_ARG;
1008 :
1009 : // Header values MUST NOT contain line-breaks. RFC 2616 technically
1010 : // permits CTL characters, including CR and LF, in header values provided
1011 : // they are quoted. However, this can lead to problems if servers do not
1012 : // interpret quoted strings properly. Disallowing CR and LF here seems
1013 : // reasonable and keeps things simple. We also disallow a null byte.
1014 15251 : if (flatValue.FindCharInSet("\r\n") != kNotFound ||
1015 7625 : flatValue.Length() != strlen(flatValue.get()))
1016 2 : return NS_ERROR_INVALID_ARG;
1017 :
1018 7624 : nsHttpAtom atom = nsHttp::ResolveAtom(flatHeader.get());
1019 7624 : if (!atom) {
1020 0 : NS_WARNING("failed to resolve atom");
1021 0 : return NS_ERROR_NOT_AVAILABLE;
1022 : }
1023 :
1024 7624 : return mRequestHead.SetHeader(atom, flatValue, aMerge);
1025 : }
1026 :
1027 : NS_IMETHODIMP
1028 0 : HttpBaseChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *visitor)
1029 : {
1030 0 : return mRequestHead.Headers().VisitHeaders(visitor);
1031 : }
1032 :
1033 : NS_IMETHODIMP
1034 7605 : HttpBaseChannel::GetResponseHeader(const nsACString &header, nsACString &value)
1035 : {
1036 7605 : if (!mResponseHead)
1037 22 : return NS_ERROR_NOT_AVAILABLE;
1038 :
1039 7583 : nsHttpAtom atom = nsHttp::ResolveAtom(header);
1040 7583 : if (!atom)
1041 0 : return NS_ERROR_NOT_AVAILABLE;
1042 :
1043 7583 : return mResponseHead->GetHeader(atom, value);
1044 : }
1045 :
1046 : NS_IMETHODIMP
1047 1 : HttpBaseChannel::SetResponseHeader(const nsACString& header,
1048 : const nsACString& value,
1049 : bool merge)
1050 : {
1051 1 : LOG(("HttpBaseChannel::SetResponseHeader [this=%p header=\"%s\" value=\"%s\" merge=%u]\n",
1052 : this, PromiseFlatCString(header).get(), PromiseFlatCString(value).get(), merge));
1053 :
1054 1 : if (!mResponseHead)
1055 0 : return NS_ERROR_NOT_AVAILABLE;
1056 :
1057 1 : nsHttpAtom atom = nsHttp::ResolveAtom(header);
1058 1 : if (!atom)
1059 0 : return NS_ERROR_NOT_AVAILABLE;
1060 :
1061 : // these response headers must not be changed
1062 5 : if (atom == nsHttp::Content_Type ||
1063 1 : atom == nsHttp::Content_Length ||
1064 1 : atom == nsHttp::Content_Encoding ||
1065 1 : atom == nsHttp::Trailer ||
1066 1 : atom == nsHttp::Transfer_Encoding)
1067 0 : return NS_ERROR_ILLEGAL_VALUE;
1068 :
1069 1 : mResponseHeadersModified = true;
1070 :
1071 1 : return mResponseHead->SetHeader(atom, value, merge);
1072 : }
1073 :
1074 : NS_IMETHODIMP
1075 1387 : HttpBaseChannel::VisitResponseHeaders(nsIHttpHeaderVisitor *visitor)
1076 : {
1077 1387 : if (!mResponseHead)
1078 1 : return NS_ERROR_NOT_AVAILABLE;
1079 1386 : return mResponseHead->Headers().VisitHeaders(visitor);
1080 : }
1081 :
1082 : NS_IMETHODIMP
1083 0 : HttpBaseChannel::GetAllowPipelining(bool *value)
1084 : {
1085 0 : NS_ENSURE_ARG_POINTER(value);
1086 0 : *value = mAllowPipelining;
1087 0 : return NS_OK;
1088 : }
1089 :
1090 : NS_IMETHODIMP
1091 153 : HttpBaseChannel::SetAllowPipelining(bool value)
1092 : {
1093 153 : ENSURE_CALLED_BEFORE_ASYNC_OPEN();
1094 :
1095 153 : mAllowPipelining = value;
1096 153 : return NS_OK;
1097 : }
1098 :
1099 : NS_IMETHODIMP
1100 0 : HttpBaseChannel::GetRedirectionLimit(PRUint32 *value)
1101 : {
1102 0 : NS_ENSURE_ARG_POINTER(value);
1103 0 : *value = mRedirectionLimit;
1104 0 : return NS_OK;
1105 : }
1106 :
1107 : NS_IMETHODIMP
1108 153 : HttpBaseChannel::SetRedirectionLimit(PRUint32 value)
1109 : {
1110 153 : ENSURE_CALLED_BEFORE_ASYNC_OPEN();
1111 :
1112 153 : mRedirectionLimit = NS_MIN<PRUint32>(value, 0xff);
1113 153 : return NS_OK;
1114 : }
1115 :
1116 : NS_IMETHODIMP
1117 8 : HttpBaseChannel::IsNoStoreResponse(bool *value)
1118 : {
1119 8 : if (!mResponseHead)
1120 0 : return NS_ERROR_NOT_AVAILABLE;
1121 8 : *value = mResponseHead->NoStore();
1122 8 : return NS_OK;
1123 : }
1124 :
1125 : NS_IMETHODIMP
1126 4 : HttpBaseChannel::IsNoCacheResponse(bool *value)
1127 : {
1128 4 : if (!mResponseHead)
1129 0 : return NS_ERROR_NOT_AVAILABLE;
1130 4 : *value = mResponseHead->NoCache();
1131 4 : if (!*value)
1132 4 : *value = mResponseHead->ExpiresInPast();
1133 4 : return NS_OK;
1134 : }
1135 :
1136 : NS_IMETHODIMP
1137 2623 : HttpBaseChannel::GetResponseStatus(PRUint32 *aValue)
1138 : {
1139 2623 : if (!mResponseHead)
1140 73 : return NS_ERROR_NOT_AVAILABLE;
1141 2550 : *aValue = mResponseHead->Status();
1142 2550 : return NS_OK;
1143 : }
1144 :
1145 : NS_IMETHODIMP
1146 99 : HttpBaseChannel::GetResponseStatusText(nsACString& aValue)
1147 : {
1148 99 : if (!mResponseHead)
1149 0 : return NS_ERROR_NOT_AVAILABLE;
1150 99 : aValue = mResponseHead->StatusText();
1151 99 : return NS_OK;
1152 : }
1153 :
1154 : NS_IMETHODIMP
1155 6333 : HttpBaseChannel::GetRequestSucceeded(bool *aValue)
1156 : {
1157 6333 : if (!mResponseHead)
1158 120 : return NS_ERROR_NOT_AVAILABLE;
1159 6213 : PRUint32 status = mResponseHead->Status();
1160 6213 : *aValue = (status / 100 == 2);
1161 6213 : return NS_OK;
1162 : }
1163 :
1164 : //-----------------------------------------------------------------------------
1165 : // HttpBaseChannel::nsIHttpChannelInternal
1166 : //-----------------------------------------------------------------------------
1167 :
1168 : NS_IMETHODIMP
1169 0 : HttpBaseChannel::GetDocumentURI(nsIURI **aDocumentURI)
1170 : {
1171 0 : NS_ENSURE_ARG_POINTER(aDocumentURI);
1172 0 : *aDocumentURI = mDocumentURI;
1173 0 : NS_IF_ADDREF(*aDocumentURI);
1174 0 : return NS_OK;
1175 : }
1176 :
1177 : NS_IMETHODIMP
1178 155 : HttpBaseChannel::SetDocumentURI(nsIURI *aDocumentURI)
1179 : {
1180 155 : ENSURE_CALLED_BEFORE_ASYNC_OPEN();
1181 :
1182 155 : mDocumentURI = aDocumentURI;
1183 155 : return NS_OK;
1184 : }
1185 :
1186 : NS_IMETHODIMP
1187 2 : HttpBaseChannel::GetRequestVersion(PRUint32 *major, PRUint32 *minor)
1188 : {
1189 2 : nsHttpVersion version = mRequestHead.Version();
1190 :
1191 2 : if (major) { *major = version / 10; }
1192 2 : if (minor) { *minor = version % 10; }
1193 :
1194 2 : return NS_OK;
1195 : }
1196 :
1197 : NS_IMETHODIMP
1198 13 : HttpBaseChannel::GetResponseVersion(PRUint32 *major, PRUint32 *minor)
1199 : {
1200 13 : if (!mResponseHead)
1201 : {
1202 0 : *major = *minor = 0; // we should at least be kind about it
1203 0 : return NS_ERROR_NOT_AVAILABLE;
1204 : }
1205 :
1206 13 : nsHttpVersion version = mResponseHead->Version();
1207 :
1208 13 : if (major) { *major = version / 10; }
1209 13 : if (minor) { *minor = version % 10; }
1210 :
1211 13 : return NS_OK;
1212 : }
1213 :
1214 : NS_IMETHODIMP
1215 2819 : HttpBaseChannel::SetCookie(const char *aCookieHeader)
1216 : {
1217 2819 : if (mLoadFlags & LOAD_ANONYMOUS)
1218 1 : return NS_OK;
1219 :
1220 : // empty header isn't an error
1221 2818 : if (!(aCookieHeader && *aCookieHeader))
1222 2816 : return NS_OK;
1223 :
1224 2 : nsICookieService *cs = gHttpHandler->GetCookieService();
1225 2 : NS_ENSURE_TRUE(cs, NS_ERROR_FAILURE);
1226 :
1227 : return cs->SetCookieStringFromHttp(mURI, nsnull, nsnull, aCookieHeader,
1228 : mResponseHead->PeekHeader(nsHttp::Date),
1229 2 : this);
1230 : }
1231 :
1232 : NS_IMETHODIMP
1233 21 : HttpBaseChannel::GetForceAllowThirdPartyCookie(bool *aForce)
1234 : {
1235 21 : *aForce = mForceAllowThirdPartyCookie;
1236 21 : return NS_OK;
1237 : }
1238 :
1239 : NS_IMETHODIMP
1240 409 : HttpBaseChannel::SetForceAllowThirdPartyCookie(bool aForce)
1241 : {
1242 409 : ENSURE_CALLED_BEFORE_ASYNC_OPEN();
1243 :
1244 409 : mForceAllowThirdPartyCookie = aForce;
1245 409 : return NS_OK;
1246 : }
1247 :
1248 : NS_IMETHODIMP
1249 526 : HttpBaseChannel::GetCanceled(bool *aCanceled)
1250 : {
1251 526 : *aCanceled = mCanceled;
1252 526 : return NS_OK;
1253 : }
1254 :
1255 : NS_IMETHODIMP
1256 0 : HttpBaseChannel::GetChannelIsForDownload(bool *aChannelIsForDownload)
1257 : {
1258 0 : *aChannelIsForDownload = mChannelIsForDownload;
1259 0 : return NS_OK;
1260 : }
1261 :
1262 : NS_IMETHODIMP
1263 6 : HttpBaseChannel::SetChannelIsForDownload(bool aChannelIsForDownload)
1264 : {
1265 6 : mChannelIsForDownload = aChannelIsForDownload;
1266 6 : return NS_OK;
1267 : }
1268 :
1269 : NS_IMETHODIMP
1270 11 : HttpBaseChannel::SetCacheKeysRedirectChain(nsTArray<nsCString> *cacheKeys)
1271 : {
1272 11 : mRedirectedCachekeys = cacheKeys;
1273 11 : return NS_OK;
1274 : }
1275 :
1276 : NS_IMETHODIMP
1277 0 : HttpBaseChannel::GetLocalAddress(nsACString& addr)
1278 : {
1279 0 : if (mSelfAddr.raw.family == PR_AF_UNSPEC)
1280 0 : return NS_ERROR_NOT_AVAILABLE;
1281 :
1282 0 : addr.SetCapacity(64);
1283 0 : PR_NetAddrToString(&mSelfAddr, addr.BeginWriting(), 64);
1284 0 : addr.SetLength(strlen(addr.BeginReading()));
1285 :
1286 0 : return NS_OK;
1287 : }
1288 :
1289 : NS_IMETHODIMP
1290 0 : HttpBaseChannel::GetLocalPort(PRInt32* port)
1291 : {
1292 0 : NS_ENSURE_ARG_POINTER(port);
1293 :
1294 0 : if (mSelfAddr.raw.family == PR_AF_INET) {
1295 0 : *port = (PRInt32)PR_ntohs(mSelfAddr.inet.port);
1296 : }
1297 0 : else if (mSelfAddr.raw.family == PR_AF_INET6) {
1298 0 : *port = (PRInt32)PR_ntohs(mSelfAddr.ipv6.port);
1299 : }
1300 : else
1301 0 : return NS_ERROR_NOT_AVAILABLE;
1302 :
1303 0 : return NS_OK;
1304 : }
1305 :
1306 : NS_IMETHODIMP
1307 0 : HttpBaseChannel::GetRemoteAddress(nsACString& addr)
1308 : {
1309 0 : if (mPeerAddr.raw.family == PR_AF_UNSPEC)
1310 0 : return NS_ERROR_NOT_AVAILABLE;
1311 :
1312 0 : addr.SetCapacity(64);
1313 0 : PR_NetAddrToString(&mPeerAddr, addr.BeginWriting(), 64);
1314 0 : addr.SetLength(strlen(addr.BeginReading()));
1315 :
1316 0 : return NS_OK;
1317 : }
1318 :
1319 : NS_IMETHODIMP
1320 0 : HttpBaseChannel::GetRemotePort(PRInt32* port)
1321 : {
1322 0 : NS_ENSURE_ARG_POINTER(port);
1323 :
1324 0 : if (mPeerAddr.raw.family == PR_AF_INET) {
1325 0 : *port = (PRInt32)PR_ntohs(mPeerAddr.inet.port);
1326 : }
1327 0 : else if (mPeerAddr.raw.family == PR_AF_INET6) {
1328 0 : *port = (PRInt32)PR_ntohs(mPeerAddr.ipv6.port);
1329 : }
1330 : else
1331 0 : return NS_ERROR_NOT_AVAILABLE;
1332 :
1333 0 : return NS_OK;
1334 : }
1335 :
1336 : NS_IMETHODIMP
1337 0 : HttpBaseChannel::HTTPUpgrade(const nsACString &aProtocolName,
1338 : nsIHttpUpgradeListener *aListener)
1339 : {
1340 0 : NS_ENSURE_ARG(!aProtocolName.IsEmpty());
1341 0 : NS_ENSURE_ARG_POINTER(aListener);
1342 :
1343 0 : mUpgradeProtocol = aProtocolName;
1344 0 : mUpgradeProtocolCallback = aListener;
1345 0 : return NS_OK;
1346 : }
1347 :
1348 : NS_IMETHODIMP
1349 0 : HttpBaseChannel::GetAllowSpdy(bool *aAllowSpdy)
1350 : {
1351 0 : NS_ENSURE_ARG_POINTER(aAllowSpdy);
1352 :
1353 0 : *aAllowSpdy = mAllowSpdy;
1354 0 : return NS_OK;
1355 : }
1356 :
1357 : NS_IMETHODIMP
1358 153 : HttpBaseChannel::SetAllowSpdy(bool aAllowSpdy)
1359 : {
1360 153 : mAllowSpdy = aAllowSpdy;
1361 153 : return NS_OK;
1362 : }
1363 :
1364 : //-----------------------------------------------------------------------------
1365 : // HttpBaseChannel::nsISupportsPriority
1366 : //-----------------------------------------------------------------------------
1367 :
1368 : NS_IMETHODIMP
1369 0 : HttpBaseChannel::GetPriority(PRInt32 *value)
1370 : {
1371 0 : *value = mPriority;
1372 0 : return NS_OK;
1373 : }
1374 :
1375 : NS_IMETHODIMP
1376 2 : HttpBaseChannel::AdjustPriority(PRInt32 delta)
1377 : {
1378 2 : return SetPriority(mPriority + delta);
1379 : }
1380 :
1381 : //-----------------------------------------------------------------------------
1382 : // HttpBaseChannel::nsIResumableChannel
1383 : //-----------------------------------------------------------------------------
1384 :
1385 : NS_IMETHODIMP
1386 38 : HttpBaseChannel::GetEntityID(nsACString& aEntityID)
1387 : {
1388 : // Don't return an entity ID for Non-GET requests which require
1389 : // additional data
1390 38 : if (mRequestHead.Method() != nsHttp::Get) {
1391 0 : return NS_ERROR_NOT_RESUMABLE;
1392 : }
1393 :
1394 38 : PRUint64 size = LL_MAXUINT;
1395 76 : nsCAutoString etag, lastmod;
1396 38 : if (mResponseHead) {
1397 : // Don't return an entity if the server sent the following header:
1398 : // Accept-Ranges: none
1399 : // Not sending the Accept-Ranges header means we can still try
1400 : // sending range requests.
1401 : const char* acceptRanges =
1402 38 : mResponseHead->PeekHeader(nsHttp::Accept_Ranges);
1403 51 : if (acceptRanges &&
1404 13 : !nsHttp::FindToken(acceptRanges, "bytes", HTTP_HEADER_VALUE_SEPS)) {
1405 5 : return NS_ERROR_NOT_RESUMABLE;
1406 : }
1407 :
1408 33 : size = mResponseHead->TotalEntitySize();
1409 33 : const char* cLastMod = mResponseHead->PeekHeader(nsHttp::Last_Modified);
1410 33 : if (cLastMod)
1411 7 : lastmod = cLastMod;
1412 33 : const char* cEtag = mResponseHead->PeekHeader(nsHttp::ETag);
1413 33 : if (cEtag)
1414 0 : etag = cEtag;
1415 : }
1416 66 : nsCString entityID;
1417 33 : NS_EscapeURL(etag.BeginReading(), etag.Length(), esc_AlwaysCopy |
1418 33 : esc_FileBaseName | esc_Forced, entityID);
1419 33 : entityID.Append('/');
1420 33 : entityID.AppendInt(PRInt64(size));
1421 33 : entityID.Append('/');
1422 33 : entityID.Append(lastmod);
1423 : // NOTE: Appending lastmod as the last part avoids having to escape it
1424 :
1425 33 : aEntityID = entityID;
1426 :
1427 33 : return NS_OK;
1428 : }
1429 :
1430 : //-----------------------------------------------------------------------------
1431 : // nsStreamListenerWrapper <private>
1432 : //-----------------------------------------------------------------------------
1433 :
1434 : // Wrapper class to make replacement of nsHttpChannel's listener
1435 : // from JavaScript possible. It is workaround for bug 433711.
1436 : class nsStreamListenerWrapper : public nsIStreamListener
1437 : {
1438 : public:
1439 : nsStreamListenerWrapper(nsIStreamListener *listener);
1440 :
1441 : NS_DECL_ISUPPORTS
1442 2 : NS_FORWARD_NSIREQUESTOBSERVER(mListener->)
1443 1 : NS_FORWARD_NSISTREAMLISTENER(mListener->)
1444 :
1445 : private:
1446 1 : ~nsStreamListenerWrapper() {}
1447 : nsCOMPtr<nsIStreamListener> mListener;
1448 : };
1449 :
1450 1 : nsStreamListenerWrapper::nsStreamListenerWrapper(nsIStreamListener *listener)
1451 1 : : mListener(listener)
1452 : {
1453 1 : NS_ASSERTION(mListener, "no stream listener specified");
1454 1 : }
1455 :
1456 26 : NS_IMPL_ISUPPORTS2(nsStreamListenerWrapper,
1457 : nsIStreamListener,
1458 : nsIRequestObserver)
1459 :
1460 : //-----------------------------------------------------------------------------
1461 : // nsHttpChannel::nsITraceableChannel
1462 : //-----------------------------------------------------------------------------
1463 :
1464 : NS_IMETHODIMP
1465 2 : HttpBaseChannel::SetNewListener(nsIStreamListener *aListener, nsIStreamListener **_retval)
1466 : {
1467 2 : if (!mTracingEnabled)
1468 1 : return NS_ERROR_FAILURE;
1469 :
1470 1 : NS_ENSURE_ARG_POINTER(aListener);
1471 :
1472 3 : nsCOMPtr<nsIStreamListener> wrapper = new nsStreamListenerWrapper(mListener);
1473 :
1474 1 : wrapper.forget(_retval);
1475 1 : mListener = aListener;
1476 1 : return NS_OK;
1477 : }
1478 :
1479 : //-----------------------------------------------------------------------------
1480 : // HttpBaseChannel helpers
1481 : //-----------------------------------------------------------------------------
1482 :
1483 : void
1484 42 : HttpBaseChannel::DoNotifyListener()
1485 : {
1486 : // Make sure mIsPending is set to false. At this moment we are done from
1487 : // the point of view of our consumer and we have to report our self
1488 : // as not-pending.
1489 42 : if (mListener) {
1490 42 : mListener->OnStartRequest(this, mListenerContext);
1491 42 : mIsPending = false;
1492 42 : mListener->OnStopRequest(this, mListenerContext, mStatus);
1493 42 : mListener = 0;
1494 42 : mListenerContext = 0;
1495 : } else {
1496 0 : mIsPending = false;
1497 : }
1498 : // We have to make sure to drop the reference to the callbacks too
1499 42 : mCallbacks = nsnull;
1500 42 : mProgressSink = nsnull;
1501 :
1502 42 : DoNotifyListenerCleanup();
1503 42 : }
1504 :
1505 : void
1506 3506 : HttpBaseChannel::AddCookiesToRequest()
1507 : {
1508 3506 : if (mLoadFlags & LOAD_ANONYMOUS) {
1509 1 : return;
1510 : }
1511 :
1512 : bool useCookieService =
1513 3505 : (XRE_GetProcessType() == GeckoProcessType_Default);
1514 7010 : nsXPIDLCString cookie;
1515 3505 : if (useCookieService) {
1516 3505 : nsICookieService *cs = gHttpHandler->GetCookieService();
1517 3505 : if (cs) {
1518 : cs->GetCookieStringFromHttp(mURI,
1519 : nsnull,
1520 3505 : this, getter_Copies(cookie));
1521 : }
1522 :
1523 3505 : if (cookie.IsEmpty()) {
1524 3498 : cookie = mUserSetCookieHeader;
1525 : }
1526 7 : else if (!mUserSetCookieHeader.IsEmpty()) {
1527 1 : cookie.Append(NS_LITERAL_CSTRING("; ") + mUserSetCookieHeader);
1528 : }
1529 : }
1530 : else {
1531 0 : cookie = mUserSetCookieHeader;
1532 : }
1533 :
1534 : // If we are in the child process, we want the parent seeing any
1535 : // cookie headers that might have been set by SetRequestHeader()
1536 3505 : SetRequestHeader(nsDependentCString(nsHttp::Cookie), cookie, false);
1537 : }
1538 :
1539 : static PLDHashOperator
1540 0 : CopyProperties(const nsAString& aKey, nsIVariant *aData, void *aClosure)
1541 : {
1542 : nsIWritablePropertyBag* bag = static_cast<nsIWritablePropertyBag*>
1543 0 : (aClosure);
1544 0 : bag->SetProperty(aKey, aData);
1545 0 : return PL_DHASH_NEXT;
1546 : }
1547 :
1548 : // Return whether upon a redirect code of httpStatus for method, the
1549 : // request method should be rewritten to GET.
1550 : //
1551 : bool
1552 151 : HttpBaseChannel::ShouldRewriteRedirectToGET(PRUint32 httpStatus,
1553 : nsHttpAtom method)
1554 : {
1555 : // for 301 and 302, only rewrite POST
1556 151 : if (httpStatus == 301 || httpStatus == 302)
1557 123 : return method == nsHttp::Post;
1558 :
1559 : // rewrite for 303 unless it was HEAD
1560 28 : if (httpStatus == 303)
1561 12 : return method != nsHttp::Head;
1562 :
1563 : // otherwise, such as for 307, do not rewrite
1564 16 : return false;
1565 : }
1566 :
1567 : // Return whether the specified method is safe as per RFC 2616, Section 9.1.1.
1568 : bool
1569 135 : HttpBaseChannel::IsSafeMethod(nsHttpAtom method)
1570 : {
1571 : // This code will need to be extended for new safe methods, otherwise
1572 : // they'll default to "not safe".
1573 135 : return method == nsHttp::Get ||
1574 30 : method == nsHttp::Head ||
1575 22 : method == nsHttp::Options ||
1576 22 : method == nsHttp::Propfind ||
1577 16 : method == nsHttp::Report ||
1578 16 : method == nsHttp::Search ||
1579 241 : method == nsHttp::Trace;
1580 : }
1581 :
1582 : nsresult
1583 155 : HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI,
1584 : nsIChannel *newChannel,
1585 : bool preserveMethod,
1586 : bool forProxy)
1587 : {
1588 155 : LOG(("HttpBaseChannel::SetupReplacementChannel "
1589 : "[this=%p newChannel=%p preserveMethod=%d forProxy=%d]",
1590 : this, newChannel, preserveMethod, forProxy));
1591 155 : PRUint32 newLoadFlags = mLoadFlags | LOAD_REPLACE;
1592 : // if the original channel was using SSL and this channel is not using
1593 : // SSL, then no need to inhibit persistent caching. however, if the
1594 : // original channel was not using SSL and has INHIBIT_PERSISTENT_CACHING
1595 : // set, then allow the flag to apply to the redirected channel as well.
1596 : // since we force set INHIBIT_PERSISTENT_CACHING on all HTTPS channels,
1597 : // we only need to check if the original channel was using SSL.
1598 155 : if (mConnectionInfo->UsingSSL())
1599 0 : newLoadFlags &= ~INHIBIT_PERSISTENT_CACHING;
1600 :
1601 : // Do not pass along LOAD_CHECK_OFFLINE_CACHE
1602 155 : newLoadFlags &= ~nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE;
1603 :
1604 155 : newChannel->SetLoadGroup(mLoadGroup);
1605 155 : newChannel->SetNotificationCallbacks(mCallbacks);
1606 155 : newChannel->SetLoadFlags(newLoadFlags);
1607 :
1608 310 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
1609 155 : if (!httpChannel)
1610 2 : return NS_OK; // no other options to set
1611 :
1612 153 : if (preserveMethod) {
1613 : nsCOMPtr<nsIUploadChannel> uploadChannel =
1614 274 : do_QueryInterface(httpChannel);
1615 : nsCOMPtr<nsIUploadChannel2> uploadChannel2 =
1616 274 : do_QueryInterface(httpChannel);
1617 137 : if (mUploadStream && (uploadChannel2 || uploadChannel)) {
1618 : // rewind upload stream
1619 4 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
1620 2 : if (seekable)
1621 2 : seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1622 :
1623 : // replicate original call to SetUploadStream...
1624 2 : if (uploadChannel2) {
1625 2 : const char *ctype = mRequestHead.PeekHeader(nsHttp::Content_Type);
1626 2 : if (!ctype)
1627 1 : ctype = "";
1628 2 : const char *clen = mRequestHead.PeekHeader(nsHttp::Content_Length);
1629 2 : PRInt64 len = clen ? nsCRT::atoll(clen) : -1;
1630 2 : uploadChannel2->ExplicitSetUploadStream(
1631 2 : mUploadStream, nsDependentCString(ctype), len,
1632 4 : nsDependentCString(mRequestHead.Method()),
1633 4 : mUploadStreamHasHeaders);
1634 : } else {
1635 0 : if (mUploadStreamHasHeaders) {
1636 0 : uploadChannel->SetUploadStream(mUploadStream, EmptyCString(),
1637 0 : -1);
1638 : } else {
1639 : const char *ctype =
1640 0 : mRequestHead.PeekHeader(nsHttp::Content_Type);
1641 : const char *clen =
1642 0 : mRequestHead.PeekHeader(nsHttp::Content_Length);
1643 0 : if (!ctype) {
1644 0 : ctype = "application/octet-stream";
1645 : }
1646 0 : if (clen) {
1647 0 : uploadChannel->SetUploadStream(mUploadStream,
1648 0 : nsDependentCString(ctype),
1649 0 : atoi(clen));
1650 : }
1651 : }
1652 : }
1653 : }
1654 : // since preserveMethod is true, we need to ensure that the appropriate
1655 : // request method gets set on the channel, regardless of whether or not
1656 : // we set the upload stream above. This means SetRequestMethod() will
1657 : // be called twice if ExplicitSetUploadStream() gets called above.
1658 :
1659 137 : httpChannel->SetRequestMethod(nsDependentCString(mRequestHead.Method()));
1660 : }
1661 : // convey the referrer if one was used for this channel to the next one
1662 153 : if (mReferrer)
1663 0 : httpChannel->SetReferrer(mReferrer);
1664 : // convey the mAllowPipelining flag
1665 153 : httpChannel->SetAllowPipelining(mAllowPipelining);
1666 : // convey the new redirection limit
1667 153 : httpChannel->SetRedirectionLimit(mRedirectionLimit - 1);
1668 :
1669 306 : nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel);
1670 153 : if (httpInternal) {
1671 : // convey the mForceAllowThirdPartyCookie flag
1672 153 : httpInternal->SetForceAllowThirdPartyCookie(mForceAllowThirdPartyCookie);
1673 : // convey the spdy flag
1674 153 : httpInternal->SetAllowSpdy(mAllowSpdy);
1675 :
1676 : // update the DocumentURI indicator since we are being redirected.
1677 : // if this was a top-level document channel, then the new channel
1678 : // should have its mDocumentURI point to newURI; otherwise, we
1679 : // just need to pass along our mDocumentURI to the new channel.
1680 153 : if (newURI && (mURI == mDocumentURI))
1681 0 : httpInternal->SetDocumentURI(newURI);
1682 : else
1683 153 : httpInternal->SetDocumentURI(mDocumentURI);
1684 :
1685 : // if there is a chain of keys for redirect-responses we transfer it to
1686 : // the new channel (see bug #561276)
1687 153 : if (mRedirectedCachekeys) {
1688 11 : LOG(("HttpBaseChannel::SetupReplacementChannel "
1689 : "[this=%p] transferring chain of redirect cache-keys", this));
1690 11 : httpInternal->SetCacheKeysRedirectChain(mRedirectedCachekeys);
1691 11 : mRedirectedCachekeys = nsnull;
1692 : }
1693 : }
1694 :
1695 : // transfer application cache information
1696 : nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
1697 306 : do_QueryInterface(newChannel);
1698 153 : if (appCacheChannel) {
1699 153 : appCacheChannel->SetApplicationCache(mApplicationCache);
1700 153 : appCacheChannel->SetInheritApplicationCache(mInheritApplicationCache);
1701 : // We purposely avoid transfering mChooseApplicationCache.
1702 : }
1703 :
1704 : // transfer any properties
1705 306 : nsCOMPtr<nsIWritablePropertyBag> bag(do_QueryInterface(newChannel));
1706 153 : if (bag)
1707 153 : mPropertyHash.EnumerateRead(CopyProperties, bag.get());
1708 :
1709 : // transfer timed channel enabled status
1710 306 : nsCOMPtr<nsITimedChannel> timed(do_QueryInterface(newChannel));
1711 153 : if (timed)
1712 153 : timed->SetTimingEnabled(mTimingEnabled);
1713 :
1714 153 : if (forProxy) {
1715 : // Transfer all the headers from the previous channel
1716 : // this is needed for any headers that are not covered by the code above
1717 : // or have been set separately. e.g. manually setting Referer without
1718 : // setting up mReferrer
1719 12 : PRUint32 count = mRequestHead.Headers().Count();
1720 87 : for (PRUint32 i = 0; i < count; ++i) {
1721 : nsHttpAtom header;
1722 75 : const char *value = mRequestHead.Headers().PeekHeaderAt(i, header);
1723 :
1724 150 : httpChannel->SetRequestHeader(nsDependentCString(header),
1725 150 : nsDependentCString(value), false);
1726 : }
1727 : }
1728 :
1729 153 : return NS_OK;
1730 : }
1731 :
1732 : //------------------------------------------------------------------------------
1733 :
1734 : } // namespace net
1735 : } // namespace mozilla
1736 :
|