Line data Source code
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/. */
6 :
7 : // HttpLog.h should generally be included first
8 : #include "HttpLog.h"
9 :
10 : #include "base/basictypes.h"
11 :
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 "nsNetCID.h"
19 : #include "nsNetUtil.h"
20 : #include "nsIChannel.h"
21 : #include "nsIPipe.h"
22 : #include "nsCRT.h"
23 : #include "mozilla/Tokenizer.h"
24 : #include "TCPFastOpenLayer.h"
25 :
26 : #include "nsISeekableStream.h"
27 : #include "nsMultiplexInputStream.h"
28 : #include "nsStringStream.h"
29 :
30 : #include "nsComponentManagerUtils.h" // do_CreateInstance
31 : #include "nsIHttpActivityObserver.h"
32 : #include "nsSocketTransportService2.h"
33 : #include "nsICancelable.h"
34 : #include "nsIClassOfService.h"
35 : #include "nsIEventTarget.h"
36 : #include "nsIHttpChannelInternal.h"
37 : #include "nsIInputStream.h"
38 : #include "nsIThrottledInputChannel.h"
39 : #include "nsITransport.h"
40 : #include "nsIOService.h"
41 : #include "nsIRequestContext.h"
42 : #include "nsIHttpAuthenticator.h"
43 : #include "NSSErrorsService.h"
44 : #include "sslerr.h"
45 : #include <algorithm>
46 :
47 : //-----------------------------------------------------------------------------
48 :
49 : static NS_DEFINE_CID(kMultiplexInputStream, NS_MULTIPLEXINPUTSTREAM_CID);
50 :
51 : // Place a limit on how much non-compliant HTTP can be skipped while
52 : // looking for a response header
53 : #define MAX_INVALID_RESPONSE_BODY_SIZE (1024 * 128)
54 :
55 : using namespace mozilla::net;
56 :
57 : namespace mozilla {
58 : namespace net {
59 :
60 : //-----------------------------------------------------------------------------
61 : // helpers
62 : //-----------------------------------------------------------------------------
63 :
64 : static void
65 0 : LogHeaders(const char *lineStart)
66 : {
67 0 : nsAutoCString buf;
68 : char *endOfLine;
69 0 : while ((endOfLine = PL_strstr(lineStart, "\r\n"))) {
70 0 : buf.Assign(lineStart, endOfLine - lineStart);
71 0 : if (PL_strcasestr(buf.get(), "authorization: ") ||
72 0 : PL_strcasestr(buf.get(), "proxy-authorization: ")) {
73 0 : char *p = PL_strchr(PL_strchr(buf.get(), ' ') + 1, ' ');
74 0 : while (p && *++p)
75 0 : *p = '*';
76 : }
77 0 : LOG3((" %s\n", buf.get()));
78 0 : lineStart = endOfLine + 2;
79 : }
80 0 : }
81 :
82 : //-----------------------------------------------------------------------------
83 : // nsHttpTransaction <public>
84 : //-----------------------------------------------------------------------------
85 :
86 3 : nsHttpTransaction::nsHttpTransaction()
87 : : mLock("transaction lock")
88 : , mRequestSize(0)
89 : , mRequestHead(nullptr)
90 : , mResponseHead(nullptr)
91 : , mReader(nullptr)
92 : , mWriter(nullptr)
93 : , mContentLength(-1)
94 : , mContentRead(0)
95 : , mTransferSize(0)
96 : , mInvalidResponseBytesRead(0)
97 : , mPushedStream(nullptr)
98 : , mInitialRwin(0)
99 : , mChunkedDecoder(nullptr)
100 : , mStatus(NS_OK)
101 : , mPriority(0)
102 : , mRestartCount(0)
103 : , mCaps(0)
104 : , mHttpVersion(NS_HTTP_VERSION_UNKNOWN)
105 : , mHttpResponseCode(0)
106 : , mCurrentHttpResponseHeaderSize(0)
107 : , mCapsToClear(0)
108 : , mResponseIsComplete(false)
109 : , mReadingStopped(false)
110 : , mClosed(false)
111 : , mConnected(false)
112 : , mActivated(false)
113 : , mActivatedAsH2(false)
114 : , mHaveStatusLine(false)
115 : , mHaveAllHeaders(false)
116 : , mTransactionDone(false)
117 : , mDidContentStart(false)
118 : , mNoContent(false)
119 : , mSentData(false)
120 : , mReceivedData(false)
121 : , mStatusEventPending(false)
122 : , mHasRequestBody(false)
123 : , mProxyConnectFailed(false)
124 : , mHttpResponseMatched(false)
125 : , mPreserveStream(false)
126 : , mDispatchedAsBlocking(false)
127 : , mResponseTimeoutEnabled(true)
128 : , mForceRestart(false)
129 : , mReuseOnRestart(false)
130 : , mContentDecoding(false)
131 : , mContentDecodingCheck(false)
132 : , mDeferredSendProgress(false)
133 : , mWaitingOnPipeOut(false)
134 : , mReportedStart(false)
135 : , mReportedResponseHeader(false)
136 : , mForTakeResponseHead(nullptr)
137 : , mResponseHeadTaken(false)
138 : , mTopLevelOuterContentWindowId(0)
139 : , mSubmittedRatePacing(false)
140 : , mPassedRatePacing(false)
141 : , mSynchronousRatePaceRequest(false)
142 : , mClassOfService(0)
143 : , m0RTTInProgress(false)
144 : , mEarlyDataDisposition(EARLY_NONE)
145 3 : , mFastOpenStatus(TFO_NOT_TRIED)
146 : {
147 3 : LOG(("Creating nsHttpTransaction @%p\n", this));
148 :
149 : #ifdef MOZ_VALGRIND
150 : memset(&mSelfAddr, 0, sizeof(NetAddr));
151 : memset(&mPeerAddr, 0, sizeof(NetAddr));
152 : #endif
153 3 : mSelfAddr.raw.family = PR_AF_UNSPEC;
154 3 : mPeerAddr.raw.family = PR_AF_UNSPEC;
155 3 : }
156 :
157 0 : void nsHttpTransaction::ResumeReading()
158 : {
159 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
160 :
161 0 : if (!mReadingStopped) {
162 0 : return;
163 : }
164 :
165 0 : LOG(("nsHttpTransaction::ResumeReading %p", this));
166 :
167 0 : mReadingStopped = false;
168 0 : if (mConnection) {
169 0 : nsresult rv = mConnection->ResumeRecv();
170 0 : if (NS_FAILED(rv)) {
171 0 : LOG((" resume failed with rv=%" PRIx32, static_cast<uint32_t>(rv)));
172 : }
173 : }
174 : }
175 :
176 3 : void nsHttpTransaction::SetClassOfService(uint32_t cos)
177 : {
178 3 : bool wasThrottling = mClassOfService & nsIClassOfService::Throttleable;
179 :
180 3 : mClassOfService = cos;
181 :
182 3 : bool isThrottling = mClassOfService & nsIClassOfService::Throttleable;
183 :
184 3 : if (mConnection && wasThrottling != isThrottling) {
185 : // Do nothing until we are actually activated. For now
186 : // only remember the throttle flag. Call to MoveActiveTransaction
187 : // would add this transaction to the list too early.
188 0 : gHttpHandler->ConnMgr()->MoveActiveTransaction(this, isThrottling);
189 :
190 0 : if (mReadingStopped && !isThrottling) {
191 0 : ResumeReading();
192 : }
193 : }
194 3 : }
195 :
196 9 : nsHttpTransaction::~nsHttpTransaction()
197 : {
198 3 : LOG(("Destroying nsHttpTransaction @%p\n", this));
199 3 : if (mTransactionObserver) {
200 0 : mTransactionObserver->Complete(this, NS_OK);
201 : }
202 3 : if (mPushedStream) {
203 0 : mPushedStream->OnPushFailed();
204 0 : mPushedStream = nullptr;
205 : }
206 :
207 3 : if (mTokenBucketCancel) {
208 0 : mTokenBucketCancel->Cancel(NS_ERROR_ABORT);
209 0 : mTokenBucketCancel = nullptr;
210 : }
211 :
212 : // Force the callbacks and connection to be released right now
213 3 : mCallbacks = nullptr;
214 3 : mConnection = nullptr;
215 :
216 3 : delete mResponseHead;
217 3 : delete mForTakeResponseHead;
218 3 : delete mChunkedDecoder;
219 3 : ReleaseBlockingTransaction();
220 9 : }
221 :
222 : nsresult
223 3 : nsHttpTransaction::Init(uint32_t caps,
224 : nsHttpConnectionInfo *cinfo,
225 : nsHttpRequestHead *requestHead,
226 : nsIInputStream *requestBody,
227 : uint64_t requestContentLength,
228 : bool requestBodyHasHeaders,
229 : nsIEventTarget *target,
230 : nsIInterfaceRequestor *callbacks,
231 : nsITransportEventSink *eventsink,
232 : uint64_t topLevelOuterContentWindowId,
233 : nsIAsyncInputStream **responseBody)
234 : {
235 : nsresult rv;
236 :
237 3 : LOG(("nsHttpTransaction::Init [this=%p caps=%x]\n", this, caps));
238 :
239 3 : MOZ_ASSERT(cinfo);
240 3 : MOZ_ASSERT(requestHead);
241 3 : MOZ_ASSERT(target);
242 3 : MOZ_ASSERT(NS_IsMainThread());
243 :
244 3 : mTopLevelOuterContentWindowId = topLevelOuterContentWindowId;
245 3 : LOG((" window-id = %" PRIx64, mTopLevelOuterContentWindowId));
246 :
247 3 : mActivityDistributor = services::GetActivityDistributor();
248 3 : if (!mActivityDistributor) {
249 0 : return NS_ERROR_NOT_AVAILABLE;
250 : }
251 :
252 : bool activityDistributorActive;
253 3 : rv = mActivityDistributor->GetIsActive(&activityDistributorActive);
254 3 : if (NS_SUCCEEDED(rv) && activityDistributorActive) {
255 : // there are some observers registered at activity distributor, gather
256 : // nsISupports for the channel that called Init()
257 0 : LOG(("nsHttpTransaction::Init() " \
258 : "mActivityDistributor is active " \
259 : "this=%p", this));
260 : } else {
261 : // there is no observer, so don't use it
262 3 : activityDistributorActive = false;
263 3 : mActivityDistributor = nullptr;
264 : }
265 3 : mChannel = do_QueryInterface(eventsink);
266 :
267 : nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
268 6 : do_QueryInterface(eventsink);
269 3 : if (httpChannelInternal) {
270 6 : rv = httpChannelInternal->GetResponseTimeoutEnabled(
271 3 : &mResponseTimeoutEnabled);
272 3 : if (NS_WARN_IF(NS_FAILED(rv))) {
273 0 : return rv;
274 : }
275 3 : rv = httpChannelInternal->GetInitialRwin(&mInitialRwin);
276 3 : MOZ_ASSERT(NS_SUCCEEDED(rv));
277 : }
278 :
279 : // create transport event sink proxy. it coalesces consecutive
280 : // events of the same status type.
281 3 : rv = net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink),
282 : eventsink, target);
283 :
284 3 : if (NS_FAILED(rv)) return rv;
285 :
286 3 : mConnInfo = cinfo;
287 3 : mCallbacks = callbacks;
288 3 : mConsumerTarget = target;
289 3 : mCaps = caps;
290 :
291 3 : if (requestHead->IsHead()) {
292 0 : mNoContent = true;
293 : }
294 :
295 : // Make sure that there is "Content-Length: 0" header in the requestHead
296 : // in case of POST and PUT methods when there is no requestBody and
297 : // requestHead doesn't contain "Transfer-Encoding" header.
298 : //
299 : // RFC1945 section 7.2.2:
300 : // HTTP/1.0 requests containing an entity body must include a valid
301 : // Content-Length header field.
302 : //
303 : // RFC2616 section 4.4:
304 : // For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
305 : // containing a message-body MUST include a valid Content-Length header
306 : // field unless the server is known to be HTTP/1.1 compliant.
307 8 : if ((requestHead->IsPost() || requestHead->IsPut()) &&
308 3 : !requestBody && !requestHead->HasHeader(nsHttp::Transfer_Encoding)) {
309 0 : rv = requestHead->SetHeader(nsHttp::Content_Length, NS_LITERAL_CSTRING("0"));
310 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
311 : }
312 :
313 : // grab a weak reference to the request head
314 3 : mRequestHead = requestHead;
315 :
316 : // make sure we eliminate any proxy specific headers from
317 : // the request if we are using CONNECT
318 3 : bool pruneProxyHeaders = cinfo->UsingConnect();
319 :
320 3 : mReqHeaderBuf.Truncate();
321 3 : requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders);
322 :
323 3 : if (LOG3_ENABLED()) {
324 0 : LOG3(("http request [\n"));
325 0 : LogHeaders(mReqHeaderBuf.get());
326 0 : LOG3(("]\n"));
327 : }
328 :
329 : // If the request body does not include headers or if there is no request
330 : // body, then we must add the header/body separator manually.
331 3 : if (!requestBodyHasHeaders || !requestBody)
332 3 : mReqHeaderBuf.AppendLiteral("\r\n");
333 :
334 : // report the request header
335 3 : if (mActivityDistributor) {
336 0 : rv = mActivityDistributor->ObserveActivity(
337 : mChannel,
338 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
339 : NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER,
340 : PR_Now(), 0,
341 0 : mReqHeaderBuf);
342 0 : if (NS_FAILED(rv)) {
343 0 : LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
344 : }
345 : }
346 :
347 : // Create a string stream for the request header buf (the stream holds
348 : // a non-owning reference to the request header data, so we MUST keep
349 : // mReqHeaderBuf around).
350 6 : nsCOMPtr<nsIInputStream> headers;
351 3 : rv = NS_NewByteInputStream(getter_AddRefs(headers),
352 : mReqHeaderBuf.get(),
353 3 : mReqHeaderBuf.Length());
354 3 : if (NS_FAILED(rv)) return rv;
355 :
356 3 : mHasRequestBody = !!requestBody;
357 3 : if (mHasRequestBody && !requestContentLength) {
358 0 : mHasRequestBody = false;
359 : }
360 :
361 3 : requestContentLength += mReqHeaderBuf.Length();
362 :
363 3 : if (mHasRequestBody) {
364 : // wrap the headers and request body in a multiplexed input stream.
365 : nsCOMPtr<nsIMultiplexInputStream> multi =
366 2 : do_CreateInstance(kMultiplexInputStream, &rv);
367 1 : if (NS_FAILED(rv)) return rv;
368 :
369 1 : rv = multi->AppendStream(headers);
370 1 : if (NS_FAILED(rv)) return rv;
371 :
372 1 : rv = multi->AppendStream(requestBody);
373 1 : if (NS_FAILED(rv)) return rv;
374 :
375 : // wrap the multiplexed input stream with a buffered input stream, so
376 : // that we write data in the largest chunks possible. this is actually
377 : // necessary to workaround some common server bugs (see bug 137155).
378 2 : rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream), multi,
379 1 : nsIOService::gDefaultSegmentSize);
380 1 : if (NS_FAILED(rv)) return rv;
381 : } else {
382 2 : mRequestStream = headers;
383 : }
384 :
385 6 : nsCOMPtr<nsIThrottledInputChannel> throttled = do_QueryInterface(mChannel);
386 : nsIInputChannelThrottleQueue* queue;
387 3 : if (throttled) {
388 3 : rv = throttled->GetThrottleQueue(&queue);
389 : // In case of failure, just carry on without throttling.
390 3 : if (NS_SUCCEEDED(rv) && queue) {
391 0 : nsCOMPtr<nsIAsyncInputStream> wrappedStream;
392 0 : rv = queue->WrapStream(mRequestStream, getter_AddRefs(wrappedStream));
393 : // Failure to throttle isn't sufficient reason to fail
394 : // initialization
395 0 : if (NS_SUCCEEDED(rv)) {
396 0 : MOZ_ASSERT(wrappedStream != nullptr);
397 0 : LOG(("nsHttpTransaction::Init %p wrapping input stream using throttle queue %p\n",
398 : this, queue));
399 0 : mRequestStream = do_QueryInterface(wrappedStream);
400 : }
401 : }
402 : }
403 :
404 : // make sure request content-length fits within js MAX_SAFE_INTEGER
405 3 : mRequestSize = InScriptableRange(requestContentLength) ? static_cast<int64_t>(requestContentLength) : -1;
406 :
407 : // create pipe for response stream
408 6 : rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
409 6 : getter_AddRefs(mPipeOut),
410 : true, true,
411 : nsIOService::gDefaultSegmentSize,
412 : nsIOService::gDefaultSegmentCount);
413 3 : if (NS_FAILED(rv)) return rv;
414 :
415 : #ifdef WIN32 // bug 1153929
416 : MOZ_DIAGNOSTIC_ASSERT(mPipeOut);
417 : uint32_t * vtable = (uint32_t *) mPipeOut.get();
418 : MOZ_DIAGNOSTIC_ASSERT(*vtable != 0);
419 : #endif // WIN32
420 :
421 6 : nsCOMPtr<nsIAsyncInputStream> tmp(mPipeIn);
422 3 : tmp.forget(responseBody);
423 3 : return NS_OK;
424 : }
425 :
426 : // This method should only be used on the socket thread
427 : nsAHttpConnection *
428 3 : nsHttpTransaction::Connection()
429 : {
430 3 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
431 3 : return mConnection.get();
432 : }
433 :
434 : already_AddRefed<nsAHttpConnection>
435 0 : nsHttpTransaction::GetConnectionReference()
436 : {
437 0 : MutexAutoLock lock(mLock);
438 0 : RefPtr<nsAHttpConnection> connection(mConnection);
439 0 : return connection.forget();
440 : }
441 :
442 : nsHttpResponseHead *
443 3 : nsHttpTransaction::TakeResponseHead()
444 : {
445 3 : MOZ_ASSERT(!mResponseHeadTaken, "TakeResponseHead called 2x");
446 :
447 : // Lock TakeResponseHead() against main thread
448 6 : MutexAutoLock lock(*nsHttp::GetLock());
449 :
450 3 : mResponseHeadTaken = true;
451 :
452 : // Prefer mForTakeResponseHead over mResponseHead. It is always a complete
453 : // set of headers.
454 : nsHttpResponseHead *head;
455 3 : if (mForTakeResponseHead) {
456 0 : head = mForTakeResponseHead;
457 0 : mForTakeResponseHead = nullptr;
458 0 : return head;
459 : }
460 :
461 : // Even in OnStartRequest() the headers won't be available if we were
462 : // canceled
463 3 : if (!mHaveAllHeaders) {
464 0 : NS_WARNING("response headers not available or incomplete");
465 0 : return nullptr;
466 : }
467 :
468 3 : head = mResponseHead;
469 3 : mResponseHead = nullptr;
470 3 : return head;
471 : }
472 :
473 : void
474 0 : nsHttpTransaction::SetProxyConnectFailed()
475 : {
476 0 : mProxyConnectFailed = true;
477 0 : }
478 :
479 : nsHttpRequestHead *
480 0 : nsHttpTransaction::RequestHead()
481 : {
482 0 : return mRequestHead;
483 : }
484 :
485 : uint32_t
486 3 : nsHttpTransaction::Http1xTransactionCount()
487 : {
488 3 : return 1;
489 : }
490 :
491 : nsresult
492 0 : nsHttpTransaction::TakeSubTransactions(
493 : nsTArray<RefPtr<nsAHttpTransaction> > &outTransactions)
494 : {
495 0 : return NS_ERROR_NOT_IMPLEMENTED;
496 : }
497 :
498 : //----------------------------------------------------------------------------
499 : // nsHttpTransaction::nsAHttpTransaction
500 : //----------------------------------------------------------------------------
501 :
502 : void
503 3 : nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
504 : {
505 : {
506 6 : MutexAutoLock lock(mLock);
507 3 : mConnection = conn;
508 : }
509 3 : }
510 :
511 : void
512 3 : nsHttpTransaction::OnActivated(bool h2)
513 : {
514 3 : MOZ_ASSERT(OnSocketThread());
515 :
516 3 : mActivatedAsH2 = h2;
517 3 : if (mActivated) {
518 0 : return;
519 : }
520 :
521 3 : mActivated = true;
522 3 : gHttpHandler->ConnMgr()->AddActiveTransaction(
523 6 : this, mClassOfService & nsIClassOfService::Throttleable);
524 : }
525 :
526 : void
527 6 : nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
528 : {
529 12 : MutexAutoLock lock(mLock);
530 12 : nsCOMPtr<nsIInterfaceRequestor> tmp(mCallbacks);
531 6 : tmp.forget(cb);
532 6 : }
533 :
534 : void
535 0 : nsHttpTransaction::SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks)
536 : {
537 : {
538 0 : MutexAutoLock lock(mLock);
539 0 : mCallbacks = aCallbacks;
540 : }
541 :
542 0 : if (gSocketTransportService) {
543 0 : RefPtr<UpdateSecurityCallbacks> event = new UpdateSecurityCallbacks(this, aCallbacks);
544 0 : gSocketTransportService->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
545 : }
546 0 : }
547 :
548 : void
549 29 : nsHttpTransaction::OnTransportStatus(nsITransport* transport,
550 : nsresult status, int64_t progress)
551 : {
552 29 : LOG(("nsHttpTransaction::OnSocketStatus [this=%p status=%" PRIx32 " progress=%" PRId64 "]\n",
553 : this, static_cast<uint32_t>(status), progress));
554 :
555 29 : if (status == NS_NET_STATUS_CONNECTED_TO ||
556 : status == NS_NET_STATUS_WAITING_FOR) {
557 : nsISocketTransport *socketTransport =
558 6 : mConnection ? mConnection->Transport() : nullptr;
559 6 : if (socketTransport) {
560 10 : MutexAutoLock lock(mLock);
561 5 : socketTransport->GetSelfAddr(&mSelfAddr);
562 5 : socketTransport->GetPeerAddr(&mPeerAddr);
563 : }
564 : }
565 :
566 : // If the timing is enabled, and we are not using a persistent connection
567 : // then the requestStart timestamp will be null, so we mark the timestamps
568 : // for domainLookupStart/End and connectStart/End
569 : // If we are using a persistent connection they will remain null,
570 : // and the correct value will be returned in Performance.
571 29 : if (TimingEnabled() && GetRequestStart().IsNull()) {
572 5 : if (status == NS_NET_STATUS_RESOLVING_HOST) {
573 1 : SetDomainLookupStart(TimeStamp::Now(), true);
574 4 : } else if (status == NS_NET_STATUS_RESOLVED_HOST) {
575 1 : SetDomainLookupEnd(TimeStamp::Now());
576 3 : } else if (status == NS_NET_STATUS_CONNECTING_TO) {
577 1 : SetConnectStart(TimeStamp::Now());
578 2 : } else if (status == NS_NET_STATUS_CONNECTED_TO) {
579 1 : SetConnectEnd(TimeStamp::Now(), true);
580 1 : } else if (status == NS_NET_STATUS_TLS_HANDSHAKE_ENDED) {
581 0 : SetConnectEnd(TimeStamp::Now(), false);
582 1 : } else if (status == NS_NET_STATUS_SENDING_TO) {
583 : // Set the timestamp to Now(), only if it null
584 1 : SetRequestStart(TimeStamp::Now(), true);
585 : }
586 : }
587 :
588 29 : if (!mTransportSink)
589 0 : return;
590 :
591 29 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
592 :
593 : // Need to do this before the STATUS_RECEIVING_FROM check below, to make
594 : // sure that the activity distributor gets told about all status events.
595 29 : if (mActivityDistributor) {
596 : // upon STATUS_WAITING_FOR; report request body sent
597 0 : if ((mHasRequestBody) &&
598 : (status == NS_NET_STATUS_WAITING_FOR)) {
599 0 : nsresult rv = mActivityDistributor->ObserveActivity(
600 : mChannel,
601 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
602 : NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_BODY_SENT,
603 0 : PR_Now(), 0, EmptyCString());
604 0 : if (NS_FAILED(rv)) {
605 0 : LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
606 : }
607 : }
608 :
609 : // report the status and progress
610 0 : nsresult rv = mActivityDistributor->ObserveActivity(
611 : mChannel,
612 : NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
613 : static_cast<uint32_t>(status),
614 : PR_Now(),
615 : progress,
616 0 : EmptyCString());
617 0 : if (NS_FAILED(rv)) {
618 0 : LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
619 : }
620 : }
621 :
622 : // nsHttpChannel synthesizes progress events in OnDataAvailable
623 29 : if (status == NS_NET_STATUS_RECEIVING_FROM)
624 10 : return;
625 :
626 : int64_t progressMax;
627 :
628 19 : if (status == NS_NET_STATUS_SENDING_TO) {
629 : // suppress progress when only writing request headers
630 4 : if (!mHasRequestBody) {
631 2 : LOG(("nsHttpTransaction::OnTransportStatus %p "
632 : "SENDING_TO without request body\n", this));
633 5 : return;
634 : }
635 :
636 2 : if (mReader) {
637 : // A mRequestStream method is on the stack - wait.
638 1 : LOG(("nsHttpTransaction::OnSocketStatus [this=%p] "
639 : "Skipping Re-Entrant NS_NET_STATUS_SENDING_TO\n", this));
640 : // its ok to coalesce several of these into one deferred event
641 1 : mDeferredSendProgress = true;
642 1 : return;
643 : }
644 :
645 2 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
646 1 : if (!seekable) {
647 0 : LOG(("nsHttpTransaction::OnTransportStatus %p "
648 : "SENDING_TO without seekable request stream\n", this));
649 0 : progress = 0;
650 : } else {
651 1 : int64_t prog = 0;
652 1 : seekable->Tell(&prog);
653 1 : progress = prog;
654 : }
655 :
656 : // when uploading, we include the request headers in the progress
657 : // notifications.
658 1 : progressMax = mRequestSize;
659 : }
660 : else {
661 15 : progress = 0;
662 15 : progressMax = 0;
663 : }
664 :
665 16 : mTransportSink->OnTransportStatus(transport, status, progress, progressMax);
666 : }
667 :
668 : bool
669 0 : nsHttpTransaction::IsDone()
670 : {
671 0 : return mTransactionDone;
672 : }
673 :
674 : nsresult
675 3 : nsHttpTransaction::Status()
676 : {
677 3 : return mStatus;
678 : }
679 :
680 : uint32_t
681 43 : nsHttpTransaction::Caps()
682 : {
683 43 : return mCaps & ~mCapsToClear;
684 : }
685 :
686 : void
687 0 : nsHttpTransaction::SetDNSWasRefreshed()
688 : {
689 0 : MOZ_ASSERT(NS_IsMainThread(), "SetDNSWasRefreshed on main thread only!");
690 0 : mCapsToClear |= NS_HTTP_REFRESH_DNS;
691 0 : }
692 :
693 : nsresult
694 3 : nsHttpTransaction::ReadRequestSegment(nsIInputStream *stream,
695 : void *closure,
696 : const char *buf,
697 : uint32_t offset,
698 : uint32_t count,
699 : uint32_t *countRead)
700 : {
701 : // For the tracking of sent bytes that we used to do for the networkstats
702 : // API, please see bug 1318883 where it was removed.
703 :
704 3 : nsHttpTransaction *trans = (nsHttpTransaction *) closure;
705 3 : nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead);
706 3 : if (NS_FAILED(rv)) return rv;
707 :
708 3 : trans->mSentData = true;
709 3 : return NS_OK;
710 : }
711 :
712 : nsresult
713 8 : nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
714 : uint32_t count, uint32_t *countRead)
715 : {
716 8 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
717 :
718 8 : if (mTransactionDone) {
719 0 : *countRead = 0;
720 0 : return mStatus;
721 : }
722 :
723 8 : if (!mConnected && !m0RTTInProgress) {
724 3 : mConnected = true;
725 3 : mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
726 : }
727 :
728 8 : mDeferredSendProgress = false;
729 8 : mReader = reader;
730 8 : nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
731 8 : mReader = nullptr;
732 :
733 16 : if (m0RTTInProgress && (mEarlyDataDisposition == EARLY_NONE) &&
734 8 : NS_SUCCEEDED(rv) && (*countRead > 0)) {
735 0 : mEarlyDataDisposition = EARLY_SENT;
736 : }
737 :
738 8 : if (mDeferredSendProgress && mConnection && mConnection->Transport()) {
739 : // to avoid using mRequestStream concurrently, OnTransportStatus()
740 : // did not report upload status off the ReadSegments() stack from nsSocketTransport
741 : // do it now.
742 1 : OnTransportStatus(mConnection->Transport(), NS_NET_STATUS_SENDING_TO, 0);
743 : }
744 8 : mDeferredSendProgress = false;
745 :
746 8 : if (mForceRestart) {
747 : // The forceRestart condition was dealt with on the stack, but it did not
748 : // clear the flag because nsPipe in the readsegment stack clears out
749 : // return codes, so we need to use the flag here as a cue to return ERETARGETED
750 0 : if (NS_SUCCEEDED(rv)) {
751 0 : rv = NS_BINDING_RETARGETED;
752 : }
753 0 : mForceRestart = false;
754 : }
755 :
756 : // if read would block then we need to AsyncWait on the request stream.
757 : // have callback occur on socket thread so we stay synchronized.
758 8 : if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
759 : nsCOMPtr<nsIAsyncInputStream> asyncIn =
760 0 : do_QueryInterface(mRequestStream);
761 0 : if (asyncIn) {
762 0 : nsCOMPtr<nsIEventTarget> target;
763 0 : Unused << gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
764 0 : if (target)
765 0 : asyncIn->AsyncWait(this, 0, 0, target);
766 : else {
767 0 : NS_ERROR("no socket thread event target");
768 0 : rv = NS_ERROR_UNEXPECTED;
769 : }
770 : }
771 : }
772 :
773 8 : return rv;
774 : }
775 :
776 : nsresult
777 15 : nsHttpTransaction::WritePipeSegment(nsIOutputStream *stream,
778 : void *closure,
779 : char *buf,
780 : uint32_t offset,
781 : uint32_t count,
782 : uint32_t *countWritten)
783 : {
784 15 : nsHttpTransaction *trans = (nsHttpTransaction *) closure;
785 :
786 15 : if (trans->mTransactionDone)
787 2 : return NS_BASE_STREAM_CLOSED; // stop iterating
788 :
789 13 : if (trans->TimingEnabled()) {
790 : // Set the timestamp to Now(), only if it null
791 5 : trans->SetResponseStart(TimeStamp::Now(), true);
792 : }
793 :
794 : // Bug 1153929 - add checks to fix windows crash
795 13 : MOZ_ASSERT(trans->mWriter);
796 13 : if (!trans->mWriter) {
797 0 : return NS_ERROR_UNEXPECTED;
798 : }
799 :
800 : nsresult rv;
801 : //
802 : // OK, now let the caller fill this segment with data.
803 : //
804 13 : rv = trans->mWriter->OnWriteSegment(buf, count, countWritten);
805 13 : if (NS_FAILED(rv)) return rv; // caller didn't want to write anything
806 :
807 10 : MOZ_ASSERT(*countWritten > 0, "bad writer");
808 10 : trans->mReceivedData = true;
809 10 : trans->mTransferSize += *countWritten;
810 :
811 : // Let the transaction "play" with the buffer. It is free to modify
812 : // the contents of the buffer and/or modify countWritten.
813 : // - Bytes in HTTP headers don't count towards countWritten, so the input
814 : // side of pipe (aka nsHttpChannel's mTransactionPump) won't hit
815 : // OnInputStreamReady until all headers have been parsed.
816 : //
817 10 : rv = trans->ProcessData(buf, *countWritten, countWritten);
818 10 : if (NS_FAILED(rv))
819 0 : trans->Close(rv);
820 :
821 10 : return rv; // failure code only stops WriteSegments; it is not propagated.
822 : }
823 :
824 12 : bool nsHttpTransaction::ShouldStopReading()
825 : {
826 12 : if (mActivatedAsH2) {
827 : // Throttling feature is now disabled for http/2 transactions
828 : // because of bug 1367861. The logic around mActivatedAsH2
829 : // will be removed when that is fixed
830 0 : return false;
831 : }
832 :
833 12 : if (!gHttpHandler->ConnMgr()->ShouldStopReading(
834 12 : this, mClassOfService & nsIClassOfService::Throttleable)) {
835 : // We are not obligated to throttle
836 12 : return false;
837 : }
838 :
839 0 : if (mContentRead < 16000) {
840 : // Let the first bytes go, it may also well be all the content we get
841 0 : LOG(("nsHttpTransaction::ShouldStopReading too few content (%" PRIi64 ") this=%p", mContentRead, this));
842 0 : return false;
843 : }
844 :
845 0 : if (gHttpHandler->ConnMgr()->IsConnEntryUnderPressure(mConnInfo)) {
846 0 : LOG(("nsHttpTransaction::ShouldStopReading entry pressure this=%p", this));
847 : // This is expensive to check (two hashtable lookups) but may help
848 : // freeing connections for active tab transactions.
849 0 : return false;
850 : }
851 :
852 0 : return true;
853 : }
854 :
855 : nsresult
856 14 : nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
857 : uint32_t count, uint32_t *countWritten)
858 : {
859 14 : LOG(("nsHttpTransaction::WriteSegments %p", this));
860 :
861 14 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
862 :
863 14 : if (mTransactionDone) {
864 2 : return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
865 : }
866 :
867 12 : if (ShouldStopReading()) {
868 : // Must remember that we have to call ResumeRecv() on our connection when
869 : // called back by the conn manager to resume reading.
870 0 : LOG(("nsHttpTransaction::WriteSegments %p response throttled", this));
871 0 : mReadingStopped = true;
872 : // This makes the underlaying connection or stream wait for explicit resume.
873 : // For h1 this means we stop reading from the socket.
874 : // For h2 this means we stop updating recv window for the stream.
875 0 : return NS_BASE_STREAM_WOULD_BLOCK;
876 : }
877 :
878 12 : mWriter = writer;
879 :
880 : #ifdef WIN32 // bug 1153929
881 : MOZ_DIAGNOSTIC_ASSERT(mPipeOut);
882 : uint32_t * vtable = (uint32_t *) mPipeOut.get();
883 : MOZ_DIAGNOSTIC_ASSERT(*vtable != 0);
884 : #endif // WIN32
885 :
886 12 : if (!mPipeOut) {
887 0 : return NS_ERROR_UNEXPECTED;
888 : }
889 :
890 12 : nsresult rv = mPipeOut->WriteSegments(WritePipeSegment, this, count, countWritten);
891 :
892 12 : mWriter = nullptr;
893 :
894 12 : if (mForceRestart) {
895 : // The forceRestart condition was dealt with on the stack, but it did not
896 : // clear the flag because nsPipe in the writesegment stack clears out
897 : // return codes, so we need to use the flag here as a cue to return ERETARGETED
898 0 : if (NS_SUCCEEDED(rv)) {
899 0 : rv = NS_BINDING_RETARGETED;
900 : }
901 0 : mForceRestart = false;
902 : }
903 :
904 : // if pipe would block then we need to AsyncWait on it. have callback
905 : // occur on socket thread so we stay synchronized.
906 12 : if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
907 0 : nsCOMPtr<nsIEventTarget> target;
908 0 : Unused << gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
909 0 : if (target) {
910 0 : mPipeOut->AsyncWait(this, 0, 0, target);
911 0 : mWaitingOnPipeOut = true;
912 : } else {
913 0 : NS_ERROR("no socket thread event target");
914 0 : rv = NS_ERROR_UNEXPECTED;
915 : }
916 : }
917 :
918 12 : return rv;
919 : }
920 :
921 : void
922 3 : nsHttpTransaction::Close(nsresult reason)
923 : {
924 3 : LOG(("nsHttpTransaction::Close [this=%p reason=%" PRIx32 "]\n",
925 : this, static_cast<uint32_t>(reason)));
926 :
927 3 : if (!mClosed) {
928 3 : gHttpHandler->ConnMgr()->RemoveActiveTransaction(
929 6 : this, mClassOfService & nsIClassOfService::Throttleable);
930 3 : mActivated = false;
931 : }
932 :
933 3 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
934 3 : if (reason == NS_BINDING_RETARGETED) {
935 0 : LOG((" close %p skipped due to ERETARGETED\n", this));
936 0 : return;
937 : }
938 :
939 3 : if (mClosed) {
940 0 : LOG((" already closed\n"));
941 0 : return;
942 : }
943 :
944 3 : if (mTransactionObserver) {
945 0 : mTransactionObserver->Complete(this, reason);
946 0 : mTransactionObserver = nullptr;
947 : }
948 :
949 3 : if (mTokenBucketCancel) {
950 0 : mTokenBucketCancel->Cancel(reason);
951 0 : mTokenBucketCancel = nullptr;
952 : }
953 :
954 3 : if (mActivityDistributor) {
955 : // report the reponse is complete if not already reported
956 0 : if (!mResponseIsComplete) {
957 0 : nsresult rv = mActivityDistributor->ObserveActivity(
958 : mChannel,
959 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
960 : NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
961 : PR_Now(),
962 0 : static_cast<uint64_t>(mContentRead),
963 0 : EmptyCString());
964 0 : if (NS_FAILED(rv)) {
965 0 : LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
966 : }
967 : }
968 :
969 : // report that this transaction is closing
970 0 : nsresult rv = mActivityDistributor->ObserveActivity(
971 : mChannel,
972 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
973 : NS_HTTP_ACTIVITY_SUBTYPE_TRANSACTION_CLOSE,
974 0 : PR_Now(), 0, EmptyCString());
975 0 : if (NS_FAILED(rv)) {
976 0 : LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
977 : }
978 : }
979 :
980 : // we must no longer reference the connection! find out if the
981 : // connection was being reused before letting it go.
982 3 : bool connReused = false;
983 3 : if (mConnection) {
984 3 : connReused = mConnection->IsReused();
985 : }
986 3 : mConnected = false;
987 3 : mTunnelProvider = nullptr;
988 :
989 : //
990 : // if the connection was reset or closed before we wrote any part of the
991 : // request or if we wrote the request but didn't receive any part of the
992 : // response and the connection was being reused, then we can (and really
993 : // should) assume that we wrote to a stale connection and we must therefore
994 : // repeat the request over a new connection.
995 : //
996 : // We have decided to retry not only in case of the reused connections, but
997 : // all safe methods(bug 1236277).
998 : //
999 : // NOTE: the conditions under which we will automatically retry the HTTP
1000 : // request have to be carefully selected to avoid duplication of the
1001 : // request from the point-of-view of the server. such duplication could
1002 : // have dire consequences including repeated purchases, etc.
1003 : //
1004 : // NOTE: because of the way SSL proxy CONNECT is implemented, it is
1005 : // possible that the transaction may have received data without having
1006 : // sent any data. for this reason, mSendData == FALSE does not imply
1007 : // mReceivedData == FALSE. (see bug 203057 for more info.)
1008 : //
1009 : // Never restart transactions that are marked as sticky to their conenction.
1010 : // We use that capability to identify transactions bound to connection based
1011 : // authentication. Reissuing them on a different connections will break
1012 : // this bondage. Major issue may arise when there is an NTLM message auth
1013 : // header on the transaction and we send it to a different NTLM authenticated
1014 : // connection. It will break that connection and also confuse the channel's
1015 : // auth provider, beliving the cached credentials are wrong and asking for
1016 : // the password mistakenly again from the user.
1017 6 : if ((reason == NS_ERROR_NET_RESET ||
1018 0 : reason == NS_OK ||
1019 9 : reason == psm::GetXPCOMFromNSSError(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA)) &&
1020 3 : (!(mCaps & NS_HTTP_STICKY_CONNECTION) || (mCaps & NS_HTTP_CONNECTION_RESTARTABLE))) {
1021 :
1022 3 : if (mForceRestart && NS_SUCCEEDED(Restart())) {
1023 0 : if (mResponseHead) {
1024 0 : mResponseHead->Reset();
1025 : }
1026 0 : mContentRead = 0;
1027 0 : mContentLength = -1;
1028 0 : delete mChunkedDecoder;
1029 0 : mChunkedDecoder = nullptr;
1030 0 : mHaveStatusLine = false;
1031 0 : mHaveAllHeaders = false;
1032 0 : mHttpResponseMatched = false;
1033 0 : mResponseIsComplete = false;
1034 0 : mDidContentStart = false;
1035 0 : mNoContent = false;
1036 0 : mSentData = false;
1037 0 : mReceivedData = false;
1038 0 : LOG(("transaction force restarted\n"));
1039 0 : return;
1040 : }
1041 :
1042 : // reallySentData is meant to separate the instances where data has
1043 : // been sent by this transaction but buffered at a higher level while
1044 : // a TLS session (perhaps via a tunnel) is setup.
1045 : bool reallySentData =
1046 3 : mSentData && (!mConnection || mConnection->BytesWritten());
1047 :
1048 6 : if (reason == psm::GetXPCOMFromNSSError(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA) ||
1049 3 : (!mReceivedData &&
1050 0 : ((mRequestHead && mRequestHead->IsSafeMethod()) ||
1051 0 : !reallySentData || connReused))) {
1052 : // if restarting fails, then we must proceed to close the pipe,
1053 : // which will notify the channel that the transaction failed.
1054 :
1055 0 : if (NS_SUCCEEDED(Restart()))
1056 0 : return;
1057 : }
1058 : }
1059 :
1060 5 : if ((mChunkedDecoder || (mContentLength >= int64_t(0))) &&
1061 4 : (NS_SUCCEEDED(reason) && !mResponseIsComplete)) {
1062 :
1063 0 : NS_WARNING("Partial transfer, incomplete HTTP response received");
1064 :
1065 0 : if ((mHttpResponseCode / 100 == 2) &&
1066 0 : (mHttpVersion >= NS_HTTP_VERSION_1_1)) {
1067 0 : FrameCheckLevel clevel = gHttpHandler->GetEnforceH1Framing();
1068 0 : if (clevel >= FRAMECHECK_BARELY) {
1069 0 : if ((clevel == FRAMECHECK_STRICT) ||
1070 0 : (mChunkedDecoder && mChunkedDecoder->GetChunkRemaining()) ||
1071 0 : (!mChunkedDecoder && !mContentDecoding && mContentDecodingCheck) ) {
1072 0 : reason = NS_ERROR_NET_PARTIAL_TRANSFER;
1073 0 : LOG(("Partial transfer, incomplete HTTP response received: %s",
1074 : mChunkedDecoder ? "broken chunk" : "c-l underrun"));
1075 : }
1076 : }
1077 : }
1078 :
1079 0 : if (mConnection) {
1080 : // whether or not we generate an error for the transaction
1081 : // bad framing means we don't want a pconn
1082 0 : mConnection->DontReuse();
1083 : }
1084 : }
1085 :
1086 3 : bool relConn = true;
1087 3 : if (NS_SUCCEEDED(reason)) {
1088 :
1089 : // the server has not sent the final \r\n terminating the header
1090 : // section, and there may still be a header line unparsed. let's make
1091 : // sure we parse the remaining header line, and then hopefully, the
1092 : // response will be usable (see bug 88792).
1093 3 : if (!mHaveAllHeaders) {
1094 0 : char data = '\n';
1095 : uint32_t unused;
1096 0 : Unused << ParseHead(&data, 1, &unused);
1097 :
1098 0 : if (mResponseHead->Version() == NS_HTTP_VERSION_0_9) {
1099 : // Reject 0 byte HTTP/0.9 Responses - bug 423506
1100 0 : LOG(("nsHttpTransaction::Close %p 0 Byte 0.9 Response", this));
1101 0 : reason = NS_ERROR_NET_RESET;
1102 : }
1103 : }
1104 :
1105 : // honor the sticky connection flag...
1106 3 : if (mCaps & NS_HTTP_STICKY_CONNECTION)
1107 0 : relConn = false;
1108 : }
1109 :
1110 : // mTimings.responseEnd is normally recorded based on the end of a
1111 : // HTTP delimiter such as chunked-encodings or content-length. However,
1112 : // EOF or an error still require an end time be recorded.
1113 3 : if (TimingEnabled()) {
1114 1 : const TimingStruct timings = Timings();
1115 1 : if (timings.responseEnd.IsNull() && !timings.responseStart.IsNull()) {
1116 0 : SetResponseEnd(TimeStamp::Now());
1117 : }
1118 : }
1119 :
1120 3 : if (relConn && mConnection) {
1121 6 : MutexAutoLock lock(mLock);
1122 3 : mConnection = nullptr;
1123 : }
1124 :
1125 3 : mStatus = reason;
1126 3 : mTransactionDone = true; // forcibly flag the transaction as complete
1127 3 : mClosed = true;
1128 3 : ReleaseBlockingTransaction();
1129 :
1130 : // release some resources that we no longer need
1131 3 : mRequestStream = nullptr;
1132 3 : mReqHeaderBuf.Truncate();
1133 3 : mLineBuf.Truncate();
1134 3 : if (mChunkedDecoder) {
1135 0 : delete mChunkedDecoder;
1136 0 : mChunkedDecoder = nullptr;
1137 : }
1138 :
1139 : // closing this pipe triggers the channel's OnStopRequest method.
1140 3 : mPipeOut->CloseWithStatus(reason);
1141 :
1142 : #ifdef WIN32 // bug 1153929
1143 : MOZ_DIAGNOSTIC_ASSERT(mPipeOut);
1144 : uint32_t * vtable = (uint32_t *) mPipeOut.get();
1145 : MOZ_DIAGNOSTIC_ASSERT(*vtable != 0);
1146 : mPipeOut = nullptr; // just in case
1147 : #endif // WIN32
1148 : }
1149 :
1150 : nsHttpConnectionInfo *
1151 6 : nsHttpTransaction::ConnectionInfo()
1152 : {
1153 6 : return mConnInfo.get();
1154 : }
1155 :
1156 : bool // NOTE BASE CLASS
1157 0 : nsAHttpTransaction::ResponseTimeoutEnabled() const
1158 : {
1159 0 : return false;
1160 : }
1161 :
1162 : PRIntervalTime // NOTE BASE CLASS
1163 0 : nsAHttpTransaction::ResponseTimeout()
1164 : {
1165 0 : return gHttpHandler->ResponseTimeout();
1166 : }
1167 :
1168 : bool
1169 0 : nsHttpTransaction::ResponseTimeoutEnabled() const
1170 : {
1171 0 : return mResponseTimeoutEnabled;
1172 : }
1173 :
1174 : //-----------------------------------------------------------------------------
1175 : // nsHttpTransaction <private>
1176 : //-----------------------------------------------------------------------------
1177 :
1178 : nsresult
1179 0 : nsHttpTransaction::Restart()
1180 : {
1181 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1182 :
1183 : // limit the number of restart attempts - bug 92224
1184 0 : if (++mRestartCount >= gHttpHandler->MaxRequestAttempts()) {
1185 0 : LOG(("reached max request attempts, failing transaction @%p\n", this));
1186 0 : return NS_ERROR_NET_RESET;
1187 : }
1188 :
1189 0 : LOG(("restarting transaction @%p\n", this));
1190 0 : mTunnelProvider = nullptr;
1191 :
1192 : // rewind streams in case we already wrote out the request
1193 0 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
1194 0 : if (seekable)
1195 0 : seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1196 :
1197 : // clear old connection state...
1198 0 : mSecurityInfo = nullptr;
1199 0 : if (mConnection) {
1200 0 : if (!mReuseOnRestart) {
1201 0 : mConnection->DontReuse();
1202 : }
1203 0 : MutexAutoLock lock(mLock);
1204 0 : mConnection = nullptr;
1205 : }
1206 :
1207 : // Reset this to our default state, since this may change from one restart
1208 : // to the next
1209 0 : mReuseOnRestart = false;
1210 :
1211 0 : if (!mConnInfo->GetRoutedHost().IsEmpty()) {
1212 0 : MutexAutoLock lock(*nsHttp::GetLock());
1213 0 : RefPtr<nsHttpConnectionInfo> ci;
1214 0 : mConnInfo->CloneAsDirectRoute(getter_AddRefs(ci));
1215 0 : mConnInfo = ci;
1216 0 : if (mRequestHead) {
1217 : DebugOnly<nsresult> rv =
1218 0 : mRequestHead->SetHeader(nsHttp::Alternate_Service_Used,
1219 0 : NS_LITERAL_CSTRING("0"));
1220 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1221 : }
1222 : }
1223 :
1224 0 : return gHttpHandler->InitiateTransaction(this, mPriority);
1225 : }
1226 :
1227 : char *
1228 3 : nsHttpTransaction::LocateHttpStart(char *buf, uint32_t len,
1229 : bool aAllowPartialMatch)
1230 : {
1231 3 : MOZ_ASSERT(!aAllowPartialMatch || mLineBuf.IsEmpty());
1232 :
1233 : static const char HTTPHeader[] = "HTTP/1.";
1234 : static const uint32_t HTTPHeaderLen = sizeof(HTTPHeader) - 1;
1235 : static const char HTTP2Header[] = "HTTP/2.0";
1236 : static const uint32_t HTTP2HeaderLen = sizeof(HTTP2Header) - 1;
1237 : // ShoutCast ICY is treated as HTTP/1.0
1238 : static const char ICYHeader[] = "ICY ";
1239 : static const uint32_t ICYHeaderLen = sizeof(ICYHeader) - 1;
1240 :
1241 3 : if (aAllowPartialMatch && (len < HTTPHeaderLen))
1242 0 : return (PL_strncasecmp(buf, HTTPHeader, len) == 0) ? buf : nullptr;
1243 :
1244 : // mLineBuf can contain partial match from previous search
1245 3 : if (!mLineBuf.IsEmpty()) {
1246 0 : MOZ_ASSERT(mLineBuf.Length() < HTTPHeaderLen);
1247 0 : int32_t checkChars = std::min(len, HTTPHeaderLen - mLineBuf.Length());
1248 0 : if (PL_strncasecmp(buf, HTTPHeader + mLineBuf.Length(),
1249 : checkChars) == 0) {
1250 0 : mLineBuf.Append(buf, checkChars);
1251 0 : if (mLineBuf.Length() == HTTPHeaderLen) {
1252 : // We've found whole HTTPHeader sequence. Return pointer at the
1253 : // end of matched sequence since it is stored in mLineBuf.
1254 0 : return (buf + checkChars);
1255 : }
1256 : // Response matches pattern but is still incomplete.
1257 0 : return 0;
1258 : }
1259 : // Previous partial match together with new data doesn't match the
1260 : // pattern. Start the search again.
1261 0 : mLineBuf.Truncate();
1262 : }
1263 :
1264 3 : bool firstByte = true;
1265 3 : while (len > 0) {
1266 3 : if (PL_strncasecmp(buf, HTTPHeader, std::min<uint32_t>(len, HTTPHeaderLen)) == 0) {
1267 3 : if (len < HTTPHeaderLen) {
1268 : // partial HTTPHeader sequence found
1269 : // save partial match to mLineBuf
1270 0 : mLineBuf.Assign(buf, len);
1271 0 : return 0;
1272 : }
1273 :
1274 : // whole HTTPHeader sequence found
1275 3 : return buf;
1276 : }
1277 :
1278 : // At least "SmarterTools/2.0.3974.16813" generates nonsensical
1279 : // HTTP/2.0 responses to our HTTP/1 requests. Treat the minimal case of
1280 : // it as HTTP/1.1 to be compatible with old versions of ourselves and
1281 : // other browsers
1282 :
1283 0 : if (firstByte && !mInvalidResponseBytesRead && len >= HTTP2HeaderLen &&
1284 0 : (PL_strncasecmp(buf, HTTP2Header, HTTP2HeaderLen) == 0)) {
1285 0 : LOG(("nsHttpTransaction:: Identified HTTP/2.0 treating as 1.x\n"));
1286 0 : return buf;
1287 : }
1288 :
1289 : // Treat ICY (AOL/Nullsoft ShoutCast) non-standard header in same fashion
1290 : // as HTTP/2.0 is treated above. This will allow "ICY " to be interpretted
1291 : // as HTTP/1.0 in nsHttpResponseHead::ParseVersion
1292 :
1293 0 : if (firstByte && !mInvalidResponseBytesRead && len >= ICYHeaderLen &&
1294 0 : (PL_strncasecmp(buf, ICYHeader, ICYHeaderLen) == 0)) {
1295 0 : LOG(("nsHttpTransaction:: Identified ICY treating as HTTP/1.0\n"));
1296 0 : return buf;
1297 : }
1298 :
1299 0 : if (!nsCRT::IsAsciiSpace(*buf))
1300 0 : firstByte = false;
1301 0 : buf++;
1302 0 : len--;
1303 : }
1304 0 : return 0;
1305 : }
1306 :
1307 : nsresult
1308 18 : nsHttpTransaction::ParseLine(nsACString &line)
1309 : {
1310 18 : LOG(("nsHttpTransaction::ParseLine [%s]\n", PromiseFlatCString(line).get()));
1311 18 : nsresult rv = NS_OK;
1312 :
1313 18 : if (!mHaveStatusLine) {
1314 3 : mResponseHead->ParseStatusLine(line);
1315 3 : mHaveStatusLine = true;
1316 : // XXX this should probably never happen
1317 3 : if (mResponseHead->Version() == NS_HTTP_VERSION_0_9)
1318 0 : mHaveAllHeaders = true;
1319 : }
1320 : else {
1321 15 : rv = mResponseHead->ParseHeaderLine(line);
1322 : }
1323 18 : return rv;
1324 : }
1325 :
1326 : nsresult
1327 21 : nsHttpTransaction::ParseLineSegment(char *segment, uint32_t len)
1328 : {
1329 21 : NS_PRECONDITION(!mHaveAllHeaders, "already have all headers");
1330 :
1331 21 : if (!mLineBuf.IsEmpty() && mLineBuf.Last() == '\n') {
1332 : // trim off the new line char, and if this segment is
1333 : // not a continuation of the previous or if we haven't
1334 : // parsed the status line yet, then parse the contents
1335 : // of mLineBuf.
1336 18 : mLineBuf.Truncate(mLineBuf.Length() - 1);
1337 18 : if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
1338 18 : nsresult rv = ParseLine(mLineBuf);
1339 18 : mLineBuf.Truncate();
1340 18 : if (NS_FAILED(rv)) {
1341 0 : return rv;
1342 : }
1343 : }
1344 : }
1345 :
1346 : // append segment to mLineBuf...
1347 21 : mLineBuf.Append(segment, len);
1348 :
1349 : // a line buf with only a new line char signifies the end of headers.
1350 21 : if (mLineBuf.First() == '\n') {
1351 3 : mLineBuf.Truncate();
1352 : // discard this response if it is a 100 continue or other 1xx status.
1353 3 : uint16_t status = mResponseHead->Status();
1354 3 : if ((status != 101) && (status / 100 == 1)) {
1355 0 : LOG(("ignoring 1xx response\n"));
1356 0 : mHaveStatusLine = false;
1357 0 : mHttpResponseMatched = false;
1358 0 : mConnection->SetLastTransactionExpectedNoContent(true);
1359 0 : mResponseHead->Reset();
1360 0 : return NS_OK;
1361 : }
1362 3 : mHaveAllHeaders = true;
1363 : }
1364 21 : return NS_OK;
1365 : }
1366 :
1367 : nsresult
1368 8 : nsHttpTransaction::ParseHead(char *buf,
1369 : uint32_t count,
1370 : uint32_t *countRead)
1371 : {
1372 : nsresult rv;
1373 : uint32_t len;
1374 : char *eol;
1375 :
1376 8 : LOG(("nsHttpTransaction::ParseHead [count=%u]\n", count));
1377 :
1378 8 : *countRead = 0;
1379 :
1380 8 : NS_PRECONDITION(!mHaveAllHeaders, "oops");
1381 :
1382 : // allocate the response head object if necessary
1383 8 : if (!mResponseHead) {
1384 3 : mResponseHead = new nsHttpResponseHead();
1385 3 : if (!mResponseHead)
1386 0 : return NS_ERROR_OUT_OF_MEMORY;
1387 :
1388 : // report that we have a least some of the response
1389 3 : if (mActivityDistributor && !mReportedStart) {
1390 0 : mReportedStart = true;
1391 0 : rv = mActivityDistributor->ObserveActivity(
1392 : mChannel,
1393 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
1394 : NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START,
1395 0 : PR_Now(), 0, EmptyCString());
1396 0 : if (NS_FAILED(rv)) {
1397 0 : LOG3(("ObserveActivity failed (%08x)",
1398 : static_cast<uint32_t>(rv)));
1399 : }
1400 : }
1401 : }
1402 :
1403 8 : if (!mHttpResponseMatched) {
1404 : // Normally we insist on seeing HTTP/1.x in the first few bytes,
1405 : // but if we are on a persistent connection and the previous transaction
1406 : // was not supposed to have any content then we need to be prepared
1407 : // to skip over a response body that the server may have sent even
1408 : // though it wasn't allowed.
1409 3 : if (!mConnection || !mConnection->LastTransactionExpectedNoContent()) {
1410 : // tolerate only minor junk before the status line
1411 3 : mHttpResponseMatched = true;
1412 3 : char *p = LocateHttpStart(buf, std::min<uint32_t>(count, 11), true);
1413 3 : if (!p) {
1414 : // Treat any 0.9 style response of a put as a failure.
1415 0 : if (mRequestHead->IsPut())
1416 0 : return NS_ERROR_ABORT;
1417 :
1418 0 : mResponseHead->ParseStatusLine(EmptyCString());
1419 0 : mHaveStatusLine = true;
1420 0 : mHaveAllHeaders = true;
1421 0 : return NS_OK;
1422 : }
1423 3 : if (p > buf) {
1424 : // skip over the junk
1425 0 : mInvalidResponseBytesRead += p - buf;
1426 0 : *countRead = p - buf;
1427 0 : buf = p;
1428 : }
1429 : }
1430 : else {
1431 0 : char *p = LocateHttpStart(buf, count, false);
1432 0 : if (p) {
1433 0 : mInvalidResponseBytesRead += p - buf;
1434 0 : *countRead = p - buf;
1435 0 : buf = p;
1436 0 : mHttpResponseMatched = true;
1437 : } else {
1438 0 : mInvalidResponseBytesRead += count;
1439 0 : *countRead = count;
1440 0 : if (mInvalidResponseBytesRead > MAX_INVALID_RESPONSE_BODY_SIZE) {
1441 0 : LOG(("nsHttpTransaction::ParseHead() "
1442 : "Cannot find Response Header\n"));
1443 : // cannot go back and call this 0.9 anymore as we
1444 : // have thrown away a lot of the leading junk
1445 0 : return NS_ERROR_ABORT;
1446 : }
1447 0 : return NS_OK;
1448 : }
1449 : }
1450 : }
1451 : // otherwise we can assume that we don't have a HTTP/0.9 response.
1452 :
1453 8 : MOZ_ASSERT (mHttpResponseMatched);
1454 44 : while ((eol = static_cast<char *>(memchr(buf, '\n', count - *countRead))) != nullptr) {
1455 : // found line in range [buf:eol]
1456 21 : len = eol - buf + 1;
1457 :
1458 21 : *countRead += len;
1459 :
1460 : // actually, the line is in the range [buf:eol-1]
1461 21 : if ((eol > buf) && (*(eol-1) == '\r'))
1462 21 : len--;
1463 :
1464 21 : buf[len-1] = '\n';
1465 21 : rv = ParseLineSegment(buf, len);
1466 21 : if (NS_FAILED(rv))
1467 0 : return rv;
1468 :
1469 21 : if (mHaveAllHeaders)
1470 3 : return NS_OK;
1471 :
1472 : // skip over line
1473 18 : buf = eol + 1;
1474 :
1475 18 : if (!mHttpResponseMatched) {
1476 : // a 100 class response has caused us to throw away that set of
1477 : // response headers and look for the next response
1478 0 : return NS_ERROR_NET_INTERRUPT;
1479 : }
1480 : }
1481 :
1482 : // do something about a partial header line
1483 5 : if (!mHaveAllHeaders && (len = count - *countRead)) {
1484 0 : *countRead = count;
1485 : // ignore a trailing carriage return, and don't bother calling
1486 : // ParseLineSegment if buf only contains a carriage return.
1487 0 : if ((buf[len-1] == '\r') && (--len == 0))
1488 0 : return NS_OK;
1489 0 : rv = ParseLineSegment(buf, len);
1490 0 : if (NS_FAILED(rv))
1491 0 : return rv;
1492 : }
1493 5 : return NS_OK;
1494 : }
1495 :
1496 : nsresult
1497 3 : nsHttpTransaction::HandleContentStart()
1498 : {
1499 3 : LOG(("nsHttpTransaction::HandleContentStart [this=%p]\n", this));
1500 3 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1501 :
1502 3 : if (mResponseHead) {
1503 3 : if (mEarlyDataDisposition == EARLY_ACCEPTED) {
1504 0 : Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_Early_Data, NS_LITERAL_CSTRING("accepted"));
1505 3 : } else if (mEarlyDataDisposition == EARLY_SENT) {
1506 0 : Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_Early_Data, NS_LITERAL_CSTRING("sent"));
1507 : } // no header on NONE case
1508 :
1509 3 : if (mFastOpenStatus == TFO_DATA_SENT) {
1510 0 : Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_TCP_Fast_Open, NS_LITERAL_CSTRING("data sent"));
1511 3 : } else if (mFastOpenStatus == TFO_TRIED) {
1512 2 : Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_TCP_Fast_Open, NS_LITERAL_CSTRING("tried negotiating"));
1513 1 : } else if (mFastOpenStatus == TFO_FAILED) {
1514 0 : Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_TCP_Fast_Open, NS_LITERAL_CSTRING("failed"));
1515 : } // no header on TFO_NOT_TRIED case
1516 :
1517 3 : if (LOG3_ENABLED()) {
1518 0 : LOG3(("http response [\n"));
1519 0 : nsAutoCString headers;
1520 0 : mResponseHead->Flatten(headers, false);
1521 0 : headers.AppendLiteral(" OriginalHeaders");
1522 0 : headers.AppendLiteral("\r\n");
1523 0 : mResponseHead->FlattenNetworkOriginalHeaders(headers);
1524 0 : LogHeaders(headers.get());
1525 0 : LOG3(("]\n"));
1526 : }
1527 :
1528 3 : CheckForStickyAuthScheme();
1529 :
1530 : // Save http version, mResponseHead isn't available anymore after
1531 : // TakeResponseHead() is called
1532 3 : mHttpVersion = mResponseHead->Version();
1533 3 : mHttpResponseCode = mResponseHead->Status();
1534 :
1535 : // notify the connection, give it a chance to cause a reset.
1536 3 : bool reset = false;
1537 6 : nsresult rv = mConnection->OnHeadersAvailable(this, mRequestHead,
1538 6 : mResponseHead, &reset);
1539 3 : NS_ENSURE_SUCCESS(rv, rv);
1540 :
1541 : // looks like we should ignore this response, resetting...
1542 3 : if (reset) {
1543 0 : LOG(("resetting transaction's response head\n"));
1544 0 : mHaveAllHeaders = false;
1545 0 : mHaveStatusLine = false;
1546 0 : mReceivedData = false;
1547 0 : mSentData = false;
1548 0 : mHttpResponseMatched = false;
1549 0 : mResponseHead->Reset();
1550 : // wait to be called again...
1551 0 : return NS_OK;
1552 : }
1553 :
1554 : // check if this is a no-content response
1555 3 : switch (mResponseHead->Status()) {
1556 : case 101:
1557 0 : mPreserveStream = true;
1558 : MOZ_FALLTHROUGH; // to other no content cases:
1559 : case 204:
1560 : case 205:
1561 : case 304:
1562 0 : mNoContent = true;
1563 0 : LOG(("this response should not contain a body.\n"));
1564 0 : break;
1565 : case 421:
1566 0 : LOG(("Misdirected Request.\n"));
1567 0 : gHttpHandler->ConnMgr()->ClearHostMapping(mConnInfo);
1568 :
1569 : // retry on a new connection - just in case
1570 0 : if (!mRestartCount) {
1571 0 : mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
1572 0 : mForceRestart = true; // force restart has built in loop protection
1573 0 : return NS_ERROR_NET_RESET;
1574 : }
1575 0 : break;
1576 : }
1577 :
1578 4 : if (mResponseHead->Status() == 200 &&
1579 1 : mConnection->IsProxyConnectInProgress()) {
1580 : // successful CONNECTs do not have response bodies
1581 0 : mNoContent = true;
1582 : }
1583 3 : mConnection->SetLastTransactionExpectedNoContent(mNoContent);
1584 :
1585 3 : if (mNoContent) {
1586 0 : mContentLength = 0;
1587 : } else {
1588 : // grab the content-length from the response headers
1589 3 : mContentLength = mResponseHead->ContentLength();
1590 :
1591 : // handle chunked encoding here, so we'll know immediately when
1592 : // we're done with the socket. please note that _all_ other
1593 : // decoding is done when the channel receives the content data
1594 : // so as not to block the socket transport thread too much.
1595 6 : if (mResponseHead->Version() >= NS_HTTP_VERSION_1_0 &&
1596 3 : mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) {
1597 : // we only support the "chunked" transfer encoding right now.
1598 0 : mChunkedDecoder = new nsHttpChunkedDecoder();
1599 0 : LOG(("nsHttpTransaction %p chunked decoder created\n", this));
1600 : // Ignore server specified Content-Length.
1601 0 : if (mContentLength != int64_t(-1)) {
1602 0 : LOG(("nsHttpTransaction %p chunked with C-L ignores C-L\n", this));
1603 0 : mContentLength = -1;
1604 0 : if (mConnection) {
1605 0 : mConnection->DontReuse();
1606 : }
1607 : }
1608 : }
1609 3 : else if (mContentLength == int64_t(-1))
1610 1 : LOG(("waiting for the server to close the connection.\n"));
1611 : }
1612 : }
1613 :
1614 3 : mDidContentStart = true;
1615 3 : return NS_OK;
1616 : }
1617 :
1618 : // called on the socket thread
1619 : nsresult
1620 5 : nsHttpTransaction::HandleContent(char *buf,
1621 : uint32_t count,
1622 : uint32_t *contentRead,
1623 : uint32_t *contentRemaining)
1624 : {
1625 : nsresult rv;
1626 :
1627 5 : LOG(("nsHttpTransaction::HandleContent [this=%p count=%u]\n", this, count));
1628 :
1629 5 : *contentRead = 0;
1630 5 : *contentRemaining = 0;
1631 :
1632 5 : MOZ_ASSERT(mConnection);
1633 :
1634 5 : if (!mDidContentStart) {
1635 3 : rv = HandleContentStart();
1636 3 : if (NS_FAILED(rv)) return rv;
1637 : // Do not write content to the pipe if we haven't started streaming yet
1638 3 : if (!mDidContentStart)
1639 0 : return NS_OK;
1640 : }
1641 :
1642 5 : if (mChunkedDecoder) {
1643 : // give the buf over to the chunked decoder so it can reformat the
1644 : // data and tell us how much is really there.
1645 0 : rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining);
1646 0 : if (NS_FAILED(rv)) return rv;
1647 : }
1648 5 : else if (mContentLength >= int64_t(0)) {
1649 : // HTTP/1.0 servers have been known to send erroneous Content-Length
1650 : // headers. So, unless the connection is persistent, we must make
1651 : // allowances for a possibly invalid Content-Length header. Thus, if
1652 : // NOT persistent, we simply accept everything in |buf|.
1653 5 : if (mConnection->IsPersistent() || mPreserveStream ||
1654 2 : mHttpVersion >= NS_HTTP_VERSION_1_1) {
1655 1 : int64_t remaining = mContentLength - mContentRead;
1656 1 : *contentRead = uint32_t(std::min<int64_t>(count, remaining));
1657 1 : *contentRemaining = count - *contentRead;
1658 : }
1659 : else {
1660 2 : *contentRead = count;
1661 : // mContentLength might need to be increased...
1662 2 : int64_t position = mContentRead + int64_t(count);
1663 2 : if (position > mContentLength) {
1664 0 : mContentLength = position;
1665 : //mResponseHead->SetContentLength(mContentLength);
1666 : }
1667 : }
1668 : }
1669 : else {
1670 : // when we are just waiting for the server to close the connection...
1671 : // (no explicit content-length given)
1672 2 : *contentRead = count;
1673 : }
1674 :
1675 5 : if (*contentRead) {
1676 : // update count of content bytes read and report progress...
1677 3 : mContentRead += *contentRead;
1678 : }
1679 :
1680 5 : LOG(("nsHttpTransaction::HandleContent [this=%p count=%u read=%u mContentRead=%" PRId64 " mContentLength=%" PRId64 "]\n",
1681 : this, count, *contentRead, mContentRead, mContentLength));
1682 :
1683 : // check for end-of-file
1684 10 : if ((mContentRead == mContentLength) ||
1685 3 : (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
1686 : // the transaction is done with a complete response.
1687 2 : mTransactionDone = true;
1688 2 : mResponseIsComplete = true;
1689 2 : ReleaseBlockingTransaction();
1690 :
1691 2 : if (TimingEnabled()) {
1692 1 : SetResponseEnd(TimeStamp::Now());
1693 : }
1694 :
1695 : // report the entire response has arrived
1696 2 : if (mActivityDistributor) {
1697 0 : rv = mActivityDistributor->ObserveActivity(
1698 : mChannel,
1699 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
1700 : NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
1701 : PR_Now(),
1702 0 : static_cast<uint64_t>(mContentRead),
1703 0 : EmptyCString());
1704 0 : if (NS_FAILED(rv)) {
1705 0 : LOG3(("ObserveActivity failed (%08x)",
1706 : static_cast<uint32_t>(rv)));
1707 : }
1708 : }
1709 : }
1710 :
1711 5 : return NS_OK;
1712 : }
1713 :
1714 : nsresult
1715 10 : nsHttpTransaction::ProcessData(char *buf, uint32_t count, uint32_t *countRead)
1716 : {
1717 : nsresult rv;
1718 :
1719 10 : LOG(("nsHttpTransaction::ProcessData [this=%p count=%u]\n", this, count));
1720 :
1721 10 : *countRead = 0;
1722 :
1723 : // we may not have read all of the headers yet...
1724 10 : if (!mHaveAllHeaders) {
1725 8 : uint32_t bytesConsumed = 0;
1726 :
1727 0 : do {
1728 8 : uint32_t localBytesConsumed = 0;
1729 8 : char *localBuf = buf + bytesConsumed;
1730 8 : uint32_t localCount = count - bytesConsumed;
1731 :
1732 8 : rv = ParseHead(localBuf, localCount, &localBytesConsumed);
1733 8 : if (NS_FAILED(rv) && rv != NS_ERROR_NET_INTERRUPT)
1734 0 : return rv;
1735 8 : bytesConsumed += localBytesConsumed;
1736 8 : } while (rv == NS_ERROR_NET_INTERRUPT);
1737 :
1738 8 : mCurrentHttpResponseHeaderSize += bytesConsumed;
1739 16 : if (mCurrentHttpResponseHeaderSize >
1740 8 : gHttpHandler->MaxHttpResponseHeaderSize()) {
1741 0 : LOG(("nsHttpTransaction %p The response header exceeds the limit.\n",
1742 : this));
1743 0 : return NS_ERROR_FILE_TOO_BIG;
1744 : }
1745 8 : count -= bytesConsumed;
1746 :
1747 : // if buf has some content in it, shift bytes to top of buf.
1748 8 : if (count && bytesConsumed)
1749 1 : memmove(buf, buf + bytesConsumed, count);
1750 :
1751 : // report the completed response header
1752 8 : if (mActivityDistributor && mResponseHead && mHaveAllHeaders &&
1753 0 : !mReportedResponseHeader) {
1754 0 : mReportedResponseHeader = true;
1755 0 : nsAutoCString completeResponseHeaders;
1756 0 : mResponseHead->Flatten(completeResponseHeaders, false);
1757 0 : completeResponseHeaders.AppendLiteral("\r\n");
1758 0 : rv = mActivityDistributor->ObserveActivity(
1759 : mChannel,
1760 : NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
1761 : NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_HEADER,
1762 : PR_Now(), 0,
1763 0 : completeResponseHeaders);
1764 0 : if (NS_FAILED(rv)) {
1765 0 : LOG3(("ObserveActivity failed (%08x)",
1766 : static_cast<uint32_t>(rv)));
1767 : }
1768 : }
1769 : }
1770 :
1771 : // even though count may be 0, we still want to call HandleContent
1772 : // so it can complete the transaction if this is a "no-content" response.
1773 10 : if (mHaveAllHeaders) {
1774 5 : uint32_t countRemaining = 0;
1775 : //
1776 : // buf layout:
1777 : //
1778 : // +--------------------------------------+----------------+-----+
1779 : // | countRead | countRemaining | |
1780 : // +--------------------------------------+----------------+-----+
1781 : //
1782 : // count : bytes read from the socket
1783 : // countRead : bytes corresponding to this transaction
1784 : // countRemaining : bytes corresponding to next transaction on conn
1785 : //
1786 : // NOTE:
1787 : // count > countRead + countRemaining <==> chunked transfer encoding
1788 : //
1789 5 : rv = HandleContent(buf, count, countRead, &countRemaining);
1790 5 : if (NS_FAILED(rv)) return rv;
1791 : // we may have read more than our share, in which case we must give
1792 : // the excess bytes back to the connection
1793 5 : if (mResponseIsComplete && countRemaining) {
1794 0 : MOZ_ASSERT(mConnection);
1795 0 : rv = mConnection->PushBack(buf + *countRead, countRemaining);
1796 0 : NS_ENSURE_SUCCESS(rv, rv);
1797 : }
1798 :
1799 5 : if (!mContentDecodingCheck && mResponseHead) {
1800 3 : mContentDecoding =
1801 3 : mResponseHead->HasHeader(nsHttp::Content_Encoding);
1802 3 : mContentDecodingCheck = true;
1803 : }
1804 : }
1805 :
1806 10 : return NS_OK;
1807 : }
1808 :
1809 : void
1810 1 : nsHttpTransaction::SetRequestContext(nsIRequestContext *aRequestContext)
1811 : {
1812 1 : LOG(("nsHttpTransaction %p SetRequestContext %p\n", this, aRequestContext));
1813 1 : mRequestContext = aRequestContext;
1814 1 : }
1815 :
1816 : // Called when the transaction marked for blocking is associated with a connection
1817 : // (i.e. added to a new h1 conn, an idle http connection, etc..)
1818 : // It is safe to call this multiple times with it only
1819 : // having an effect once.
1820 : void
1821 0 : nsHttpTransaction::DispatchedAsBlocking()
1822 : {
1823 0 : if (mDispatchedAsBlocking)
1824 0 : return;
1825 :
1826 0 : LOG(("nsHttpTransaction %p dispatched as blocking\n", this));
1827 :
1828 0 : if (!mRequestContext)
1829 0 : return;
1830 :
1831 0 : LOG(("nsHttpTransaction adding blocking transaction %p from "
1832 : "request context %p\n", this, mRequestContext.get()));
1833 :
1834 0 : mRequestContext->AddBlockingTransaction();
1835 0 : mDispatchedAsBlocking = true;
1836 : }
1837 :
1838 : void
1839 8 : nsHttpTransaction::RemoveDispatchedAsBlocking()
1840 : {
1841 8 : if (!mRequestContext || !mDispatchedAsBlocking) {
1842 8 : LOG(("nsHttpTransaction::RemoveDispatchedAsBlocking this=%p not blocking",
1843 : this));
1844 8 : return;
1845 : }
1846 :
1847 0 : uint32_t blockers = 0;
1848 0 : nsresult rv = mRequestContext->RemoveBlockingTransaction(&blockers);
1849 :
1850 0 : LOG(("nsHttpTransaction removing blocking transaction %p from "
1851 : "request context %p. %d blockers remain.\n", this,
1852 : mRequestContext.get(), blockers));
1853 :
1854 0 : if (NS_SUCCEEDED(rv) && !blockers) {
1855 0 : LOG(("nsHttpTransaction %p triggering release of blocked channels "
1856 : " with request context=%p\n", this, mRequestContext.get()));
1857 0 : rv = gHttpHandler->ConnMgr()->ProcessPendingQ();
1858 0 : if (NS_FAILED(rv)) {
1859 0 : LOG(("nsHttpTransaction::RemoveDispatchedAsBlocking\n"
1860 : " failed to process pending queue\n"));
1861 : }
1862 : }
1863 :
1864 0 : mDispatchedAsBlocking = false;
1865 : }
1866 :
1867 : void
1868 8 : nsHttpTransaction::ReleaseBlockingTransaction()
1869 : {
1870 8 : RemoveDispatchedAsBlocking();
1871 8 : LOG(("nsHttpTransaction %p request context set to null "
1872 : "in ReleaseBlockingTransaction() - was %p\n", this, mRequestContext.get()));
1873 8 : mRequestContext = nullptr;
1874 8 : }
1875 :
1876 : void
1877 0 : nsHttpTransaction::DisableSpdy()
1878 : {
1879 0 : mCaps |= NS_HTTP_DISALLOW_SPDY;
1880 0 : if (mConnInfo) {
1881 : // This is our clone of the connection info, not the persistent one that
1882 : // is owned by the connection manager, so we're safe to change this here
1883 0 : mConnInfo->SetNoSpdy(true);
1884 : }
1885 0 : }
1886 :
1887 : void
1888 3 : nsHttpTransaction::CheckForStickyAuthScheme()
1889 : {
1890 3 : LOG(("nsHttpTransaction::CheckForStickyAuthScheme this=%p", this));
1891 :
1892 3 : MOZ_ASSERT(mHaveAllHeaders);
1893 3 : MOZ_ASSERT(mResponseHead);
1894 3 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1895 :
1896 3 : CheckForStickyAuthSchemeAt(nsHttp::WWW_Authenticate);
1897 3 : CheckForStickyAuthSchemeAt(nsHttp::Proxy_Authenticate);
1898 3 : }
1899 :
1900 : void
1901 6 : nsHttpTransaction::CheckForStickyAuthSchemeAt(nsHttpAtom const& header)
1902 : {
1903 6 : if (mCaps & NS_HTTP_STICKY_CONNECTION) {
1904 0 : LOG((" already sticky"));
1905 6 : return;
1906 : }
1907 :
1908 6 : nsAutoCString auth;
1909 6 : if (NS_FAILED(mResponseHead->GetHeader(header, auth))) {
1910 6 : return;
1911 : }
1912 :
1913 0 : Tokenizer p(auth);
1914 0 : nsAutoCString schema;
1915 0 : while (p.ReadWord(schema)) {
1916 0 : ToLowerCase(schema);
1917 :
1918 0 : nsAutoCString contractid;
1919 0 : contractid.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
1920 0 : contractid.Append(schema);
1921 :
1922 : // using a new instance because of thread safety of auth modules refcnt
1923 0 : nsCOMPtr<nsIHttpAuthenticator> authenticator(do_CreateInstance(contractid.get()));
1924 0 : if (authenticator) {
1925 : uint32_t flags;
1926 0 : nsresult rv = authenticator->GetAuthFlags(&flags);
1927 0 : if (NS_SUCCEEDED(rv) && (flags & nsIHttpAuthenticator::CONNECTION_BASED)) {
1928 0 : LOG((" connection made sticky, found %s auth shema", schema.get()));
1929 : // This is enough to make this transaction keep it's current connection,
1930 : // prevents the connection from being released back to the pool.
1931 0 : mCaps |= NS_HTTP_STICKY_CONNECTION;
1932 0 : break;
1933 : }
1934 : }
1935 :
1936 : // schemes are separated with LFs, nsHttpHeaderArray::MergeHeader
1937 0 : p.SkipUntil(Tokenizer::Token::NewLine());
1938 0 : p.SkipWhites(Tokenizer::INCLUDE_NEW_LINE);
1939 : }
1940 : }
1941 :
1942 : const TimingStruct
1943 4 : nsHttpTransaction::Timings()
1944 : {
1945 8 : mozilla::MutexAutoLock lock(mLock);
1946 4 : TimingStruct timings = mTimings;
1947 8 : return timings;
1948 : }
1949 :
1950 : void
1951 1 : nsHttpTransaction::SetDomainLookupStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
1952 : {
1953 2 : mozilla::MutexAutoLock lock(mLock);
1954 1 : if (onlyIfNull && !mTimings.domainLookupStart.IsNull()) {
1955 0 : return; // We only set the timestamp if it was previously null
1956 : }
1957 1 : mTimings.domainLookupStart = timeStamp;
1958 : }
1959 :
1960 : void
1961 1 : nsHttpTransaction::SetDomainLookupEnd(mozilla::TimeStamp timeStamp, bool onlyIfNull)
1962 : {
1963 2 : mozilla::MutexAutoLock lock(mLock);
1964 1 : if (onlyIfNull && !mTimings.domainLookupEnd.IsNull()) {
1965 0 : return; // We only set the timestamp if it was previously null
1966 : }
1967 1 : mTimings.domainLookupEnd = timeStamp;
1968 : }
1969 :
1970 : void
1971 1 : nsHttpTransaction::SetConnectStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
1972 : {
1973 2 : mozilla::MutexAutoLock lock(mLock);
1974 1 : if (onlyIfNull && !mTimings.connectStart.IsNull()) {
1975 0 : return; // We only set the timestamp if it was previously null
1976 : }
1977 1 : mTimings.connectStart = timeStamp;
1978 : }
1979 :
1980 : void
1981 1 : nsHttpTransaction::SetConnectEnd(mozilla::TimeStamp timeStamp, bool onlyIfNull)
1982 : {
1983 2 : mozilla::MutexAutoLock lock(mLock);
1984 1 : if (onlyIfNull && !mTimings.connectEnd.IsNull()) {
1985 0 : return; // We only set the timestamp if it was previously null
1986 : }
1987 1 : mTimings.connectEnd = timeStamp;
1988 : }
1989 :
1990 : void
1991 1 : nsHttpTransaction::SetRequestStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
1992 : {
1993 2 : mozilla::MutexAutoLock lock(mLock);
1994 1 : if (onlyIfNull && !mTimings.requestStart.IsNull()) {
1995 0 : return; // We only set the timestamp if it was previously null
1996 : }
1997 1 : mTimings.requestStart = timeStamp;
1998 : }
1999 :
2000 : void
2001 5 : nsHttpTransaction::SetResponseStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
2002 : {
2003 6 : mozilla::MutexAutoLock lock(mLock);
2004 5 : if (onlyIfNull && !mTimings.responseStart.IsNull()) {
2005 4 : return; // We only set the timestamp if it was previously null
2006 : }
2007 1 : mTimings.responseStart = timeStamp;
2008 : }
2009 :
2010 : void
2011 1 : nsHttpTransaction::SetResponseEnd(mozilla::TimeStamp timeStamp, bool onlyIfNull)
2012 : {
2013 2 : mozilla::MutexAutoLock lock(mLock);
2014 1 : if (onlyIfNull && !mTimings.responseEnd.IsNull()) {
2015 0 : return; // We only set the timestamp if it was previously null
2016 : }
2017 1 : mTimings.responseEnd = timeStamp;
2018 : }
2019 :
2020 : mozilla::TimeStamp
2021 0 : nsHttpTransaction::GetDomainLookupStart()
2022 : {
2023 0 : mozilla::MutexAutoLock lock(mLock);
2024 0 : return mTimings.domainLookupStart;
2025 : }
2026 :
2027 : mozilla::TimeStamp
2028 0 : nsHttpTransaction::GetDomainLookupEnd()
2029 : {
2030 0 : mozilla::MutexAutoLock lock(mLock);
2031 0 : return mTimings.domainLookupEnd;
2032 : }
2033 :
2034 : mozilla::TimeStamp
2035 0 : nsHttpTransaction::GetConnectStart()
2036 : {
2037 0 : mozilla::MutexAutoLock lock(mLock);
2038 0 : return mTimings.connectStart;
2039 : }
2040 :
2041 : mozilla::TimeStamp
2042 0 : nsHttpTransaction::GetConnectEnd()
2043 : {
2044 0 : mozilla::MutexAutoLock lock(mLock);
2045 0 : return mTimings.connectEnd;
2046 : }
2047 :
2048 : mozilla::TimeStamp
2049 10 : nsHttpTransaction::GetRequestStart()
2050 : {
2051 20 : mozilla::MutexAutoLock lock(mLock);
2052 20 : return mTimings.requestStart;
2053 : }
2054 :
2055 : mozilla::TimeStamp
2056 0 : nsHttpTransaction::GetResponseStart()
2057 : {
2058 0 : mozilla::MutexAutoLock lock(mLock);
2059 0 : return mTimings.responseStart;
2060 : }
2061 :
2062 : mozilla::TimeStamp
2063 0 : nsHttpTransaction::GetResponseEnd()
2064 : {
2065 0 : mozilla::MutexAutoLock lock(mLock);
2066 0 : return mTimings.responseEnd;
2067 : }
2068 :
2069 : //-----------------------------------------------------------------------------
2070 : // nsHttpTransaction deletion event
2071 : //-----------------------------------------------------------------------------
2072 :
2073 0 : class DeleteHttpTransaction : public Runnable {
2074 : public:
2075 0 : explicit DeleteHttpTransaction(nsHttpTransaction* trans)
2076 0 : : Runnable("net::DeleteHttpTransaction")
2077 0 : , mTrans(trans)
2078 : {
2079 0 : }
2080 :
2081 0 : NS_IMETHOD Run() override
2082 : {
2083 0 : delete mTrans;
2084 0 : return NS_OK;
2085 : }
2086 : private:
2087 : nsHttpTransaction *mTrans;
2088 : };
2089 :
2090 : void
2091 3 : nsHttpTransaction::DeleteSelfOnConsumerThread()
2092 : {
2093 3 : LOG(("nsHttpTransaction::DeleteSelfOnConsumerThread [this=%p]\n", this));
2094 :
2095 : bool val;
2096 9 : if (!mConsumerTarget ||
2097 6 : (NS_SUCCEEDED(mConsumerTarget->IsOnCurrentThread(&val)) && val)) {
2098 3 : delete this;
2099 : } else {
2100 0 : LOG(("proxying delete to consumer thread...\n"));
2101 0 : nsCOMPtr<nsIRunnable> event = new DeleteHttpTransaction(this);
2102 0 : if (NS_FAILED(mConsumerTarget->Dispatch(event, NS_DISPATCH_NORMAL)))
2103 0 : NS_WARNING("failed to dispatch nsHttpDeleteTransaction event");
2104 : }
2105 3 : }
2106 :
2107 : bool
2108 3 : nsHttpTransaction::TryToRunPacedRequest()
2109 : {
2110 3 : if (mSubmittedRatePacing)
2111 0 : return mPassedRatePacing;
2112 :
2113 3 : mSubmittedRatePacing = true;
2114 3 : mSynchronousRatePaceRequest = true;
2115 3 : Unused << gHttpHandler->SubmitPacedRequest(this, getter_AddRefs(mTokenBucketCancel));
2116 3 : mSynchronousRatePaceRequest = false;
2117 3 : return mPassedRatePacing;
2118 : }
2119 :
2120 : void
2121 3 : nsHttpTransaction::OnTokenBucketAdmitted()
2122 : {
2123 3 : mPassedRatePacing = true;
2124 3 : mTokenBucketCancel = nullptr;
2125 :
2126 3 : if (!mSynchronousRatePaceRequest) {
2127 0 : nsresult rv = gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo);
2128 0 : if (NS_FAILED(rv)) {
2129 0 : LOG(("nsHttpTransaction::OnTokenBucketAdmitted\n"
2130 : " failed to process pending queue\n"));
2131 : }
2132 : }
2133 3 : }
2134 :
2135 : void
2136 3 : nsHttpTransaction::CancelPacing(nsresult reason)
2137 : {
2138 3 : if (mTokenBucketCancel) {
2139 0 : mTokenBucketCancel->Cancel(reason);
2140 0 : mTokenBucketCancel = nullptr;
2141 : }
2142 3 : }
2143 :
2144 : //-----------------------------------------------------------------------------
2145 : // nsHttpTransaction::nsISupports
2146 : //-----------------------------------------------------------------------------
2147 :
2148 21 : NS_IMPL_ADDREF(nsHttpTransaction)
2149 :
2150 : NS_IMETHODIMP_(MozExternalRefCountType)
2151 21 : nsHttpTransaction::Release()
2152 : {
2153 : nsrefcnt count;
2154 21 : NS_PRECONDITION(0 != mRefCnt, "dup release");
2155 21 : count = --mRefCnt;
2156 21 : NS_LOG_RELEASE(this, count, "nsHttpTransaction");
2157 21 : if (0 == count) {
2158 3 : mRefCnt = 1; /* stablize */
2159 : // it is essential that the transaction be destroyed on the consumer
2160 : // thread (we could be holding the last reference to our consumer).
2161 3 : DeleteSelfOnConsumerThread();
2162 3 : return 0;
2163 : }
2164 18 : return count;
2165 : }
2166 :
2167 0 : NS_IMPL_QUERY_INTERFACE(nsHttpTransaction,
2168 : nsIInputStreamCallback,
2169 : nsIOutputStreamCallback)
2170 :
2171 : //-----------------------------------------------------------------------------
2172 : // nsHttpTransaction::nsIInputStreamCallback
2173 : //-----------------------------------------------------------------------------
2174 :
2175 : // called on the socket thread
2176 : NS_IMETHODIMP
2177 0 : nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
2178 : {
2179 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2180 0 : if (mConnection) {
2181 0 : mConnection->TransactionHasDataToWrite(this);
2182 0 : nsresult rv = mConnection->ResumeSend();
2183 0 : if (NS_FAILED(rv))
2184 0 : NS_ERROR("ResumeSend failed");
2185 : }
2186 0 : return NS_OK;
2187 : }
2188 :
2189 : //-----------------------------------------------------------------------------
2190 : // nsHttpTransaction::nsIOutputStreamCallback
2191 : //-----------------------------------------------------------------------------
2192 :
2193 : // called on the socket thread
2194 : NS_IMETHODIMP
2195 0 : nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
2196 : {
2197 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2198 0 : mWaitingOnPipeOut = false;
2199 0 : if (mConnection) {
2200 0 : mConnection->TransactionHasDataToRecv(this);
2201 0 : nsresult rv = mConnection->ResumeRecv();
2202 0 : if (NS_FAILED(rv))
2203 0 : NS_ERROR("ResumeRecv failed");
2204 : }
2205 0 : return NS_OK;
2206 : }
2207 :
2208 : void
2209 6 : nsHttpTransaction::GetNetworkAddresses(NetAddr &self, NetAddr &peer)
2210 : {
2211 12 : MutexAutoLock lock(mLock);
2212 6 : self = mSelfAddr;
2213 6 : peer = mPeerAddr;
2214 6 : }
2215 :
2216 : bool
2217 3 : nsHttpTransaction::CanDo0RTT()
2218 : {
2219 7 : if (mRequestHead->IsSafeMethod() &&
2220 2 : (!mConnection ||
2221 0 : !mConnection->IsProxyConnectInProgress())) {
2222 2 : return true;
2223 : }
2224 1 : return false;
2225 : }
2226 :
2227 : bool
2228 0 : nsHttpTransaction::Do0RTT()
2229 : {
2230 0 : if (mRequestHead->IsSafeMethod() &&
2231 0 : (!mConnection ||
2232 0 : !mConnection->IsProxyConnectInProgress())) {
2233 0 : m0RTTInProgress = true;
2234 : }
2235 0 : return m0RTTInProgress;
2236 : }
2237 :
2238 : nsresult
2239 0 : nsHttpTransaction::Finish0RTT(bool aRestart, bool aAlpnChanged /* ignored */)
2240 : {
2241 0 : LOG(("nsHttpTransaction::Finish0RTT %p %d %d\n", this, aRestart, aAlpnChanged));
2242 0 : MOZ_ASSERT(m0RTTInProgress);
2243 0 : m0RTTInProgress = false;
2244 0 : if (!aRestart && (mEarlyDataDisposition == EARLY_SENT)) {
2245 : // note that if this is invoked by a 3 param version of finish0rtt this
2246 : // disposition might be reverted
2247 0 : mEarlyDataDisposition = EARLY_ACCEPTED;
2248 : }
2249 0 : if (aRestart) {
2250 : // Reset request headers to be sent again.
2251 : nsCOMPtr<nsISeekableStream> seekable =
2252 0 : do_QueryInterface(mRequestStream);
2253 0 : if (seekable) {
2254 0 : seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
2255 : } else {
2256 0 : return NS_ERROR_FAILURE;
2257 : }
2258 0 : } else if (!mConnected) {
2259 : // this is code that was skipped in ::ReadSegments while in 0RTT
2260 0 : mConnected = true;
2261 0 : mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
2262 : }
2263 0 : return NS_OK;
2264 : }
2265 :
2266 : nsresult
2267 0 : nsHttpTransaction::RestartOnFastOpenError()
2268 : {
2269 : // This will happen on connection error during Fast Open or if connect
2270 : // during Fast Open takes too long. So we should not have received any
2271 : // data!
2272 0 : MOZ_ASSERT(!mReceivedData);
2273 0 : MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2274 :
2275 0 : LOG(("nsHttpTransaction::RestartOnFastOpenError - restarting transaction "
2276 : "%p\n", this));
2277 :
2278 : // rewind streams in case we already wrote out the request
2279 0 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
2280 0 : if (seekable)
2281 0 : seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
2282 : // clear old connection state...
2283 0 : mSecurityInfo = nullptr;
2284 :
2285 0 : if (!mConnInfo->GetRoutedHost().IsEmpty()) {
2286 0 : MutexAutoLock lock(*nsHttp::GetLock());
2287 0 : RefPtr<nsHttpConnectionInfo> ci;
2288 0 : mConnInfo->CloneAsDirectRoute(getter_AddRefs(ci));
2289 0 : mConnInfo = ci;
2290 : }
2291 0 : mEarlyDataDisposition = EARLY_NONE;
2292 0 : m0RTTInProgress = false;
2293 0 : mFastOpenStatus = TFO_FAILED;
2294 0 : return NS_OK;
2295 : }
2296 :
2297 : void
2298 2 : nsHttpTransaction::SetFastOpenStatus(uint8_t aStatus)
2299 : {
2300 2 : LOG(("nsHttpTransaction::SetFastOpenStatus %d [this=%p]\n",
2301 : aStatus, this));
2302 2 : mFastOpenStatus = aStatus;
2303 2 : }
2304 :
2305 : void
2306 0 : nsHttpTransaction::Refused0RTT()
2307 : {
2308 0 : LOG(("nsHttpTransaction::Refused0RTT %p\n", this));
2309 0 : if (mEarlyDataDisposition == EARLY_ACCEPTED) {
2310 0 : mEarlyDataDisposition = EARLY_SENT; // undo accepted state
2311 : }
2312 0 : }
2313 :
2314 : } // namespace net
2315 : } // namespace mozilla
|