bug 1204614 - use h2 per stream flow control to deal with suspended channels r=hurley

This commit is contained in:
Patrick McManus 2015-09-11 15:53:27 -04:00
parent 966dd0a6dd
commit 357e0e244c
18 changed files with 330 additions and 62 deletions

View File

@ -1409,7 +1409,8 @@ pref("network.http.spdy.ping-threshold", 58);
pref("network.http.spdy.ping-timeout", 8);
pref("network.http.spdy.send-buffer-size", 131072);
pref("network.http.spdy.allow-push", true);
pref("network.http.spdy.push-allowance", 131072);
pref("network.http.spdy.push-allowance", 131072); // 128KB
pref("network.http.spdy.pull-allowance", 12582912); // 12MB
pref("network.http.spdy.default-concurrent", 100);
// alt-svc allows separation of transport routing from

View File

@ -108,6 +108,7 @@ struct HttpChannelOpenArgs
uint32_t cacheKey;
nsCString schedulingContextID;
OptionalCorsPreflightArgs preflightArgs;
uint32_t initialRwin;
};
struct HttpChannelConnectArgs

View File

@ -51,11 +51,9 @@ public:
const static uint32_t kSendingChunkSize = 4095;
const static uint32_t kTCPSendBufferSize = 131072;
// until we have an API that can push back on receiving data (right now
// WriteSegments is obligated to accept data and buffer) there is no
// reason to throttle with the rwin other than in server push
// scenarios.
const static uint32_t kInitialRwin = 256 * 1024 * 1024;
// This is roughly the amount of data a suspended channel will have to
// buffer before h2 flow control kicks in.
const static uint32_t kInitialRwin = 12 * 1024 * 1024; // 12MB
const static uint32_t kDefaultMaxConcurrent = 100;

View File

@ -103,6 +103,7 @@ Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t versio
, mServerInitialStreamWindow(kDefaultRwin)
, mLocalSessionWindow(kDefaultRwin)
, mServerSessionWindow(kDefaultRwin)
, mInitialRwin(ASpdySession::kInitialRwin)
, mOutputQueueSize(kDefaultQueueSize)
, mOutputQueueUsed(0)
, mOutputQueueSent(0)
@ -126,6 +127,7 @@ Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t versio
mDecompressor.SetCompressor(&mCompressor);
mPushAllowance = gHttpHandler->SpdyPushAllowance();
mInitialRwin = std::max(gHttpHandler->SpdyPullAllowance(), mPushAllowance);
mMaxConcurrent = gHttpHandler->DefaultSpdyConcurrent();
mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
SendHello();
@ -891,10 +893,10 @@ Http2Session::SendHello()
LogIO(this, nullptr, "Generate Settings", packet, kFrameHeaderBytes + dataLen);
// now bump the local session window from 64KB
uint32_t sessionWindowBump = ASpdySession::kInitialRwin - kDefaultRwin;
if (kDefaultRwin < ASpdySession::kInitialRwin) {
uint32_t sessionWindowBump = mInitialRwin - kDefaultRwin;
if (kDefaultRwin < mInitialRwin) {
// send a window update for the session (Stream 0) for something large
mLocalSessionWindow = ASpdySession::kInitialRwin;
mLocalSessionWindow = mInitialRwin;
packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
CreateFrameHeader(packet, 4, FRAME_TYPE_WINDOW_UPDATE, 0, 0);
@ -1106,7 +1108,8 @@ Http2Session::RemoveStreamFromQueues(Http2Stream *aStream)
{
RemoveStreamFromQueue(aStream, mReadyForWrite);
RemoveStreamFromQueue(aStream, mQueuedStreams);
RemoveStreamFromQueue(aStream, mReadyForRead);
RemoveStreamFromQueue(aStream, mPushesReadyForRead);
RemoveStreamFromQueue(aStream, mSlowConsumersReadyForRead);
}
void
@ -2456,35 +2459,20 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
// trigger that data pump here. This only reads from buffers (not the network)
// so mDownstreamState doesn't matter.
Http2Stream *pushConnectedStream =
static_cast<Http2Stream *>(mReadyForRead.PopFront());
static_cast<Http2Stream *>(mPushesReadyForRead.PopFront());
if (pushConnectedStream) {
LOG3(("Http2Session::WriteSegments %p processing pushed stream 0x%X\n",
this, pushConnectedStream->StreamID()));
mSegmentWriter = writer;
rv = pushConnectedStream->WriteSegments(this, count, countWritten);
mSegmentWriter = nullptr;
// The pipe in nsHttpTransaction rewrites CLOSED error codes into OK
// so we need this check to determine the truth.
if (NS_SUCCEEDED(rv) && !*countWritten &&
pushConnectedStream->PushSource() &&
pushConnectedStream->PushSource()->GetPushComplete()) {
rv = NS_BASE_STREAM_CLOSED;
}
if (rv == NS_BASE_STREAM_CLOSED) {
CleanupStream(pushConnectedStream, NS_OK, CANCEL_ERROR);
rv = NS_OK;
}
// if we return OK to nsHttpConnection it will use mSocketInCondition
// to determine whether to schedule more reads, incorrectly
// assuming that nsHttpConnection::OnSocketWrite() was called.
if (NS_SUCCEEDED(rv) || rv == NS_BASE_STREAM_WOULD_BLOCK) {
rv = NS_BASE_STREAM_WOULD_BLOCK;
ResumeRecv();
return ProcessConnectedPush(pushConnectedStream, writer, count, countWritten);
}
// feed gecko channels that previously stopped consuming data
// only take data from stored buffers
Http2Stream *slowConsumer =
static_cast<Http2Stream *>(mSlowConsumersReadyForRead.PopFront());
if (slowConsumer) {
internalStateType savedState = mDownstreamState;
mDownstreamState = NOT_USING_NETWORK;
rv = ProcessSlowConsumer(slowConsumer, writer, count, countWritten);
mDownstreamState = savedState;
return rv;
}
@ -2833,6 +2821,69 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
return rv;
}
nsresult
Http2Session::ProcessConnectedPush(Http2Stream *pushConnectedStream,
nsAHttpSegmentWriter * writer,
uint32_t count, uint32_t *countWritten)
{
LOG3(("Http2Session::ProcessConnectedPush %p 0x%X\n",
this, pushConnectedStream->StreamID()));
mSegmentWriter = writer;
nsresult rv = pushConnectedStream->WriteSegments(this, count, countWritten);
mSegmentWriter = nullptr;
// The pipe in nsHttpTransaction rewrites CLOSED error codes into OK
// so we need this check to determine the truth.
if (NS_SUCCEEDED(rv) && !*countWritten &&
pushConnectedStream->PushSource() &&
pushConnectedStream->PushSource()->GetPushComplete()) {
rv = NS_BASE_STREAM_CLOSED;
}
if (rv == NS_BASE_STREAM_CLOSED) {
CleanupStream(pushConnectedStream, NS_OK, CANCEL_ERROR);
rv = NS_OK;
}
// if we return OK to nsHttpConnection it will use mSocketInCondition
// to determine whether to schedule more reads, incorrectly
// assuming that nsHttpConnection::OnSocketWrite() was called.
if (NS_SUCCEEDED(rv) || rv == NS_BASE_STREAM_WOULD_BLOCK) {
rv = NS_BASE_STREAM_WOULD_BLOCK;
ResumeRecv();
}
return rv;
}
nsresult
Http2Session::ProcessSlowConsumer(Http2Stream *slowConsumer,
nsAHttpSegmentWriter * writer,
uint32_t count, uint32_t *countWritten)
{
LOG3(("Http2Session::ProcessSlowConsumer %p 0x%X\n",
this, slowConsumer->StreamID()));
mSegmentWriter = writer;
nsresult rv = slowConsumer->WriteSegments(this, count, countWritten);
mSegmentWriter = nullptr;
LOG3(("Http2Session::ProcessSlowConsumer Writesegments %p 0x%X rv %X %d\n",
this, slowConsumer->StreamID(), rv, *countWritten));
if (NS_SUCCEEDED(rv) && !*countWritten && slowConsumer->RecvdFin()) {
rv = NS_BASE_STREAM_CLOSED;
}
if (NS_SUCCEEDED(rv)) {
UpdateLocalRwin(slowConsumer, 0);
ConnectSlowConsumer(slowConsumer);
}
if (rv == NS_BASE_STREAM_CLOSED) {
CleanupStream(slowConsumer, NS_OK, CANCEL_ERROR);
rv = NS_OK;
}
return rv;
}
void
Http2Session::UpdateLocalStreamWindow(Http2Stream *stream, uint32_t bytes)
{
@ -2907,12 +2958,12 @@ Http2Session::UpdateLocalSessionWindow(uint32_t bytes)
// Don't necessarily ack every data packet. Only do it
// after a significant amount of data.
if ((mLocalSessionWindow > (ASpdySession::kInitialRwin - kMinimumToAck)) &&
if ((mLocalSessionWindow > (mInitialRwin - kMinimumToAck)) &&
(mLocalSessionWindow > kEmergencyWindowThreshold))
return;
// Only send max bits of window updates at a time.
uint64_t toack64 = ASpdySession::kInitialRwin - mLocalSessionWindow;
uint64_t toack64 = mInitialRwin - mLocalSessionWindow;
uint32_t toack = (toack64 <= 0x7fffffffU) ? toack64 : 0x7fffffffU;
LOG3(("Http2Session::UpdateLocalSessionWindow Ack this=%p acksize=%u\n",
@ -3123,6 +3174,10 @@ Http2Session::OnWriteSegment(char *buf,
return NS_ERROR_FAILURE;
}
if (mDownstreamState == NOT_USING_NETWORK) {
return NS_BASE_STREAM_WOULD_BLOCK;
}
if (mDownstreamState == PROCESSING_DATA_FRAME) {
if (mInputFrameFinal &&
@ -3220,7 +3275,16 @@ Http2Session::SetNeedsCleanup()
void
Http2Session::ConnectPushedStream(Http2Stream *stream)
{
mReadyForRead.Push(stream);
mPushesReadyForRead.Push(stream);
ForceRecv();
}
void
Http2Session::ConnectSlowConsumer(Http2Stream *stream)
{
LOG3(("Http2Session::ConnectSlowConsumer %p 0x%X\n",
this, stream->StreamID()));
mSlowConsumersReadyForRead.Push(stream);
ForceRecv();
}
@ -3476,7 +3540,25 @@ Http2Session::TransactionHasDataToWrite(nsAHttpTransaction *caller)
// that are ready - so we can get into a deadlock waiting for the system IO
// to come back here if we don't force the send loop manually.
ForceSend();
}
void
Http2Session::TransactionHasDataToRecv(nsAHttpTransaction *caller)
{
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
LOG3(("Http2Session::TransactionHasDataToRecv %p trans=%p", this, caller));
// a signal from the http transaction to the connection that it will consume more
Http2Stream *stream = mStreamTransactionHash.Get(caller);
if (!stream || !VerifyStream(stream)) {
LOG3(("Http2Session::TransactionHasDataToRecv %p caller %p not found",
this, caller));
return;
}
LOG3(("Http2Session::TransactionHasDataToRecv %p ID is 0x%X\n",
this, stream->StreamID()));
ConnectSlowConsumer(stream);
}
void

View File

@ -192,8 +192,9 @@ public:
static void LogIO(Http2Session *, Http2Stream *, const char *,
const char *, uint32_t);
// an overload of nsAHttpConnection
// overload of nsAHttpConnection
void TransactionHasDataToWrite(nsAHttpTransaction *) override;
void TransactionHasDataToRecv(nsAHttpTransaction *) override;
// a similar version for Http2Stream
void TransactionHasDataToWrite(Http2Stream *);
@ -208,6 +209,7 @@ public:
bool TryToActivate(Http2Stream *stream);
void ConnectPushedStream(Http2Stream *stream);
void ConnectSlowConsumer(Http2Stream *stream);
nsresult ConfirmTLSProfile();
static bool ALPNCallback(nsISupports *securityInfo);
@ -223,6 +225,7 @@ public:
nsISocketTransport *SocketTransport() { return mSocketTransport; }
int64_t ServerSessionWindow() { return mServerSessionWindow; }
void DecrementServerSessionWindow (uint32_t bytes) { mServerSessionWindow -= bytes; }
uint32_t InitialRwin() { return mInitialRwin; }
void SendPing() override;
bool MaybeReTunnel(nsAHttpTransaction *) override;
@ -240,7 +243,8 @@ private:
DISCARDING_DATA_FRAME_PADDING,
DISCARDING_DATA_FRAME,
PROCESSING_COMPLETE_HEADERS,
PROCESSING_CONTROL_RST_STREAM
PROCESSING_CONTROL_RST_STREAM,
NOT_USING_NETWORK
};
static const uint8_t kMagicHello[24];
@ -267,6 +271,11 @@ private:
void RealignOutputQueue();
void ProcessPending();
nsresult ProcessConnectedPush(Http2Stream *, nsAHttpSegmentWriter *,
uint32_t, uint32_t *);
nsresult ProcessSlowConsumer(Http2Stream *, nsAHttpSegmentWriter *,
uint32_t, uint32_t *);
nsresult SetInputFrameDataStream(uint32_t);
void CreatePriorityNode(uint32_t, uint32_t, uint8_t, const char *);
bool VerifyStream(Http2Stream *, uint32_t);
@ -335,7 +344,8 @@ private:
nsDeque mReadyForWrite;
nsDeque mQueuedStreams;
nsDeque mReadyForRead;
nsDeque mPushesReadyForRead;
nsDeque mSlowConsumersReadyForRead;
nsTArray<Http2PushedStream *> mPushedStreams;
// Compression contexts for header transport.
@ -451,6 +461,9 @@ private:
// signed because asynchronous changes via SETTINGS can drive it negative.
int64_t mServerSessionWindow;
// The initial value of the local stream and session window
uint32_t mInitialRwin;
// This is a output queue of bytes ready to be written to the SSL stream.
// When that streams returns WOULD_BLOCK on direct write the bytes get
// coalesced together here. This results in larger writes to the SSL layer.

View File

@ -27,6 +27,7 @@
#include "nsHttpHandler.h"
#include "nsHttpRequestHead.h"
#include "nsIClassOfService.h"
#include "nsIPipe.h"
#include "nsISocketTransport.h"
#include "nsStandardURL.h"
#include "prnetdb.h"
@ -63,6 +64,7 @@ Http2Stream::Http2Stream(nsAHttpTransaction *httpTransaction,
, mSentFin(0)
, mSentWaitingFor(0)
, mSetTCPSocketBuffer(0)
, mBypassInputBuffer(0)
, mTxInlineFrameSize(Http2Session::kDefaultBufferSize)
, mTxInlineFrameUsed(0)
, mTxStreamFrameSize(0)
@ -236,6 +238,69 @@ Http2Stream::ReadSegments(nsAHttpSegmentReader *reader,
return rv;
}
static bool
IsDataAvailable(nsIInputStream *stream)
{
if (!stream) {
return false;
}
uint64_t avail;
if (NS_FAILED(stream->Available(&avail))) {
return false;
}
return (avail > 0);
}
uint64_t
Http2Stream::LocalUnAcked()
{
// reduce unacked by the amount of undelivered data
// to help assert flow control
uint64_t undelivered = 0;
if (mInputBufferIn) {
mInputBufferIn->Available(&undelivered);
}
if (undelivered > mLocalUnacked) {
return 0;
}
return mLocalUnacked - undelivered;
}
nsresult
Http2Stream::BufferInput(uint32_t count, uint32_t *countWritten)
{
static const uint32_t segmentSize = 32768;
char buf[segmentSize];
count = std::min(segmentSize, count);
if (!mInputBufferOut) {
NS_NewPipe(getter_AddRefs(mInputBufferIn), getter_AddRefs(mInputBufferOut),
segmentSize, UINT32_MAX);
if (!mInputBufferOut) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
mBypassInputBuffer = 1;
nsresult rv = mSegmentWriter->OnWriteSegment(buf, count, countWritten);
mBypassInputBuffer = 0;
if (NS_SUCCEEDED(rv)) {
uint32_t buffered;
rv = mInputBufferOut->Write(buf, *countWritten, &buffered);
if (NS_SUCCEEDED(rv) && (buffered != *countWritten)) {
rv = NS_ERROR_OUT_OF_MEMORY;
}
}
return rv;
}
bool
Http2Stream::DeferCleanup(nsresult status)
{
// do not cleanup a stream that has data buffered for the transaction
return (NS_SUCCEEDED(status) && IsDataAvailable(mInputBufferIn));
}
// WriteSegments() is used to read data off the socket. Generally this is
// just a call through to the associated nsHttpTransaction for this stream
// for the remaining data bytes indicated by the current DATA frame.
@ -253,8 +318,16 @@ Http2Stream::WriteSegments(nsAHttpSegmentWriter *writer,
mSegmentWriter = writer;
nsresult rv = mTransaction->WriteSegments(this, count, countWritten);
mSegmentWriter = nullptr;
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
// consuming transaction won't take data. but we need to read it into a buffer so that it
// won't block other streams. but we should not advance the flow control window
// so that we'll eventually push back on the sender.
// stash this data
rv = BufferInput(count, countWritten);
}
mSegmentWriter = nullptr;
return rv;
}
@ -623,8 +696,21 @@ Http2Stream::AdjustInitialWindow()
return;
}
MOZ_ASSERT(mClientReceiveWindow <= ASpdySession::kInitialRwin);
uint32_t bump = ASpdySession::kInitialRwin - mClientReceiveWindow;
// right now mClientReceiveWindow is the lower push limit
// bump it up to the pull limit set by the channel or session
// don't allow windows less than push
uint32_t bump = 0;
nsHttpTransaction *trans = mTransaction->QueryHttpTransaction();
if (trans && trans->InitialRwin()) {
bump = (trans->InitialRwin() > mClientReceiveWindow) ?
(trans->InitialRwin() - mClientReceiveWindow) : 0;
} else {
MOZ_ASSERT(mSession->InitialRwin() >= mClientReceiveWindow);
bump = mSession->InitialRwin() - mClientReceiveWindow;
}
LOG3(("AdjustInitialwindow increased flow control window %p 0x%X %u\n",
this, stream->mStreamID, bump));
if (!bump) { // nothing to do
return;
}
@ -641,8 +727,6 @@ Http2Stream::AdjustInitialWindow()
mClientReceiveWindow += bump;
bump = PR_htonl(bump);
memcpy(packet + Http2Session::kFrameHeaderBytes, &bump, 4);
LOG3(("AdjustInitialwindow increased flow control window %p 0x%X\n",
this, stream->mStreamID));
}
void
@ -1318,9 +1402,7 @@ Http2Stream::OnWriteSegment(char *buf,
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
MOZ_ASSERT(mSegmentWriter);
if (!mPushSource)
return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
if (mPushSource) {
nsresult rv;
rv = mPushSource->GetBufferedData(buf, count, countWritten);
if (NS_FAILED(rv))
@ -1328,6 +1410,26 @@ Http2Stream::OnWriteSegment(char *buf,
mSession->ConnectPushedStream(this);
return NS_OK;
}
// sometimes we have read data from the network and stored it in a pipe
// so that other streams can proceed when the gecko caller is not processing
// data events fast enough and flow control hasn't caught up yet. This
// gets the stored data out of that pipe
if (!mBypassInputBuffer && IsDataAvailable(mInputBufferIn)) {
nsresult rv = mInputBufferIn->Read(buf, count, countWritten);
LOG3(("Http2Stream::OnWriteSegment read from flow control buffer %p %x %d\n",
this, mStreamID, *countWritten));
if (!IsDataAvailable(mInputBufferIn)) {
// drop the pipe if we don't need it anymore
mInputBufferIn = nullptr;
mInputBufferOut = nullptr;
}
return rv;
}
// read from the network
return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
}
/// connect tunnels

View File

@ -52,7 +52,7 @@ public:
virtual nsresult ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *);
virtual nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *);
virtual bool DeferCleanup(nsresult status) { return false; }
virtual bool DeferCleanup(nsresult status);
// The consumer stream is the synthetic pull stream hooked up to this stream
// http2PushedStream overrides it
@ -124,7 +124,7 @@ public:
mLocalUnacked -= delta;
}
uint64_t LocalUnAcked() { return mLocalUnacked; }
uint64_t LocalUnAcked();
int64_t ClientReceiveWindow() { return mClientReceiveWindow; }
bool BlockedOnRwin() { return mBlockedOnRwin; }
@ -210,6 +210,8 @@ private:
nsresult TransmitFrame(const char *, uint32_t *, bool forceCommitment);
void GenerateDataFrameHeader(uint32_t, bool);
nsresult BufferInput(uint32_t , uint32_t *);
// The underlying HTTP transaction. This pointer is used as the key
// in the Http2Session mStreamTransactionHash so it is important to
// keep a reference to it as long as this stream is a member of that hash.
@ -258,6 +260,10 @@ private:
// Flag is set after TCP send autotuning has been disabled
uint32_t mSetTCPSocketBuffer : 1;
// Flag is set when OnWriteSegment is being called directly from stream instead
// of transaction
uint32_t mBypassInputBuffer : 1;
// The InlineFrame and associated data is used for composing control
// frames and data frame headers.
nsAutoArrayPtr<uint8_t> mTxInlineFrame;
@ -312,6 +318,11 @@ private:
// For Http2Push
Http2PushedStream *mPushSource;
// A pipe used to store stream data when the transaction cannot keep up
// and flow control has not yet kicked in.
nsCOMPtr<nsIInputStream> mInputBufferIn;
nsCOMPtr<nsIOutputStream> mInputBufferOut;
/// connect tunnels
public:
bool IsTunnel() { return mIsTunnel; }

View File

@ -80,6 +80,7 @@ HttpBaseChannel::HttpBaseChannel()
, mForceNoIntercept(false)
, mResponseCouldBeSynthesized(false)
, mSuspendCount(0)
, mInitialRwin(0)
, mProxyResolveFlags(0)
, mProxyURI(nullptr)
, mContentDispositionHint(UINT32_MAX)
@ -2055,6 +2056,24 @@ HttpBaseChannel::SetResponseTimeoutEnabled(bool aEnable)
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::GetInitialRwin(uint32_t *aRwin)
{
if (NS_WARN_IF(!aRwin)) {
return NS_ERROR_NULL_POINTER;
}
*aRwin = mInitialRwin;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetInitialRwin(uint32_t aRwin)
{
ENSURE_CALLED_BEFORE_CONNECT();
mInitialRwin = aRwin;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::ForcePending(bool aForcePending)
{

View File

@ -188,6 +188,8 @@ public:
NS_IMETHOD TakeAllSecurityMessages(nsCOMArray<nsISecurityConsoleMessage> &aMessages) override;
NS_IMETHOD GetResponseTimeoutEnabled(bool *aEnable) override;
NS_IMETHOD SetResponseTimeoutEnabled(bool aEnable) override;
NS_IMETHOD GetInitialRwin(uint32_t* aRwin) override;
NS_IMETHOD SetInitialRwin(uint32_t aRwin) override;
NS_IMETHOD GetNetworkInterfaceId(nsACString& aNetworkInterfaceId) override;
NS_IMETHOD SetNetworkInterfaceId(const nsACString& aNetworkInterfaceId) override;
NS_IMETHOD ForcePending(bool aForcePending) override;
@ -399,6 +401,9 @@ protected:
// Current suspension depth for this channel object
uint32_t mSuspendCount;
// Per channel transport window override (0 means no override)
uint32_t mInitialRwin;
nsCOMPtr<nsIURI> mAPIRedirectToURI;
nsAutoPtr<nsTArray<nsCString> > mRedirectedCachekeys;

View File

@ -1719,6 +1719,7 @@ HttpChannelChild::ContinueAsyncOpen()
openArgs.appCacheClientID() = appCacheClientId;
openArgs.allowSpdy() = mAllowSpdy;
openArgs.allowAltSvc() = mAllowAltSvc;
openArgs.initialRwin() = mInitialRwin;
uint32_t cacheKey = 0;
if (mCacheKey) {

View File

@ -127,7 +127,8 @@ HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
a.appCacheClientID(), a.allowSpdy(), a.allowAltSvc(), a.fds(),
a.loadInfo(), a.synthesizedResponseHead(),
a.synthesizedSecurityInfoSerialization(),
a.cacheKey(), a.schedulingContextID(), a.preflightArgs());
a.cacheKey(), a.schedulingContextID(), a.preflightArgs(),
a.initialRwin());
}
case HttpChannelCreationArgs::THttpChannelConnectArgs:
{
@ -286,7 +287,8 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
const nsCString& aSecurityInfoSerialization,
const uint32_t& aCacheKey,
const nsCString& aSchedulingContextID,
const OptionalCorsPreflightArgs& aCorsPreflightArgs)
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
const uint32_t& aInitialRwin)
{
nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
if (!uri) {
@ -451,6 +453,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
mChannel->SetThirdPartyFlags(thirdPartyFlags);
mChannel->SetAllowSpdy(allowSpdy);
mChannel->SetAllowAltSvc(allowAltSvc);
mChannel->SetInitialRwin(aInitialRwin);
nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
do_QueryObject(mChannel);

View File

@ -121,7 +121,8 @@ protected:
const nsCString& aSecurityInfoSerialization,
const uint32_t& aCacheKey,
const nsCString& aSchedulingContextID,
const OptionalCorsPreflightArgs& aCorsPreflightArgs);
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
const uint32_t& aInitialRwin);
virtual bool RecvSetPriority(const uint16_t& priority) override;
virtual bool RecvSetClassOfService(const uint32_t& cos) override;

View File

@ -70,7 +70,15 @@ public:
// by default do nothing - only multiplexed protocols need to overload
return;
}
//
// This is the companion to *HasDataToWrite() for the case
// when a gecko caller has called ResumeRecv() after being paused
virtual void TransactionHasDataToRecv(nsAHttpTransaction *)
{
// by default do nothing - only multiplexed protocols need to overload
return;
}
// called by the connection manager to close a transaction being processed
// by this connection.
//

View File

@ -197,6 +197,7 @@ nsHttpHandler::nsHttpHandler()
, mSpdySendingChunkSize(ASpdySession::kSendingChunkSize)
, mSpdySendBufferSize(ASpdySession::kTCPSendBufferSize)
, mSpdyPushAllowance(32768)
, mSpdyPullAllowance(ASpdySession::kInitialRwin)
, mDefaultSpdyConcurrent(ASpdySession::kDefaultMaxConcurrent)
, mSpdyPingThreshold(PR_SecondsToInterval(58))
, mSpdyPingTimeout(PR_SecondsToInterval(8))
@ -1315,6 +1316,14 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
}
}
if (PREF_CHANGED(HTTP_PREF("spdy.pull-allowance"))) {
rv = prefs->GetIntPref(HTTP_PREF("spdy.pull-allowance"), &val);
if (NS_SUCCEEDED(rv)) {
mSpdyPullAllowance =
static_cast<uint32_t>(clamped(val, 1024, 0x7fffffff));
}
}
if (PREF_CHANGED(HTTP_PREF("spdy.default-concurrent"))) {
rv = prefs->GetIntPref(HTTP_PREF("spdy.default-concurrent"), &val);
if (NS_SUCCEEDED(rv)) {

View File

@ -111,6 +111,7 @@ public:
uint32_t SpdySendingChunkSize() { return mSpdySendingChunkSize; }
uint32_t SpdySendBufferSize() { return mSpdySendBufferSize; }
uint32_t SpdyPushAllowance() { return mSpdyPushAllowance; }
uint32_t SpdyPullAllowance() { return mSpdyPullAllowance; }
uint32_t DefaultSpdyConcurrent() { return mDefaultSpdyConcurrent; }
PRIntervalTime SpdyPingThreshold() { return mSpdyPingThreshold; }
PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
@ -502,6 +503,7 @@ private:
uint32_t mSpdySendingChunkSize;
uint32_t mSpdySendBufferSize;
uint32_t mSpdyPushAllowance;
uint32_t mSpdyPullAllowance;
uint32_t mDefaultSpdyConcurrent;
PRIntervalTime mSpdyPingThreshold;
PRIntervalTime mSpdyPingTimeout;

View File

@ -98,6 +98,7 @@ nsHttpTransaction::nsHttpTransaction()
, mContentRead(0)
, mInvalidResponseBytesRead(0)
, mPushedStream(nullptr)
, mInitialRwin(0)
, mChunkedDecoder(nullptr)
, mStatus(NS_OK)
, mPriority(0)
@ -269,6 +270,7 @@ nsHttpTransaction::Init(uint32_t caps,
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
httpChannelInternal->GetInitialRwin(&mInitialRwin);
}
// create transport event sink proxy. it coalesces all events if and only
@ -2170,6 +2172,7 @@ NS_IMETHODIMP
nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
{
if (mConnection) {
mConnection->TransactionHasDataToRecv(this);
nsresult rv = mConnection->ResumeRecv();
if (NS_FAILED(rv))
NS_ERROR("ResumeRecv failed");

View File

@ -140,6 +140,7 @@ public:
return r;
}
void SetPushedStream(Http2PushedStream *push) { mPushedStream = push; }
uint32_t InitialRwin() const { return mInitialRwin; };
// Locked methods to get and set timing info
const TimingStruct Timings();
@ -248,6 +249,7 @@ private:
uint32_t mInvalidResponseBytesRead;
Http2PushedStream *mPushedStream;
uint32_t mInitialRwin;
nsHttpChunkedDecoder *mChunkedDecoder;

View File

@ -39,7 +39,7 @@ interface nsIHttpUpgradeListener : nsISupports
* using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned.
*/
[scriptable, uuid(46ef729f-4c9b-4084-b9e2-498992a31aee)]
[scriptable, uuid(5f019a6f-6f6f-4b5c-8994-70cdbc40258e)]
interface nsIHttpChannelInternal : nsISupports
{
@ -202,6 +202,13 @@ interface nsIHttpChannelInternal : nsISupports
*/
attribute boolean responseTimeoutEnabled;
/**
* If the underlying transport supports RWIN manipulation, this is the
* intiial window value for the channel. HTTP/2 implements this.
* 0 means no override from system default. Set before opening channel.
*/
attribute unsigned long initialRwin;
/**
* Get value of the URI passed to nsIHttpChannel.redirectTo() if any.
* May return null when redirectTo() has not been called.