mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
b808ffac2d
- Bug 1196391, part 3 - Make argument count assertions fatal in js::ExpandErrorArgumentsVA(). r=Waldo (8233c0afac) - minor cleanup (1da0b2c3e9) - Bug 1191765: Make Debugger.Object.prototype.getScript properly recognize functions without scripts. r=fitzgen (3e2753577f) - Bug 1165807 - display WeakSet and WeakMap contents in console; r=bz,fitzgen (d8f70d8e6a) - Bug 1226024 - Expose the root of the dominator tree to JavaScript; r=bz,sfink x # Please enter the commit message for your changes. Lines starting (1949832288) - Bug 1220702 - Part 1: Replace callback() and newNode() with variadic templates. What could go wrong? r=Waldo. (80e1b40871) - Bug 1220702 - Part 2: Fix the .method property of certain FunctionDeclaration nodes. r=Waldo. (47f244a6e0) - Bug 1220702 - Part 3: Distinguish ES6 generators from legacy generators in Reflect.parse() output. r=Waldo. (362bb8ea4b) - bit of Bug 1180017 - Fix up the badly-horked backout and re-land. (f4f92ff88c) - Bug 1155303 - Add telemetry for async DeferredFinalize max pause. r=smaug (3ddc7b8856) - Bug 1174796 - Make sure ReleaseNow releases everything. r=smaug (5065aa1f52) - Bug 1191918 - Remove printf debugging r=me (48e95e425e) - update some tests (26ae4a8050) - Bug 1218643 - correct a DOM test. r=smaug " (75c6302bbb) - space (26dfeca131) - Bug 1166805 part 1 - refactor common tests for whether an animated list mirrors the base version of the list into methods. r=dholbert (e538b90e00) - Bug 1166805 part 2 - Call SetCapacity before calling DOMSVGXXXList::MaybeInsertNullInAnimValListAt, to prevent fallible InsertElementAt calls from failing r=dholbert (918397681f) - Bug 1092125 - Part 1 - Add non-scaling-stroke support to nsSVGPathGeometryElement::GetGeometryBounds (except line). r=jwatt (19ee6f9517) - Bug 1092125 - part 2 - add non-scaling-stroke support to SVGLineElement::GetGeometryBounds. r=jwatt (2c84b88ff1) - Bug 1140080 - ensure we only create stop frames for gradients. r=dholbert (636db91975) - Bug 1182496 - Don't create frames for SVG <text> descendants with failing conditional processing attributes. r=dholbert (4e976c1587) - Bug 1149542 - Part 3: Crashtest. r=dholbert (cde5ca0f57) - Bug 1209525 - Protect GetGeometryBounds from a singular non-scaling-stroke transform. r=longsonr (2cd1f2e0a4) - Bug 325427 - Add crashtest. (98ab5e6907) - Bug 803562 - force -moz-appearance: none on foreignObject elements. r=dbaron (0ba37f76ad) - Bug 950324 - Add crashtest. (6e2f7bc4c2) - Bug 1178159 - Ignore stroke-linecap:"square" on circle and ellipse. r=longsonr (42f4a9a71c) - Bug 1187770 - work around draw targets that don't display zero-length lines. r=longsonr (a612616ecb) - Bug 1222812 - add a null check in case there is no old style. r=dholbert (6a883edea2) - Bug 958160 - Compute bounds in transformed space instead of user space in GetCoveredRegion. r=longsonr (d020a10c56) - Bug 1224061: Followup to fix b2g bustage r=me CLOSED TREE (1dc2693955) - Bug 1173573 - Fix possible crash initializing sessionstorage. r=honzab (c6c77ccf7d) - Bug 536509 - Update localStorage to use common StorageAllowedForWindow logic, r=ehsan (825ee71ff3) - Bug 606655 - delete cookies UI option AskMeEveryTime and its related comments and tests. r=mak (828dfe54a5) - Bug 1194052 - Append to redirectchain before asyncopen() is called (r=sicking,mayhemer) (bb051ceb94) - Bug 1145503 - TP exceptions added while in Private Browsing mode persist beyond the Private Browsing session. r=ehsan Import url-classifier and private browsing modules. (4492b2de09) - Bug 1138979 - Pref to turn TP on when in Private Browsing mode. r=mmc , r=ehsan (5078eaa914) - Bug 1168635 - Extend nsITLSServerSocket to customize cipher suites. r=keeler (185a551640) - Bug 1165423 - WebRTC Fix DTLS handshake by expanding UDP buffer. r=rjesup (1f207e03ee) - Bug 1219939 - make nsTemporaryFileInputStream nsISeekableStream, r=jduell (f2a5ddfbf2) - Bug 1125816 - Parse FTP directory listings of Windows CE and WEC7 FTP Server r=jduell (4137b29d21) - Bug 1171016 - Initialize the linelen variable at its declaration in ParseFTPList.cpp. r=mcmanus (2a3960897d) - Bug 1197313 - remove PR_snprintf calls in netwerk/; r=froydnj (005da76d31) - Bug 1219910 - make gSocketThread a relaxed atomic variable; r=mcmanus (620d299605) - Bug 1222867 - part 1 - return already_AddRefed from WebSocketEventService::CreateFrameIfNeeded; r=mcmanus (0eee829a08) - Bug 1222867 - part 2 - be smarter about transferring ownership of WebSocketFrame; r=mcmanus (4a9fd71798) - Bug 1211001 - constant ASSERTION: nsITimer->SetDelay() called when the one-shot timer is not set up, r=mcmanus (451c903cbe) - Bug 1130822 - properly decode arbitrarily aligned data for non-tier1 platforms. r=mcmanus (bcb99913dc) - Bug 1204731 - telemetry for peer h2 goaway r=hurley (d6748682b4) - Bug 1205810 - telemetry for local h2 goaway code r=hurley (e142625588) - bug 1194818 - h2 header priority handling r=hurley (51766fff44) - bug 1194820 - h2 push promise padding handling r=hurley (560ee1f480) - bug 1208114 - fix h2 connect tunnels r=hurley (d17f920c31) - Bug 1213060 (part 1) - Properly handle discarding padding in Http2Session::OnWriteSegment. r=mcmanus (2cce4ac006) - Bug 1213060 (part 2) - Re-add state assertion in Http2Session::OnWriteSegment. r=mcmanus (6b1c030780) - fix build (39845819f6) - Bug 1214076 - allow TokenServerClient errors to be JSON.stringify'd. r=rnewman (ab6085fa97) - Bug 1220007 P1 Allow ConsoleReportCollectors to flush to another collector. r=bz (a97b2c5a57) - Bug 1220007 P2 Make InterceptedChannel's collect logs locally and only flush to nsIChannel on main thread r=bz (85b77c5a44) - Bug 867407 - Fix cloning of file URIs with search query strings (r=sworkman) (da6fd51c15) - Bug 1220728 Clear pending exceptions if string conversion fails in SWintercept error handling. r=bz (b188c34862) - Bug 1147913 - Change NS_SOCKETTRANSPORTSERVICE_CONTRACTID to NS_STREAMTRANSPORTSERVICE_CONTRACTID in RespondWithHandler::ResolvedCallback. r=ehsan (5b443e88ab) - fix namespace (562ed51caa) - Bug 1206060 - Show pinning status at about:cache. r=michal (75ed53663f) - Bug 1032254 - Generic way to pin reasource in the HTTP cache, r=michal (eeb860f8e3) - Bug 1211504. Remove unused member from RefLayer. (fab6bae915)
916 lines
26 KiB
C++
916 lines
26 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
// HttpLog.h should generally be included first
|
|
#include "HttpLog.h"
|
|
|
|
#include "nsHttpPipeline.h"
|
|
#include "nsHttpHandler.h"
|
|
#include "nsIOService.h"
|
|
#include "nsISocketTransport.h"
|
|
#include "nsIPipe.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsSocketTransportService2.h"
|
|
#include <algorithm>
|
|
|
|
#ifdef DEBUG
|
|
#include "prthread.h"
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
namespace net {
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsHttpPushBackWriter
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class nsHttpPushBackWriter : public nsAHttpSegmentWriter
|
|
{
|
|
public:
|
|
nsHttpPushBackWriter(const char *buf, uint32_t bufLen)
|
|
: mBuf(buf)
|
|
, mBufLen(bufLen)
|
|
{ }
|
|
virtual ~nsHttpPushBackWriter() {}
|
|
|
|
nsresult OnWriteSegment(char *buf, uint32_t count, uint32_t *countWritten)
|
|
{
|
|
if (mBufLen == 0)
|
|
return NS_BASE_STREAM_CLOSED;
|
|
|
|
if (count > mBufLen)
|
|
count = mBufLen;
|
|
|
|
memcpy(buf, mBuf, count);
|
|
|
|
mBuf += count;
|
|
mBufLen -= count;
|
|
*countWritten = count;
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
const char *mBuf;
|
|
uint32_t mBufLen;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsHttpPipeline <public>
|
|
//-----------------------------------------------------------------------------
|
|
|
|
nsHttpPipeline::nsHttpPipeline()
|
|
: mStatus(NS_OK)
|
|
, mRequestIsPartial(false)
|
|
, mResponseIsPartial(false)
|
|
, mClosed(false)
|
|
, mUtilizedPipeline(false)
|
|
, mPushBackBuf(nullptr)
|
|
, mPushBackLen(0)
|
|
, mPushBackMax(0)
|
|
, mHttp1xTransactionCount(0)
|
|
, mReceivingFromProgress(0)
|
|
, mSendingToProgress(0)
|
|
, mSuppressSendEvents(true)
|
|
{
|
|
}
|
|
|
|
nsHttpPipeline::~nsHttpPipeline()
|
|
{
|
|
// make sure we aren't still holding onto any transactions!
|
|
Close(NS_ERROR_ABORT);
|
|
|
|
if (mPushBackBuf)
|
|
moz_free(mPushBackBuf);
|
|
}
|
|
|
|
nsresult
|
|
nsHttpPipeline::AddTransaction(nsAHttpTransaction *trans)
|
|
{
|
|
LOG(("nsHttpPipeline::AddTransaction [this=%p trans=%x]\n", this, trans));
|
|
|
|
if (mRequestQ.Length() || mResponseQ.Length())
|
|
mUtilizedPipeline = true;
|
|
|
|
NS_ADDREF(trans);
|
|
mRequestQ.AppendElement(trans);
|
|
uint32_t qlen = PipelineDepth();
|
|
|
|
if (qlen != 1) {
|
|
trans->SetPipelinePosition(qlen);
|
|
}
|
|
else {
|
|
// do it for this case in case an idempotent cancellation
|
|
// is being repeated and an old value needs to be cleared
|
|
trans->SetPipelinePosition(0);
|
|
}
|
|
|
|
// trans->SetConnection() needs to be updated to point back at
|
|
// the pipeline object.
|
|
trans->SetConnection(this);
|
|
|
|
if (mConnection && !mClosed && mRequestQ.Length() == 1)
|
|
mConnection->ResumeSend();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
uint32_t
|
|
nsHttpPipeline::PipelineDepth()
|
|
{
|
|
return mRequestQ.Length() + mResponseQ.Length();
|
|
}
|
|
|
|
nsresult
|
|
nsHttpPipeline::SetPipelinePosition(int32_t position)
|
|
{
|
|
nsAHttpTransaction *trans = Response(0);
|
|
if (trans)
|
|
return trans->SetPipelinePosition(position);
|
|
return NS_OK;
|
|
}
|
|
|
|
int32_t
|
|
nsHttpPipeline::PipelinePosition()
|
|
{
|
|
nsAHttpTransaction *trans = Response(0);
|
|
if (trans)
|
|
return trans->PipelinePosition();
|
|
|
|
// The response queue is empty, so return oldest request
|
|
if (mRequestQ.Length())
|
|
return Request(mRequestQ.Length() - 1)->PipelinePosition();
|
|
|
|
// No transactions in the pipeline
|
|
return 0;
|
|
}
|
|
|
|
nsHttpPipeline *
|
|
nsHttpPipeline::QueryPipeline()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsHttpPipeline::nsISupports
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NS_IMPL_ADDREF(nsHttpPipeline)
|
|
NS_IMPL_RELEASE(nsHttpPipeline)
|
|
|
|
// multiple inheritance fun :-)
|
|
NS_INTERFACE_MAP_BEGIN(nsHttpPipeline)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsHttpPipeline::nsAHttpConnection
|
|
//-----------------------------------------------------------------------------
|
|
|
|
nsresult
|
|
nsHttpPipeline::OnHeadersAvailable(nsAHttpTransaction *trans,
|
|
nsHttpRequestHead *requestHead,
|
|
nsHttpResponseHead *responseHead,
|
|
bool *reset)
|
|
{
|
|
LOG(("nsHttpPipeline::OnHeadersAvailable [this=%p]\n", this));
|
|
|
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
|
MOZ_ASSERT(mConnection, "no connection");
|
|
|
|
RefPtr<nsHttpConnectionInfo> ci;
|
|
GetConnectionInfo(getter_AddRefs(ci));
|
|
MOZ_ASSERT(ci);
|
|
|
|
bool pipeliningBefore = gHttpHandler->ConnMgr()->SupportsPipelining(ci);
|
|
|
|
// trans has now received its response headers; forward to the real connection
|
|
nsresult rv = mConnection->OnHeadersAvailable(trans,
|
|
requestHead,
|
|
responseHead,
|
|
reset);
|
|
|
|
if (!pipeliningBefore && gHttpHandler->ConnMgr()->SupportsPipelining(ci))
|
|
// The received headers have expanded the eligible
|
|
// pipeline depth for this connection
|
|
gHttpHandler->ConnMgr()->ProcessPendingQForEntry(ci);
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsHttpPipeline::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
|
|
{
|
|
LOG(("nsHttpPipeline::CloseTransaction [this=%p trans=%x reason=%x]\n",
|
|
this, trans, reason));
|
|
|
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
|
MOZ_ASSERT(NS_FAILED(reason), "expecting failure code");
|
|
|
|
// the specified transaction is to be closed with the given "reason"
|
|
|
|
int32_t index;
|
|
bool killPipeline = false;
|
|
|
|
index = mRequestQ.IndexOf(trans);
|
|
if (index >= 0) {
|
|
if (index == 0 && mRequestIsPartial) {
|
|
// the transaction is in the request queue. check to see if any of
|
|
// its data has been written out yet.
|
|
killPipeline = true;
|
|
}
|
|
mRequestQ.RemoveElementAt(index);
|
|
}
|
|
else {
|
|
index = mResponseQ.IndexOf(trans);
|
|
if (index >= 0)
|
|
mResponseQ.RemoveElementAt(index);
|
|
// while we could avoid killing the pipeline if this transaction is the
|
|
// last transaction in the pipeline, there doesn't seem to be that much
|
|
// value in doing so. most likely if this transaction is going away,
|
|
// the others will be shortly as well.
|
|
killPipeline = true;
|
|
}
|
|
|
|
// Marking this connection as non-reusable prevents other items from being
|
|
// added to it and causes it to be torn down soon.
|
|
DontReuse();
|
|
|
|
trans->Close(reason);
|
|
NS_RELEASE(trans);
|
|
|
|
if (killPipeline) {
|
|
// reschedule anything from this pipeline onto a different connection
|
|
CancelPipeline(reason);
|
|
}
|
|
|
|
// If all the transactions have been removed then we can close the connection
|
|
// right away.
|
|
if (!mRequestQ.Length() && !mResponseQ.Length() && mConnection)
|
|
mConnection->CloseTransaction(this, reason);
|
|
}
|
|
|
|
nsresult
|
|
nsHttpPipeline::TakeTransport(nsISocketTransport **aTransport,
|
|
nsIAsyncInputStream **aInputStream,
|
|
nsIAsyncOutputStream **aOutputStream)
|
|
{
|
|
return mConnection->TakeTransport(aTransport, aInputStream, aOutputStream);
|
|
}
|
|
|
|
bool
|
|
nsHttpPipeline::IsPersistent()
|
|
{
|
|
return true; // pipelining requires this
|
|
}
|
|
|
|
bool
|
|
nsHttpPipeline::IsReused()
|
|
{
|
|
if (!mUtilizedPipeline && mConnection)
|
|
return mConnection->IsReused();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsHttpPipeline::DontReuse()
|
|
{
|
|
if (mConnection)
|
|
mConnection->DontReuse();
|
|
}
|
|
|
|
nsresult
|
|
nsHttpPipeline::PushBack(const char *data, uint32_t length)
|
|
{
|
|
LOG(("nsHttpPipeline::PushBack [this=%p len=%u]\n", this, length));
|
|
|
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
|
MOZ_ASSERT(mPushBackLen == 0, "push back buffer already has data!");
|
|
|
|
// If we have no chance for a pipeline (e.g. due to an Upgrade)
|
|
// then push this data down to original connection
|
|
if (!mConnection->IsPersistent())
|
|
return mConnection->PushBack(data, length);
|
|
|
|
// PushBack is called recursively from WriteSegments
|
|
|
|
// XXX we have a design decision to make here. either we buffer the data
|
|
// and process it when we return to WriteSegments, or we attempt to move
|
|
// onto the next transaction from here. doing so adds complexity with the
|
|
// benefit of eliminating the extra buffer copy. the buffer is at most
|
|
// 4096 bytes, so it is really unclear if there is any value in the added
|
|
// complexity. besides simplicity, buffering this data has the advantage
|
|
// that we'll call close on the transaction sooner, which will wake up
|
|
// the HTTP channel sooner to continue with its work.
|
|
|
|
if (!mPushBackBuf) {
|
|
mPushBackMax = length;
|
|
mPushBackBuf = (char *) moz_xmalloc(mPushBackMax);
|
|
if (!mPushBackBuf)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
else if (length > mPushBackMax) {
|
|
// grow push back buffer as necessary.
|
|
MOZ_ASSERT(length <= nsIOService::gDefaultSegmentSize, "too big");
|
|
mPushBackMax = length;
|
|
mPushBackBuf = (char *) moz_xrealloc(mPushBackBuf, mPushBackMax);
|
|
if (!mPushBackBuf)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
memcpy(mPushBackBuf, data, length);
|
|
mPushBackLen = length;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsHttpConnection *
|
|
nsHttpPipeline::TakeHttpConnection()
|
|
{
|
|
if (mConnection)
|
|
return mConnection->TakeHttpConnection();
|
|
return nullptr;
|
|
}
|
|
|
|
nsAHttpTransaction::Classifier
|
|
nsHttpPipeline::Classification()
|
|
{
|
|
if (mConnection)
|
|
return mConnection->Classification();
|
|
|
|
LOG(("nsHttpPipeline::Classification this=%p "
|
|
"has null mConnection using CLASS_SOLO default", this));
|
|
return nsAHttpTransaction::CLASS_SOLO;
|
|
}
|
|
|
|
void
|
|
nsHttpPipeline::SetProxyConnectFailed()
|
|
{
|
|
nsAHttpTransaction *trans = Request(0);
|
|
|
|
if (trans)
|
|
trans->SetProxyConnectFailed();
|
|
}
|
|
|
|
nsHttpRequestHead *
|
|
nsHttpPipeline::RequestHead()
|
|
{
|
|
nsAHttpTransaction *trans = Request(0);
|
|
|
|
if (trans)
|
|
return trans->RequestHead();
|
|
return nullptr;
|
|
}
|
|
|
|
uint32_t
|
|
nsHttpPipeline::Http1xTransactionCount()
|
|
{
|
|
return mHttp1xTransactionCount;
|
|
}
|
|
|
|
nsresult
|
|
nsHttpPipeline::TakeSubTransactions(
|
|
nsTArray<RefPtr<nsAHttpTransaction> > &outTransactions)
|
|
{
|
|
LOG(("nsHttpPipeline::TakeSubTransactions [this=%p]\n", this));
|
|
|
|
if (mResponseQ.Length() || mRequestIsPartial)
|
|
return NS_ERROR_ALREADY_OPENED;
|
|
|
|
int32_t i, count = mRequestQ.Length();
|
|
for (i = 0; i < count; ++i) {
|
|
nsAHttpTransaction *trans = Request(i);
|
|
// set the transaction conneciton object back to the underlying
|
|
// nsHttpConnectionHandle
|
|
trans->SetConnection(mConnection);
|
|
outTransactions.AppendElement(trans);
|
|
NS_RELEASE(trans);
|
|
}
|
|
mRequestQ.Clear();
|
|
|
|
LOG((" took %d\n", count));
|
|
return NS_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsHttpPipeline::nsAHttpTransaction
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void
|
|
nsHttpPipeline::SetConnection(nsAHttpConnection *conn)
|
|
{
|
|
LOG(("nsHttpPipeline::SetConnection [this=%p conn=%x]\n", this, conn));
|
|
|
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
|
MOZ_ASSERT(!conn || !mConnection, "already have a connection");
|
|
|
|
mConnection = conn;
|
|
}
|
|
|
|
nsAHttpConnection *
|
|
nsHttpPipeline::Connection()
|
|
{
|
|
LOG(("nsHttpPipeline::Connection [this=%p conn=%x]\n", this, mConnection.get()));
|
|
|
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
|
return mConnection;
|
|
}
|
|
|
|
void
|
|
nsHttpPipeline::GetSecurityCallbacks(nsIInterfaceRequestor **result)
|
|
{
|
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
|
|
|
// depending on timing this could be either the request or the response
|
|
// that is needed - but they both go to the same host. A request for these
|
|
// callbacks directly in nsHttpTransaction would not make a distinction
|
|
// over whether the the request had been transmitted yet.
|
|
nsAHttpTransaction *trans = Request(0);
|
|
if (!trans)
|
|
trans = Response(0);
|
|
if (trans)
|
|
trans->GetSecurityCallbacks(result);
|
|
else {
|
|
*result = nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsHttpPipeline::OnTransportStatus(nsITransport* transport,
|
|
nsresult status, int64_t progress)
|
|
{
|
|
LOG(("nsHttpPipeline::OnStatus [this=%p status=%x progress=%lld]\n",
|
|
this, status, progress));
|
|
|
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
|
|
|
nsAHttpTransaction *trans;
|
|
int32_t i, count;
|
|
|
|
switch (status) {
|
|
|
|
case NS_NET_STATUS_RESOLVING_HOST:
|
|
case NS_NET_STATUS_RESOLVED_HOST:
|
|
case NS_NET_STATUS_CONNECTING_TO:
|
|
case NS_NET_STATUS_CONNECTED_TO:
|
|
// These should only appear at most once per pipeline.
|
|
// Deliver to the first transaction.
|
|
|
|
trans = Request(0);
|
|
if (!trans)
|
|
trans = Response(0);
|
|
if (trans)
|
|
trans->OnTransportStatus(transport, status, progress);
|
|
|
|
break;
|
|
|
|
case NS_NET_STATUS_SENDING_TO:
|
|
// This is generated by the socket transport when (part) of
|
|
// a transaction is written out
|
|
//
|
|
// In pipelining this is generated out of FillSendBuf(), but it cannot do
|
|
// so until the connection is confirmed by CONNECTED_TO.
|
|
// See patch for bug 196827.
|
|
//
|
|
|
|
if (mSuppressSendEvents) {
|
|
mSuppressSendEvents = false;
|
|
|
|
// catch up by sending the event to all the transactions that have
|
|
// moved from request to response and any that have been partially
|
|
// sent. Also send WAITING_FOR to those that were completely sent
|
|
count = mResponseQ.Length();
|
|
for (i = 0; i < count; ++i) {
|
|
Response(i)->OnTransportStatus(transport,
|
|
NS_NET_STATUS_SENDING_TO,
|
|
progress);
|
|
Response(i)->OnTransportStatus(transport,
|
|
NS_NET_STATUS_WAITING_FOR,
|
|
progress);
|
|
}
|
|
if (mRequestIsPartial && Request(0))
|
|
Request(0)->OnTransportStatus(transport,
|
|
NS_NET_STATUS_SENDING_TO,
|
|
progress);
|
|
mSendingToProgress = progress;
|
|
}
|
|
// otherwise ignore it
|
|
break;
|
|
|
|
case NS_NET_STATUS_WAITING_FOR:
|
|
// Created by nsHttpConnection when request pipeline has been totally
|
|
// sent. Ignore it here because it is simulated in FillSendBuf() when
|
|
// a request is moved from request to response.
|
|
|
|
// ignore it
|
|
break;
|
|
|
|
case NS_NET_STATUS_RECEIVING_FROM:
|
|
// Forward this only to the transaction currently recieving data. It is
|
|
// normally generated by the socket transport, but can also
|
|
// be repeated by the pushbackwriter if necessary.
|
|
mReceivingFromProgress = progress;
|
|
if (Response(0))
|
|
Response(0)->OnTransportStatus(transport, status, progress);
|
|
break;
|
|
|
|
default:
|
|
// forward other notifications to all request transactions
|
|
count = mRequestQ.Length();
|
|
for (i = 0; i < count; ++i)
|
|
Request(i)->OnTransportStatus(transport, status, progress);
|
|
break;
|
|
}
|
|
}
|
|
|
|
nsHttpConnectionInfo *
|
|
nsHttpPipeline::ConnectionInfo()
|
|
{
|
|
nsAHttpTransaction *trans = Request(0) ? Request(0) : Response(0);
|
|
if (!trans) {
|
|
return nullptr;
|
|
}
|
|
return trans->ConnectionInfo();
|
|
}
|
|
|
|
bool
|
|
nsHttpPipeline::IsDone()
|
|
{
|
|
bool done = true;
|
|
|
|
uint32_t i, count = mRequestQ.Length();
|
|
for (i = 0; done && (i < count); i++)
|
|
done = Request(i)->IsDone();
|
|
|
|
count = mResponseQ.Length();
|
|
for (i = 0; done && (i < count); i++)
|
|
done = Response(i)->IsDone();
|
|
|
|
return done;
|
|
}
|
|
|
|
nsresult
|
|
nsHttpPipeline::Status()
|
|
{
|
|
return mStatus;
|
|
}
|
|
|
|
uint32_t
|
|
nsHttpPipeline::Caps()
|
|
{
|
|
nsAHttpTransaction *trans = Request(0);
|
|
if (!trans)
|
|
trans = Response(0);
|
|
|
|
return trans ? trans->Caps() : 0;
|
|
}
|
|
|
|
void
|
|
nsHttpPipeline::SetDNSWasRefreshed()
|
|
{
|
|
nsAHttpTransaction *trans = Request(0);
|
|
if (!trans)
|
|
trans = Response(0);
|
|
|
|
if (trans)
|
|
trans->SetDNSWasRefreshed();
|
|
}
|
|
|
|
uint64_t
|
|
nsHttpPipeline::Available()
|
|
{
|
|
uint64_t result = 0;
|
|
|
|
int32_t i, count = mRequestQ.Length();
|
|
for (i=0; i<count; ++i)
|
|
result += Request(i)->Available();
|
|
return result;
|
|
}
|
|
|
|
NS_METHOD
|
|
nsHttpPipeline::ReadFromPipe(nsIInputStream *stream,
|
|
void *closure,
|
|
const char *buf,
|
|
uint32_t offset,
|
|
uint32_t count,
|
|
uint32_t *countRead)
|
|
{
|
|
nsHttpPipeline *self = (nsHttpPipeline *) closure;
|
|
return self->mReader->OnReadSegment(buf, count, countRead);
|
|
}
|
|
|
|
nsresult
|
|
nsHttpPipeline::ReadSegments(nsAHttpSegmentReader *reader,
|
|
uint32_t count,
|
|
uint32_t *countRead)
|
|
{
|
|
LOG(("nsHttpPipeline::ReadSegments [this=%p count=%u]\n", this, count));
|
|
|
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
|
|
|
if (mClosed) {
|
|
*countRead = 0;
|
|
return mStatus;
|
|
}
|
|
|
|
nsresult rv;
|
|
uint64_t avail = 0;
|
|
if (mSendBufIn) {
|
|
rv = mSendBufIn->Available(&avail);
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
|
|
if (avail == 0) {
|
|
rv = FillSendBuf();
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = mSendBufIn->Available(&avail);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// return EOF if send buffer is empty
|
|
if (avail == 0) {
|
|
*countRead = 0;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// read no more than what was requested
|
|
if (avail > count)
|
|
avail = count;
|
|
|
|
mReader = reader;
|
|
|
|
// avail is under 4GB, so casting to uint32_t is safe
|
|
rv = mSendBufIn->ReadSegments(ReadFromPipe, this, (uint32_t)avail, countRead);
|
|
|
|
mReader = nullptr;
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsHttpPipeline::WriteSegments(nsAHttpSegmentWriter *writer,
|
|
uint32_t count,
|
|
uint32_t *countWritten)
|
|
{
|
|
LOG(("nsHttpPipeline::WriteSegments [this=%p count=%u]\n", this, count));
|
|
|
|
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
|
|
|
if (mClosed)
|
|
return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
|
|
|
|
nsAHttpTransaction *trans;
|
|
nsresult rv;
|
|
|
|
trans = Response(0);
|
|
// This code deals with the establishment of a CONNECT tunnel through
|
|
// an HTTP proxy. It allows the connection to do the CONNECT/200
|
|
// HTTP transaction to establish a tunnel as a precursor to the
|
|
// actual pipeline of regular HTTP transactions.
|
|
if (!trans && mRequestQ.Length() &&
|
|
mConnection->IsProxyConnectInProgress()) {
|
|
LOG(("nsHttpPipeline::WriteSegments [this=%p] Forced Delegation\n",
|
|
this));
|
|
trans = Request(0);
|
|
}
|
|
|
|
if (!trans) {
|
|
if (mRequestQ.Length() > 0)
|
|
rv = NS_BASE_STREAM_WOULD_BLOCK;
|
|
else
|
|
rv = NS_BASE_STREAM_CLOSED;
|
|
}
|
|
else {
|
|
//
|
|
// ask the transaction to consume data from the connection.
|
|
// PushBack may be called recursively.
|
|
//
|
|
rv = trans->WriteSegments(writer, count, countWritten);
|
|
|
|
if (rv == NS_BASE_STREAM_CLOSED || trans->IsDone()) {
|
|
trans->Close(NS_OK);
|
|
|
|
// Release the transaction if it is not IsProxyConnectInProgress()
|
|
if (trans == Response(0)) {
|
|
NS_RELEASE(trans);
|
|
mResponseQ.RemoveElementAt(0);
|
|
mResponseIsPartial = false;
|
|
++mHttp1xTransactionCount;
|
|
}
|
|
|
|
// ask the connection manager to add additional transactions
|
|
// to our pipeline.
|
|
RefPtr<nsHttpConnectionInfo> ci;
|
|
GetConnectionInfo(getter_AddRefs(ci));
|
|
if (ci)
|
|
gHttpHandler->ConnMgr()->ProcessPendingQForEntry(ci);
|
|
}
|
|
else
|
|
mResponseIsPartial = true;
|
|
}
|
|
|
|
if (mPushBackLen) {
|
|
nsHttpPushBackWriter writer(mPushBackBuf, mPushBackLen);
|
|
uint32_t len = mPushBackLen, n;
|
|
mPushBackLen = 0;
|
|
|
|
// This progress notification has previously been sent from
|
|
// the socket transport code, but it was delivered to the
|
|
// previous transaction on the pipeline.
|
|
nsITransport *transport = Transport();
|
|
if (transport)
|
|
OnTransportStatus(transport, NS_NET_STATUS_RECEIVING_FROM,
|
|
mReceivingFromProgress);
|
|
|
|
// the push back buffer is never larger than NS_HTTP_SEGMENT_SIZE,
|
|
// so we are guaranteed that the next response will eat the entire
|
|
// push back buffer (even though it might again call PushBack).
|
|
rv = WriteSegments(&writer, len, &n);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
uint32_t
|
|
nsHttpPipeline::CancelPipeline(nsresult originalReason)
|
|
{
|
|
uint32_t i, reqLen, respLen, total;
|
|
nsAHttpTransaction *trans;
|
|
|
|
reqLen = mRequestQ.Length();
|
|
respLen = mResponseQ.Length();
|
|
total = reqLen + respLen;
|
|
|
|
// don't count the first response, if presnet
|
|
if (respLen)
|
|
total--;
|
|
|
|
if (!total)
|
|
return 0;
|
|
|
|
// any pending requests can ignore this error and be restarted
|
|
// unless it is during a CONNECT tunnel request
|
|
for (i = 0; i < reqLen; ++i) {
|
|
trans = Request(i);
|
|
if (mConnection && mConnection->IsProxyConnectInProgress())
|
|
trans->Close(originalReason);
|
|
else
|
|
trans->Close(NS_ERROR_NET_RESET);
|
|
NS_RELEASE(trans);
|
|
}
|
|
mRequestQ.Clear();
|
|
|
|
// any pending responses can be restarted except for the first one,
|
|
// that we might want to finish on this pipeline or cancel individually.
|
|
// Higher levels of callers ensure that we don't process non-idempotent
|
|
// tranasction with the NS_HTTP_ALLOW_PIPELINING bit set
|
|
for (i = 1; i < respLen; ++i) {
|
|
trans = Response(i);
|
|
trans->Close(NS_ERROR_NET_RESET);
|
|
NS_RELEASE(trans);
|
|
}
|
|
|
|
if (respLen > 1)
|
|
mResponseQ.TruncateLength(1);
|
|
|
|
DontReuse();
|
|
Classify(nsAHttpTransaction::CLASS_SOLO);
|
|
|
|
return total;
|
|
}
|
|
|
|
void
|
|
nsHttpPipeline::Close(nsresult reason)
|
|
{
|
|
LOG(("nsHttpPipeline::Close [this=%p reason=%x]\n", this, reason));
|
|
|
|
if (mClosed) {
|
|
LOG((" already closed\n"));
|
|
return;
|
|
}
|
|
|
|
// the connection is going away!
|
|
mStatus = reason;
|
|
mClosed = true;
|
|
|
|
RefPtr<nsHttpConnectionInfo> ci;
|
|
GetConnectionInfo(getter_AddRefs(ci));
|
|
uint32_t numRescheduled = CancelPipeline(reason);
|
|
|
|
// numRescheduled can be 0 if there is just a single response in the
|
|
// pipeline object. That isn't really a meaningful pipeline that
|
|
// has been forced to be rescheduled so it does not need to generate
|
|
// negative feedback.
|
|
if (ci && numRescheduled)
|
|
gHttpHandler->ConnMgr()->PipelineFeedbackInfo(
|
|
ci, nsHttpConnectionMgr::RedCanceledPipeline, nullptr, 0);
|
|
|
|
nsAHttpTransaction *trans = Response(0);
|
|
if (!trans)
|
|
return;
|
|
|
|
// The current transaction can be restarted via reset
|
|
// if the response has not started to arrive and the reason
|
|
// for failure is innocuous (e.g. not an SSL error)
|
|
if (!mResponseIsPartial &&
|
|
(reason == NS_ERROR_NET_RESET ||
|
|
reason == NS_OK ||
|
|
reason == NS_ERROR_NET_TIMEOUT ||
|
|
reason == NS_BASE_STREAM_CLOSED)) {
|
|
trans->Close(NS_ERROR_NET_RESET);
|
|
}
|
|
else {
|
|
trans->Close(reason);
|
|
}
|
|
|
|
NS_RELEASE(trans);
|
|
mResponseQ.Clear();
|
|
}
|
|
|
|
nsresult
|
|
nsHttpPipeline::OnReadSegment(const char *segment,
|
|
uint32_t count,
|
|
uint32_t *countRead)
|
|
{
|
|
return mSendBufOut->Write(segment, count, countRead);
|
|
}
|
|
|
|
nsresult
|
|
nsHttpPipeline::FillSendBuf()
|
|
{
|
|
// reads from request queue, moving transactions to response queue
|
|
// when they have been completely read.
|
|
|
|
nsresult rv;
|
|
|
|
if (!mSendBufIn) {
|
|
// allocate a single-segment pipe
|
|
rv = NS_NewPipe(getter_AddRefs(mSendBufIn),
|
|
getter_AddRefs(mSendBufOut),
|
|
nsIOService::gDefaultSegmentSize, /* segment size */
|
|
nsIOService::gDefaultSegmentSize, /* max size */
|
|
true, true);
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
|
|
uint32_t n;
|
|
uint64_t avail;
|
|
nsAHttpTransaction *trans;
|
|
nsITransport *transport = Transport();
|
|
|
|
while ((trans = Request(0)) != nullptr) {
|
|
avail = trans->Available();
|
|
if (avail) {
|
|
// if there is already a response in the responseq then this
|
|
// new data comprises a pipeline. Update the transaction in the
|
|
// response queue to reflect that if necessary. We are now sending
|
|
// out a request while we haven't received all responses.
|
|
nsAHttpTransaction *response = Response(0);
|
|
if (response && !response->PipelinePosition())
|
|
response->SetPipelinePosition(1);
|
|
rv = trans->ReadSegments(this, (uint32_t)std::min(avail, (uint64_t)UINT32_MAX), &n);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (n == 0) {
|
|
LOG(("send pipe is full"));
|
|
break;
|
|
}
|
|
|
|
mSendingToProgress += n;
|
|
if (!mSuppressSendEvents && transport) {
|
|
// Simulate a SENDING_TO event
|
|
trans->OnTransportStatus(transport,
|
|
NS_NET_STATUS_SENDING_TO,
|
|
mSendingToProgress);
|
|
}
|
|
}
|
|
|
|
avail = trans->Available();
|
|
if (avail == 0) {
|
|
// move transaction from request queue to response queue
|
|
mRequestQ.RemoveElementAt(0);
|
|
mResponseQ.AppendElement(trans);
|
|
mRequestIsPartial = false;
|
|
|
|
if (!mSuppressSendEvents && transport) {
|
|
// Simulate a WAITING_FOR event
|
|
trans->OnTransportStatus(transport,
|
|
NS_NET_STATUS_WAITING_FOR,
|
|
mSendingToProgress);
|
|
}
|
|
|
|
// It would be good to re-enable data read handlers via ResumeRecv()
|
|
// except the read handler code can be synchronously dispatched on
|
|
// the stack.
|
|
}
|
|
else
|
|
mRequestIsPartial = true;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace net
|
|
} // namespace mozilla
|