mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 1204614 - use h2 per stream flow control to deal with suspended channels r=hurley
This commit is contained in:
parent
966dd0a6dd
commit
357e0e244c
@ -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
|
||||
|
@ -108,6 +108,7 @@ struct HttpChannelOpenArgs
|
||||
uint32_t cacheKey;
|
||||
nsCString schedulingContextID;
|
||||
OptionalCorsPreflightArgs preflightArgs;
|
||||
uint32_t initialRwin;
|
||||
};
|
||||
|
||||
struct HttpChannelConnectArgs
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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,16 +1402,34 @@ 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))
|
||||
return rv;
|
||||
|
||||
nsresult rv;
|
||||
rv = mPushSource->GetBufferedData(buf, count, countWritten);
|
||||
if (NS_FAILED(rv))
|
||||
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;
|
||||
}
|
||||
|
||||
mSession->ConnectPushedStream(this);
|
||||
return NS_OK;
|
||||
// read from the network
|
||||
return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
|
||||
}
|
||||
|
||||
/// connect tunnels
|
||||
|
@ -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; }
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1719,6 +1719,7 @@ HttpChannelChild::ContinueAsyncOpen()
|
||||
openArgs.appCacheClientID() = appCacheClientId;
|
||||
openArgs.allowSpdy() = mAllowSpdy;
|
||||
openArgs.allowAltSvc() = mAllowAltSvc;
|
||||
openArgs.initialRwin() = mInitialRwin;
|
||||
|
||||
uint32_t cacheKey = 0;
|
||||
if (mCacheKey) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
//
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user