|
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 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
5 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
7 |
|
8 // HttpLog.h should generally be included first |
|
9 #include "HttpLog.h" |
|
10 |
|
11 #include "mozilla/net/HttpBaseChannel.h" |
|
12 |
|
13 #include "nsHttpHandler.h" |
|
14 #include "nsMimeTypes.h" |
|
15 #include "nsNetUtil.h" |
|
16 |
|
17 #include "nsICachingChannel.h" |
|
18 #include "nsIPrincipal.h" |
|
19 #include "nsISeekableStream.h" |
|
20 #include "nsITimedChannel.h" |
|
21 #include "nsIEncodedChannel.h" |
|
22 #include "nsIApplicationCacheChannel.h" |
|
23 #include "nsEscape.h" |
|
24 #include "nsStreamListenerWrapper.h" |
|
25 #include "nsISecurityConsoleMessage.h" |
|
26 #include "nsURLHelper.h" |
|
27 #include "nsICookieService.h" |
|
28 #include "nsIStreamConverterService.h" |
|
29 #include "nsCRT.h" |
|
30 #include "nsContentUtils.h" |
|
31 #include "nsIScriptSecurityManager.h" |
|
32 #include "nsIObserverService.h" |
|
33 |
|
34 #include <algorithm> |
|
35 |
|
36 namespace mozilla { |
|
37 namespace net { |
|
38 |
|
39 HttpBaseChannel::HttpBaseChannel() |
|
40 : mStartPos(UINT64_MAX) |
|
41 , mStatus(NS_OK) |
|
42 , mLoadFlags(LOAD_NORMAL) |
|
43 , mCaps(0) |
|
44 , mPriority(PRIORITY_NORMAL) |
|
45 , mRedirectionLimit(gHttpHandler->RedirectionLimit()) |
|
46 , mApplyConversion(true) |
|
47 , mCanceled(false) |
|
48 , mIsPending(false) |
|
49 , mWasOpened(false) |
|
50 , mRequestObserversCalled(false) |
|
51 , mResponseHeadersModified(false) |
|
52 , mAllowPipelining(true) |
|
53 , mForceAllowThirdPartyCookie(false) |
|
54 , mUploadStreamHasHeaders(false) |
|
55 , mInheritApplicationCache(true) |
|
56 , mChooseApplicationCache(false) |
|
57 , mLoadedFromApplicationCache(false) |
|
58 , mChannelIsForDownload(false) |
|
59 , mTracingEnabled(true) |
|
60 , mTimingEnabled(false) |
|
61 , mAllowSpdy(true) |
|
62 , mLoadAsBlocking(false) |
|
63 , mLoadUnblocked(false) |
|
64 , mResponseTimeoutEnabled(true) |
|
65 , mAllRedirectsSameOrigin(true) |
|
66 , mSuspendCount(0) |
|
67 , mProxyResolveFlags(0) |
|
68 , mContentDispositionHint(UINT32_MAX) |
|
69 , mHttpHandler(gHttpHandler) |
|
70 , mRedirectCount(0) |
|
71 , mProxyURI(nullptr) |
|
72 { |
|
73 LOG(("Creating HttpBaseChannel @%x\n", this)); |
|
74 |
|
75 // Subfields of unions cannot be targeted in an initializer list |
|
76 mSelfAddr.raw.family = PR_AF_UNSPEC; |
|
77 mPeerAddr.raw.family = PR_AF_UNSPEC; |
|
78 } |
|
79 |
|
80 HttpBaseChannel::~HttpBaseChannel() |
|
81 { |
|
82 LOG(("Destroying HttpBaseChannel @%x\n", this)); |
|
83 |
|
84 // Make sure we don't leak |
|
85 CleanRedirectCacheChainIfNecessary(); |
|
86 } |
|
87 |
|
88 nsresult |
|
89 HttpBaseChannel::Init(nsIURI *aURI, |
|
90 uint32_t aCaps, |
|
91 nsProxyInfo *aProxyInfo, |
|
92 uint32_t aProxyResolveFlags, |
|
93 nsIURI *aProxyURI) |
|
94 { |
|
95 LOG(("HttpBaseChannel::Init [this=%p]\n", this)); |
|
96 |
|
97 NS_PRECONDITION(aURI, "null uri"); |
|
98 |
|
99 mURI = aURI; |
|
100 mOriginalURI = aURI; |
|
101 mDocumentURI = nullptr; |
|
102 mCaps = aCaps; |
|
103 mProxyResolveFlags = aProxyResolveFlags; |
|
104 mProxyURI = aProxyURI; |
|
105 |
|
106 // Construct connection info object |
|
107 nsAutoCString host; |
|
108 int32_t port = -1; |
|
109 bool usingSSL = false; |
|
110 |
|
111 nsresult rv = mURI->SchemeIs("https", &usingSSL); |
|
112 if (NS_FAILED(rv)) return rv; |
|
113 |
|
114 rv = mURI->GetAsciiHost(host); |
|
115 if (NS_FAILED(rv)) return rv; |
|
116 |
|
117 // Reject the URL if it doesn't specify a host |
|
118 if (host.IsEmpty()) |
|
119 return NS_ERROR_MALFORMED_URI; |
|
120 |
|
121 rv = mURI->GetPort(&port); |
|
122 if (NS_FAILED(rv)) return rv; |
|
123 |
|
124 LOG(("host=%s port=%d\n", host.get(), port)); |
|
125 |
|
126 rv = mURI->GetAsciiSpec(mSpec); |
|
127 if (NS_FAILED(rv)) return rv; |
|
128 LOG(("uri=%s\n", mSpec.get())); |
|
129 |
|
130 // Assert default request method |
|
131 MOZ_ASSERT(mRequestHead.EqualsMethod(nsHttpRequestHead::kMethod_Get)); |
|
132 |
|
133 // Set request headers |
|
134 nsAutoCString hostLine; |
|
135 rv = nsHttpHandler::GenerateHostPort(host, port, hostLine); |
|
136 if (NS_FAILED(rv)) return rv; |
|
137 |
|
138 rv = mRequestHead.SetHeader(nsHttp::Host, hostLine); |
|
139 if (NS_FAILED(rv)) return rv; |
|
140 |
|
141 rv = gHttpHandler->AddStandardRequestHeaders(&mRequestHead.Headers()); |
|
142 if (NS_FAILED(rv)) return rv; |
|
143 |
|
144 nsAutoCString type; |
|
145 if (aProxyInfo && NS_SUCCEEDED(aProxyInfo->GetType(type)) && |
|
146 !type.EqualsLiteral("unknown")) |
|
147 mProxyInfo = aProxyInfo; |
|
148 |
|
149 return rv; |
|
150 } |
|
151 |
|
152 //----------------------------------------------------------------------------- |
|
153 // HttpBaseChannel::nsISupports |
|
154 //----------------------------------------------------------------------------- |
|
155 |
|
156 NS_IMPL_ADDREF(HttpBaseChannel) |
|
157 NS_IMPL_RELEASE(HttpBaseChannel) |
|
158 |
|
159 NS_INTERFACE_MAP_BEGIN(HttpBaseChannel) |
|
160 NS_INTERFACE_MAP_ENTRY(nsIRequest) |
|
161 NS_INTERFACE_MAP_ENTRY(nsIChannel) |
|
162 NS_INTERFACE_MAP_ENTRY(nsIEncodedChannel) |
|
163 NS_INTERFACE_MAP_ENTRY(nsIHttpChannel) |
|
164 NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal) |
|
165 NS_INTERFACE_MAP_ENTRY(nsIUploadChannel) |
|
166 NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2) |
|
167 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) |
|
168 NS_INTERFACE_MAP_ENTRY(nsITraceableChannel) |
|
169 NS_INTERFACE_MAP_ENTRY(nsIPrivateBrowsingChannel) |
|
170 NS_INTERFACE_MAP_ENTRY(nsITimedChannel) |
|
171 NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag) |
|
172 |
|
173 //----------------------------------------------------------------------------- |
|
174 // HttpBaseChannel::nsIRequest |
|
175 //----------------------------------------------------------------------------- |
|
176 |
|
177 NS_IMETHODIMP |
|
178 HttpBaseChannel::GetName(nsACString& aName) |
|
179 { |
|
180 aName = mSpec; |
|
181 return NS_OK; |
|
182 } |
|
183 |
|
184 NS_IMETHODIMP |
|
185 HttpBaseChannel::IsPending(bool *aIsPending) |
|
186 { |
|
187 NS_ENSURE_ARG_POINTER(aIsPending); |
|
188 *aIsPending = mIsPending; |
|
189 return NS_OK; |
|
190 } |
|
191 |
|
192 NS_IMETHODIMP |
|
193 HttpBaseChannel::GetStatus(nsresult *aStatus) |
|
194 { |
|
195 NS_ENSURE_ARG_POINTER(aStatus); |
|
196 *aStatus = mStatus; |
|
197 return NS_OK; |
|
198 } |
|
199 |
|
200 NS_IMETHODIMP |
|
201 HttpBaseChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) |
|
202 { |
|
203 NS_ENSURE_ARG_POINTER(aLoadGroup); |
|
204 *aLoadGroup = mLoadGroup; |
|
205 NS_IF_ADDREF(*aLoadGroup); |
|
206 return NS_OK; |
|
207 } |
|
208 |
|
209 NS_IMETHODIMP |
|
210 HttpBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) |
|
211 { |
|
212 MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread."); |
|
213 |
|
214 if (!CanSetLoadGroup(aLoadGroup)) { |
|
215 return NS_ERROR_FAILURE; |
|
216 } |
|
217 |
|
218 mLoadGroup = aLoadGroup; |
|
219 mProgressSink = nullptr; |
|
220 mPrivateBrowsing = NS_UsePrivateBrowsing(this); |
|
221 return NS_OK; |
|
222 } |
|
223 |
|
224 NS_IMETHODIMP |
|
225 HttpBaseChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) |
|
226 { |
|
227 NS_ENSURE_ARG_POINTER(aLoadFlags); |
|
228 *aLoadFlags = mLoadFlags; |
|
229 return NS_OK; |
|
230 } |
|
231 |
|
232 NS_IMETHODIMP |
|
233 HttpBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) |
|
234 { |
|
235 mLoadFlags = aLoadFlags; |
|
236 return NS_OK; |
|
237 } |
|
238 |
|
239 //----------------------------------------------------------------------------- |
|
240 // HttpBaseChannel::nsIChannel |
|
241 //----------------------------------------------------------------------------- |
|
242 |
|
243 NS_IMETHODIMP |
|
244 HttpBaseChannel::GetOriginalURI(nsIURI **aOriginalURI) |
|
245 { |
|
246 NS_ENSURE_ARG_POINTER(aOriginalURI); |
|
247 *aOriginalURI = mOriginalURI; |
|
248 NS_ADDREF(*aOriginalURI); |
|
249 return NS_OK; |
|
250 } |
|
251 |
|
252 NS_IMETHODIMP |
|
253 HttpBaseChannel::SetOriginalURI(nsIURI *aOriginalURI) |
|
254 { |
|
255 ENSURE_CALLED_BEFORE_CONNECT(); |
|
256 |
|
257 NS_ENSURE_ARG_POINTER(aOriginalURI); |
|
258 mOriginalURI = aOriginalURI; |
|
259 return NS_OK; |
|
260 } |
|
261 |
|
262 NS_IMETHODIMP |
|
263 HttpBaseChannel::GetURI(nsIURI **aURI) |
|
264 { |
|
265 NS_ENSURE_ARG_POINTER(aURI); |
|
266 *aURI = mURI; |
|
267 NS_ADDREF(*aURI); |
|
268 return NS_OK; |
|
269 } |
|
270 |
|
271 NS_IMETHODIMP |
|
272 HttpBaseChannel::GetOwner(nsISupports **aOwner) |
|
273 { |
|
274 NS_ENSURE_ARG_POINTER(aOwner); |
|
275 *aOwner = mOwner; |
|
276 NS_IF_ADDREF(*aOwner); |
|
277 return NS_OK; |
|
278 } |
|
279 |
|
280 NS_IMETHODIMP |
|
281 HttpBaseChannel::SetOwner(nsISupports *aOwner) |
|
282 { |
|
283 mOwner = aOwner; |
|
284 return NS_OK; |
|
285 } |
|
286 |
|
287 NS_IMETHODIMP |
|
288 HttpBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) |
|
289 { |
|
290 *aCallbacks = mCallbacks; |
|
291 NS_IF_ADDREF(*aCallbacks); |
|
292 return NS_OK; |
|
293 } |
|
294 |
|
295 NS_IMETHODIMP |
|
296 HttpBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) |
|
297 { |
|
298 MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread."); |
|
299 |
|
300 if (!CanSetCallbacks(aCallbacks)) { |
|
301 return NS_ERROR_FAILURE; |
|
302 } |
|
303 |
|
304 mCallbacks = aCallbacks; |
|
305 mProgressSink = nullptr; |
|
306 |
|
307 mPrivateBrowsing = NS_UsePrivateBrowsing(this); |
|
308 return NS_OK; |
|
309 } |
|
310 |
|
311 NS_IMETHODIMP |
|
312 HttpBaseChannel::GetContentType(nsACString& aContentType) |
|
313 { |
|
314 if (!mResponseHead) { |
|
315 aContentType.Truncate(); |
|
316 return NS_ERROR_NOT_AVAILABLE; |
|
317 } |
|
318 |
|
319 if (!mResponseHead->ContentType().IsEmpty()) { |
|
320 aContentType = mResponseHead->ContentType(); |
|
321 return NS_OK; |
|
322 } |
|
323 |
|
324 aContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); |
|
325 return NS_OK; |
|
326 } |
|
327 |
|
328 NS_IMETHODIMP |
|
329 HttpBaseChannel::SetContentType(const nsACString& aContentType) |
|
330 { |
|
331 if (mListener || mWasOpened) { |
|
332 if (!mResponseHead) |
|
333 return NS_ERROR_NOT_AVAILABLE; |
|
334 |
|
335 nsAutoCString contentTypeBuf, charsetBuf; |
|
336 bool hadCharset; |
|
337 net_ParseContentType(aContentType, contentTypeBuf, charsetBuf, &hadCharset); |
|
338 |
|
339 mResponseHead->SetContentType(contentTypeBuf); |
|
340 |
|
341 // take care not to stomp on an existing charset |
|
342 if (hadCharset) |
|
343 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 &dummy); |
|
350 } |
|
351 |
|
352 return NS_OK; |
|
353 } |
|
354 |
|
355 NS_IMETHODIMP |
|
356 HttpBaseChannel::GetContentCharset(nsACString& aContentCharset) |
|
357 { |
|
358 if (!mResponseHead) |
|
359 return NS_ERROR_NOT_AVAILABLE; |
|
360 |
|
361 aContentCharset = mResponseHead->ContentCharset(); |
|
362 return NS_OK; |
|
363 } |
|
364 |
|
365 NS_IMETHODIMP |
|
366 HttpBaseChannel::SetContentCharset(const nsACString& aContentCharset) |
|
367 { |
|
368 if (mListener) { |
|
369 if (!mResponseHead) |
|
370 return NS_ERROR_NOT_AVAILABLE; |
|
371 |
|
372 mResponseHead->SetContentCharset(aContentCharset); |
|
373 } else { |
|
374 // Charset hint |
|
375 mContentCharsetHint = aContentCharset; |
|
376 } |
|
377 return NS_OK; |
|
378 } |
|
379 |
|
380 NS_IMETHODIMP |
|
381 HttpBaseChannel::GetContentDisposition(uint32_t *aContentDisposition) |
|
382 { |
|
383 nsresult rv; |
|
384 nsCString header; |
|
385 |
|
386 rv = GetContentDispositionHeader(header); |
|
387 if (NS_FAILED(rv)) { |
|
388 if (mContentDispositionHint == UINT32_MAX) |
|
389 return rv; |
|
390 |
|
391 *aContentDisposition = mContentDispositionHint; |
|
392 return NS_OK; |
|
393 } |
|
394 |
|
395 *aContentDisposition = NS_GetContentDispositionFromHeader(header, this); |
|
396 return NS_OK; |
|
397 } |
|
398 |
|
399 NS_IMETHODIMP |
|
400 HttpBaseChannel::SetContentDisposition(uint32_t aContentDisposition) |
|
401 { |
|
402 mContentDispositionHint = aContentDisposition; |
|
403 return NS_OK; |
|
404 } |
|
405 |
|
406 NS_IMETHODIMP |
|
407 HttpBaseChannel::GetContentDispositionFilename(nsAString& aContentDispositionFilename) |
|
408 { |
|
409 aContentDispositionFilename.Truncate(); |
|
410 nsresult rv; |
|
411 nsCString header; |
|
412 |
|
413 rv = GetContentDispositionHeader(header); |
|
414 if (NS_FAILED(rv)) { |
|
415 if (!mContentDispositionFilename) |
|
416 return rv; |
|
417 |
|
418 aContentDispositionFilename = *mContentDispositionFilename; |
|
419 return NS_OK; |
|
420 } |
|
421 |
|
422 return NS_GetFilenameFromDisposition(aContentDispositionFilename, |
|
423 header, mURI); |
|
424 } |
|
425 |
|
426 NS_IMETHODIMP |
|
427 HttpBaseChannel::SetContentDispositionFilename(const nsAString& aContentDispositionFilename) |
|
428 { |
|
429 mContentDispositionFilename = new nsString(aContentDispositionFilename); |
|
430 return NS_OK; |
|
431 } |
|
432 |
|
433 NS_IMETHODIMP |
|
434 HttpBaseChannel::GetContentDispositionHeader(nsACString& aContentDispositionHeader) |
|
435 { |
|
436 if (!mResponseHead) |
|
437 return NS_ERROR_NOT_AVAILABLE; |
|
438 |
|
439 nsresult rv = mResponseHead->GetHeader(nsHttp::Content_Disposition, |
|
440 aContentDispositionHeader); |
|
441 if (NS_FAILED(rv) || aContentDispositionHeader.IsEmpty()) |
|
442 return NS_ERROR_NOT_AVAILABLE; |
|
443 |
|
444 return NS_OK; |
|
445 } |
|
446 |
|
447 NS_IMETHODIMP |
|
448 HttpBaseChannel::GetContentLength(int64_t *aContentLength) |
|
449 { |
|
450 NS_ENSURE_ARG_POINTER(aContentLength); |
|
451 |
|
452 if (!mResponseHead) |
|
453 return NS_ERROR_NOT_AVAILABLE; |
|
454 |
|
455 *aContentLength = mResponseHead->ContentLength(); |
|
456 return NS_OK; |
|
457 } |
|
458 |
|
459 NS_IMETHODIMP |
|
460 HttpBaseChannel::SetContentLength(int64_t value) |
|
461 { |
|
462 NS_NOTYETIMPLEMENTED("HttpBaseChannel::SetContentLength"); |
|
463 return NS_ERROR_NOT_IMPLEMENTED; |
|
464 } |
|
465 |
|
466 NS_IMETHODIMP |
|
467 HttpBaseChannel::Open(nsIInputStream **aResult) |
|
468 { |
|
469 NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS); |
|
470 return NS_ImplementChannelOpen(this, aResult); |
|
471 } |
|
472 |
|
473 //----------------------------------------------------------------------------- |
|
474 // HttpBaseChannel::nsIUploadChannel |
|
475 //----------------------------------------------------------------------------- |
|
476 |
|
477 NS_IMETHODIMP |
|
478 HttpBaseChannel::GetUploadStream(nsIInputStream **stream) |
|
479 { |
|
480 NS_ENSURE_ARG_POINTER(stream); |
|
481 *stream = mUploadStream; |
|
482 NS_IF_ADDREF(*stream); |
|
483 return NS_OK; |
|
484 } |
|
485 |
|
486 NS_IMETHODIMP |
|
487 HttpBaseChannel::SetUploadStream(nsIInputStream *stream, |
|
488 const nsACString &contentType, |
|
489 int64_t contentLength) |
|
490 { |
|
491 // NOTE: for backwards compatibility and for compatibility with old style |
|
492 // plugins, |stream| may include headers, specifically Content-Type and |
|
493 // Content-Length headers. in this case, |contentType| and |contentLength| |
|
494 // would be unspecified. this is traditionally the case of a POST request, |
|
495 // and so we select POST as the request method if contentType and |
|
496 // contentLength are unspecified. |
|
497 |
|
498 if (stream) { |
|
499 nsAutoCString method; |
|
500 bool hasHeaders; |
|
501 |
|
502 if (contentType.IsEmpty()) { |
|
503 method = NS_LITERAL_CSTRING("POST"); |
|
504 hasHeaders = true; |
|
505 } else { |
|
506 method = NS_LITERAL_CSTRING("PUT"); |
|
507 hasHeaders = false; |
|
508 } |
|
509 return ExplicitSetUploadStream(stream, contentType, contentLength, |
|
510 method, hasHeaders); |
|
511 } |
|
512 |
|
513 // if stream is null, ExplicitSetUploadStream returns error. |
|
514 // So we need special case for GET method. |
|
515 mUploadStreamHasHeaders = false; |
|
516 mRequestHead.SetMethod(NS_LITERAL_CSTRING("GET")); // revert to GET request |
|
517 mUploadStream = stream; |
|
518 return NS_OK; |
|
519 } |
|
520 |
|
521 //----------------------------------------------------------------------------- |
|
522 // HttpBaseChannel::nsIUploadChannel2 |
|
523 //----------------------------------------------------------------------------- |
|
524 |
|
525 NS_IMETHODIMP |
|
526 HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream *aStream, |
|
527 const nsACString &aContentType, |
|
528 int64_t aContentLength, |
|
529 const nsACString &aMethod, |
|
530 bool aStreamHasHeaders) |
|
531 { |
|
532 // Ensure stream is set and method is valid |
|
533 NS_ENSURE_TRUE(aStream, NS_ERROR_FAILURE); |
|
534 |
|
535 if (aContentLength < 0 && !aStreamHasHeaders) { |
|
536 nsresult rv = aStream->Available(reinterpret_cast<uint64_t*>(&aContentLength)); |
|
537 if (NS_FAILED(rv) || aContentLength < 0) { |
|
538 NS_ERROR("unable to determine content length"); |
|
539 return NS_ERROR_FAILURE; |
|
540 } |
|
541 } |
|
542 |
|
543 nsresult rv = SetRequestMethod(aMethod); |
|
544 NS_ENSURE_SUCCESS(rv, rv); |
|
545 |
|
546 if (!aStreamHasHeaders) { |
|
547 // SetRequestHeader propagates headers to chrome if HttpChannelChild |
|
548 nsAutoCString contentLengthStr; |
|
549 contentLengthStr.AppendInt(aContentLength); |
|
550 SetRequestHeader(NS_LITERAL_CSTRING("Content-Length"), contentLengthStr, |
|
551 false); |
|
552 SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), aContentType, |
|
553 false); |
|
554 } |
|
555 |
|
556 mUploadStreamHasHeaders = aStreamHasHeaders; |
|
557 mUploadStream = aStream; |
|
558 return NS_OK; |
|
559 } |
|
560 |
|
561 NS_IMETHODIMP |
|
562 HttpBaseChannel::GetUploadStreamHasHeaders(bool *hasHeaders) |
|
563 { |
|
564 NS_ENSURE_ARG(hasHeaders); |
|
565 |
|
566 *hasHeaders = mUploadStreamHasHeaders; |
|
567 return NS_OK; |
|
568 } |
|
569 |
|
570 //----------------------------------------------------------------------------- |
|
571 // HttpBaseChannel::nsIEncodedChannel |
|
572 //----------------------------------------------------------------------------- |
|
573 |
|
574 NS_IMETHODIMP |
|
575 HttpBaseChannel::GetApplyConversion(bool *value) |
|
576 { |
|
577 *value = mApplyConversion; |
|
578 return NS_OK; |
|
579 } |
|
580 |
|
581 NS_IMETHODIMP |
|
582 HttpBaseChannel::SetApplyConversion(bool value) |
|
583 { |
|
584 LOG(("HttpBaseChannel::SetApplyConversion [this=%p value=%d]\n", this, value)); |
|
585 mApplyConversion = value; |
|
586 return NS_OK; |
|
587 } |
|
588 |
|
589 nsresult |
|
590 HttpBaseChannel::ApplyContentConversions() |
|
591 { |
|
592 if (!mResponseHead) |
|
593 return NS_OK; |
|
594 |
|
595 LOG(("HttpBaseChannel::ApplyContentConversions [this=%p]\n", this)); |
|
596 |
|
597 if (!mApplyConversion) { |
|
598 LOG(("not applying conversion per mApplyConversion\n")); |
|
599 return NS_OK; |
|
600 } |
|
601 |
|
602 nsAutoCString contentEncoding; |
|
603 char *cePtr, *val; |
|
604 nsresult rv; |
|
605 |
|
606 rv = mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding); |
|
607 if (NS_FAILED(rv) || contentEncoding.IsEmpty()) |
|
608 return NS_OK; |
|
609 |
|
610 // The encodings are listed in the order they were applied |
|
611 // (see rfc 2616 section 14.11), so they need to removed in reverse |
|
612 // order. This is accomplished because the converter chain ends up |
|
613 // being a stack with the last converter created being the first one |
|
614 // to accept the raw network data. |
|
615 |
|
616 cePtr = contentEncoding.BeginWriting(); |
|
617 uint32_t count = 0; |
|
618 while ((val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr))) { |
|
619 if (++count > 16) { |
|
620 // That's ridiculous. We only understand 2 different ones :) |
|
621 // but for compatibility with old code, we will just carry on without |
|
622 // removing the encodings |
|
623 LOG(("Too many Content-Encodings. Ignoring remainder.\n")); |
|
624 break; |
|
625 } |
|
626 |
|
627 if (gHttpHandler->IsAcceptableEncoding(val)) { |
|
628 nsCOMPtr<nsIStreamConverterService> serv; |
|
629 rv = gHttpHandler->GetStreamConverterService(getter_AddRefs(serv)); |
|
630 |
|
631 // we won't fail to load the page just because we couldn't load the |
|
632 // stream converter service.. carry on.. |
|
633 if (NS_FAILED(rv)) { |
|
634 if (val) |
|
635 LOG(("Unknown content encoding '%s', ignoring\n", val)); |
|
636 continue; |
|
637 } |
|
638 |
|
639 nsCOMPtr<nsIStreamListener> converter; |
|
640 nsAutoCString from(val); |
|
641 ToLowerCase(from); |
|
642 rv = serv->AsyncConvertData(from.get(), |
|
643 "uncompressed", |
|
644 mListener, |
|
645 mListenerContext, |
|
646 getter_AddRefs(converter)); |
|
647 if (NS_FAILED(rv)) { |
|
648 LOG(("Unexpected failure of AsyncConvertData %s\n", val)); |
|
649 return rv; |
|
650 } |
|
651 |
|
652 LOG(("converter removed '%s' content-encoding\n", val)); |
|
653 mListener = converter; |
|
654 } |
|
655 else { |
|
656 if (val) |
|
657 LOG(("Unknown content encoding '%s', ignoring\n", val)); |
|
658 } |
|
659 } |
|
660 |
|
661 return NS_OK; |
|
662 } |
|
663 |
|
664 NS_IMETHODIMP |
|
665 HttpBaseChannel::GetContentEncodings(nsIUTF8StringEnumerator** aEncodings) |
|
666 { |
|
667 if (!mResponseHead) { |
|
668 *aEncodings = nullptr; |
|
669 return NS_OK; |
|
670 } |
|
671 |
|
672 const char *encoding = mResponseHead->PeekHeader(nsHttp::Content_Encoding); |
|
673 if (!encoding) { |
|
674 *aEncodings = nullptr; |
|
675 return NS_OK; |
|
676 } |
|
677 nsContentEncodings* enumerator = new nsContentEncodings(this, encoding); |
|
678 NS_ADDREF(*aEncodings = enumerator); |
|
679 return NS_OK; |
|
680 } |
|
681 |
|
682 //----------------------------------------------------------------------------- |
|
683 // HttpBaseChannel::nsContentEncodings <public> |
|
684 //----------------------------------------------------------------------------- |
|
685 |
|
686 HttpBaseChannel::nsContentEncodings::nsContentEncodings(nsIHttpChannel* aChannel, |
|
687 const char* aEncodingHeader) |
|
688 : mEncodingHeader(aEncodingHeader) |
|
689 , mChannel(aChannel) |
|
690 , mReady(false) |
|
691 { |
|
692 mCurEnd = aEncodingHeader + strlen(aEncodingHeader); |
|
693 mCurStart = mCurEnd; |
|
694 } |
|
695 |
|
696 HttpBaseChannel::nsContentEncodings::~nsContentEncodings() |
|
697 { |
|
698 } |
|
699 |
|
700 //----------------------------------------------------------------------------- |
|
701 // HttpBaseChannel::nsContentEncodings::nsISimpleEnumerator |
|
702 //----------------------------------------------------------------------------- |
|
703 |
|
704 NS_IMETHODIMP |
|
705 HttpBaseChannel::nsContentEncodings::HasMore(bool* aMoreEncodings) |
|
706 { |
|
707 if (mReady) { |
|
708 *aMoreEncodings = true; |
|
709 return NS_OK; |
|
710 } |
|
711 |
|
712 nsresult rv = PrepareForNext(); |
|
713 *aMoreEncodings = NS_SUCCEEDED(rv); |
|
714 return NS_OK; |
|
715 } |
|
716 |
|
717 NS_IMETHODIMP |
|
718 HttpBaseChannel::nsContentEncodings::GetNext(nsACString& aNextEncoding) |
|
719 { |
|
720 aNextEncoding.Truncate(); |
|
721 if (!mReady) { |
|
722 nsresult rv = PrepareForNext(); |
|
723 if (NS_FAILED(rv)) { |
|
724 return NS_ERROR_FAILURE; |
|
725 } |
|
726 } |
|
727 |
|
728 const nsACString & encoding = Substring(mCurStart, mCurEnd); |
|
729 |
|
730 nsACString::const_iterator start, end; |
|
731 encoding.BeginReading(start); |
|
732 encoding.EndReading(end); |
|
733 |
|
734 bool haveType = false; |
|
735 if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("gzip"), start, end)) { |
|
736 aNextEncoding.AssignLiteral(APPLICATION_GZIP); |
|
737 haveType = true; |
|
738 } |
|
739 |
|
740 if (!haveType) { |
|
741 encoding.BeginReading(start); |
|
742 if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("compress"), start, end)) { |
|
743 aNextEncoding.AssignLiteral(APPLICATION_COMPRESS); |
|
744 haveType = true; |
|
745 } |
|
746 } |
|
747 |
|
748 if (!haveType) { |
|
749 encoding.BeginReading(start); |
|
750 if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("deflate"), start, end)) { |
|
751 aNextEncoding.AssignLiteral(APPLICATION_ZIP); |
|
752 haveType = true; |
|
753 } |
|
754 } |
|
755 |
|
756 // Prepare to fetch the next encoding |
|
757 mCurEnd = mCurStart; |
|
758 mReady = false; |
|
759 |
|
760 if (haveType) |
|
761 return NS_OK; |
|
762 |
|
763 NS_WARNING("Unknown encoding type"); |
|
764 return NS_ERROR_FAILURE; |
|
765 } |
|
766 |
|
767 //----------------------------------------------------------------------------- |
|
768 // HttpBaseChannel::nsContentEncodings::nsISupports |
|
769 //----------------------------------------------------------------------------- |
|
770 |
|
771 NS_IMPL_ISUPPORTS(HttpBaseChannel::nsContentEncodings, nsIUTF8StringEnumerator) |
|
772 |
|
773 //----------------------------------------------------------------------------- |
|
774 // HttpBaseChannel::nsContentEncodings <private> |
|
775 //----------------------------------------------------------------------------- |
|
776 |
|
777 nsresult |
|
778 HttpBaseChannel::nsContentEncodings::PrepareForNext(void) |
|
779 { |
|
780 MOZ_ASSERT(mCurStart == mCurEnd, "Indeterminate state"); |
|
781 |
|
782 // At this point both mCurStart and mCurEnd point to somewhere |
|
783 // past the end of the next thing we want to return |
|
784 |
|
785 while (mCurEnd != mEncodingHeader) { |
|
786 --mCurEnd; |
|
787 if (*mCurEnd != ',' && !nsCRT::IsAsciiSpace(*mCurEnd)) |
|
788 break; |
|
789 } |
|
790 if (mCurEnd == mEncodingHeader) |
|
791 return NS_ERROR_NOT_AVAILABLE; // no more encodings |
|
792 ++mCurEnd; |
|
793 |
|
794 // At this point mCurEnd points to the first char _after_ the |
|
795 // header we want. Furthermore, mCurEnd - 1 != mEncodingHeader |
|
796 |
|
797 mCurStart = mCurEnd - 1; |
|
798 while (mCurStart != mEncodingHeader && |
|
799 *mCurStart != ',' && !nsCRT::IsAsciiSpace(*mCurStart)) |
|
800 --mCurStart; |
|
801 if (*mCurStart == ',' || nsCRT::IsAsciiSpace(*mCurStart)) |
|
802 ++mCurStart; // we stopped because of a weird char, so move up one |
|
803 |
|
804 // At this point mCurStart and mCurEnd bracket the encoding string |
|
805 // we want. Check that it's not "identity" |
|
806 if (Substring(mCurStart, mCurEnd).Equals("identity", |
|
807 nsCaseInsensitiveCStringComparator())) { |
|
808 mCurEnd = mCurStart; |
|
809 return PrepareForNext(); |
|
810 } |
|
811 |
|
812 mReady = true; |
|
813 return NS_OK; |
|
814 } |
|
815 |
|
816 |
|
817 //----------------------------------------------------------------------------- |
|
818 // HttpBaseChannel::nsIHttpChannel |
|
819 //----------------------------------------------------------------------------- |
|
820 |
|
821 NS_IMETHODIMP |
|
822 HttpBaseChannel::GetRequestMethod(nsACString& aMethod) |
|
823 { |
|
824 aMethod = mRequestHead.Method(); |
|
825 return NS_OK; |
|
826 } |
|
827 |
|
828 NS_IMETHODIMP |
|
829 HttpBaseChannel::SetRequestMethod(const nsACString& aMethod) |
|
830 { |
|
831 ENSURE_CALLED_BEFORE_CONNECT(); |
|
832 |
|
833 const nsCString& flatMethod = PromiseFlatCString(aMethod); |
|
834 |
|
835 // Method names are restricted to valid HTTP tokens. |
|
836 if (!nsHttp::IsValidToken(flatMethod)) |
|
837 return NS_ERROR_INVALID_ARG; |
|
838 |
|
839 mRequestHead.SetMethod(flatMethod); |
|
840 return NS_OK; |
|
841 } |
|
842 |
|
843 NS_IMETHODIMP |
|
844 HttpBaseChannel::GetReferrer(nsIURI **referrer) |
|
845 { |
|
846 NS_ENSURE_ARG_POINTER(referrer); |
|
847 *referrer = mReferrer; |
|
848 NS_IF_ADDREF(*referrer); |
|
849 return NS_OK; |
|
850 } |
|
851 |
|
852 NS_IMETHODIMP |
|
853 HttpBaseChannel::SetReferrer(nsIURI *referrer) |
|
854 { |
|
855 ENSURE_CALLED_BEFORE_CONNECT(); |
|
856 |
|
857 // clear existing referrer, if any |
|
858 mReferrer = nullptr; |
|
859 mRequestHead.ClearHeader(nsHttp::Referer); |
|
860 |
|
861 if (!referrer) |
|
862 return NS_OK; |
|
863 |
|
864 // 0: never send referer |
|
865 // 1: send referer for direct user action |
|
866 // 2: always send referer |
|
867 uint32_t userReferrerLevel = gHttpHandler->ReferrerLevel(); |
|
868 |
|
869 // false: use real referrer |
|
870 // true: spoof with URI of the current request |
|
871 bool userSpoofReferrerSource = gHttpHandler->SpoofReferrerSource(); |
|
872 |
|
873 // 0: full URI |
|
874 // 1: scheme+host+port+path |
|
875 // 2: scheme+host+port |
|
876 int userReferrerTrimmingPolicy = gHttpHandler->ReferrerTrimmingPolicy(); |
|
877 |
|
878 // 0: send referer no matter what |
|
879 // 1: send referer ONLY when base domains match |
|
880 // 2: send referer ONLY when hosts match |
|
881 int userReferrerXOriginPolicy = gHttpHandler->ReferrerXOriginPolicy(); |
|
882 |
|
883 // check referrer blocking pref |
|
884 uint32_t referrerLevel; |
|
885 if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) |
|
886 referrerLevel = 1; // user action |
|
887 else |
|
888 referrerLevel = 2; // inline content |
|
889 if (userReferrerLevel < referrerLevel) |
|
890 return NS_OK; |
|
891 |
|
892 nsCOMPtr<nsIURI> referrerGrip; |
|
893 nsresult rv; |
|
894 bool match; |
|
895 |
|
896 // |
|
897 // Strip off "wyciwyg://123/" from wyciwyg referrers. |
|
898 // |
|
899 // XXX this really belongs elsewhere since wyciwyg URLs aren't part of necko. |
|
900 // perhaps some sort of generic nsINestedURI could be used. then, if an URI |
|
901 // fails the whitelist test, then we could check for an inner URI and try |
|
902 // that instead. though, that might be too automatic. |
|
903 // |
|
904 rv = referrer->SchemeIs("wyciwyg", &match); |
|
905 if (NS_FAILED(rv)) return rv; |
|
906 if (match) { |
|
907 nsAutoCString path; |
|
908 rv = referrer->GetPath(path); |
|
909 if (NS_FAILED(rv)) return rv; |
|
910 |
|
911 uint32_t pathLength = path.Length(); |
|
912 if (pathLength <= 2) return NS_ERROR_FAILURE; |
|
913 |
|
914 // Path is of the form "//123/http://foo/bar", with a variable number of |
|
915 // digits. To figure out where the "real" URL starts, search path for a |
|
916 // '/', starting at the third character. |
|
917 int32_t slashIndex = path.FindChar('/', 2); |
|
918 if (slashIndex == kNotFound) return NS_ERROR_FAILURE; |
|
919 |
|
920 // Get charset of the original URI so we can pass it to our fixed up URI. |
|
921 nsAutoCString charset; |
|
922 referrer->GetOriginCharset(charset); |
|
923 |
|
924 // Replace |referrer| with a URI without wyciwyg://123/. |
|
925 rv = NS_NewURI(getter_AddRefs(referrerGrip), |
|
926 Substring(path, slashIndex + 1, pathLength - slashIndex - 1), |
|
927 charset.get()); |
|
928 if (NS_FAILED(rv)) return rv; |
|
929 |
|
930 referrer = referrerGrip.get(); |
|
931 } |
|
932 |
|
933 // |
|
934 // block referrer if not on our white list... |
|
935 // |
|
936 static const char *const referrerWhiteList[] = { |
|
937 "http", |
|
938 "https", |
|
939 "ftp", |
|
940 nullptr |
|
941 }; |
|
942 match = false; |
|
943 const char *const *scheme = referrerWhiteList; |
|
944 for (; *scheme && !match; ++scheme) { |
|
945 rv = referrer->SchemeIs(*scheme, &match); |
|
946 if (NS_FAILED(rv)) return rv; |
|
947 } |
|
948 if (!match) |
|
949 return NS_OK; // kick out.... |
|
950 |
|
951 // |
|
952 // Handle secure referrals. |
|
953 // |
|
954 // Support referrals from a secure server if this is a secure site |
|
955 // and (optionally) if the host names are the same. |
|
956 // |
|
957 rv = referrer->SchemeIs("https", &match); |
|
958 if (NS_FAILED(rv)) return rv; |
|
959 if (match) { |
|
960 rv = mURI->SchemeIs("https", &match); |
|
961 if (NS_FAILED(rv)) return rv; |
|
962 if (!match) |
|
963 return NS_OK; |
|
964 |
|
965 if (!gHttpHandler->SendSecureXSiteReferrer()) { |
|
966 nsAutoCString referrerHost; |
|
967 nsAutoCString host; |
|
968 |
|
969 rv = referrer->GetAsciiHost(referrerHost); |
|
970 if (NS_FAILED(rv)) return rv; |
|
971 |
|
972 rv = mURI->GetAsciiHost(host); |
|
973 if (NS_FAILED(rv)) return rv; |
|
974 |
|
975 // GetAsciiHost returns lowercase hostname. |
|
976 if (!referrerHost.Equals(host)) |
|
977 return NS_OK; |
|
978 } |
|
979 } |
|
980 |
|
981 nsCOMPtr<nsIURI> clone; |
|
982 // |
|
983 // we need to clone the referrer, so we can: |
|
984 // (1) modify it |
|
985 // (2) keep a reference to it after returning from this function |
|
986 // |
|
987 // Use CloneIgnoringRef to strip away any fragment per RFC 2616 section 14.36 |
|
988 rv = referrer->CloneIgnoringRef(getter_AddRefs(clone)); |
|
989 if (NS_FAILED(rv)) return rv; |
|
990 |
|
991 nsAutoCString currentHost; |
|
992 nsAutoCString referrerHost; |
|
993 |
|
994 rv = mURI->GetAsciiHost(currentHost); |
|
995 if (NS_FAILED(rv)) return rv; |
|
996 |
|
997 rv = clone->GetAsciiHost(referrerHost); |
|
998 if (NS_FAILED(rv)) return rv; |
|
999 |
|
1000 // check policy for sending ref only when hosts match |
|
1001 if (userReferrerXOriginPolicy == 2 && !currentHost.Equals(referrerHost)) |
|
1002 return NS_OK; |
|
1003 |
|
1004 if (userReferrerXOriginPolicy == 1) { |
|
1005 nsAutoCString currentDomain = currentHost; |
|
1006 nsAutoCString referrerDomain = referrerHost; |
|
1007 uint32_t extraDomains = 0; |
|
1008 nsCOMPtr<nsIEffectiveTLDService> eTLDService = do_GetService( |
|
1009 NS_EFFECTIVETLDSERVICE_CONTRACTID); |
|
1010 if (eTLDService) { |
|
1011 rv = eTLDService->GetBaseDomain(mURI, extraDomains, currentDomain); |
|
1012 if (NS_FAILED(rv)) return rv; |
|
1013 rv = eTLDService->GetBaseDomain(clone, extraDomains, referrerDomain); |
|
1014 if (NS_FAILED(rv)) return rv; |
|
1015 } |
|
1016 |
|
1017 // check policy for sending only when effective top level domain matches. |
|
1018 // this falls back on using host if eTLDService does not work |
|
1019 if (!currentDomain.Equals(referrerDomain)) |
|
1020 return NS_OK; |
|
1021 } |
|
1022 |
|
1023 // send spoofed referrer if desired |
|
1024 if (userSpoofReferrerSource) { |
|
1025 nsCOMPtr<nsIURI> mURIclone; |
|
1026 rv = mURI->CloneIgnoringRef(getter_AddRefs(mURIclone)); |
|
1027 if (NS_FAILED(rv)) return rv; |
|
1028 clone = mURIclone; |
|
1029 currentHost = referrerHost; |
|
1030 } |
|
1031 |
|
1032 // strip away any userpass; we don't want to be giving out passwords ;-) |
|
1033 rv = clone->SetUserPass(EmptyCString()); |
|
1034 if (NS_FAILED(rv)) return rv; |
|
1035 |
|
1036 nsAutoCString spec; |
|
1037 |
|
1038 // check how much referer to send |
|
1039 switch (userReferrerTrimmingPolicy) { |
|
1040 |
|
1041 case 1: { |
|
1042 // scheme+host+port+path |
|
1043 nsAutoCString prepath, path; |
|
1044 rv = clone->GetPrePath(prepath); |
|
1045 if (NS_FAILED(rv)) return rv; |
|
1046 |
|
1047 nsCOMPtr<nsIURL> url(do_QueryInterface(clone)); |
|
1048 if (!url) { |
|
1049 // if this isn't a url, play it safe |
|
1050 // and just send the prepath |
|
1051 spec = prepath; |
|
1052 break; |
|
1053 } |
|
1054 rv = url->GetFilePath(path); |
|
1055 if (NS_FAILED(rv)) return rv; |
|
1056 spec = prepath + path; |
|
1057 break; |
|
1058 } |
|
1059 case 2: |
|
1060 // scheme+host+port |
|
1061 rv = clone->GetPrePath(spec); |
|
1062 if (NS_FAILED(rv)) return rv; |
|
1063 break; |
|
1064 |
|
1065 default: |
|
1066 // full URI |
|
1067 rv = clone->GetAsciiSpec(spec); |
|
1068 if (NS_FAILED(rv)) return rv; |
|
1069 break; |
|
1070 } |
|
1071 |
|
1072 // finally, remember the referrer URI and set the Referer header. |
|
1073 mReferrer = clone; |
|
1074 mRequestHead.SetHeader(nsHttp::Referer, spec); |
|
1075 return NS_OK; |
|
1076 } |
|
1077 |
|
1078 NS_IMETHODIMP |
|
1079 HttpBaseChannel::GetProxyURI(nsIURI** proxyURI) |
|
1080 { |
|
1081 NS_ENSURE_ARG_POINTER(proxyURI); |
|
1082 *proxyURI = mProxyURI; |
|
1083 NS_IF_ADDREF(*proxyURI); |
|
1084 return NS_OK; |
|
1085 } |
|
1086 |
|
1087 NS_IMETHODIMP |
|
1088 HttpBaseChannel::GetRequestHeader(const nsACString& aHeader, |
|
1089 nsACString& aValue) |
|
1090 { |
|
1091 // XXX might be better to search the header list directly instead of |
|
1092 // hitting the http atom hash table. |
|
1093 nsHttpAtom atom = nsHttp::ResolveAtom(aHeader); |
|
1094 if (!atom) |
|
1095 return NS_ERROR_NOT_AVAILABLE; |
|
1096 |
|
1097 return mRequestHead.GetHeader(atom, aValue); |
|
1098 } |
|
1099 |
|
1100 NS_IMETHODIMP |
|
1101 HttpBaseChannel::SetRequestHeader(const nsACString& aHeader, |
|
1102 const nsACString& aValue, |
|
1103 bool aMerge) |
|
1104 { |
|
1105 const nsCString &flatHeader = PromiseFlatCString(aHeader); |
|
1106 const nsCString &flatValue = PromiseFlatCString(aValue); |
|
1107 |
|
1108 LOG(("HttpBaseChannel::SetRequestHeader [this=%p header=\"%s\" value=\"%s\" merge=%u]\n", |
|
1109 this, flatHeader.get(), flatValue.get(), aMerge)); |
|
1110 |
|
1111 // Header names are restricted to valid HTTP tokens. |
|
1112 if (!nsHttp::IsValidToken(flatHeader)) |
|
1113 return NS_ERROR_INVALID_ARG; |
|
1114 |
|
1115 // Header values MUST NOT contain line-breaks. RFC 2616 technically |
|
1116 // permits CTL characters, including CR and LF, in header values provided |
|
1117 // they are quoted. However, this can lead to problems if servers do not |
|
1118 // interpret quoted strings properly. Disallowing CR and LF here seems |
|
1119 // reasonable and keeps things simple. We also disallow a null byte. |
|
1120 if (flatValue.FindCharInSet("\r\n") != kNotFound || |
|
1121 flatValue.Length() != strlen(flatValue.get())) |
|
1122 return NS_ERROR_INVALID_ARG; |
|
1123 |
|
1124 nsHttpAtom atom = nsHttp::ResolveAtom(flatHeader.get()); |
|
1125 if (!atom) { |
|
1126 NS_WARNING("failed to resolve atom"); |
|
1127 return NS_ERROR_NOT_AVAILABLE; |
|
1128 } |
|
1129 |
|
1130 return mRequestHead.SetHeader(atom, flatValue, aMerge); |
|
1131 } |
|
1132 |
|
1133 NS_IMETHODIMP |
|
1134 HttpBaseChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *visitor) |
|
1135 { |
|
1136 return mRequestHead.Headers().VisitHeaders(visitor); |
|
1137 } |
|
1138 |
|
1139 NS_IMETHODIMP |
|
1140 HttpBaseChannel::GetResponseHeader(const nsACString &header, nsACString &value) |
|
1141 { |
|
1142 if (!mResponseHead) |
|
1143 return NS_ERROR_NOT_AVAILABLE; |
|
1144 |
|
1145 nsHttpAtom atom = nsHttp::ResolveAtom(header); |
|
1146 if (!atom) |
|
1147 return NS_ERROR_NOT_AVAILABLE; |
|
1148 |
|
1149 return mResponseHead->GetHeader(atom, value); |
|
1150 } |
|
1151 |
|
1152 NS_IMETHODIMP |
|
1153 HttpBaseChannel::SetResponseHeader(const nsACString& header, |
|
1154 const nsACString& value, |
|
1155 bool merge) |
|
1156 { |
|
1157 LOG(("HttpBaseChannel::SetResponseHeader [this=%p header=\"%s\" value=\"%s\" merge=%u]\n", |
|
1158 this, PromiseFlatCString(header).get(), PromiseFlatCString(value).get(), merge)); |
|
1159 |
|
1160 if (!mResponseHead) |
|
1161 return NS_ERROR_NOT_AVAILABLE; |
|
1162 |
|
1163 nsHttpAtom atom = nsHttp::ResolveAtom(header); |
|
1164 if (!atom) |
|
1165 return NS_ERROR_NOT_AVAILABLE; |
|
1166 |
|
1167 // these response headers must not be changed |
|
1168 if (atom == nsHttp::Content_Type || |
|
1169 atom == nsHttp::Content_Length || |
|
1170 atom == nsHttp::Content_Encoding || |
|
1171 atom == nsHttp::Trailer || |
|
1172 atom == nsHttp::Transfer_Encoding) |
|
1173 return NS_ERROR_ILLEGAL_VALUE; |
|
1174 |
|
1175 mResponseHeadersModified = true; |
|
1176 |
|
1177 return mResponseHead->SetHeader(atom, value, merge); |
|
1178 } |
|
1179 |
|
1180 NS_IMETHODIMP |
|
1181 HttpBaseChannel::VisitResponseHeaders(nsIHttpHeaderVisitor *visitor) |
|
1182 { |
|
1183 if (!mResponseHead) |
|
1184 return NS_ERROR_NOT_AVAILABLE; |
|
1185 return mResponseHead->Headers().VisitHeaders(visitor); |
|
1186 } |
|
1187 |
|
1188 NS_IMETHODIMP |
|
1189 HttpBaseChannel::GetAllowPipelining(bool *value) |
|
1190 { |
|
1191 NS_ENSURE_ARG_POINTER(value); |
|
1192 *value = mAllowPipelining; |
|
1193 return NS_OK; |
|
1194 } |
|
1195 |
|
1196 NS_IMETHODIMP |
|
1197 HttpBaseChannel::SetAllowPipelining(bool value) |
|
1198 { |
|
1199 ENSURE_CALLED_BEFORE_CONNECT(); |
|
1200 |
|
1201 mAllowPipelining = value; |
|
1202 return NS_OK; |
|
1203 } |
|
1204 |
|
1205 NS_IMETHODIMP |
|
1206 HttpBaseChannel::GetRedirectionLimit(uint32_t *value) |
|
1207 { |
|
1208 NS_ENSURE_ARG_POINTER(value); |
|
1209 *value = mRedirectionLimit; |
|
1210 return NS_OK; |
|
1211 } |
|
1212 |
|
1213 NS_IMETHODIMP |
|
1214 HttpBaseChannel::SetRedirectionLimit(uint32_t value) |
|
1215 { |
|
1216 ENSURE_CALLED_BEFORE_CONNECT(); |
|
1217 |
|
1218 mRedirectionLimit = std::min<uint32_t>(value, 0xff); |
|
1219 return NS_OK; |
|
1220 } |
|
1221 |
|
1222 NS_IMETHODIMP |
|
1223 HttpBaseChannel::IsNoStoreResponse(bool *value) |
|
1224 { |
|
1225 if (!mResponseHead) |
|
1226 return NS_ERROR_NOT_AVAILABLE; |
|
1227 *value = mResponseHead->NoStore(); |
|
1228 return NS_OK; |
|
1229 } |
|
1230 |
|
1231 NS_IMETHODIMP |
|
1232 HttpBaseChannel::IsNoCacheResponse(bool *value) |
|
1233 { |
|
1234 if (!mResponseHead) |
|
1235 return NS_ERROR_NOT_AVAILABLE; |
|
1236 *value = mResponseHead->NoCache(); |
|
1237 if (!*value) |
|
1238 *value = mResponseHead->ExpiresInPast(); |
|
1239 return NS_OK; |
|
1240 } |
|
1241 |
|
1242 NS_IMETHODIMP |
|
1243 HttpBaseChannel::GetResponseStatus(uint32_t *aValue) |
|
1244 { |
|
1245 if (!mResponseHead) |
|
1246 return NS_ERROR_NOT_AVAILABLE; |
|
1247 *aValue = mResponseHead->Status(); |
|
1248 return NS_OK; |
|
1249 } |
|
1250 |
|
1251 NS_IMETHODIMP |
|
1252 HttpBaseChannel::GetResponseStatusText(nsACString& aValue) |
|
1253 { |
|
1254 if (!mResponseHead) |
|
1255 return NS_ERROR_NOT_AVAILABLE; |
|
1256 aValue = mResponseHead->StatusText(); |
|
1257 return NS_OK; |
|
1258 } |
|
1259 |
|
1260 NS_IMETHODIMP |
|
1261 HttpBaseChannel::GetRequestSucceeded(bool *aValue) |
|
1262 { |
|
1263 if (!mResponseHead) |
|
1264 return NS_ERROR_NOT_AVAILABLE; |
|
1265 uint32_t status = mResponseHead->Status(); |
|
1266 *aValue = (status / 100 == 2); |
|
1267 return NS_OK; |
|
1268 } |
|
1269 |
|
1270 NS_IMETHODIMP |
|
1271 HttpBaseChannel::RedirectTo(nsIURI *newURI) |
|
1272 { |
|
1273 // We can only redirect unopened channels |
|
1274 ENSURE_CALLED_BEFORE_CONNECT(); |
|
1275 |
|
1276 // The redirect is stored internally for use in AsyncOpen |
|
1277 mAPIRedirectToURI = newURI; |
|
1278 |
|
1279 return NS_OK; |
|
1280 } |
|
1281 |
|
1282 //----------------------------------------------------------------------------- |
|
1283 // HttpBaseChannel::nsIHttpChannelInternal |
|
1284 //----------------------------------------------------------------------------- |
|
1285 |
|
1286 NS_IMETHODIMP |
|
1287 HttpBaseChannel::GetDocumentURI(nsIURI **aDocumentURI) |
|
1288 { |
|
1289 NS_ENSURE_ARG_POINTER(aDocumentURI); |
|
1290 *aDocumentURI = mDocumentURI; |
|
1291 NS_IF_ADDREF(*aDocumentURI); |
|
1292 return NS_OK; |
|
1293 } |
|
1294 |
|
1295 NS_IMETHODIMP |
|
1296 HttpBaseChannel::SetDocumentURI(nsIURI *aDocumentURI) |
|
1297 { |
|
1298 ENSURE_CALLED_BEFORE_CONNECT(); |
|
1299 |
|
1300 mDocumentURI = aDocumentURI; |
|
1301 return NS_OK; |
|
1302 } |
|
1303 |
|
1304 NS_IMETHODIMP |
|
1305 HttpBaseChannel::GetRequestVersion(uint32_t *major, uint32_t *minor) |
|
1306 { |
|
1307 nsHttpVersion version = mRequestHead.Version(); |
|
1308 |
|
1309 if (major) { *major = version / 10; } |
|
1310 if (minor) { *minor = version % 10; } |
|
1311 |
|
1312 return NS_OK; |
|
1313 } |
|
1314 |
|
1315 NS_IMETHODIMP |
|
1316 HttpBaseChannel::GetResponseVersion(uint32_t *major, uint32_t *minor) |
|
1317 { |
|
1318 if (!mResponseHead) |
|
1319 { |
|
1320 *major = *minor = 0; // we should at least be kind about it |
|
1321 return NS_ERROR_NOT_AVAILABLE; |
|
1322 } |
|
1323 |
|
1324 nsHttpVersion version = mResponseHead->Version(); |
|
1325 |
|
1326 if (major) { *major = version / 10; } |
|
1327 if (minor) { *minor = version % 10; } |
|
1328 |
|
1329 return NS_OK; |
|
1330 } |
|
1331 |
|
1332 namespace { |
|
1333 |
|
1334 class CookieNotifierRunnable : public nsRunnable |
|
1335 { |
|
1336 public: |
|
1337 CookieNotifierRunnable(HttpBaseChannel* aChannel, char const * aCookie) |
|
1338 : mChannel(aChannel), mCookie(aCookie) |
|
1339 { } |
|
1340 |
|
1341 NS_IMETHOD Run() |
|
1342 { |
|
1343 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); |
|
1344 if (obs) { |
|
1345 obs->NotifyObservers(static_cast<nsIChannel*>(mChannel.get()), |
|
1346 "http-on-response-set-cookie", |
|
1347 mCookie.get()); |
|
1348 } |
|
1349 return NS_OK; |
|
1350 } |
|
1351 |
|
1352 private: |
|
1353 nsRefPtr<HttpBaseChannel> mChannel; |
|
1354 NS_ConvertASCIItoUTF16 mCookie; |
|
1355 }; |
|
1356 |
|
1357 } // anonymous namespace |
|
1358 |
|
1359 NS_IMETHODIMP |
|
1360 HttpBaseChannel::SetCookie(const char *aCookieHeader) |
|
1361 { |
|
1362 if (mLoadFlags & LOAD_ANONYMOUS) |
|
1363 return NS_OK; |
|
1364 |
|
1365 // empty header isn't an error |
|
1366 if (!(aCookieHeader && *aCookieHeader)) |
|
1367 return NS_OK; |
|
1368 |
|
1369 nsICookieService *cs = gHttpHandler->GetCookieService(); |
|
1370 NS_ENSURE_TRUE(cs, NS_ERROR_FAILURE); |
|
1371 |
|
1372 nsresult rv = |
|
1373 cs->SetCookieStringFromHttp(mURI, nullptr, nullptr, aCookieHeader, |
|
1374 mResponseHead->PeekHeader(nsHttp::Date), this); |
|
1375 if (NS_SUCCEEDED(rv)) { |
|
1376 nsRefPtr<CookieNotifierRunnable> r = |
|
1377 new CookieNotifierRunnable(this, aCookieHeader); |
|
1378 NS_DispatchToMainThread(r); |
|
1379 } |
|
1380 return rv; |
|
1381 } |
|
1382 |
|
1383 NS_IMETHODIMP |
|
1384 HttpBaseChannel::GetForceAllowThirdPartyCookie(bool *aForce) |
|
1385 { |
|
1386 *aForce = mForceAllowThirdPartyCookie; |
|
1387 return NS_OK; |
|
1388 } |
|
1389 |
|
1390 NS_IMETHODIMP |
|
1391 HttpBaseChannel::SetForceAllowThirdPartyCookie(bool aForce) |
|
1392 { |
|
1393 ENSURE_CALLED_BEFORE_ASYNC_OPEN(); |
|
1394 |
|
1395 mForceAllowThirdPartyCookie = aForce; |
|
1396 return NS_OK; |
|
1397 } |
|
1398 |
|
1399 NS_IMETHODIMP |
|
1400 HttpBaseChannel::GetCanceled(bool *aCanceled) |
|
1401 { |
|
1402 *aCanceled = mCanceled; |
|
1403 return NS_OK; |
|
1404 } |
|
1405 |
|
1406 NS_IMETHODIMP |
|
1407 HttpBaseChannel::GetChannelIsForDownload(bool *aChannelIsForDownload) |
|
1408 { |
|
1409 *aChannelIsForDownload = mChannelIsForDownload; |
|
1410 return NS_OK; |
|
1411 } |
|
1412 |
|
1413 NS_IMETHODIMP |
|
1414 HttpBaseChannel::SetChannelIsForDownload(bool aChannelIsForDownload) |
|
1415 { |
|
1416 mChannelIsForDownload = aChannelIsForDownload; |
|
1417 return NS_OK; |
|
1418 } |
|
1419 |
|
1420 NS_IMETHODIMP |
|
1421 HttpBaseChannel::SetCacheKeysRedirectChain(nsTArray<nsCString> *cacheKeys) |
|
1422 { |
|
1423 mRedirectedCachekeys = cacheKeys; |
|
1424 return NS_OK; |
|
1425 } |
|
1426 |
|
1427 NS_IMETHODIMP |
|
1428 HttpBaseChannel::GetLocalAddress(nsACString& addr) |
|
1429 { |
|
1430 if (mSelfAddr.raw.family == PR_AF_UNSPEC) |
|
1431 return NS_ERROR_NOT_AVAILABLE; |
|
1432 |
|
1433 addr.SetCapacity(kIPv6CStrBufSize); |
|
1434 NetAddrToString(&mSelfAddr, addr.BeginWriting(), kIPv6CStrBufSize); |
|
1435 addr.SetLength(strlen(addr.BeginReading())); |
|
1436 |
|
1437 return NS_OK; |
|
1438 } |
|
1439 |
|
1440 NS_IMETHODIMP |
|
1441 HttpBaseChannel::TakeAllSecurityMessages( |
|
1442 nsCOMArray<nsISecurityConsoleMessage> &aMessages) |
|
1443 { |
|
1444 aMessages.Clear(); |
|
1445 aMessages.SwapElements(mSecurityConsoleMessages); |
|
1446 return NS_OK; |
|
1447 } |
|
1448 |
|
1449 /* Please use this method with care. This can cause the message |
|
1450 * queue to grow large and cause the channel to take up a lot |
|
1451 * of memory. Use only static string messages and do not add |
|
1452 * server side data to the queue, as that can be large. |
|
1453 * Add only a limited number of messages to the queue to keep |
|
1454 * the channel size down and do so only in rare erroneous situations. |
|
1455 * More information can be found here: |
|
1456 * https://bugzilla.mozilla.org/show_bug.cgi?id=846918 |
|
1457 */ |
|
1458 NS_IMETHODIMP |
|
1459 HttpBaseChannel::AddSecurityMessage(const nsAString &aMessageTag, |
|
1460 const nsAString &aMessageCategory) |
|
1461 { |
|
1462 nsresult rv; |
|
1463 nsCOMPtr<nsISecurityConsoleMessage> message = |
|
1464 do_CreateInstance(NS_SECURITY_CONSOLE_MESSAGE_CONTRACTID, &rv); |
|
1465 NS_ENSURE_SUCCESS(rv, rv); |
|
1466 message->SetTag(aMessageTag); |
|
1467 message->SetCategory(aMessageCategory); |
|
1468 mSecurityConsoleMessages.AppendElement(message); |
|
1469 return NS_OK; |
|
1470 } |
|
1471 |
|
1472 NS_IMETHODIMP |
|
1473 HttpBaseChannel::GetLocalPort(int32_t* port) |
|
1474 { |
|
1475 NS_ENSURE_ARG_POINTER(port); |
|
1476 |
|
1477 if (mSelfAddr.raw.family == PR_AF_INET) { |
|
1478 *port = (int32_t)ntohs(mSelfAddr.inet.port); |
|
1479 } |
|
1480 else if (mSelfAddr.raw.family == PR_AF_INET6) { |
|
1481 *port = (int32_t)ntohs(mSelfAddr.inet6.port); |
|
1482 } |
|
1483 else |
|
1484 return NS_ERROR_NOT_AVAILABLE; |
|
1485 |
|
1486 return NS_OK; |
|
1487 } |
|
1488 |
|
1489 NS_IMETHODIMP |
|
1490 HttpBaseChannel::GetRemoteAddress(nsACString& addr) |
|
1491 { |
|
1492 if (mPeerAddr.raw.family == PR_AF_UNSPEC) |
|
1493 return NS_ERROR_NOT_AVAILABLE; |
|
1494 |
|
1495 addr.SetCapacity(kIPv6CStrBufSize); |
|
1496 NetAddrToString(&mPeerAddr, addr.BeginWriting(), kIPv6CStrBufSize); |
|
1497 addr.SetLength(strlen(addr.BeginReading())); |
|
1498 |
|
1499 return NS_OK; |
|
1500 } |
|
1501 |
|
1502 NS_IMETHODIMP |
|
1503 HttpBaseChannel::GetRemotePort(int32_t* port) |
|
1504 { |
|
1505 NS_ENSURE_ARG_POINTER(port); |
|
1506 |
|
1507 if (mPeerAddr.raw.family == PR_AF_INET) { |
|
1508 *port = (int32_t)ntohs(mPeerAddr.inet.port); |
|
1509 } |
|
1510 else if (mPeerAddr.raw.family == PR_AF_INET6) { |
|
1511 *port = (int32_t)ntohs(mPeerAddr.inet6.port); |
|
1512 } |
|
1513 else |
|
1514 return NS_ERROR_NOT_AVAILABLE; |
|
1515 |
|
1516 return NS_OK; |
|
1517 } |
|
1518 |
|
1519 NS_IMETHODIMP |
|
1520 HttpBaseChannel::HTTPUpgrade(const nsACString &aProtocolName, |
|
1521 nsIHttpUpgradeListener *aListener) |
|
1522 { |
|
1523 NS_ENSURE_ARG(!aProtocolName.IsEmpty()); |
|
1524 NS_ENSURE_ARG_POINTER(aListener); |
|
1525 |
|
1526 mUpgradeProtocol = aProtocolName; |
|
1527 mUpgradeProtocolCallback = aListener; |
|
1528 return NS_OK; |
|
1529 } |
|
1530 |
|
1531 NS_IMETHODIMP |
|
1532 HttpBaseChannel::GetAllowSpdy(bool *aAllowSpdy) |
|
1533 { |
|
1534 NS_ENSURE_ARG_POINTER(aAllowSpdy); |
|
1535 |
|
1536 *aAllowSpdy = mAllowSpdy; |
|
1537 return NS_OK; |
|
1538 } |
|
1539 |
|
1540 NS_IMETHODIMP |
|
1541 HttpBaseChannel::SetAllowSpdy(bool aAllowSpdy) |
|
1542 { |
|
1543 mAllowSpdy = aAllowSpdy; |
|
1544 return NS_OK; |
|
1545 } |
|
1546 |
|
1547 NS_IMETHODIMP |
|
1548 HttpBaseChannel::GetLoadAsBlocking(bool *aLoadAsBlocking) |
|
1549 { |
|
1550 NS_ENSURE_ARG_POINTER(aLoadAsBlocking); |
|
1551 *aLoadAsBlocking = mLoadAsBlocking; |
|
1552 return NS_OK; |
|
1553 } |
|
1554 |
|
1555 NS_IMETHODIMP |
|
1556 HttpBaseChannel::SetLoadAsBlocking(bool aLoadAsBlocking) |
|
1557 { |
|
1558 mLoadAsBlocking = aLoadAsBlocking; |
|
1559 return NS_OK; |
|
1560 } |
|
1561 |
|
1562 NS_IMETHODIMP |
|
1563 HttpBaseChannel::GetLoadUnblocked(bool *aLoadUnblocked) |
|
1564 { |
|
1565 NS_ENSURE_ARG_POINTER(aLoadUnblocked); |
|
1566 *aLoadUnblocked = mLoadUnblocked; |
|
1567 return NS_OK; |
|
1568 } |
|
1569 |
|
1570 NS_IMETHODIMP |
|
1571 HttpBaseChannel::SetLoadUnblocked(bool aLoadUnblocked) |
|
1572 { |
|
1573 mLoadUnblocked = aLoadUnblocked; |
|
1574 return NS_OK; |
|
1575 } |
|
1576 |
|
1577 NS_IMETHODIMP |
|
1578 HttpBaseChannel::GetApiRedirectToURI(nsIURI ** aResult) |
|
1579 { |
|
1580 NS_ENSURE_ARG_POINTER(aResult); |
|
1581 NS_IF_ADDREF(*aResult = mAPIRedirectToURI); |
|
1582 return NS_OK; |
|
1583 } |
|
1584 |
|
1585 NS_IMETHODIMP |
|
1586 HttpBaseChannel::GetResponseTimeoutEnabled(bool *aEnable) |
|
1587 { |
|
1588 if (NS_WARN_IF(!aEnable)) { |
|
1589 return NS_ERROR_NULL_POINTER; |
|
1590 } |
|
1591 *aEnable = mResponseTimeoutEnabled; |
|
1592 return NS_OK; |
|
1593 } |
|
1594 |
|
1595 NS_IMETHODIMP |
|
1596 HttpBaseChannel::SetResponseTimeoutEnabled(bool aEnable) |
|
1597 { |
|
1598 mResponseTimeoutEnabled = aEnable; |
|
1599 return NS_OK; |
|
1600 } |
|
1601 |
|
1602 //----------------------------------------------------------------------------- |
|
1603 // HttpBaseChannel::nsISupportsPriority |
|
1604 //----------------------------------------------------------------------------- |
|
1605 |
|
1606 NS_IMETHODIMP |
|
1607 HttpBaseChannel::GetPriority(int32_t *value) |
|
1608 { |
|
1609 *value = mPriority; |
|
1610 return NS_OK; |
|
1611 } |
|
1612 |
|
1613 NS_IMETHODIMP |
|
1614 HttpBaseChannel::AdjustPriority(int32_t delta) |
|
1615 { |
|
1616 return SetPriority(mPriority + delta); |
|
1617 } |
|
1618 |
|
1619 //----------------------------------------------------------------------------- |
|
1620 // HttpBaseChannel::nsIResumableChannel |
|
1621 //----------------------------------------------------------------------------- |
|
1622 |
|
1623 NS_IMETHODIMP |
|
1624 HttpBaseChannel::GetEntityID(nsACString& aEntityID) |
|
1625 { |
|
1626 // Don't return an entity ID for Non-GET requests which require |
|
1627 // additional data |
|
1628 if (!mRequestHead.IsGet()) { |
|
1629 return NS_ERROR_NOT_RESUMABLE; |
|
1630 } |
|
1631 |
|
1632 uint64_t size = UINT64_MAX; |
|
1633 nsAutoCString etag, lastmod; |
|
1634 if (mResponseHead) { |
|
1635 // Don't return an entity if the server sent the following header: |
|
1636 // Accept-Ranges: none |
|
1637 // Not sending the Accept-Ranges header means we can still try |
|
1638 // sending range requests. |
|
1639 const char* acceptRanges = |
|
1640 mResponseHead->PeekHeader(nsHttp::Accept_Ranges); |
|
1641 if (acceptRanges && |
|
1642 !nsHttp::FindToken(acceptRanges, "bytes", HTTP_HEADER_VALUE_SEPS)) { |
|
1643 return NS_ERROR_NOT_RESUMABLE; |
|
1644 } |
|
1645 |
|
1646 size = mResponseHead->TotalEntitySize(); |
|
1647 const char* cLastMod = mResponseHead->PeekHeader(nsHttp::Last_Modified); |
|
1648 if (cLastMod) |
|
1649 lastmod = cLastMod; |
|
1650 const char* cEtag = mResponseHead->PeekHeader(nsHttp::ETag); |
|
1651 if (cEtag) |
|
1652 etag = cEtag; |
|
1653 } |
|
1654 nsCString entityID; |
|
1655 NS_EscapeURL(etag.BeginReading(), etag.Length(), esc_AlwaysCopy | |
|
1656 esc_FileBaseName | esc_Forced, entityID); |
|
1657 entityID.Append('/'); |
|
1658 entityID.AppendInt(int64_t(size)); |
|
1659 entityID.Append('/'); |
|
1660 entityID.Append(lastmod); |
|
1661 // NOTE: Appending lastmod as the last part avoids having to escape it |
|
1662 |
|
1663 aEntityID = entityID; |
|
1664 |
|
1665 return NS_OK; |
|
1666 } |
|
1667 |
|
1668 //----------------------------------------------------------------------------- |
|
1669 // nsHttpChannel::nsITraceableChannel |
|
1670 //----------------------------------------------------------------------------- |
|
1671 |
|
1672 NS_IMETHODIMP |
|
1673 HttpBaseChannel::SetNewListener(nsIStreamListener *aListener, nsIStreamListener **_retval) |
|
1674 { |
|
1675 if (!mTracingEnabled) |
|
1676 return NS_ERROR_FAILURE; |
|
1677 |
|
1678 NS_ENSURE_ARG_POINTER(aListener); |
|
1679 |
|
1680 nsCOMPtr<nsIStreamListener> wrapper = new nsStreamListenerWrapper(mListener); |
|
1681 |
|
1682 wrapper.forget(_retval); |
|
1683 mListener = aListener; |
|
1684 return NS_OK; |
|
1685 } |
|
1686 |
|
1687 //----------------------------------------------------------------------------- |
|
1688 // HttpBaseChannel helpers |
|
1689 //----------------------------------------------------------------------------- |
|
1690 |
|
1691 void |
|
1692 HttpBaseChannel::ReleaseListeners() |
|
1693 { |
|
1694 MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread."); |
|
1695 |
|
1696 mListener = nullptr; |
|
1697 mListenerContext = nullptr; |
|
1698 mCallbacks = nullptr; |
|
1699 mProgressSink = nullptr; |
|
1700 } |
|
1701 |
|
1702 void |
|
1703 HttpBaseChannel::DoNotifyListener() |
|
1704 { |
|
1705 // Make sure mIsPending is set to false. At this moment we are done from |
|
1706 // the point of view of our consumer and we have to report our self |
|
1707 // as not-pending. |
|
1708 if (mListener) { |
|
1709 mListener->OnStartRequest(this, mListenerContext); |
|
1710 mIsPending = false; |
|
1711 mListener->OnStopRequest(this, mListenerContext, mStatus); |
|
1712 } else { |
|
1713 mIsPending = false; |
|
1714 } |
|
1715 // We have to make sure to drop the references to listeners and callbacks |
|
1716 // no longer needed |
|
1717 ReleaseListeners(); |
|
1718 |
|
1719 DoNotifyListenerCleanup(); |
|
1720 } |
|
1721 |
|
1722 void |
|
1723 HttpBaseChannel::AddCookiesToRequest() |
|
1724 { |
|
1725 if (mLoadFlags & LOAD_ANONYMOUS) { |
|
1726 return; |
|
1727 } |
|
1728 |
|
1729 bool useCookieService = |
|
1730 (XRE_GetProcessType() == GeckoProcessType_Default); |
|
1731 nsXPIDLCString cookie; |
|
1732 if (useCookieService) { |
|
1733 nsICookieService *cs = gHttpHandler->GetCookieService(); |
|
1734 if (cs) { |
|
1735 cs->GetCookieStringFromHttp(mURI, |
|
1736 nullptr, |
|
1737 this, getter_Copies(cookie)); |
|
1738 } |
|
1739 |
|
1740 if (cookie.IsEmpty()) { |
|
1741 cookie = mUserSetCookieHeader; |
|
1742 } |
|
1743 else if (!mUserSetCookieHeader.IsEmpty()) { |
|
1744 cookie.Append(NS_LITERAL_CSTRING("; ") + mUserSetCookieHeader); |
|
1745 } |
|
1746 } |
|
1747 else { |
|
1748 cookie = mUserSetCookieHeader; |
|
1749 } |
|
1750 |
|
1751 // If we are in the child process, we want the parent seeing any |
|
1752 // cookie headers that might have been set by SetRequestHeader() |
|
1753 SetRequestHeader(nsDependentCString(nsHttp::Cookie), cookie, false); |
|
1754 } |
|
1755 |
|
1756 static PLDHashOperator |
|
1757 CopyProperties(const nsAString& aKey, nsIVariant *aData, void *aClosure) |
|
1758 { |
|
1759 nsIWritablePropertyBag* bag = static_cast<nsIWritablePropertyBag*> |
|
1760 (aClosure); |
|
1761 bag->SetProperty(aKey, aData); |
|
1762 return PL_DHASH_NEXT; |
|
1763 } |
|
1764 |
|
1765 bool |
|
1766 HttpBaseChannel::ShouldRewriteRedirectToGET(uint32_t httpStatus, |
|
1767 nsHttpRequestHead::ParsedMethodType method) |
|
1768 { |
|
1769 // for 301 and 302, only rewrite POST |
|
1770 if (httpStatus == 301 || httpStatus == 302) |
|
1771 return method == nsHttpRequestHead::kMethod_Post; |
|
1772 |
|
1773 // rewrite for 303 unless it was HEAD |
|
1774 if (httpStatus == 303) |
|
1775 return method != nsHttpRequestHead::kMethod_Head; |
|
1776 |
|
1777 // otherwise, such as for 307, do not rewrite |
|
1778 return false; |
|
1779 } |
|
1780 |
|
1781 nsresult |
|
1782 HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI, |
|
1783 nsIChannel *newChannel, |
|
1784 bool preserveMethod) |
|
1785 { |
|
1786 LOG(("HttpBaseChannel::SetupReplacementChannel " |
|
1787 "[this=%p newChannel=%p preserveMethod=%d]", |
|
1788 this, newChannel, preserveMethod)); |
|
1789 uint32_t newLoadFlags = mLoadFlags | LOAD_REPLACE; |
|
1790 // if the original channel was using SSL and this channel is not using |
|
1791 // SSL, then no need to inhibit persistent caching. however, if the |
|
1792 // original channel was not using SSL and has INHIBIT_PERSISTENT_CACHING |
|
1793 // set, then allow the flag to apply to the redirected channel as well. |
|
1794 // since we force set INHIBIT_PERSISTENT_CACHING on all HTTPS channels, |
|
1795 // we only need to check if the original channel was using SSL. |
|
1796 bool usingSSL = false; |
|
1797 nsresult rv = mURI->SchemeIs("https", &usingSSL); |
|
1798 if (NS_SUCCEEDED(rv) && usingSSL) |
|
1799 newLoadFlags &= ~INHIBIT_PERSISTENT_CACHING; |
|
1800 |
|
1801 // Do not pass along LOAD_CHECK_OFFLINE_CACHE |
|
1802 newLoadFlags &= ~nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE; |
|
1803 |
|
1804 newChannel->SetLoadGroup(mLoadGroup); |
|
1805 newChannel->SetNotificationCallbacks(mCallbacks); |
|
1806 newChannel->SetLoadFlags(newLoadFlags); |
|
1807 |
|
1808 // If our owner is a null principal it will have been set as a security |
|
1809 // measure, so we want to propagate it to the new channel. |
|
1810 nsCOMPtr<nsIPrincipal> ownerPrincipal = do_QueryInterface(mOwner); |
|
1811 if (ownerPrincipal && ownerPrincipal->GetIsNullPrincipal()) { |
|
1812 newChannel->SetOwner(mOwner); |
|
1813 } |
|
1814 |
|
1815 // Try to preserve the privacy bit if it has been overridden |
|
1816 if (mPrivateBrowsingOverriden) { |
|
1817 nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel = |
|
1818 do_QueryInterface(newChannel); |
|
1819 if (newPBChannel) { |
|
1820 newPBChannel->SetPrivate(mPrivateBrowsing); |
|
1821 } |
|
1822 } |
|
1823 |
|
1824 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel); |
|
1825 if (!httpChannel) |
|
1826 return NS_OK; // no other options to set |
|
1827 |
|
1828 if (preserveMethod) { |
|
1829 nsCOMPtr<nsIUploadChannel> uploadChannel = |
|
1830 do_QueryInterface(httpChannel); |
|
1831 nsCOMPtr<nsIUploadChannel2> uploadChannel2 = |
|
1832 do_QueryInterface(httpChannel); |
|
1833 if (mUploadStream && (uploadChannel2 || uploadChannel)) { |
|
1834 // rewind upload stream |
|
1835 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream); |
|
1836 if (seekable) |
|
1837 seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); |
|
1838 |
|
1839 // replicate original call to SetUploadStream... |
|
1840 if (uploadChannel2) { |
|
1841 const char *ctype = mRequestHead.PeekHeader(nsHttp::Content_Type); |
|
1842 if (!ctype) |
|
1843 ctype = ""; |
|
1844 const char *clen = mRequestHead.PeekHeader(nsHttp::Content_Length); |
|
1845 int64_t len = clen ? nsCRT::atoll(clen) : -1; |
|
1846 uploadChannel2->ExplicitSetUploadStream( |
|
1847 mUploadStream, nsDependentCString(ctype), len, |
|
1848 mRequestHead.Method(), |
|
1849 mUploadStreamHasHeaders); |
|
1850 } else { |
|
1851 if (mUploadStreamHasHeaders) { |
|
1852 uploadChannel->SetUploadStream(mUploadStream, EmptyCString(), |
|
1853 -1); |
|
1854 } else { |
|
1855 const char *ctype = |
|
1856 mRequestHead.PeekHeader(nsHttp::Content_Type); |
|
1857 const char *clen = |
|
1858 mRequestHead.PeekHeader(nsHttp::Content_Length); |
|
1859 if (!ctype) { |
|
1860 ctype = "application/octet-stream"; |
|
1861 } |
|
1862 if (clen) { |
|
1863 uploadChannel->SetUploadStream(mUploadStream, |
|
1864 nsDependentCString(ctype), |
|
1865 nsCRT::atoll(clen)); |
|
1866 } |
|
1867 } |
|
1868 } |
|
1869 } |
|
1870 // since preserveMethod is true, we need to ensure that the appropriate |
|
1871 // request method gets set on the channel, regardless of whether or not |
|
1872 // we set the upload stream above. This means SetRequestMethod() will |
|
1873 // be called twice if ExplicitSetUploadStream() gets called above. |
|
1874 |
|
1875 httpChannel->SetRequestMethod(mRequestHead.Method()); |
|
1876 } |
|
1877 // convey the referrer if one was used for this channel to the next one |
|
1878 if (mReferrer) |
|
1879 httpChannel->SetReferrer(mReferrer); |
|
1880 // convey the mAllowPipelining flag |
|
1881 httpChannel->SetAllowPipelining(mAllowPipelining); |
|
1882 // convey the new redirection limit |
|
1883 httpChannel->SetRedirectionLimit(mRedirectionLimit - 1); |
|
1884 |
|
1885 // convey the Accept header value |
|
1886 { |
|
1887 nsAutoCString oldAcceptValue; |
|
1888 nsresult hasHeader = mRequestHead.GetHeader(nsHttp::Accept, oldAcceptValue); |
|
1889 if (NS_SUCCEEDED(hasHeader)) { |
|
1890 httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), |
|
1891 oldAcceptValue, |
|
1892 false); |
|
1893 } |
|
1894 } |
|
1895 |
|
1896 nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel); |
|
1897 if (httpInternal) { |
|
1898 // convey the mForceAllowThirdPartyCookie flag |
|
1899 httpInternal->SetForceAllowThirdPartyCookie(mForceAllowThirdPartyCookie); |
|
1900 // convey the spdy flag |
|
1901 httpInternal->SetAllowSpdy(mAllowSpdy); |
|
1902 |
|
1903 // update the DocumentURI indicator since we are being redirected. |
|
1904 // if this was a top-level document channel, then the new channel |
|
1905 // should have its mDocumentURI point to newURI; otherwise, we |
|
1906 // just need to pass along our mDocumentURI to the new channel. |
|
1907 if (newURI && (mURI == mDocumentURI)) |
|
1908 httpInternal->SetDocumentURI(newURI); |
|
1909 else |
|
1910 httpInternal->SetDocumentURI(mDocumentURI); |
|
1911 |
|
1912 // if there is a chain of keys for redirect-responses we transfer it to |
|
1913 // the new channel (see bug #561276) |
|
1914 if (mRedirectedCachekeys) { |
|
1915 LOG(("HttpBaseChannel::SetupReplacementChannel " |
|
1916 "[this=%p] transferring chain of redirect cache-keys", this)); |
|
1917 httpInternal->SetCacheKeysRedirectChain(mRedirectedCachekeys.forget()); |
|
1918 } |
|
1919 } |
|
1920 |
|
1921 // transfer application cache information |
|
1922 nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel = |
|
1923 do_QueryInterface(newChannel); |
|
1924 if (appCacheChannel) { |
|
1925 appCacheChannel->SetApplicationCache(mApplicationCache); |
|
1926 appCacheChannel->SetInheritApplicationCache(mInheritApplicationCache); |
|
1927 // We purposely avoid transfering mChooseApplicationCache. |
|
1928 } |
|
1929 |
|
1930 // transfer any properties |
|
1931 nsCOMPtr<nsIWritablePropertyBag> bag(do_QueryInterface(newChannel)); |
|
1932 if (bag) |
|
1933 mPropertyHash.EnumerateRead(CopyProperties, bag.get()); |
|
1934 |
|
1935 // Transfer the timing data (if we are dealing with an nsITimedChannel). |
|
1936 nsCOMPtr<nsITimedChannel> newTimedChannel(do_QueryInterface(newChannel)); |
|
1937 nsCOMPtr<nsITimedChannel> oldTimedChannel( |
|
1938 do_QueryInterface(static_cast<nsIHttpChannel*>(this))); |
|
1939 if (oldTimedChannel && newTimedChannel) { |
|
1940 newTimedChannel->SetTimingEnabled(mTimingEnabled); |
|
1941 newTimedChannel->SetRedirectCount(mRedirectCount + 1); |
|
1942 |
|
1943 // If the RedirectStart is null, we will use the AsyncOpen value of the |
|
1944 // previous channel (this is the first redirect in the redirects chain). |
|
1945 if (mRedirectStartTimeStamp.IsNull()) { |
|
1946 TimeStamp asyncOpen; |
|
1947 oldTimedChannel->GetAsyncOpen(&asyncOpen); |
|
1948 newTimedChannel->SetRedirectStart(asyncOpen); |
|
1949 } |
|
1950 else { |
|
1951 newTimedChannel->SetRedirectStart(mRedirectStartTimeStamp); |
|
1952 } |
|
1953 |
|
1954 // The RedirectEnd timestamp is equal to the previous channel response end. |
|
1955 TimeStamp prevResponseEnd; |
|
1956 oldTimedChannel->GetResponseEnd(&prevResponseEnd); |
|
1957 newTimedChannel->SetRedirectEnd(prevResponseEnd); |
|
1958 |
|
1959 // Check whether or not this was a cross-domain redirect. |
|
1960 newTimedChannel->SetAllRedirectsSameOrigin( |
|
1961 mAllRedirectsSameOrigin && SameOriginWithOriginalUri(newURI)); |
|
1962 } |
|
1963 |
|
1964 return NS_OK; |
|
1965 } |
|
1966 |
|
1967 // Redirect Tracking |
|
1968 bool |
|
1969 HttpBaseChannel::SameOriginWithOriginalUri(nsIURI *aURI) |
|
1970 { |
|
1971 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); |
|
1972 nsresult rv = ssm->CheckSameOriginURI(aURI, mOriginalURI, false); |
|
1973 return (NS_SUCCEEDED(rv)); |
|
1974 } |
|
1975 |
|
1976 |
|
1977 |
|
1978 //----------------------------------------------------------------------------- |
|
1979 // HttpBaseChannel::nsITimedChannel |
|
1980 //----------------------------------------------------------------------------- |
|
1981 |
|
1982 NS_IMETHODIMP |
|
1983 HttpBaseChannel::SetTimingEnabled(bool enabled) { |
|
1984 mTimingEnabled = enabled; |
|
1985 return NS_OK; |
|
1986 } |
|
1987 |
|
1988 NS_IMETHODIMP |
|
1989 HttpBaseChannel::GetTimingEnabled(bool* _retval) { |
|
1990 *_retval = mTimingEnabled; |
|
1991 return NS_OK; |
|
1992 } |
|
1993 |
|
1994 NS_IMETHODIMP |
|
1995 HttpBaseChannel::GetChannelCreation(TimeStamp* _retval) { |
|
1996 *_retval = mChannelCreationTimestamp; |
|
1997 return NS_OK; |
|
1998 } |
|
1999 |
|
2000 NS_IMETHODIMP |
|
2001 HttpBaseChannel::GetAsyncOpen(TimeStamp* _retval) { |
|
2002 *_retval = mAsyncOpenTime; |
|
2003 return NS_OK; |
|
2004 } |
|
2005 |
|
2006 /** |
|
2007 * @return the number of redirects. There is no check for cross-domain |
|
2008 * redirects. This check must be done by the consumers. |
|
2009 */ |
|
2010 NS_IMETHODIMP |
|
2011 HttpBaseChannel::GetRedirectCount(uint16_t *aRedirectCount) |
|
2012 { |
|
2013 *aRedirectCount = mRedirectCount; |
|
2014 return NS_OK; |
|
2015 } |
|
2016 |
|
2017 NS_IMETHODIMP |
|
2018 HttpBaseChannel::SetRedirectCount(uint16_t aRedirectCount) |
|
2019 { |
|
2020 mRedirectCount = aRedirectCount; |
|
2021 return NS_OK; |
|
2022 } |
|
2023 |
|
2024 NS_IMETHODIMP |
|
2025 HttpBaseChannel::GetRedirectStart(TimeStamp* _retval) |
|
2026 { |
|
2027 *_retval = mRedirectStartTimeStamp; |
|
2028 return NS_OK; |
|
2029 } |
|
2030 |
|
2031 NS_IMETHODIMP |
|
2032 HttpBaseChannel::SetRedirectStart(TimeStamp aRedirectStart) |
|
2033 { |
|
2034 mRedirectStartTimeStamp = aRedirectStart; |
|
2035 return NS_OK; |
|
2036 } |
|
2037 |
|
2038 NS_IMETHODIMP |
|
2039 HttpBaseChannel::GetRedirectEnd(TimeStamp* _retval) |
|
2040 { |
|
2041 *_retval = mRedirectEndTimeStamp; |
|
2042 return NS_OK; |
|
2043 } |
|
2044 |
|
2045 NS_IMETHODIMP |
|
2046 HttpBaseChannel::SetRedirectEnd(TimeStamp aRedirectEnd) |
|
2047 { |
|
2048 mRedirectEndTimeStamp = aRedirectEnd; |
|
2049 return NS_OK; |
|
2050 } |
|
2051 |
|
2052 NS_IMETHODIMP |
|
2053 HttpBaseChannel::GetAllRedirectsSameOrigin(bool *aAllRedirectsSameOrigin) |
|
2054 { |
|
2055 *aAllRedirectsSameOrigin = mAllRedirectsSameOrigin; |
|
2056 return NS_OK; |
|
2057 } |
|
2058 |
|
2059 NS_IMETHODIMP |
|
2060 HttpBaseChannel::SetAllRedirectsSameOrigin(bool aAllRedirectsSameOrigin) |
|
2061 { |
|
2062 mAllRedirectsSameOrigin = aAllRedirectsSameOrigin; |
|
2063 return NS_OK; |
|
2064 } |
|
2065 |
|
2066 NS_IMETHODIMP |
|
2067 HttpBaseChannel::GetDomainLookupStart(TimeStamp* _retval) { |
|
2068 *_retval = mTransactionTimings.domainLookupStart; |
|
2069 return NS_OK; |
|
2070 } |
|
2071 |
|
2072 NS_IMETHODIMP |
|
2073 HttpBaseChannel::GetDomainLookupEnd(TimeStamp* _retval) { |
|
2074 *_retval = mTransactionTimings.domainLookupEnd; |
|
2075 return NS_OK; |
|
2076 } |
|
2077 |
|
2078 NS_IMETHODIMP |
|
2079 HttpBaseChannel::GetConnectStart(TimeStamp* _retval) { |
|
2080 *_retval = mTransactionTimings.connectStart; |
|
2081 return NS_OK; |
|
2082 } |
|
2083 |
|
2084 NS_IMETHODIMP |
|
2085 HttpBaseChannel::GetConnectEnd(TimeStamp* _retval) { |
|
2086 *_retval = mTransactionTimings.connectEnd; |
|
2087 return NS_OK; |
|
2088 } |
|
2089 |
|
2090 NS_IMETHODIMP |
|
2091 HttpBaseChannel::GetRequestStart(TimeStamp* _retval) { |
|
2092 *_retval = mTransactionTimings.requestStart; |
|
2093 return NS_OK; |
|
2094 } |
|
2095 |
|
2096 NS_IMETHODIMP |
|
2097 HttpBaseChannel::GetResponseStart(TimeStamp* _retval) { |
|
2098 *_retval = mTransactionTimings.responseStart; |
|
2099 return NS_OK; |
|
2100 } |
|
2101 |
|
2102 NS_IMETHODIMP |
|
2103 HttpBaseChannel::GetResponseEnd(TimeStamp* _retval) { |
|
2104 *_retval = mTransactionTimings.responseEnd; |
|
2105 return NS_OK; |
|
2106 } |
|
2107 |
|
2108 NS_IMETHODIMP |
|
2109 HttpBaseChannel::GetCacheReadStart(TimeStamp* _retval) { |
|
2110 *_retval = mCacheReadStart; |
|
2111 return NS_OK; |
|
2112 } |
|
2113 |
|
2114 NS_IMETHODIMP |
|
2115 HttpBaseChannel::GetCacheReadEnd(TimeStamp* _retval) { |
|
2116 *_retval = mCacheReadEnd; |
|
2117 return NS_OK; |
|
2118 } |
|
2119 |
|
2120 NS_IMETHODIMP |
|
2121 HttpBaseChannel::GetInitiatorType(nsAString & aInitiatorType) |
|
2122 { |
|
2123 aInitiatorType = mInitiatorType; |
|
2124 return NS_OK; |
|
2125 } |
|
2126 |
|
2127 NS_IMETHODIMP |
|
2128 HttpBaseChannel::SetInitiatorType(const nsAString & aInitiatorType) |
|
2129 { |
|
2130 mInitiatorType = aInitiatorType; |
|
2131 return NS_OK; |
|
2132 } |
|
2133 |
|
2134 #define IMPL_TIMING_ATTR(name) \ |
|
2135 NS_IMETHODIMP \ |
|
2136 HttpBaseChannel::Get##name##Time(PRTime* _retval) { \ |
|
2137 TimeStamp stamp; \ |
|
2138 Get##name(&stamp); \ |
|
2139 if (stamp.IsNull()) { \ |
|
2140 *_retval = 0; \ |
|
2141 return NS_OK; \ |
|
2142 } \ |
|
2143 *_retval = mChannelCreationTime + \ |
|
2144 (PRTime) ((stamp - mChannelCreationTimestamp).ToSeconds() * 1e6); \ |
|
2145 return NS_OK; \ |
|
2146 } |
|
2147 |
|
2148 IMPL_TIMING_ATTR(ChannelCreation) |
|
2149 IMPL_TIMING_ATTR(AsyncOpen) |
|
2150 IMPL_TIMING_ATTR(DomainLookupStart) |
|
2151 IMPL_TIMING_ATTR(DomainLookupEnd) |
|
2152 IMPL_TIMING_ATTR(ConnectStart) |
|
2153 IMPL_TIMING_ATTR(ConnectEnd) |
|
2154 IMPL_TIMING_ATTR(RequestStart) |
|
2155 IMPL_TIMING_ATTR(ResponseStart) |
|
2156 IMPL_TIMING_ATTR(ResponseEnd) |
|
2157 IMPL_TIMING_ATTR(CacheReadStart) |
|
2158 IMPL_TIMING_ATTR(CacheReadEnd) |
|
2159 IMPL_TIMING_ATTR(RedirectStart) |
|
2160 IMPL_TIMING_ATTR(RedirectEnd) |
|
2161 |
|
2162 #undef IMPL_TIMING_ATTR |
|
2163 |
|
2164 |
|
2165 //------------------------------------------------------------------------------ |
|
2166 |
|
2167 } // namespace net |
|
2168 } // namespace mozilla |
|
2169 |