netwerk/protocol/http/nsHttpPipeline.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial