Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set ts=4 sw=4 sts=4 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // HttpLog.h should generally be included first
8 #include "HttpLog.h"
10 #include "base/basictypes.h"
12 #include "nsHttpHandler.h"
13 #include "nsHttpTransaction.h"
14 #include "nsHttpRequestHead.h"
15 #include "nsHttpResponseHead.h"
16 #include "nsHttpChunkedDecoder.h"
17 #include "nsTransportUtils.h"
18 #include "nsNetUtil.h"
19 #include "nsCRT.h"
21 #include "nsISeekableStream.h"
22 #include "nsMultiplexInputStream.h"
23 #include "nsStringStream.h"
24 #include "mozilla/VisualEventTracer.h"
26 #include "nsComponentManagerUtils.h" // do_CreateInstance
27 #include "nsServiceManagerUtils.h" // do_GetService
28 #include "nsIHttpActivityObserver.h"
29 #include "nsSocketTransportService2.h"
30 #include "nsICancelable.h"
31 #include "nsIEventTarget.h"
32 #include "nsIHttpChannelInternal.h"
33 #include "nsIInputStream.h"
34 #include "nsITransport.h"
35 #include "nsIOService.h"
36 #include <algorithm>
38 #ifdef MOZ_WIDGET_GONK
39 #include "NetStatistics.h"
40 #endif
42 //-----------------------------------------------------------------------------
44 #ifdef DEBUG
45 // defined by the socket transport service while active
46 extern PRThread *gSocketThread;
47 #endif
49 //-----------------------------------------------------------------------------
51 static NS_DEFINE_CID(kMultiplexInputStream, NS_MULTIPLEXINPUTSTREAM_CID);
53 // Place a limit on how much non-compliant HTTP can be skipped while
54 // looking for a response header
55 #define MAX_INVALID_RESPONSE_BODY_SIZE (1024 * 128)
57 using namespace mozilla::net;
59 namespace mozilla {
60 namespace net {
62 //-----------------------------------------------------------------------------
63 // helpers
64 //-----------------------------------------------------------------------------
66 #if defined(PR_LOGGING)
67 static void
68 LogHeaders(const char *lineStart)
69 {
70 nsAutoCString buf;
71 char *endOfLine;
72 while ((endOfLine = PL_strstr(lineStart, "\r\n"))) {
73 buf.Assign(lineStart, endOfLine - lineStart);
74 if (PL_strcasestr(buf.get(), "authorization: ") ||
75 PL_strcasestr(buf.get(), "proxy-authorization: ")) {
76 char *p = PL_strchr(PL_strchr(buf.get(), ' ') + 1, ' ');
77 while (p && *++p)
78 *p = '*';
79 }
80 LOG3((" %s\n", buf.get()));
81 lineStart = endOfLine + 2;
82 }
83 }
84 #endif
86 //-----------------------------------------------------------------------------
87 // nsHttpTransaction <public>
88 //-----------------------------------------------------------------------------
90 nsHttpTransaction::nsHttpTransaction()
91 : mLock("transaction lock")
92 , mRequestSize(0)
93 , mConnection(nullptr)
94 , mConnInfo(nullptr)
95 , mRequestHead(nullptr)
96 , mResponseHead(nullptr)
97 , mContentLength(-1)
98 , mContentRead(0)
99 , mInvalidResponseBytesRead(0)
100 , mChunkedDecoder(nullptr)
101 , mStatus(NS_OK)
102 , mPriority(0)
103 , mRestartCount(0)
104 , mCaps(0)
105 , mCapsToClear(0)
106 , mClassification(CLASS_GENERAL)
107 , mPipelinePosition(0)
108 , mHttpVersion(NS_HTTP_VERSION_UNKNOWN)
109 , mClosed(false)
110 , mConnected(false)
111 , mHaveStatusLine(false)
112 , mHaveAllHeaders(false)
113 , mTransactionDone(false)
114 , mResponseIsComplete(false)
115 , mDidContentStart(false)
116 , mNoContent(false)
117 , mSentData(false)
118 , mReceivedData(false)
119 , mStatusEventPending(false)
120 , mHasRequestBody(false)
121 , mProxyConnectFailed(false)
122 , mHttpResponseMatched(false)
123 , mPreserveStream(false)
124 , mDispatchedAsBlocking(false)
125 , mResponseTimeoutEnabled(true)
126 , mReportedStart(false)
127 , mReportedResponseHeader(false)
128 , mForTakeResponseHead(nullptr)
129 , mResponseHeadTaken(false)
130 , mSubmittedRatePacing(false)
131 , mPassedRatePacing(false)
132 , mSynchronousRatePaceRequest(false)
133 , mCountRecv(0)
134 , mCountSent(0)
135 , mAppId(NECKO_NO_APP_ID)
136 {
137 LOG(("Creating nsHttpTransaction @%p\n", this));
138 gHttpHandler->GetMaxPipelineObjectSize(&mMaxPipelineObjectSize);
139 }
141 nsHttpTransaction::~nsHttpTransaction()
142 {
143 LOG(("Destroying nsHttpTransaction @%p\n", this));
145 if (mTokenBucketCancel) {
146 mTokenBucketCancel->Cancel(NS_ERROR_ABORT);
147 mTokenBucketCancel = nullptr;
148 }
150 // Force the callbacks to be released right now
151 mCallbacks = nullptr;
153 NS_IF_RELEASE(mConnection);
154 NS_IF_RELEASE(mConnInfo);
156 delete mResponseHead;
157 delete mForTakeResponseHead;
158 delete mChunkedDecoder;
159 ReleaseBlockingTransaction();
160 }
162 nsHttpTransaction::Classifier
163 nsHttpTransaction::Classify()
164 {
165 if (!(mCaps & NS_HTTP_ALLOW_PIPELINING))
166 return (mClassification = CLASS_SOLO);
168 if (mRequestHead->PeekHeader(nsHttp::If_Modified_Since) ||
169 mRequestHead->PeekHeader(nsHttp::If_None_Match))
170 return (mClassification = CLASS_REVALIDATION);
172 const char *accept = mRequestHead->PeekHeader(nsHttp::Accept);
173 if (accept && !PL_strncmp(accept, "image/", 6))
174 return (mClassification = CLASS_IMAGE);
176 if (accept && !PL_strncmp(accept, "text/css", 8))
177 return (mClassification = CLASS_SCRIPT);
179 mClassification = CLASS_GENERAL;
181 int32_t queryPos = mRequestHead->RequestURI().FindChar('?');
182 if (queryPos == kNotFound) {
183 if (StringEndsWith(mRequestHead->RequestURI(),
184 NS_LITERAL_CSTRING(".js")))
185 mClassification = CLASS_SCRIPT;
186 }
187 else if (queryPos >= 3 &&
188 Substring(mRequestHead->RequestURI(), queryPos - 3, 3).
189 EqualsLiteral(".js")) {
190 mClassification = CLASS_SCRIPT;
191 }
193 return mClassification;
194 }
196 nsresult
197 nsHttpTransaction::Init(uint32_t caps,
198 nsHttpConnectionInfo *cinfo,
199 nsHttpRequestHead *requestHead,
200 nsIInputStream *requestBody,
201 bool requestBodyHasHeaders,
202 nsIEventTarget *target,
203 nsIInterfaceRequestor *callbacks,
204 nsITransportEventSink *eventsink,
205 nsIAsyncInputStream **responseBody)
206 {
207 MOZ_EVENT_TRACER_COMPOUND_NAME(static_cast<nsAHttpTransaction*>(this),
208 requestHead->PeekHeader(nsHttp::Host),
209 requestHead->RequestURI().BeginReading());
211 MOZ_EVENT_TRACER_WAIT(static_cast<nsAHttpTransaction*>(this),
212 "net::http::transaction");
213 nsresult rv;
215 LOG(("nsHttpTransaction::Init [this=%p caps=%x]\n", this, caps));
217 MOZ_ASSERT(cinfo);
218 MOZ_ASSERT(requestHead);
219 MOZ_ASSERT(target);
221 mActivityDistributor = do_GetService(NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID, &rv);
222 if (NS_FAILED(rv)) return rv;
224 bool activityDistributorActive;
225 rv = mActivityDistributor->GetIsActive(&activityDistributorActive);
226 if (NS_SUCCEEDED(rv) && activityDistributorActive) {
227 // there are some observers registered at activity distributor, gather
228 // nsISupports for the channel that called Init()
229 mChannel = do_QueryInterface(eventsink);
230 LOG(("nsHttpTransaction::Init() " \
231 "mActivityDistributor is active " \
232 "this=%p", this));
233 } else {
234 // there is no observer, so don't use it
235 activityDistributorActive = false;
236 mActivityDistributor = nullptr;
237 }
239 nsCOMPtr<nsIChannel> channel = do_QueryInterface(eventsink);
240 if (channel) {
241 bool isInBrowser;
242 NS_GetAppInfo(channel, &mAppId, &isInBrowser);
243 }
245 #ifdef MOZ_WIDGET_GONK
246 if (mAppId != NECKO_NO_APP_ID) {
247 nsCOMPtr<nsINetworkInterface> activeNetwork;
248 GetActiveNetworkInterface(activeNetwork);
249 mActiveNetwork =
250 new nsMainThreadPtrHolder<nsINetworkInterface>(activeNetwork);
251 }
252 #endif
254 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
255 do_QueryInterface(eventsink);
256 if (httpChannelInternal) {
257 rv = httpChannelInternal->GetResponseTimeoutEnabled(
258 &mResponseTimeoutEnabled);
259 if (NS_WARN_IF(NS_FAILED(rv))) {
260 return rv;
261 }
262 }
264 // create transport event sink proxy. it coalesces all events if and only
265 // if the activity observer is not active. when the observer is active
266 // we need not to coalesce any events to get all expected notifications
267 // of the transaction state, necessary for correct debugging and logging.
268 rv = net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink),
269 eventsink, target,
270 !activityDistributorActive);
271 if (NS_FAILED(rv)) return rv;
273 NS_ADDREF(mConnInfo = cinfo);
274 mCallbacks = callbacks;
275 mConsumerTarget = target;
276 mCaps = caps;
278 if (requestHead->IsHead()) {
279 mNoContent = true;
280 }
282 // Make sure that there is "Content-Length: 0" header in the requestHead
283 // in case of POST and PUT methods when there is no requestBody and
284 // requestHead doesn't contain "Transfer-Encoding" header.
285 //
286 // RFC1945 section 7.2.2:
287 // HTTP/1.0 requests containing an entity body must include a valid
288 // Content-Length header field.
289 //
290 // RFC2616 section 4.4:
291 // For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
292 // containing a message-body MUST include a valid Content-Length header
293 // field unless the server is known to be HTTP/1.1 compliant.
294 if ((requestHead->IsPost() || requestHead->IsPut()) &&
295 !requestBody && !requestHead->PeekHeader(nsHttp::Transfer_Encoding)) {
296 requestHead->SetHeader(nsHttp::Content_Length, NS_LITERAL_CSTRING("0"));
297 }
299 // grab a weak reference to the request head
300 mRequestHead = requestHead;
302 // make sure we eliminate any proxy specific headers from
303 // the request if we are using CONNECT
304 bool pruneProxyHeaders = cinfo->UsingConnect();
306 mReqHeaderBuf.Truncate();
307 requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders);
309 #if defined(PR_LOGGING)
310 if (LOG3_ENABLED()) {
311 LOG3(("http request [\n"));
312 LogHeaders(mReqHeaderBuf.get());
313 LOG3(("]\n"));
314 }
315 #endif
317 // If the request body does not include headers or if there is no request
318 // body, then we must add the header/body separator manually.
319 if (!requestBodyHasHeaders || !requestBody)
320 mReqHeaderBuf.AppendLiteral("\r\n");
322 // report the request header
323 if (mActivityDistributor)
324 mActivityDistributor->ObserveActivity(
325 mChannel,
326 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
327 NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER,
328 PR_Now(), 0,
329 mReqHeaderBuf);
331 // Create a string stream for the request header buf (the stream holds
332 // a non-owning reference to the request header data, so we MUST keep
333 // mReqHeaderBuf around).
334 nsCOMPtr<nsIInputStream> headers;
335 rv = NS_NewByteInputStream(getter_AddRefs(headers),
336 mReqHeaderBuf.get(),
337 mReqHeaderBuf.Length());
338 if (NS_FAILED(rv)) return rv;
340 if (requestBody) {
341 mHasRequestBody = true;
343 // wrap the headers and request body in a multiplexed input stream.
344 nsCOMPtr<nsIMultiplexInputStream> multi =
345 do_CreateInstance(kMultiplexInputStream, &rv);
346 if (NS_FAILED(rv)) return rv;
348 rv = multi->AppendStream(headers);
349 if (NS_FAILED(rv)) return rv;
351 rv = multi->AppendStream(requestBody);
352 if (NS_FAILED(rv)) return rv;
354 // wrap the multiplexed input stream with a buffered input stream, so
355 // that we write data in the largest chunks possible. this is actually
356 // necessary to workaround some common server bugs (see bug 137155).
357 rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream), multi,
358 nsIOService::gDefaultSegmentSize);
359 if (NS_FAILED(rv)) return rv;
360 }
361 else
362 mRequestStream = headers;
364 rv = mRequestStream->Available(&mRequestSize);
365 if (NS_FAILED(rv)) return rv;
367 // create pipe for response stream
368 rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
369 getter_AddRefs(mPipeOut),
370 true, true,
371 nsIOService::gDefaultSegmentSize,
372 nsIOService::gDefaultSegmentCount);
373 if (NS_FAILED(rv)) return rv;
375 Classify();
377 NS_ADDREF(*responseBody = mPipeIn);
378 return NS_OK;
379 }
381 // This method should only be used on the socket thread
382 nsAHttpConnection *
383 nsHttpTransaction::Connection()
384 {
385 return mConnection;
386 }
388 already_AddRefed<nsAHttpConnection>
389 nsHttpTransaction::GetConnectionReference()
390 {
391 MutexAutoLock lock(mLock);
392 nsRefPtr<nsAHttpConnection> connection = mConnection;
393 return connection.forget();
394 }
396 nsHttpResponseHead *
397 nsHttpTransaction::TakeResponseHead()
398 {
399 MOZ_ASSERT(!mResponseHeadTaken, "TakeResponseHead called 2x");
401 // Lock RestartInProgress() and TakeResponseHead() against main thread
402 MutexAutoLock lock(*nsHttp::GetLock());
404 mResponseHeadTaken = true;
406 // Prefer mForTakeResponseHead over mResponseHead. It is always a complete
407 // set of headers.
408 nsHttpResponseHead *head;
409 if (mForTakeResponseHead) {
410 head = mForTakeResponseHead;
411 mForTakeResponseHead = nullptr;
412 return head;
413 }
415 // Even in OnStartRequest() the headers won't be available if we were
416 // canceled
417 if (!mHaveAllHeaders) {
418 NS_WARNING("response headers not available or incomplete");
419 return nullptr;
420 }
422 head = mResponseHead;
423 mResponseHead = nullptr;
424 return head;
425 }
427 void
428 nsHttpTransaction::SetProxyConnectFailed()
429 {
430 mProxyConnectFailed = true;
431 }
433 nsHttpRequestHead *
434 nsHttpTransaction::RequestHead()
435 {
436 return mRequestHead;
437 }
439 uint32_t
440 nsHttpTransaction::Http1xTransactionCount()
441 {
442 return 1;
443 }
445 nsresult
446 nsHttpTransaction::TakeSubTransactions(
447 nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
448 {
449 return NS_ERROR_NOT_IMPLEMENTED;
450 }
452 //----------------------------------------------------------------------------
453 // nsHttpTransaction::nsAHttpTransaction
454 //----------------------------------------------------------------------------
456 void
457 nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
458 {
459 {
460 MutexAutoLock lock(mLock);
461 NS_IF_RELEASE(mConnection);
462 NS_IF_ADDREF(mConnection = conn);
463 }
465 if (conn) {
466 MOZ_EVENT_TRACER_EXEC(static_cast<nsAHttpTransaction*>(this),
467 "net::http::transaction");
468 }
469 }
471 void
472 nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
473 {
474 MutexAutoLock lock(mLock);
475 NS_IF_ADDREF(*cb = mCallbacks);
476 }
478 void
479 nsHttpTransaction::SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks)
480 {
481 {
482 MutexAutoLock lock(mLock);
483 mCallbacks = aCallbacks;
484 }
486 if (gSocketTransportService) {
487 nsRefPtr<UpdateSecurityCallbacks> event = new UpdateSecurityCallbacks(this, aCallbacks);
488 gSocketTransportService->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
489 }
490 }
492 void
493 nsHttpTransaction::OnTransportStatus(nsITransport* transport,
494 nsresult status, uint64_t progress)
495 {
496 LOG(("nsHttpTransaction::OnSocketStatus [this=%p status=%x progress=%llu]\n",
497 this, status, progress));
499 if (TimingEnabled()) {
500 if (status == NS_NET_STATUS_RESOLVING_HOST) {
501 mTimings.domainLookupStart = TimeStamp::Now();
502 } else if (status == NS_NET_STATUS_RESOLVED_HOST) {
503 mTimings.domainLookupEnd = TimeStamp::Now();
504 } else if (status == NS_NET_STATUS_CONNECTING_TO) {
505 mTimings.connectStart = TimeStamp::Now();
506 } else if (status == NS_NET_STATUS_CONNECTED_TO) {
507 mTimings.connectEnd = TimeStamp::Now();
508 }
509 }
511 if (!mTransportSink)
512 return;
514 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
516 // Need to do this before the STATUS_RECEIVING_FROM check below, to make
517 // sure that the activity distributor gets told about all status events.
518 if (mActivityDistributor) {
519 // upon STATUS_WAITING_FOR; report request body sent
520 if ((mHasRequestBody) &&
521 (status == NS_NET_STATUS_WAITING_FOR))
522 mActivityDistributor->ObserveActivity(
523 mChannel,
524 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
525 NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_BODY_SENT,
526 PR_Now(), 0, EmptyCString());
528 // report the status and progress
529 if (!mRestartInProgressVerifier.IsDiscardingContent())
530 mActivityDistributor->ObserveActivity(
531 mChannel,
532 NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
533 static_cast<uint32_t>(status),
534 PR_Now(),
535 progress,
536 EmptyCString());
537 }
539 // nsHttpChannel synthesizes progress events in OnDataAvailable
540 if (status == NS_NET_STATUS_RECEIVING_FROM)
541 return;
543 uint64_t progressMax;
545 if (status == NS_NET_STATUS_SENDING_TO) {
546 // suppress progress when only writing request headers
547 if (!mHasRequestBody)
548 return;
550 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
551 MOZ_ASSERT(seekable, "Request stream isn't seekable?!?");
553 int64_t prog = 0;
554 seekable->Tell(&prog);
555 progress = prog;
557 // when uploading, we include the request headers in the progress
558 // notifications.
559 progressMax = mRequestSize; // XXX mRequestSize is 32-bit!
560 }
561 else {
562 progress = 0;
563 progressMax = 0;
564 }
566 mTransportSink->OnTransportStatus(transport, status, progress, progressMax);
567 }
569 bool
570 nsHttpTransaction::IsDone()
571 {
572 return mTransactionDone;
573 }
575 nsresult
576 nsHttpTransaction::Status()
577 {
578 return mStatus;
579 }
581 uint32_t
582 nsHttpTransaction::Caps()
583 {
584 return mCaps & ~mCapsToClear;
585 }
587 void
588 nsHttpTransaction::SetDNSWasRefreshed()
589 {
590 MOZ_ASSERT(NS_IsMainThread(), "SetDNSWasRefreshed on main thread only!");
591 mCapsToClear |= NS_HTTP_REFRESH_DNS;
592 }
594 uint64_t
595 nsHttpTransaction::Available()
596 {
597 uint64_t size;
598 if (NS_FAILED(mRequestStream->Available(&size)))
599 size = 0;
600 return size;
601 }
603 NS_METHOD
604 nsHttpTransaction::ReadRequestSegment(nsIInputStream *stream,
605 void *closure,
606 const char *buf,
607 uint32_t offset,
608 uint32_t count,
609 uint32_t *countRead)
610 {
611 nsHttpTransaction *trans = (nsHttpTransaction *) closure;
612 nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead);
613 if (NS_FAILED(rv)) return rv;
615 if (trans->TimingEnabled() && trans->mTimings.requestStart.IsNull()) {
616 // First data we're sending -> this is requestStart
617 trans->mTimings.requestStart = TimeStamp::Now();
618 }
620 if (!trans->mSentData) {
621 MOZ_EVENT_TRACER_MARK(static_cast<nsAHttpTransaction*>(trans),
622 "net::http::first-write");
623 }
625 trans->CountSentBytes(*countRead);
626 trans->mSentData = true;
627 return NS_OK;
628 }
630 nsresult
631 nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
632 uint32_t count, uint32_t *countRead)
633 {
634 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
636 if (mTransactionDone) {
637 *countRead = 0;
638 return mStatus;
639 }
641 if (!mConnected) {
642 mConnected = true;
643 mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
644 }
646 mReader = reader;
648 nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
650 mReader = nullptr;
652 // if read would block then we need to AsyncWait on the request stream.
653 // have callback occur on socket thread so we stay synchronized.
654 if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
655 nsCOMPtr<nsIAsyncInputStream> asyncIn =
656 do_QueryInterface(mRequestStream);
657 if (asyncIn) {
658 nsCOMPtr<nsIEventTarget> target;
659 gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
660 if (target)
661 asyncIn->AsyncWait(this, 0, 0, target);
662 else {
663 NS_ERROR("no socket thread event target");
664 rv = NS_ERROR_UNEXPECTED;
665 }
666 }
667 }
669 return rv;
670 }
672 NS_METHOD
673 nsHttpTransaction::WritePipeSegment(nsIOutputStream *stream,
674 void *closure,
675 char *buf,
676 uint32_t offset,
677 uint32_t count,
678 uint32_t *countWritten)
679 {
680 nsHttpTransaction *trans = (nsHttpTransaction *) closure;
682 if (trans->mTransactionDone)
683 return NS_BASE_STREAM_CLOSED; // stop iterating
685 if (trans->TimingEnabled() && trans->mTimings.responseStart.IsNull()) {
686 trans->mTimings.responseStart = TimeStamp::Now();
687 }
689 nsresult rv;
690 //
691 // OK, now let the caller fill this segment with data.
692 //
693 rv = trans->mWriter->OnWriteSegment(buf, count, countWritten);
694 if (NS_FAILED(rv)) return rv; // caller didn't want to write anything
696 if (!trans->mReceivedData) {
697 MOZ_EVENT_TRACER_MARK(static_cast<nsAHttpTransaction*>(trans),
698 "net::http::first-read");
699 }
701 MOZ_ASSERT(*countWritten > 0, "bad writer");
702 trans->CountRecvBytes(*countWritten);
703 trans->mReceivedData = true;
705 // Let the transaction "play" with the buffer. It is free to modify
706 // the contents of the buffer and/or modify countWritten.
707 // - Bytes in HTTP headers don't count towards countWritten, so the input
708 // side of pipe (aka nsHttpChannel's mTransactionPump) won't hit
709 // OnInputStreamReady until all headers have been parsed.
710 //
711 rv = trans->ProcessData(buf, *countWritten, countWritten);
712 if (NS_FAILED(rv))
713 trans->Close(rv);
715 return rv; // failure code only stops WriteSegments; it is not propagated.
716 }
718 nsresult
719 nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
720 uint32_t count, uint32_t *countWritten)
721 {
722 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
724 if (mTransactionDone)
725 return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
727 mWriter = writer;
729 nsresult rv = mPipeOut->WriteSegments(WritePipeSegment, this, count, countWritten);
731 mWriter = nullptr;
733 // if pipe would block then we need to AsyncWait on it. have callback
734 // occur on socket thread so we stay synchronized.
735 if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
736 nsCOMPtr<nsIEventTarget> target;
737 gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
738 if (target)
739 mPipeOut->AsyncWait(this, 0, 0, target);
740 else {
741 NS_ERROR("no socket thread event target");
742 rv = NS_ERROR_UNEXPECTED;
743 }
744 }
746 return rv;
747 }
749 nsresult
750 nsHttpTransaction::SaveNetworkStats(bool enforce)
751 {
752 #ifdef MOZ_WIDGET_GONK
753 // Check if active network and appid are valid.
754 if (!mActiveNetwork || mAppId == NECKO_NO_APP_ID) {
755 return NS_OK;
756 }
758 if (mCountRecv <= 0 && mCountSent <= 0) {
759 // There is no traffic, no need to save.
760 return NS_OK;
761 }
763 // If |enforce| is false, the traffic amount is saved
764 // only when the total amount exceeds the predefined
765 // threshold.
766 uint64_t totalBytes = mCountRecv + mCountSent;
767 if (!enforce && totalBytes < NETWORK_STATS_THRESHOLD) {
768 return NS_OK;
769 }
771 // Create the event to save the network statistics.
772 // the event is then dispathed to the main thread.
773 nsRefPtr<nsRunnable> event =
774 new SaveNetworkStatsEvent(mAppId, mActiveNetwork,
775 mCountRecv, mCountSent, false);
776 NS_DispatchToMainThread(event);
778 // Reset the counters after saving.
779 mCountSent = 0;
780 mCountRecv = 0;
782 return NS_OK;
783 #else
784 return NS_ERROR_NOT_IMPLEMENTED;
785 #endif
786 }
788 void
789 nsHttpTransaction::Close(nsresult reason)
790 {
791 LOG(("nsHttpTransaction::Close [this=%p reason=%x]\n", this, reason));
793 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
795 if (mClosed) {
796 LOG((" already closed\n"));
797 return;
798 }
800 if (mTokenBucketCancel) {
801 mTokenBucketCancel->Cancel(reason);
802 mTokenBucketCancel = nullptr;
803 }
805 if (mActivityDistributor) {
806 // report the reponse is complete if not already reported
807 if (!mResponseIsComplete)
808 mActivityDistributor->ObserveActivity(
809 mChannel,
810 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
811 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
812 PR_Now(),
813 static_cast<uint64_t>(mContentRead),
814 EmptyCString());
816 // report that this transaction is closing
817 mActivityDistributor->ObserveActivity(
818 mChannel,
819 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
820 NS_HTTP_ACTIVITY_SUBTYPE_TRANSACTION_CLOSE,
821 PR_Now(), 0, EmptyCString());
822 }
824 // we must no longer reference the connection! find out if the
825 // connection was being reused before letting it go.
826 bool connReused = false;
827 if (mConnection)
828 connReused = mConnection->IsReused();
829 mConnected = false;
831 //
832 // if the connection was reset or closed before we wrote any part of the
833 // request or if we wrote the request but didn't receive any part of the
834 // response and the connection was being reused, then we can (and really
835 // should) assume that we wrote to a stale connection and we must therefore
836 // repeat the request over a new connection.
837 //
838 // NOTE: the conditions under which we will automatically retry the HTTP
839 // request have to be carefully selected to avoid duplication of the
840 // request from the point-of-view of the server. such duplication could
841 // have dire consequences including repeated purchases, etc.
842 //
843 // NOTE: because of the way SSL proxy CONNECT is implemented, it is
844 // possible that the transaction may have received data without having
845 // sent any data. for this reason, mSendData == FALSE does not imply
846 // mReceivedData == FALSE. (see bug 203057 for more info.)
847 //
848 if (reason == NS_ERROR_NET_RESET || reason == NS_OK) {
850 // reallySentData is meant to separate the instances where data has
851 // been sent by this transaction but buffered at a higher level while
852 // a TLS session (perhaps via a tunnel) is setup.
853 bool reallySentData =
854 mSentData && (!mConnection || mConnection->BytesWritten());
856 if (!mReceivedData &&
857 (!reallySentData || connReused || mPipelinePosition)) {
858 // if restarting fails, then we must proceed to close the pipe,
859 // which will notify the channel that the transaction failed.
861 if (mPipelinePosition) {
862 gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
863 mConnInfo, nsHttpConnectionMgr::RedCanceledPipeline,
864 nullptr, 0);
865 }
866 if (NS_SUCCEEDED(Restart()))
867 return;
868 }
869 else if (!mResponseIsComplete && mPipelinePosition &&
870 reason == NS_ERROR_NET_RESET) {
871 // due to unhandled rst on a pipeline - safe to
872 // restart as only idempotent is found there
874 gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
875 mConnInfo, nsHttpConnectionMgr::RedCorruptedContent, nullptr, 0);
876 if (NS_SUCCEEDED(RestartInProgress()))
877 return;
878 }
879 }
881 bool relConn = true;
882 if (NS_SUCCEEDED(reason)) {
883 if (!mResponseIsComplete) {
884 // The response has not been delimited with a high-confidence
885 // algorithm like Content-Length or Chunked Encoding. We
886 // need to use a strong framing mechanism to pipeline.
887 gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
888 mConnInfo, nsHttpConnectionMgr::BadInsufficientFraming,
889 nullptr, mClassification);
890 }
891 else if (mPipelinePosition) {
892 // report this success as feedback
893 gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
894 mConnInfo, nsHttpConnectionMgr::GoodCompletedOK,
895 nullptr, mPipelinePosition);
896 }
898 // the server has not sent the final \r\n terminating the header
899 // section, and there may still be a header line unparsed. let's make
900 // sure we parse the remaining header line, and then hopefully, the
901 // response will be usable (see bug 88792).
902 if (!mHaveAllHeaders) {
903 char data = '\n';
904 uint32_t unused;
905 ParseHead(&data, 1, &unused);
907 if (mResponseHead->Version() == NS_HTTP_VERSION_0_9) {
908 // Reject 0 byte HTTP/0.9 Responses - bug 423506
909 LOG(("nsHttpTransaction::Close %p 0 Byte 0.9 Response", this));
910 reason = NS_ERROR_NET_RESET;
911 }
912 }
914 // honor the sticky connection flag...
915 if (mCaps & NS_HTTP_STICKY_CONNECTION)
916 relConn = false;
917 }
919 // mTimings.responseEnd is normally recorded based on the end of a
920 // HTTP delimiter such as chunked-encodings or content-length. However,
921 // EOF or an error still require an end time be recorded.
922 if (TimingEnabled() &&
923 mTimings.responseEnd.IsNull() && !mTimings.responseStart.IsNull())
924 mTimings.responseEnd = TimeStamp::Now();
926 if (relConn && mConnection) {
927 MutexAutoLock lock(mLock);
928 NS_RELEASE(mConnection);
929 }
931 // save network statistics in the end of transaction
932 SaveNetworkStats(true);
934 mStatus = reason;
935 mTransactionDone = true; // forcibly flag the transaction as complete
936 mClosed = true;
937 ReleaseBlockingTransaction();
939 // release some resources that we no longer need
940 mRequestStream = nullptr;
941 mReqHeaderBuf.Truncate();
942 mLineBuf.Truncate();
943 if (mChunkedDecoder) {
944 delete mChunkedDecoder;
945 mChunkedDecoder = nullptr;
946 }
948 // closing this pipe triggers the channel's OnStopRequest method.
949 mPipeOut->CloseWithStatus(reason);
951 MOZ_EVENT_TRACER_DONE(static_cast<nsAHttpTransaction*>(this),
952 "net::http::transaction");
953 }
955 nsresult
956 nsHttpTransaction::AddTransaction(nsAHttpTransaction *trans)
957 {
958 return NS_ERROR_NOT_IMPLEMENTED;
959 }
961 uint32_t
962 nsHttpTransaction::PipelineDepth()
963 {
964 return IsDone() ? 0 : 1;
965 }
967 nsresult
968 nsHttpTransaction::SetPipelinePosition(int32_t position)
969 {
970 mPipelinePosition = position;
971 return NS_OK;
972 }
974 int32_t
975 nsHttpTransaction::PipelinePosition()
976 {
977 return mPipelinePosition;
978 }
980 bool // NOTE BASE CLASS
981 nsAHttpTransaction::ResponseTimeoutEnabled() const
982 {
983 return false;
984 }
986 PRIntervalTime // NOTE BASE CLASS
987 nsAHttpTransaction::ResponseTimeout()
988 {
989 return gHttpHandler->ResponseTimeout();
990 }
992 bool
993 nsHttpTransaction::ResponseTimeoutEnabled() const
994 {
995 return mResponseTimeoutEnabled;
996 }
998 //-----------------------------------------------------------------------------
999 // nsHttpTransaction <private>
1000 //-----------------------------------------------------------------------------
1002 nsresult
1003 nsHttpTransaction::RestartInProgress()
1004 {
1005 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
1007 if ((mRestartCount + 1) >= gHttpHandler->MaxRequestAttempts()) {
1008 LOG(("nsHttpTransaction::RestartInProgress() "
1009 "reached max request attempts, failing transaction %p\n", this));
1010 return NS_ERROR_NET_RESET;
1011 }
1013 // Lock RestartInProgress() and TakeResponseHead() against main thread
1014 MutexAutoLock lock(*nsHttp::GetLock());
1016 // Don't try and RestartInProgress() things that haven't gotten a response
1017 // header yet. Those should be handled under the normal restart() path if
1018 // they are eligible.
1019 if (!mHaveAllHeaders)
1020 return NS_ERROR_NET_RESET;
1022 // don't try and restart 0.9 or non 200/Get HTTP/1
1023 if (!mRestartInProgressVerifier.IsSetup())
1024 return NS_ERROR_NET_RESET;
1026 LOG(("Will restart transaction %p and skip first %lld bytes, "
1027 "old Content-Length %lld",
1028 this, mContentRead, mContentLength));
1030 mRestartInProgressVerifier.SetAlreadyProcessed(
1031 std::max(mRestartInProgressVerifier.AlreadyProcessed(), mContentRead));
1033 if (!mResponseHeadTaken && !mForTakeResponseHead) {
1034 // TakeResponseHeader() has not been called yet and this
1035 // is the first restart. Store the resp headers exclusively
1036 // for TakeResponseHead() which is called from the main thread and
1037 // could happen at any time - so we can't continue to modify those
1038 // headers (which restarting will effectively do)
1039 mForTakeResponseHead = mResponseHead;
1040 mResponseHead = nullptr;
1041 }
1043 if (mResponseHead) {
1044 mResponseHead->Reset();
1045 }
1047 mContentRead = 0;
1048 mContentLength = -1;
1049 delete mChunkedDecoder;
1050 mChunkedDecoder = nullptr;
1051 mHaveStatusLine = false;
1052 mHaveAllHeaders = false;
1053 mHttpResponseMatched = false;
1054 mResponseIsComplete = false;
1055 mDidContentStart = false;
1056 mNoContent = false;
1057 mSentData = false;
1058 mReceivedData = false;
1060 return Restart();
1061 }
1063 nsresult
1064 nsHttpTransaction::Restart()
1065 {
1066 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
1068 // limit the number of restart attempts - bug 92224
1069 if (++mRestartCount >= gHttpHandler->MaxRequestAttempts()) {
1070 LOG(("reached max request attempts, failing transaction @%p\n", this));
1071 return NS_ERROR_NET_RESET;
1072 }
1074 LOG(("restarting transaction @%p\n", this));
1076 // rewind streams in case we already wrote out the request
1077 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
1078 if (seekable)
1079 seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1081 // clear old connection state...
1082 mSecurityInfo = 0;
1083 if (mConnection) {
1084 MutexAutoLock lock(mLock);
1085 NS_RELEASE(mConnection);
1086 }
1088 // disable pipelining for the next attempt in case pipelining caused the
1089 // reset. this is being overly cautious since we don't know if pipelining
1090 // was the problem here.
1091 mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
1092 SetPipelinePosition(0);
1094 return gHttpHandler->InitiateTransaction(this, mPriority);
1095 }
1097 char *
1098 nsHttpTransaction::LocateHttpStart(char *buf, uint32_t len,
1099 bool aAllowPartialMatch)
1100 {
1101 MOZ_ASSERT(!aAllowPartialMatch || mLineBuf.IsEmpty());
1103 static const char HTTPHeader[] = "HTTP/1.";
1104 static const uint32_t HTTPHeaderLen = sizeof(HTTPHeader) - 1;
1105 static const char HTTP2Header[] = "HTTP/2.0";
1106 static const uint32_t HTTP2HeaderLen = sizeof(HTTP2Header) - 1;
1107 // ShoutCast ICY is treated as HTTP/1.0
1108 static const char ICYHeader[] = "ICY ";
1109 static const uint32_t ICYHeaderLen = sizeof(ICYHeader) - 1;
1111 if (aAllowPartialMatch && (len < HTTPHeaderLen))
1112 return (PL_strncasecmp(buf, HTTPHeader, len) == 0) ? buf : nullptr;
1114 // mLineBuf can contain partial match from previous search
1115 if (!mLineBuf.IsEmpty()) {
1116 MOZ_ASSERT(mLineBuf.Length() < HTTPHeaderLen);
1117 int32_t checkChars = std::min(len, HTTPHeaderLen - mLineBuf.Length());
1118 if (PL_strncasecmp(buf, HTTPHeader + mLineBuf.Length(),
1119 checkChars) == 0) {
1120 mLineBuf.Append(buf, checkChars);
1121 if (mLineBuf.Length() == HTTPHeaderLen) {
1122 // We've found whole HTTPHeader sequence. Return pointer at the
1123 // end of matched sequence since it is stored in mLineBuf.
1124 return (buf + checkChars);
1125 }
1126 // Response matches pattern but is still incomplete.
1127 return 0;
1128 }
1129 // Previous partial match together with new data doesn't match the
1130 // pattern. Start the search again.
1131 mLineBuf.Truncate();
1132 }
1134 bool firstByte = true;
1135 while (len > 0) {
1136 if (PL_strncasecmp(buf, HTTPHeader, std::min<uint32_t>(len, HTTPHeaderLen)) == 0) {
1137 if (len < HTTPHeaderLen) {
1138 // partial HTTPHeader sequence found
1139 // save partial match to mLineBuf
1140 mLineBuf.Assign(buf, len);
1141 return 0;
1142 }
1144 // whole HTTPHeader sequence found
1145 return buf;
1146 }
1148 // At least "SmarterTools/2.0.3974.16813" generates nonsensical
1149 // HTTP/2.0 responses to our HTTP/1 requests. Treat the minimal case of
1150 // it as HTTP/1.1 to be compatible with old versions of ourselves and
1151 // other browsers
1153 if (firstByte && !mInvalidResponseBytesRead && len >= HTTP2HeaderLen &&
1154 (PL_strncasecmp(buf, HTTP2Header, HTTP2HeaderLen) == 0)) {
1155 LOG(("nsHttpTransaction:: Identified HTTP/2.0 treating as 1.x\n"));
1156 return buf;
1157 }
1159 // Treat ICY (AOL/Nullsoft ShoutCast) non-standard header in same fashion
1160 // as HTTP/2.0 is treated above. This will allow "ICY " to be interpretted
1161 // as HTTP/1.0 in nsHttpResponseHead::ParseVersion
1163 if (firstByte && !mInvalidResponseBytesRead && len >= ICYHeaderLen &&
1164 (PL_strncasecmp(buf, ICYHeader, ICYHeaderLen) == 0)) {
1165 LOG(("nsHttpTransaction:: Identified ICY treating as HTTP/1.0\n"));
1166 return buf;
1167 }
1169 if (!nsCRT::IsAsciiSpace(*buf))
1170 firstByte = false;
1171 buf++;
1172 len--;
1173 }
1174 return 0;
1175 }
1177 nsresult
1178 nsHttpTransaction::ParseLine(char *line)
1179 {
1180 LOG(("nsHttpTransaction::ParseLine [%s]\n", line));
1181 nsresult rv = NS_OK;
1183 if (!mHaveStatusLine) {
1184 mResponseHead->ParseStatusLine(line);
1185 mHaveStatusLine = true;
1186 // XXX this should probably never happen
1187 if (mResponseHead->Version() == NS_HTTP_VERSION_0_9)
1188 mHaveAllHeaders = true;
1189 }
1190 else {
1191 rv = mResponseHead->ParseHeaderLine(line);
1192 }
1193 return rv;
1194 }
1196 nsresult
1197 nsHttpTransaction::ParseLineSegment(char *segment, uint32_t len)
1198 {
1199 NS_PRECONDITION(!mHaveAllHeaders, "already have all headers");
1201 if (!mLineBuf.IsEmpty() && mLineBuf.Last() == '\n') {
1202 // trim off the new line char, and if this segment is
1203 // not a continuation of the previous or if we haven't
1204 // parsed the status line yet, then parse the contents
1205 // of mLineBuf.
1206 mLineBuf.Truncate(mLineBuf.Length() - 1);
1207 if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
1208 nsresult rv = ParseLine(mLineBuf.BeginWriting());
1209 mLineBuf.Truncate();
1210 if (NS_FAILED(rv)) {
1211 gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
1212 mConnInfo, nsHttpConnectionMgr::RedCorruptedContent,
1213 nullptr, 0);
1214 return rv;
1215 }
1216 }
1217 }
1219 // append segment to mLineBuf...
1220 mLineBuf.Append(segment, len);
1222 // a line buf with only a new line char signifies the end of headers.
1223 if (mLineBuf.First() == '\n') {
1224 mLineBuf.Truncate();
1225 // discard this response if it is a 100 continue or other 1xx status.
1226 uint16_t status = mResponseHead->Status();
1227 if ((status != 101) && (status / 100 == 1)) {
1228 LOG(("ignoring 1xx response\n"));
1229 mHaveStatusLine = false;
1230 mHttpResponseMatched = false;
1231 mConnection->SetLastTransactionExpectedNoContent(true);
1232 mResponseHead->Reset();
1233 return NS_OK;
1234 }
1235 mHaveAllHeaders = true;
1236 }
1237 return NS_OK;
1238 }
1240 nsresult
1241 nsHttpTransaction::ParseHead(char *buf,
1242 uint32_t count,
1243 uint32_t *countRead)
1244 {
1245 nsresult rv;
1246 uint32_t len;
1247 char *eol;
1249 LOG(("nsHttpTransaction::ParseHead [count=%u]\n", count));
1251 *countRead = 0;
1253 NS_PRECONDITION(!mHaveAllHeaders, "oops");
1255 // allocate the response head object if necessary
1256 if (!mResponseHead) {
1257 mResponseHead = new nsHttpResponseHead();
1258 if (!mResponseHead)
1259 return NS_ERROR_OUT_OF_MEMORY;
1261 // report that we have a least some of the response
1262 if (mActivityDistributor && !mReportedStart) {
1263 mReportedStart = true;
1264 mActivityDistributor->ObserveActivity(
1265 mChannel,
1266 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
1267 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START,
1268 PR_Now(), 0, EmptyCString());
1269 }
1270 }
1272 if (!mHttpResponseMatched) {
1273 // Normally we insist on seeing HTTP/1.x in the first few bytes,
1274 // but if we are on a persistent connection and the previous transaction
1275 // was not supposed to have any content then we need to be prepared
1276 // to skip over a response body that the server may have sent even
1277 // though it wasn't allowed.
1278 if (!mConnection || !mConnection->LastTransactionExpectedNoContent()) {
1279 // tolerate only minor junk before the status line
1280 mHttpResponseMatched = true;
1281 char *p = LocateHttpStart(buf, std::min<uint32_t>(count, 11), true);
1282 if (!p) {
1283 // Treat any 0.9 style response of a put as a failure.
1284 if (mRequestHead->IsPut())
1285 return NS_ERROR_ABORT;
1287 mResponseHead->ParseStatusLine("");
1288 mHaveStatusLine = true;
1289 mHaveAllHeaders = true;
1290 return NS_OK;
1291 }
1292 if (p > buf) {
1293 // skip over the junk
1294 mInvalidResponseBytesRead += p - buf;
1295 *countRead = p - buf;
1296 buf = p;
1297 }
1298 }
1299 else {
1300 char *p = LocateHttpStart(buf, count, false);
1301 if (p) {
1302 mInvalidResponseBytesRead += p - buf;
1303 *countRead = p - buf;
1304 buf = p;
1305 mHttpResponseMatched = true;
1306 } else {
1307 mInvalidResponseBytesRead += count;
1308 *countRead = count;
1309 if (mInvalidResponseBytesRead > MAX_INVALID_RESPONSE_BODY_SIZE) {
1310 LOG(("nsHttpTransaction::ParseHead() "
1311 "Cannot find Response Header\n"));
1312 // cannot go back and call this 0.9 anymore as we
1313 // have thrown away a lot of the leading junk
1314 return NS_ERROR_ABORT;
1315 }
1316 return NS_OK;
1317 }
1318 }
1319 }
1320 // otherwise we can assume that we don't have a HTTP/0.9 response.
1322 MOZ_ASSERT (mHttpResponseMatched);
1323 while ((eol = static_cast<char *>(memchr(buf, '\n', count - *countRead))) != nullptr) {
1324 // found line in range [buf:eol]
1325 len = eol - buf + 1;
1327 *countRead += len;
1329 // actually, the line is in the range [buf:eol-1]
1330 if ((eol > buf) && (*(eol-1) == '\r'))
1331 len--;
1333 buf[len-1] = '\n';
1334 rv = ParseLineSegment(buf, len);
1335 if (NS_FAILED(rv))
1336 return rv;
1338 if (mHaveAllHeaders)
1339 return NS_OK;
1341 // skip over line
1342 buf = eol + 1;
1344 if (!mHttpResponseMatched) {
1345 // a 100 class response has caused us to throw away that set of
1346 // response headers and look for the next response
1347 return NS_ERROR_NET_INTERRUPT;
1348 }
1349 }
1351 // do something about a partial header line
1352 if (!mHaveAllHeaders && (len = count - *countRead)) {
1353 *countRead = count;
1354 // ignore a trailing carriage return, and don't bother calling
1355 // ParseLineSegment if buf only contains a carriage return.
1356 if ((buf[len-1] == '\r') && (--len == 0))
1357 return NS_OK;
1358 rv = ParseLineSegment(buf, len);
1359 if (NS_FAILED(rv))
1360 return rv;
1361 }
1362 return NS_OK;
1363 }
1365 // called on the socket thread
1366 nsresult
1367 nsHttpTransaction::HandleContentStart()
1368 {
1369 LOG(("nsHttpTransaction::HandleContentStart [this=%p]\n", this));
1371 if (mResponseHead) {
1372 #if defined(PR_LOGGING)
1373 if (LOG3_ENABLED()) {
1374 LOG3(("http response [\n"));
1375 nsAutoCString headers;
1376 mResponseHead->Flatten(headers, false);
1377 LogHeaders(headers.get());
1378 LOG3(("]\n"));
1379 }
1380 #endif
1381 // Save http version, mResponseHead isn't available anymore after
1382 // TakeResponseHead() is called
1383 mHttpVersion = mResponseHead->Version();
1385 // notify the connection, give it a chance to cause a reset.
1386 bool reset = false;
1387 if (!mRestartInProgressVerifier.IsSetup())
1388 mConnection->OnHeadersAvailable(this, mRequestHead, mResponseHead, &reset);
1390 // looks like we should ignore this response, resetting...
1391 if (reset) {
1392 LOG(("resetting transaction's response head\n"));
1393 mHaveAllHeaders = false;
1394 mHaveStatusLine = false;
1395 mReceivedData = false;
1396 mSentData = false;
1397 mHttpResponseMatched = false;
1398 mResponseHead->Reset();
1399 // wait to be called again...
1400 return NS_OK;
1401 }
1403 // check if this is a no-content response
1404 switch (mResponseHead->Status()) {
1405 case 101:
1406 mPreserveStream = true; // fall through to other no content
1407 case 204:
1408 case 205:
1409 case 304:
1410 mNoContent = true;
1411 LOG(("this response should not contain a body.\n"));
1412 break;
1413 }
1415 if (mResponseHead->Status() == 200 &&
1416 mConnection->IsProxyConnectInProgress()) {
1417 // successful CONNECTs do not have response bodies
1418 mNoContent = true;
1419 }
1420 mConnection->SetLastTransactionExpectedNoContent(mNoContent);
1421 if (mInvalidResponseBytesRead)
1422 gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
1423 mConnInfo, nsHttpConnectionMgr::BadInsufficientFraming,
1424 nullptr, mClassification);
1426 if (mNoContent)
1427 mContentLength = 0;
1428 else {
1429 // grab the content-length from the response headers
1430 mContentLength = mResponseHead->ContentLength();
1432 if ((mClassification != CLASS_SOLO) &&
1433 (mContentLength > mMaxPipelineObjectSize))
1434 CancelPipeline(nsHttpConnectionMgr::BadUnexpectedLarge);
1436 // handle chunked encoding here, so we'll know immediately when
1437 // we're done with the socket. please note that _all_ other
1438 // decoding is done when the channel receives the content data
1439 // so as not to block the socket transport thread too much.
1440 // ignore chunked responses from HTTP/1.0 servers and proxies.
1441 if (mResponseHead->Version() >= NS_HTTP_VERSION_1_1 &&
1442 mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) {
1443 // we only support the "chunked" transfer encoding right now.
1444 mChunkedDecoder = new nsHttpChunkedDecoder();
1445 if (!mChunkedDecoder)
1446 return NS_ERROR_OUT_OF_MEMORY;
1447 LOG(("chunked decoder created\n"));
1448 // Ignore server specified Content-Length.
1449 mContentLength = -1;
1450 }
1451 #if defined(PR_LOGGING)
1452 else if (mContentLength == int64_t(-1))
1453 LOG(("waiting for the server to close the connection.\n"));
1454 #endif
1455 }
1456 if (mRestartInProgressVerifier.IsSetup() &&
1457 !mRestartInProgressVerifier.Verify(mContentLength, mResponseHead)) {
1458 LOG(("Restart in progress subsequent transaction failed to match"));
1459 return NS_ERROR_ABORT;
1460 }
1461 }
1463 mDidContentStart = true;
1465 // The verifier only initializes itself once (from the first iteration of
1466 // a transaction that gets far enough to have response headers)
1467 if (mRequestHead->IsGet())
1468 mRestartInProgressVerifier.Set(mContentLength, mResponseHead);
1470 return NS_OK;
1471 }
1473 // called on the socket thread
1474 nsresult
1475 nsHttpTransaction::HandleContent(char *buf,
1476 uint32_t count,
1477 uint32_t *contentRead,
1478 uint32_t *contentRemaining)
1479 {
1480 nsresult rv;
1482 LOG(("nsHttpTransaction::HandleContent [this=%p count=%u]\n", this, count));
1484 *contentRead = 0;
1485 *contentRemaining = 0;
1487 MOZ_ASSERT(mConnection);
1489 if (!mDidContentStart) {
1490 rv = HandleContentStart();
1491 if (NS_FAILED(rv)) return rv;
1492 // Do not write content to the pipe if we haven't started streaming yet
1493 if (!mDidContentStart)
1494 return NS_OK;
1495 }
1497 if (mChunkedDecoder) {
1498 // give the buf over to the chunked decoder so it can reformat the
1499 // data and tell us how much is really there.
1500 rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining);
1501 if (NS_FAILED(rv)) return rv;
1502 }
1503 else if (mContentLength >= int64_t(0)) {
1504 // HTTP/1.0 servers have been known to send erroneous Content-Length
1505 // headers. So, unless the connection is persistent, we must make
1506 // allowances for a possibly invalid Content-Length header. Thus, if
1507 // NOT persistent, we simply accept everything in |buf|.
1508 if (mConnection->IsPersistent() || mPreserveStream ||
1509 mHttpVersion >= NS_HTTP_VERSION_1_1) {
1510 int64_t remaining = mContentLength - mContentRead;
1511 *contentRead = uint32_t(std::min<int64_t>(count, remaining));
1512 *contentRemaining = count - *contentRead;
1513 }
1514 else {
1515 *contentRead = count;
1516 // mContentLength might need to be increased...
1517 int64_t position = mContentRead + int64_t(count);
1518 if (position > mContentLength) {
1519 mContentLength = position;
1520 //mResponseHead->SetContentLength(mContentLength);
1521 }
1522 }
1523 }
1524 else {
1525 // when we are just waiting for the server to close the connection...
1526 // (no explicit content-length given)
1527 *contentRead = count;
1528 }
1530 int64_t toReadBeforeRestart =
1531 mRestartInProgressVerifier.ToReadBeforeRestart();
1533 if (toReadBeforeRestart && *contentRead) {
1534 uint32_t ignore =
1535 static_cast<uint32_t>(std::min<int64_t>(toReadBeforeRestart, UINT32_MAX));
1536 ignore = std::min(*contentRead, ignore);
1537 LOG(("Due To Restart ignoring %d of remaining %ld",
1538 ignore, toReadBeforeRestart));
1539 *contentRead -= ignore;
1540 mContentRead += ignore;
1541 mRestartInProgressVerifier.HaveReadBeforeRestart(ignore);
1542 memmove(buf, buf + ignore, *contentRead + *contentRemaining);
1543 }
1545 if (*contentRead) {
1546 // update count of content bytes read and report progress...
1547 mContentRead += *contentRead;
1548 /* when uncommenting, take care of 64-bit integers w/ std::max...
1549 if (mProgressSink)
1550 mProgressSink->OnProgress(nullptr, nullptr, mContentRead, std::max(0, mContentLength));
1551 */
1552 }
1554 LOG(("nsHttpTransaction::HandleContent [this=%p count=%u read=%u mContentRead=%lld mContentLength=%lld]\n",
1555 this, count, *contentRead, mContentRead, mContentLength));
1557 // Check the size of chunked responses. If we exceed the max pipeline size
1558 // for this response reschedule the pipeline
1559 if ((mClassification != CLASS_SOLO) &&
1560 mChunkedDecoder &&
1561 ((mContentRead + mChunkedDecoder->GetChunkRemaining()) >
1562 mMaxPipelineObjectSize)) {
1563 CancelPipeline(nsHttpConnectionMgr::BadUnexpectedLarge);
1564 }
1566 // check for end-of-file
1567 if ((mContentRead == mContentLength) ||
1568 (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
1569 // the transaction is done with a complete response.
1570 mTransactionDone = true;
1571 mResponseIsComplete = true;
1572 ReleaseBlockingTransaction();
1574 if (TimingEnabled())
1575 mTimings.responseEnd = TimeStamp::Now();
1577 // report the entire response has arrived
1578 if (mActivityDistributor)
1579 mActivityDistributor->ObserveActivity(
1580 mChannel,
1581 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
1582 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
1583 PR_Now(),
1584 static_cast<uint64_t>(mContentRead),
1585 EmptyCString());
1586 }
1588 return NS_OK;
1589 }
1591 nsresult
1592 nsHttpTransaction::ProcessData(char *buf, uint32_t count, uint32_t *countRead)
1593 {
1594 nsresult rv;
1596 LOG(("nsHttpTransaction::ProcessData [this=%p count=%u]\n", this, count));
1598 *countRead = 0;
1600 // we may not have read all of the headers yet...
1601 if (!mHaveAllHeaders) {
1602 uint32_t bytesConsumed = 0;
1604 do {
1605 uint32_t localBytesConsumed = 0;
1606 char *localBuf = buf + bytesConsumed;
1607 uint32_t localCount = count - bytesConsumed;
1609 rv = ParseHead(localBuf, localCount, &localBytesConsumed);
1610 if (NS_FAILED(rv) && rv != NS_ERROR_NET_INTERRUPT)
1611 return rv;
1612 bytesConsumed += localBytesConsumed;
1613 } while (rv == NS_ERROR_NET_INTERRUPT);
1615 count -= bytesConsumed;
1617 // if buf has some content in it, shift bytes to top of buf.
1618 if (count && bytesConsumed)
1619 memmove(buf, buf + bytesConsumed, count);
1621 // report the completed response header
1622 if (mActivityDistributor && mResponseHead && mHaveAllHeaders &&
1623 !mReportedResponseHeader) {
1624 mReportedResponseHeader = true;
1625 nsAutoCString completeResponseHeaders;
1626 mResponseHead->Flatten(completeResponseHeaders, false);
1627 completeResponseHeaders.AppendLiteral("\r\n");
1628 mActivityDistributor->ObserveActivity(
1629 mChannel,
1630 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
1631 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_HEADER,
1632 PR_Now(), 0,
1633 completeResponseHeaders);
1634 }
1635 }
1637 // even though count may be 0, we still want to call HandleContent
1638 // so it can complete the transaction if this is a "no-content" response.
1639 if (mHaveAllHeaders) {
1640 uint32_t countRemaining = 0;
1641 //
1642 // buf layout:
1643 //
1644 // +--------------------------------------+----------------+-----+
1645 // | countRead | countRemaining | |
1646 // +--------------------------------------+----------------+-----+
1647 //
1648 // count : bytes read from the socket
1649 // countRead : bytes corresponding to this transaction
1650 // countRemaining : bytes corresponding to next pipelined transaction
1651 //
1652 // NOTE:
1653 // count > countRead + countRemaining <==> chunked transfer encoding
1654 //
1655 rv = HandleContent(buf, count, countRead, &countRemaining);
1656 if (NS_FAILED(rv)) return rv;
1657 // we may have read more than our share, in which case we must give
1658 // the excess bytes back to the connection
1659 if (mResponseIsComplete && countRemaining) {
1660 MOZ_ASSERT(mConnection);
1661 mConnection->PushBack(buf + *countRead, countRemaining);
1662 }
1663 }
1665 return NS_OK;
1666 }
1668 void
1669 nsHttpTransaction::CancelPipeline(uint32_t reason)
1670 {
1671 // reason is casted through a uint to avoid compiler header deps
1672 gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
1673 mConnInfo,
1674 static_cast<nsHttpConnectionMgr::PipelineFeedbackInfoType>(reason),
1675 nullptr, mClassification);
1677 mConnection->CancelPipeline(NS_ERROR_ABORT);
1679 // Avoid pipelining this transaction on restart by classifying it as solo.
1680 // This also prevents BadUnexpectedLarge from being reported more
1681 // than one time per transaction.
1682 mClassification = CLASS_SOLO;
1683 }
1685 // Called when the transaction marked for blocking is associated with a connection
1686 // (i.e. added to a spdy session, an idle http connection, or placed into
1687 // a http pipeline). It is safe to call this multiple times with it only
1688 // having an effect once.
1689 void
1690 nsHttpTransaction::DispatchedAsBlocking()
1691 {
1692 if (mDispatchedAsBlocking)
1693 return;
1695 LOG(("nsHttpTransaction %p dispatched as blocking\n", this));
1697 if (!mLoadGroupCI)
1698 return;
1700 LOG(("nsHttpTransaction adding blocking channel %p from "
1701 "loadgroup %p\n", this, mLoadGroupCI.get()));
1703 mLoadGroupCI->AddBlockingTransaction();
1704 mDispatchedAsBlocking = true;
1705 }
1707 void
1708 nsHttpTransaction::RemoveDispatchedAsBlocking()
1709 {
1710 if (!mLoadGroupCI || !mDispatchedAsBlocking)
1711 return;
1713 uint32_t blockers = 0;
1714 nsresult rv = mLoadGroupCI->RemoveBlockingTransaction(&blockers);
1716 LOG(("nsHttpTransaction removing blocking channel %p from "
1717 "loadgroup %p. %d blockers remain.\n", this,
1718 mLoadGroupCI.get(), blockers));
1720 if (NS_SUCCEEDED(rv) && !blockers) {
1721 LOG(("nsHttpTransaction %p triggering release of blocked channels.\n",
1722 this));
1723 gHttpHandler->ConnMgr()->ProcessPendingQ();
1724 }
1726 mDispatchedAsBlocking = false;
1727 }
1729 void
1730 nsHttpTransaction::ReleaseBlockingTransaction()
1731 {
1732 RemoveDispatchedAsBlocking();
1733 mLoadGroupCI = nullptr;
1734 }
1736 //-----------------------------------------------------------------------------
1737 // nsHttpTransaction deletion event
1738 //-----------------------------------------------------------------------------
1740 class nsDeleteHttpTransaction : public nsRunnable {
1741 public:
1742 nsDeleteHttpTransaction(nsHttpTransaction *trans)
1743 : mTrans(trans)
1744 {}
1746 NS_IMETHOD Run()
1747 {
1748 delete mTrans;
1749 return NS_OK;
1750 }
1751 private:
1752 nsHttpTransaction *mTrans;
1753 };
1755 void
1756 nsHttpTransaction::DeleteSelfOnConsumerThread()
1757 {
1758 LOG(("nsHttpTransaction::DeleteSelfOnConsumerThread [this=%p]\n", this));
1760 bool val;
1761 if (!mConsumerTarget ||
1762 (NS_SUCCEEDED(mConsumerTarget->IsOnCurrentThread(&val)) && val)) {
1763 delete this;
1764 } else {
1765 LOG(("proxying delete to consumer thread...\n"));
1766 nsCOMPtr<nsIRunnable> event = new nsDeleteHttpTransaction(this);
1767 if (NS_FAILED(mConsumerTarget->Dispatch(event, NS_DISPATCH_NORMAL)))
1768 NS_WARNING("failed to dispatch nsHttpDeleteTransaction event");
1769 }
1770 }
1772 bool
1773 nsHttpTransaction::TryToRunPacedRequest()
1774 {
1775 if (mSubmittedRatePacing)
1776 return mPassedRatePacing;
1778 mSubmittedRatePacing = true;
1779 mSynchronousRatePaceRequest = true;
1780 gHttpHandler->SubmitPacedRequest(this, getter_AddRefs(mTokenBucketCancel));
1781 mSynchronousRatePaceRequest = false;
1782 return mPassedRatePacing;
1783 }
1785 void
1786 nsHttpTransaction::OnTokenBucketAdmitted()
1787 {
1788 mPassedRatePacing = true;
1789 mTokenBucketCancel = nullptr;
1791 if (!mSynchronousRatePaceRequest)
1792 gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo);
1793 }
1795 void
1796 nsHttpTransaction::CancelPacing(nsresult reason)
1797 {
1798 if (mTokenBucketCancel) {
1799 mTokenBucketCancel->Cancel(reason);
1800 mTokenBucketCancel = nullptr;
1801 }
1802 }
1804 //-----------------------------------------------------------------------------
1805 // nsHttpTransaction::nsISupports
1806 //-----------------------------------------------------------------------------
1808 NS_IMPL_ADDREF(nsHttpTransaction)
1810 NS_IMETHODIMP_(MozExternalRefCountType)
1811 nsHttpTransaction::Release()
1812 {
1813 nsrefcnt count;
1814 NS_PRECONDITION(0 != mRefCnt, "dup release");
1815 count = --mRefCnt;
1816 NS_LOG_RELEASE(this, count, "nsHttpTransaction");
1817 if (0 == count) {
1818 mRefCnt = 1; /* stablize */
1819 // it is essential that the transaction be destroyed on the consumer
1820 // thread (we could be holding the last reference to our consumer).
1821 DeleteSelfOnConsumerThread();
1822 return 0;
1823 }
1824 return count;
1825 }
1827 NS_IMPL_QUERY_INTERFACE(nsHttpTransaction,
1828 nsIInputStreamCallback,
1829 nsIOutputStreamCallback)
1831 //-----------------------------------------------------------------------------
1832 // nsHttpTransaction::nsIInputStreamCallback
1833 //-----------------------------------------------------------------------------
1835 // called on the socket thread
1836 NS_IMETHODIMP
1837 nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
1838 {
1839 if (mConnection) {
1840 mConnection->TransactionHasDataToWrite(this);
1841 nsresult rv = mConnection->ResumeSend();
1842 if (NS_FAILED(rv))
1843 NS_ERROR("ResumeSend failed");
1844 }
1845 return NS_OK;
1846 }
1848 //-----------------------------------------------------------------------------
1849 // nsHttpTransaction::nsIOutputStreamCallback
1850 //-----------------------------------------------------------------------------
1852 // called on the socket thread
1853 NS_IMETHODIMP
1854 nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
1855 {
1856 if (mConnection) {
1857 nsresult rv = mConnection->ResumeRecv();
1858 if (NS_FAILED(rv))
1859 NS_ERROR("ResumeRecv failed");
1860 }
1861 return NS_OK;
1862 }
1864 // nsHttpTransaction::RestartVerifier
1866 static bool
1867 matchOld(nsHttpResponseHead *newHead, nsCString &old,
1868 nsHttpAtom headerAtom)
1869 {
1870 const char *val;
1872 val = newHead->PeekHeader(headerAtom);
1873 if (val && old.IsEmpty())
1874 return false;
1875 if (!val && !old.IsEmpty())
1876 return false;
1877 if (val && !old.Equals(val))
1878 return false;
1879 return true;
1880 }
1882 bool
1883 nsHttpTransaction::RestartVerifier::Verify(int64_t contentLength,
1884 nsHttpResponseHead *newHead)
1885 {
1886 if (mContentLength != contentLength)
1887 return false;
1889 if (newHead->Status() != 200)
1890 return false;
1892 if (!matchOld(newHead, mContentRange, nsHttp::Content_Range))
1893 return false;
1895 if (!matchOld(newHead, mLastModified, nsHttp::Last_Modified))
1896 return false;
1898 if (!matchOld(newHead, mETag, nsHttp::ETag))
1899 return false;
1901 if (!matchOld(newHead, mContentEncoding, nsHttp::Content_Encoding))
1902 return false;
1904 if (!matchOld(newHead, mTransferEncoding, nsHttp::Transfer_Encoding))
1905 return false;
1907 return true;
1908 }
1910 void
1911 nsHttpTransaction::RestartVerifier::Set(int64_t contentLength,
1912 nsHttpResponseHead *head)
1913 {
1914 if (mSetup)
1915 return;
1917 // If mSetup does not transition to true RestartInPogress() is later
1918 // forbidden
1920 // Only RestartInProgress with 200 response code
1921 if (head->Status() != 200)
1922 return;
1924 mContentLength = contentLength;
1926 if (head) {
1927 const char *val;
1928 val = head->PeekHeader(nsHttp::ETag);
1929 if (val)
1930 mETag.Assign(val);
1931 val = head->PeekHeader(nsHttp::Last_Modified);
1932 if (val)
1933 mLastModified.Assign(val);
1934 val = head->PeekHeader(nsHttp::Content_Range);
1935 if (val)
1936 mContentRange.Assign(val);
1937 val = head->PeekHeader(nsHttp::Content_Encoding);
1938 if (val)
1939 mContentEncoding.Assign(val);
1940 val = head->PeekHeader(nsHttp::Transfer_Encoding);
1941 if (val)
1942 mTransferEncoding.Assign(val);
1944 // We can only restart with any confidence if we have a stored etag or
1945 // last-modified header
1946 if (mETag.IsEmpty() && mLastModified.IsEmpty())
1947 return;
1949 mSetup = true;
1950 }
1951 }
1953 } // namespace mozilla::net
1954 } // namespace mozilla