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 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // HttpLog.h should generally be included first
7 #include "HttpLog.h"
9 #include "nsHttpPipeline.h"
10 #include "nsHttpHandler.h"
11 #include "nsIOService.h"
12 #include "nsISocketTransport.h"
13 #include "nsIPipe.h"
14 #include "nsCOMPtr.h"
15 #include <algorithm>
16 #include "nsHttpRequestHead.h"
18 #ifdef DEBUG
19 #include "prthread.h"
20 // defined by the socket transport service while active
21 extern PRThread *gSocketThread;
22 #endif
24 namespace mozilla {
25 namespace net {
27 //-----------------------------------------------------------------------------
28 // nsHttpPushBackWriter
29 //-----------------------------------------------------------------------------
31 class nsHttpPushBackWriter : public nsAHttpSegmentWriter
32 {
33 public:
34 nsHttpPushBackWriter(const char *buf, uint32_t bufLen)
35 : mBuf(buf)
36 , mBufLen(bufLen)
37 { }
38 virtual ~nsHttpPushBackWriter() {}
40 nsresult OnWriteSegment(char *buf, uint32_t count, uint32_t *countWritten)
41 {
42 if (mBufLen == 0)
43 return NS_BASE_STREAM_CLOSED;
45 if (count > mBufLen)
46 count = mBufLen;
48 memcpy(buf, mBuf, count);
50 mBuf += count;
51 mBufLen -= count;
52 *countWritten = count;
53 return NS_OK;
54 }
56 private:
57 const char *mBuf;
58 uint32_t mBufLen;
59 };
61 //-----------------------------------------------------------------------------
62 // nsHttpPipeline <public>
63 //-----------------------------------------------------------------------------
65 nsHttpPipeline::nsHttpPipeline()
66 : mConnection(nullptr)
67 , mStatus(NS_OK)
68 , mRequestIsPartial(false)
69 , mResponseIsPartial(false)
70 , mClosed(false)
71 , mUtilizedPipeline(false)
72 , mPushBackBuf(nullptr)
73 , mPushBackLen(0)
74 , mPushBackMax(0)
75 , mHttp1xTransactionCount(0)
76 , mReceivingFromProgress(0)
77 , mSendingToProgress(0)
78 , mSuppressSendEvents(true)
79 {
80 }
82 nsHttpPipeline::~nsHttpPipeline()
83 {
84 // make sure we aren't still holding onto any transactions!
85 Close(NS_ERROR_ABORT);
87 NS_IF_RELEASE(mConnection);
89 if (mPushBackBuf)
90 free(mPushBackBuf);
91 }
93 // Generate a shuffled request ordering sequence
94 void
95 nsHttpPipeline::ShuffleTransOrder(uint32_t count)
96 {
97 if (count < 2)
98 return;
100 uint32_t pos = mRequestQ[0]->PipelinePosition();
101 uint32_t i = 0;
103 for (i=0; i < count; ++i) {
104 uint32_t ridx = rand() % count;
106 nsAHttpTransaction *tmp = mRequestQ[i];
107 mRequestQ[i] = mRequestQ[ridx];
108 mRequestQ[ridx] = tmp;
109 }
111 for (i=0; i < count; ++i) {
112 mRequestQ[i]->SetPipelinePosition(pos);
113 pos++;
114 }
116 LOG(("nsHttpPipeline::ShuffleTransOrder: Shuffled %d transactions.\n", count));
117 }
119 nsresult
120 nsHttpPipeline::AddTransaction(nsAHttpTransaction *trans)
121 {
122 LOG(("nsHttpPipeline::AddTransaction [this=%p trans=%x]\n", this, trans));
124 if (mRequestQ.Length() || mResponseQ.Length())
125 mUtilizedPipeline = true;
127 NS_ADDREF(trans);
128 mRequestQ.AppendElement(trans);
129 uint32_t qlen = PipelineDepth();
131 if (qlen != 1) {
132 trans->SetPipelinePosition(qlen);
133 }
134 else {
135 // do it for this case in case an idempotent cancellation
136 // is being repeated and an old value needs to be cleared
137 trans->SetPipelinePosition(0);
138 }
140 // trans->SetConnection() needs to be updated to point back at
141 // the pipeline object.
142 trans->SetConnection(this);
144 ShuffleTransOrder(mRequestQ.Length());
146 if (mConnection && !mClosed && mRequestQ.Length() == 1)
147 mConnection->ResumeSend();
149 return NS_OK;
150 }
152 uint32_t
153 nsHttpPipeline::PipelineDepth()
154 {
155 return mRequestQ.Length() + mResponseQ.Length();
156 }
158 nsresult
159 nsHttpPipeline::SetPipelinePosition(int32_t position)
160 {
161 nsAHttpTransaction *trans = Response(0);
162 if (trans)
163 return trans->SetPipelinePosition(position);
164 return NS_OK;
165 }
167 int32_t
168 nsHttpPipeline::PipelinePosition()
169 {
170 nsAHttpTransaction *trans = Response(0);
171 if (trans)
172 return trans->PipelinePosition();
174 // The response queue is empty, so return oldest request
175 if (mRequestQ.Length())
176 return Request(mRequestQ.Length() - 1)->PipelinePosition();
178 // No transactions in the pipeline
179 return 0;
180 }
182 nsHttpPipeline *
183 nsHttpPipeline::QueryPipeline()
184 {
185 return this;
186 }
188 //-----------------------------------------------------------------------------
189 // nsHttpPipeline::nsISupports
190 //-----------------------------------------------------------------------------
192 NS_IMPL_ADDREF(nsHttpPipeline)
193 NS_IMPL_RELEASE(nsHttpPipeline)
195 // multiple inheritance fun :-)
196 NS_INTERFACE_MAP_BEGIN(nsHttpPipeline)
197 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
198 NS_INTERFACE_MAP_END
201 //-----------------------------------------------------------------------------
202 // nsHttpPipeline::nsAHttpConnection
203 //-----------------------------------------------------------------------------
205 nsresult
206 nsHttpPipeline::OnHeadersAvailable(nsAHttpTransaction *trans,
207 nsHttpRequestHead *requestHead,
208 nsHttpResponseHead *responseHead,
209 bool *reset)
210 {
211 LOG(("nsHttpPipeline::OnHeadersAvailable [this=%p]\n", this));
213 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
214 MOZ_ASSERT(mConnection, "no connection");
216 nsRefPtr<nsHttpConnectionInfo> ci;
217 GetConnectionInfo(getter_AddRefs(ci));
218 MOZ_ASSERT(ci);
220 bool pipeliningBefore = gHttpHandler->ConnMgr()->SupportsPipelining(ci);
222 // trans has now received its response headers; forward to the real connection
223 nsresult rv = mConnection->OnHeadersAvailable(trans,
224 requestHead,
225 responseHead,
226 reset);
228 if (!pipeliningBefore && gHttpHandler->ConnMgr()->SupportsPipelining(ci))
229 // The received headers have expanded the eligible
230 // pipeline depth for this connection
231 gHttpHandler->ConnMgr()->ProcessPendingQForEntry(ci);
233 return rv;
234 }
236 void
237 nsHttpPipeline::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
238 {
239 LOG(("nsHttpPipeline::CloseTransaction [this=%p trans=%x reason=%x]\n",
240 this, trans, reason));
242 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
243 MOZ_ASSERT(NS_FAILED(reason), "expecting failure code");
245 // the specified transaction is to be closed with the given "reason"
247 int32_t index;
248 bool killPipeline = false;
250 index = mRequestQ.IndexOf(trans);
251 if (index >= 0) {
252 if (index == 0 && mRequestIsPartial) {
253 // the transaction is in the request queue. check to see if any of
254 // its data has been written out yet.
255 killPipeline = true;
256 }
257 mRequestQ.RemoveElementAt(index);
258 }
259 else {
260 index = mResponseQ.IndexOf(trans);
261 if (index >= 0)
262 mResponseQ.RemoveElementAt(index);
263 // while we could avoid killing the pipeline if this transaction is the
264 // last transaction in the pipeline, there doesn't seem to be that much
265 // value in doing so. most likely if this transaction is going away,
266 // the others will be shortly as well.
267 killPipeline = true;
268 }
270 // Marking this connection as non-reusable prevents other items from being
271 // added to it and causes it to be torn down soon.
272 DontReuse();
274 trans->Close(reason);
275 NS_RELEASE(trans);
277 if (killPipeline) {
278 // reschedule anything from this pipeline onto a different connection
279 CancelPipeline(reason);
280 }
282 // If all the transactions have been removed then we can close the connection
283 // right away.
284 if (!mRequestQ.Length() && !mResponseQ.Length() && mConnection)
285 mConnection->CloseTransaction(this, reason);
286 }
288 nsresult
289 nsHttpPipeline::TakeTransport(nsISocketTransport **aTransport,
290 nsIAsyncInputStream **aInputStream,
291 nsIAsyncOutputStream **aOutputStream)
292 {
293 return mConnection->TakeTransport(aTransport, aInputStream, aOutputStream);
294 }
296 bool
297 nsHttpPipeline::IsPersistent()
298 {
299 return true; // pipelining requires this
300 }
302 bool
303 nsHttpPipeline::IsReused()
304 {
305 if (!mUtilizedPipeline && mConnection)
306 return mConnection->IsReused();
307 return true;
308 }
310 void
311 nsHttpPipeline::DontReuse()
312 {
313 if (mConnection)
314 mConnection->DontReuse();
315 }
317 nsresult
318 nsHttpPipeline::PushBack(const char *data, uint32_t length)
319 {
320 LOG(("nsHttpPipeline::PushBack [this=%p len=%u]\n", this, length));
322 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
323 MOZ_ASSERT(mPushBackLen == 0, "push back buffer already has data!");
325 // If we have no chance for a pipeline (e.g. due to an Upgrade)
326 // then push this data down to original connection
327 if (!mConnection->IsPersistent())
328 return mConnection->PushBack(data, length);
330 // PushBack is called recursively from WriteSegments
332 // XXX we have a design decision to make here. either we buffer the data
333 // and process it when we return to WriteSegments, or we attempt to move
334 // onto the next transaction from here. doing so adds complexity with the
335 // benefit of eliminating the extra buffer copy. the buffer is at most
336 // 4096 bytes, so it is really unclear if there is any value in the added
337 // complexity. besides simplicity, buffering this data has the advantage
338 // that we'll call close on the transaction sooner, which will wake up
339 // the HTTP channel sooner to continue with its work.
341 if (!mPushBackBuf) {
342 mPushBackMax = length;
343 mPushBackBuf = (char *) malloc(mPushBackMax);
344 if (!mPushBackBuf)
345 return NS_ERROR_OUT_OF_MEMORY;
346 }
347 else if (length > mPushBackMax) {
348 // grow push back buffer as necessary.
349 MOZ_ASSERT(length <= nsIOService::gDefaultSegmentSize, "too big");
350 mPushBackMax = length;
351 mPushBackBuf = (char *) realloc(mPushBackBuf, mPushBackMax);
352 if (!mPushBackBuf)
353 return NS_ERROR_OUT_OF_MEMORY;
354 }
356 memcpy(mPushBackBuf, data, length);
357 mPushBackLen = length;
359 return NS_OK;
360 }
362 nsHttpConnection *
363 nsHttpPipeline::TakeHttpConnection()
364 {
365 if (mConnection)
366 return mConnection->TakeHttpConnection();
367 return nullptr;
368 }
370 nsAHttpTransaction::Classifier
371 nsHttpPipeline::Classification()
372 {
373 if (mConnection)
374 return mConnection->Classification();
376 LOG(("nsHttpPipeline::Classification this=%p "
377 "has null mConnection using CLASS_SOLO default", this));
378 return nsAHttpTransaction::CLASS_SOLO;
379 }
381 void
382 nsHttpPipeline::SetProxyConnectFailed()
383 {
384 nsAHttpTransaction *trans = Request(0);
386 if (trans)
387 trans->SetProxyConnectFailed();
388 }
390 nsHttpRequestHead *
391 nsHttpPipeline::RequestHead()
392 {
393 nsAHttpTransaction *trans = Request(0);
395 if (trans)
396 return trans->RequestHead();
397 return nullptr;
398 }
400 uint32_t
401 nsHttpPipeline::Http1xTransactionCount()
402 {
403 return mHttp1xTransactionCount;
404 }
406 nsresult
407 nsHttpPipeline::TakeSubTransactions(
408 nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
409 {
410 LOG(("nsHttpPipeline::TakeSubTransactions [this=%p]\n", this));
412 if (mResponseQ.Length() || mRequestIsPartial)
413 return NS_ERROR_ALREADY_OPENED;
415 int32_t i, count = mRequestQ.Length();
416 for (i = 0; i < count; ++i) {
417 nsAHttpTransaction *trans = Request(i);
418 // set the transaction conneciton object back to the underlying
419 // nsHttpConnectionHandle
420 trans->SetConnection(mConnection);
421 outTransactions.AppendElement(trans);
422 NS_RELEASE(trans);
423 }
424 mRequestQ.Clear();
426 LOG((" took %d\n", count));
427 return NS_OK;
428 }
430 //-----------------------------------------------------------------------------
431 // nsHttpPipeline::nsAHttpTransaction
432 //-----------------------------------------------------------------------------
434 void
435 nsHttpPipeline::SetConnection(nsAHttpConnection *conn)
436 {
437 LOG(("nsHttpPipeline::SetConnection [this=%p conn=%x]\n", this, conn));
439 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
440 MOZ_ASSERT(!mConnection, "already have a connection");
442 NS_IF_ADDREF(mConnection = conn);
443 }
445 nsAHttpConnection *
446 nsHttpPipeline::Connection()
447 {
448 LOG(("nsHttpPipeline::Connection [this=%p conn=%x]\n", this, mConnection));
450 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
451 return mConnection;
452 }
454 void
455 nsHttpPipeline::GetSecurityCallbacks(nsIInterfaceRequestor **result)
456 {
457 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
459 // depending on timing this could be either the request or the response
460 // that is needed - but they both go to the same host. A request for these
461 // callbacks directly in nsHttpTransaction would not make a distinction
462 // over whether the the request had been transmitted yet.
463 nsAHttpTransaction *trans = Request(0);
464 if (!trans)
465 trans = Response(0);
466 if (trans)
467 trans->GetSecurityCallbacks(result);
468 else {
469 *result = nullptr;
470 }
471 }
473 void
474 nsHttpPipeline::OnTransportStatus(nsITransport* transport,
475 nsresult status, uint64_t progress)
476 {
477 LOG(("nsHttpPipeline::OnStatus [this=%p status=%x progress=%llu]\n",
478 this, status, progress));
480 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
482 nsAHttpTransaction *trans;
483 int32_t i, count;
485 switch (status) {
487 case NS_NET_STATUS_RESOLVING_HOST:
488 case NS_NET_STATUS_RESOLVED_HOST:
489 case NS_NET_STATUS_CONNECTING_TO:
490 case NS_NET_STATUS_CONNECTED_TO:
491 // These should only appear at most once per pipeline.
492 // Deliver to the first transaction.
494 trans = Request(0);
495 if (!trans)
496 trans = Response(0);
497 if (trans)
498 trans->OnTransportStatus(transport, status, progress);
500 break;
502 case NS_NET_STATUS_SENDING_TO:
503 // This is generated by the socket transport when (part) of
504 // a transaction is written out
505 //
506 // In pipelining this is generated out of FillSendBuf(), but it cannot do
507 // so until the connection is confirmed by CONNECTED_TO.
508 // See patch for bug 196827.
509 //
511 if (mSuppressSendEvents) {
512 mSuppressSendEvents = false;
514 // catch up by sending the event to all the transactions that have
515 // moved from request to response and any that have been partially
516 // sent. Also send WAITING_FOR to those that were completely sent
517 count = mResponseQ.Length();
518 for (i = 0; i < count; ++i) {
519 Response(i)->OnTransportStatus(transport,
520 NS_NET_STATUS_SENDING_TO,
521 progress);
522 Response(i)->OnTransportStatus(transport,
523 NS_NET_STATUS_WAITING_FOR,
524 progress);
525 }
526 if (mRequestIsPartial && Request(0))
527 Request(0)->OnTransportStatus(transport,
528 NS_NET_STATUS_SENDING_TO,
529 progress);
530 mSendingToProgress = progress;
531 }
532 // otherwise ignore it
533 break;
535 case NS_NET_STATUS_WAITING_FOR:
536 // Created by nsHttpConnection when request pipeline has been totally
537 // sent. Ignore it here because it is simulated in FillSendBuf() when
538 // a request is moved from request to response.
540 // ignore it
541 break;
543 case NS_NET_STATUS_RECEIVING_FROM:
544 // Forward this only to the transaction currently recieving data. It is
545 // normally generated by the socket transport, but can also
546 // be repeated by the pushbackwriter if necessary.
547 mReceivingFromProgress = progress;
548 if (Response(0))
549 Response(0)->OnTransportStatus(transport, status, progress);
550 break;
552 default:
553 // forward other notifications to all request transactions
554 count = mRequestQ.Length();
555 for (i = 0; i < count; ++i)
556 Request(i)->OnTransportStatus(transport, status, progress);
557 break;
558 }
559 }
561 bool
562 nsHttpPipeline::IsDone()
563 {
564 bool done = true;
566 uint32_t i, count = mRequestQ.Length();
567 for (i = 0; done && (i < count); i++)
568 done = Request(i)->IsDone();
570 count = mResponseQ.Length();
571 for (i = 0; done && (i < count); i++)
572 done = Response(i)->IsDone();
574 return done;
575 }
577 nsresult
578 nsHttpPipeline::Status()
579 {
580 return mStatus;
581 }
583 uint32_t
584 nsHttpPipeline::Caps()
585 {
586 nsAHttpTransaction *trans = Request(0);
587 if (!trans)
588 trans = Response(0);
590 return trans ? trans->Caps() : 0;
591 }
593 void
594 nsHttpPipeline::SetDNSWasRefreshed()
595 {
596 nsAHttpTransaction *trans = Request(0);
597 if (!trans)
598 trans = Response(0);
600 if (trans)
601 trans->SetDNSWasRefreshed();
602 }
604 uint64_t
605 nsHttpPipeline::Available()
606 {
607 uint64_t result = 0;
609 int32_t i, count = mRequestQ.Length();
610 for (i=0; i<count; ++i)
611 result += Request(i)->Available();
612 return result;
613 }
615 NS_METHOD
616 nsHttpPipeline::ReadFromPipe(nsIInputStream *stream,
617 void *closure,
618 const char *buf,
619 uint32_t offset,
620 uint32_t count,
621 uint32_t *countRead)
622 {
623 nsHttpPipeline *self = (nsHttpPipeline *) closure;
624 return self->mReader->OnReadSegment(buf, count, countRead);
625 }
627 nsresult
628 nsHttpPipeline::ReadSegments(nsAHttpSegmentReader *reader,
629 uint32_t count,
630 uint32_t *countRead)
631 {
632 LOG(("nsHttpPipeline::ReadSegments [this=%p count=%u]\n", this, count));
634 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
636 if (mClosed) {
637 *countRead = 0;
638 return mStatus;
639 }
641 nsresult rv;
642 uint64_t avail = 0;
643 if (mSendBufIn) {
644 rv = mSendBufIn->Available(&avail);
645 if (NS_FAILED(rv)) return rv;
646 }
648 if (avail == 0) {
649 rv = FillSendBuf();
650 if (NS_FAILED(rv)) return rv;
652 rv = mSendBufIn->Available(&avail);
653 if (NS_FAILED(rv)) return rv;
655 // return EOF if send buffer is empty
656 if (avail == 0) {
657 *countRead = 0;
658 return NS_OK;
659 }
660 }
662 // read no more than what was requested
663 if (avail > count)
664 avail = count;
666 mReader = reader;
668 // avail is under 4GB, so casting to uint32_t is safe
669 rv = mSendBufIn->ReadSegments(ReadFromPipe, this, (uint32_t)avail, countRead);
671 mReader = nullptr;
672 return rv;
673 }
675 nsresult
676 nsHttpPipeline::WriteSegments(nsAHttpSegmentWriter *writer,
677 uint32_t count,
678 uint32_t *countWritten)
679 {
680 LOG(("nsHttpPipeline::WriteSegments [this=%p count=%u]\n", this, count));
682 MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
684 if (mClosed)
685 return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
687 nsAHttpTransaction *trans;
688 nsresult rv;
690 trans = Response(0);
691 // This code deals with the establishment of a CONNECT tunnel through
692 // an HTTP proxy. It allows the connection to do the CONNECT/200
693 // HTTP transaction to establish a tunnel as a precursor to the
694 // actual pipeline of regular HTTP transactions.
695 if (!trans && mRequestQ.Length() &&
696 mConnection->IsProxyConnectInProgress()) {
697 LOG(("nsHttpPipeline::WriteSegments [this=%p] Forced Delegation\n",
698 this));
699 trans = Request(0);
700 }
702 if (!trans) {
703 if (mRequestQ.Length() > 0)
704 rv = NS_BASE_STREAM_WOULD_BLOCK;
705 else
706 rv = NS_BASE_STREAM_CLOSED;
707 }
708 else {
709 //
710 // ask the transaction to consume data from the connection.
711 // PushBack may be called recursively.
712 //
713 rv = trans->WriteSegments(writer, count, countWritten);
715 if (rv == NS_BASE_STREAM_CLOSED || trans->IsDone()) {
716 trans->Close(NS_OK);
718 // Release the transaction if it is not IsProxyConnectInProgress()
719 if (trans == Response(0)) {
720 NS_RELEASE(trans);
721 mResponseQ.RemoveElementAt(0);
722 mResponseIsPartial = false;
723 ++mHttp1xTransactionCount;
724 }
726 // ask the connection manager to add additional transactions
727 // to our pipeline.
728 nsRefPtr<nsHttpConnectionInfo> ci;
729 GetConnectionInfo(getter_AddRefs(ci));
730 if (ci)
731 gHttpHandler->ConnMgr()->ProcessPendingQForEntry(ci);
732 }
733 else
734 mResponseIsPartial = true;
735 }
737 if (mPushBackLen) {
738 nsHttpPushBackWriter writer(mPushBackBuf, mPushBackLen);
739 uint32_t len = mPushBackLen, n;
740 mPushBackLen = 0;
742 // This progress notification has previously been sent from
743 // the socket transport code, but it was delivered to the
744 // previous transaction on the pipeline.
745 nsITransport *transport = Transport();
746 if (transport)
747 OnTransportStatus(transport, NS_NET_STATUS_RECEIVING_FROM,
748 mReceivingFromProgress);
750 // the push back buffer is never larger than NS_HTTP_SEGMENT_SIZE,
751 // so we are guaranteed that the next response will eat the entire
752 // push back buffer (even though it might again call PushBack).
753 rv = WriteSegments(&writer, len, &n);
754 }
756 return rv;
757 }
759 uint32_t
760 nsHttpPipeline::CancelPipeline(nsresult originalReason)
761 {
762 uint32_t i, reqLen, respLen, total;
763 nsAHttpTransaction *trans;
765 reqLen = mRequestQ.Length();
766 respLen = mResponseQ.Length();
767 total = reqLen + respLen;
769 // don't count the first response, if presnet
770 if (respLen)
771 total--;
773 if (!total)
774 return 0;
776 // any pending requests can ignore this error and be restarted
777 // unless it is during a CONNECT tunnel request
778 for (i = 0; i < reqLen; ++i) {
779 trans = Request(i);
780 if (mConnection && mConnection->IsProxyConnectInProgress())
781 trans->Close(originalReason);
782 else
783 trans->Close(NS_ERROR_NET_RESET);
784 NS_RELEASE(trans);
785 }
786 mRequestQ.Clear();
788 // any pending responses can be restarted except for the first one,
789 // that we might want to finish on this pipeline or cancel individually.
790 // Higher levels of callers ensure that we don't process non-idempotent
791 // tranasction with the NS_HTTP_ALLOW_PIPELINING bit set
792 for (i = 1; i < respLen; ++i) {
793 trans = Response(i);
794 trans->Close(NS_ERROR_NET_RESET);
795 NS_RELEASE(trans);
796 }
798 if (respLen > 1)
799 mResponseQ.TruncateLength(1);
801 /* Don't flag timed out connections as unreusable.. Tor is just slow :( */
802 if (originalReason != NS_ERROR_NET_TIMEOUT) {
803 DontReuse();
804 Classify(nsAHttpTransaction::CLASS_SOLO);
805 }
807 return total;
808 }
810 void
811 nsHttpPipeline::Close(nsresult reason)
812 {
813 LOG(("nsHttpPipeline::Close [this=%p reason=%x]\n", this, reason));
815 if (mClosed) {
816 LOG((" already closed\n"));
817 return;
818 }
820 // the connection is going away!
821 mStatus = reason;
822 mClosed = true;
824 nsRefPtr<nsHttpConnectionInfo> ci;
825 GetConnectionInfo(getter_AddRefs(ci));
826 uint32_t numRescheduled = CancelPipeline(reason);
828 // numRescheduled can be 0 if there is just a single response in the
829 // pipeline object. That isn't really a meaningful pipeline that
830 // has been forced to be rescheduled so it does not need to generate
831 // negative feedback.
832 if (ci && numRescheduled)
833 gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
834 ci, nsHttpConnectionMgr::RedCanceledPipeline, nullptr, 0);
836 nsAHttpTransaction *trans = Response(0);
837 if (!trans)
838 return;
840 // The current transaction can be restarted via reset
841 // if the response has not started to arrive and the reason
842 // for failure is innocuous (e.g. not an SSL error)
843 if (!mResponseIsPartial &&
844 (reason == NS_ERROR_NET_RESET ||
845 reason == NS_OK ||
846 reason == NS_ERROR_NET_TIMEOUT ||
847 reason == NS_BASE_STREAM_CLOSED)) {
848 trans->Close(NS_ERROR_NET_RESET);
849 }
850 else {
851 trans->Close(reason);
852 }
854 NS_RELEASE(trans);
855 mResponseQ.Clear();
856 }
858 nsresult
859 nsHttpPipeline::OnReadSegment(const char *segment,
860 uint32_t count,
861 uint32_t *countRead)
862 {
863 return mSendBufOut->Write(segment, count, countRead);
864 }
866 nsresult
867 nsHttpPipeline::FillSendBuf()
868 {
869 // reads from request queue, moving transactions to response queue
870 // when they have been completely read.
872 nsresult rv;
874 if (!mSendBufIn) {
875 // allocate a single-segment pipe
876 rv = NS_NewPipe(getter_AddRefs(mSendBufIn),
877 getter_AddRefs(mSendBufOut),
878 nsIOService::gDefaultSegmentSize, /* segment size */
879 nsIOService::gDefaultSegmentSize, /* max size */
880 true, true);
881 if (NS_FAILED(rv)) return rv;
882 }
884 uint32_t n;
885 uint64_t avail;
886 uint64_t totalSent = 0;
887 uint64_t reqsSent = 0;
888 uint64_t alreadyPending = 0;
890 mSendBufIn->Available(&alreadyPending);
892 nsAHttpTransaction *trans;
893 nsITransport *transport = Transport();
894 #ifdef WTF_TEST
895 uint64_t totalAvailable = Available();
896 nsRefPtr<nsHttpConnectionInfo> ci;
897 GetConnectionInfo(getter_AddRefs(ci));
898 #endif
900 while ((trans = Request(0)) != nullptr) {
901 avail = trans->Available();
902 if (avail) {
903 // if there is already a response in the responseq then this
904 // new data comprises a pipeline. Update the transaction in the
905 // response queue to reflect that if necessary. We are now sending
906 // out a request while we haven't received all responses.
907 nsAHttpTransaction *response = Response(0);
908 if (response && !response->PipelinePosition())
909 response->SetPipelinePosition(1);
910 rv = trans->ReadSegments(this, (uint32_t)std::min(avail, (uint64_t)UINT32_MAX), &n);
911 if (NS_FAILED(rv)) return rv;
913 if (n == 0) {
914 LOG(("send pipe is full"));
915 break;
916 }
918 mSendingToProgress += n;
919 totalSent += n;
920 if (!mSuppressSendEvents && transport) {
921 // Simulate a SENDING_TO event
922 trans->OnTransportStatus(transport,
923 NS_NET_STATUS_SENDING_TO,
924 mSendingToProgress);
925 }
926 }
928 avail = trans->Available();
929 if (avail == 0) {
930 #ifdef WTF_TEST
931 nsHttpRequestHead *head = trans->RequestHead();
932 fprintf(stderr, "WTF-order: Pipelined req %d/%d (%dB). Url: %s%s\n",
933 trans->PipelinePosition(), PipelineDepth(), n,
934 ci->Host(), head ? head->RequestURI().BeginReading() : "<unknown?>");
935 #endif
936 reqsSent++;
938 // move transaction from request queue to response queue
939 mRequestQ.RemoveElementAt(0);
940 mResponseQ.AppendElement(trans);
941 mRequestIsPartial = false;
943 if (!mSuppressSendEvents && transport) {
944 // Simulate a WAITING_FOR event
945 trans->OnTransportStatus(transport,
946 NS_NET_STATUS_WAITING_FOR,
947 mSendingToProgress);
948 }
950 // It would be good to re-enable data read handlers via ResumeRecv()
951 // except the read handler code can be synchronously dispatched on
952 // the stack.
953 }
954 else
955 mRequestIsPartial = true;
956 }
958 #ifdef WTF_TEST
959 if (totalSent)
960 fprintf(stderr, "WTF-combine: Sent %ld/%ld bytes of %ld combined pipelined requests for host %s\n",
961 alreadyPending+totalSent, totalAvailable, reqsSent, ci->Host());
962 #endif
964 return NS_OK;
965 }
967 } // namespace mozilla::net
968 } // namespace mozilla