зеркало из https://github.com/mozilla/gecko-dev.git
bug 378637 part 14 - https proxying for spdy31 and http2 r=hurley
--HG-- extra : rebase_source : 03a62d69b08f725c85f092b182a95de9c4eb7288
This commit is contained in:
Родитель
0a3b1e638a
Коммит
d52c2946ac
|
@ -898,7 +898,7 @@ nsresult
|
|||
Http2Compressor::EncodeHeaderBlock(const nsCString &nvInput,
|
||||
const nsACString &method, const nsACString &path,
|
||||
const nsACString &host, const nsACString &scheme,
|
||||
nsACString &output)
|
||||
bool connectForm, nsACString &output)
|
||||
{
|
||||
mAlternateReferenceSet.Clear();
|
||||
mImpliedReferenceSet.Clear();
|
||||
|
@ -917,10 +917,15 @@ Http2Compressor::EncodeHeaderBlock(const nsCString &nvInput,
|
|||
}
|
||||
|
||||
// colon headers first
|
||||
ProcessHeader(nvPair(NS_LITERAL_CSTRING(":method"), method), false);
|
||||
ProcessHeader(nvPair(NS_LITERAL_CSTRING(":path"), path), false);
|
||||
ProcessHeader(nvPair(NS_LITERAL_CSTRING(":authority"), host), false);
|
||||
ProcessHeader(nvPair(NS_LITERAL_CSTRING(":scheme"), scheme), false);
|
||||
if (!connectForm) {
|
||||
ProcessHeader(nvPair(NS_LITERAL_CSTRING(":method"), method), false);
|
||||
ProcessHeader(nvPair(NS_LITERAL_CSTRING(":path"), path), false);
|
||||
ProcessHeader(nvPair(NS_LITERAL_CSTRING(":authority"), host), false);
|
||||
ProcessHeader(nvPair(NS_LITERAL_CSTRING(":scheme"), scheme), false);
|
||||
} else {
|
||||
ProcessHeader(nvPair(NS_LITERAL_CSTRING(":method"), method), false);
|
||||
ProcessHeader(nvPair(NS_LITERAL_CSTRING(":authority"), host), false);
|
||||
}
|
||||
|
||||
// now the non colon headers
|
||||
const char *beginBuffer = nvInput.BeginReading();
|
||||
|
|
|
@ -164,7 +164,7 @@ public:
|
|||
nsresult EncodeHeaderBlock(const nsCString &nvInput,
|
||||
const nsACString &method, const nsACString &path,
|
||||
const nsACString &host, const nsACString &scheme,
|
||||
nsACString &output);
|
||||
bool connectForm, nsACString &output);
|
||||
|
||||
int64_t GetParsedContentLength() { return mParsedContentLength; } // -1 on not found
|
||||
|
||||
|
|
|
@ -386,23 +386,23 @@ Http2Session::AddStream(nsAHttpTransaction *aHttpTransaction,
|
|||
return false;
|
||||
}
|
||||
|
||||
// assert that
|
||||
// a] in the case we have a connection, that the new transaction connection
|
||||
// is either undefined or on the same connection
|
||||
// b] in the case we don't have a connection, that the new transaction
|
||||
// connection is defined so we can adopt it
|
||||
MOZ_ASSERT((mConnection && (!aHttpTransaction->Connection() ||
|
||||
mConnection == aHttpTransaction->Connection())) ||
|
||||
(!mConnection && aHttpTransaction->Connection()));
|
||||
|
||||
if (!mConnection) {
|
||||
mConnection = aHttpTransaction->Connection();
|
||||
}
|
||||
|
||||
aHttpTransaction->SetConnection(this);
|
||||
|
||||
if (aUseTunnel) {
|
||||
LOG3(("Http2Session::AddStream session=%p trans=%p OnTunnel",
|
||||
this, aHttpTransaction));
|
||||
DispatchOnTunnel(aHttpTransaction, aCallbacks);
|
||||
return true;
|
||||
}
|
||||
|
||||
Http2Stream *stream = new Http2Stream(aHttpTransaction, this, aPriority);
|
||||
|
||||
LOG3(("Http2Session::AddStream session=%p stream=%p NextID=0x%X (tentative)",
|
||||
this, stream, mNextStreamID));
|
||||
LOG3(("Http2Session::AddStream session=%p stream=%p serial=%u "
|
||||
"NextID=0x%X (tentative)", this, stream, mSerial, mNextStreamID));
|
||||
|
||||
mStreamTransactionHash.Put(aHttpTransaction, stream);
|
||||
|
||||
|
@ -991,6 +991,10 @@ Http2Session::CloseStream(Http2Stream *aStream, nsresult aResult)
|
|||
|
||||
RemoveStreamFromQueues(aStream);
|
||||
|
||||
if (aStream->IsTunnel()) {
|
||||
UnRegisterTunnel(aStream);
|
||||
}
|
||||
|
||||
// Send the stream the close() indication
|
||||
aStream->Close(aResult);
|
||||
}
|
||||
|
@ -1138,7 +1142,11 @@ Http2Session::ResponseHeadersComplete()
|
|||
// only do this once, afterwards ignore trailers
|
||||
if (mInputFrameDataStream->AllHeadersReceived())
|
||||
return NS_OK;
|
||||
mInputFrameDataStream->SetAllHeadersReceived(true);
|
||||
nsresult rv = mInputFrameDataStream->SetAllHeadersReceived(true);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// The stream needs to see flattened http headers
|
||||
// Uncompressed http/2 format headers currently live in
|
||||
|
@ -1146,11 +1154,23 @@ Http2Session::ResponseHeadersComplete()
|
|||
// mFlatHTTPResponseHeaders via ConvertHeaders()
|
||||
|
||||
mFlatHTTPResponseHeadersOut = 0;
|
||||
nsresult rv = mInputFrameDataStream->ConvertResponseHeaders(&mDecompressor,
|
||||
mDecompressBuffer,
|
||||
mFlatHTTPResponseHeaders);
|
||||
if (NS_FAILED(rv))
|
||||
rv = mInputFrameDataStream->ConvertResponseHeaders(&mDecompressor,
|
||||
mDecompressBuffer,
|
||||
mFlatHTTPResponseHeaders);
|
||||
if (rv == NS_ERROR_ABORT) {
|
||||
LOG(("Http2Session::ResponseHeadersComplete ConvertResponseHeaders aborted\n"));
|
||||
if (mInputFrameDataStream->IsTunnel()) {
|
||||
gHttpHandler->ConnMgr()->CancelTransactions(
|
||||
mInputFrameDataStream->Transaction()->ConnectionInfo(),
|
||||
NS_ERROR_CONNECTION_REFUSED);
|
||||
}
|
||||
CleanupStream(mInputFrameDataStream, rv, CANCEL_ERROR);
|
||||
ResetDownstreamState();
|
||||
return NS_OK;
|
||||
}
|
||||
else if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
ChangeDownstreamState(PROCESSING_COMPLETE_HEADERS);
|
||||
return NS_OK;
|
||||
|
@ -2278,6 +2298,7 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||
MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly");
|
||||
mNeedsCleanup = nullptr; /* just in case */
|
||||
|
||||
Http2Stream *stream = mInputFrameDataStream;
|
||||
mSegmentWriter = writer;
|
||||
rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
|
||||
mSegmentWriter = nullptr;
|
||||
|
@ -2288,7 +2309,6 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||
// This will happen when the transaction figures out it is EOF, generally
|
||||
// due to a content-length match being made. Return OK from this function
|
||||
// otherwise the whole session would be torn down.
|
||||
Http2Stream *stream = mInputFrameDataStream;
|
||||
|
||||
// if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state
|
||||
// back to PROCESSING_DATA_FRAME where we came from
|
||||
|
@ -2302,8 +2322,8 @@ Http2Session::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||
this, stream, stream ? stream->StreamID() : 0,
|
||||
mNeedsCleanup, rv));
|
||||
CleanupStream(stream, NS_OK, CANCEL_ERROR);
|
||||
MOZ_ASSERT(!mNeedsCleanup, "double cleanup out of data frame");
|
||||
mNeedsCleanup = nullptr; /* just in case */
|
||||
MOZ_ASSERT(!mNeedsCleanup || mNeedsCleanup == stream);
|
||||
mNeedsCleanup = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2794,6 +2814,80 @@ Http2Session::ConnectPushedStream(Http2Stream *stream)
|
|||
ForceRecv();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Http2Session::FindTunnelCount(nsHttpConnectionInfo *aConnInfo)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
uint32_t rv = 0;
|
||||
mTunnelHash.Get(aConnInfo->HashKey(), &rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
Http2Session::RegisterTunnel(Http2Stream *aTunnel)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
nsHttpConnectionInfo *ci = aTunnel->Transaction()->ConnectionInfo();
|
||||
uint32_t newcount = FindTunnelCount(ci) + 1;
|
||||
mTunnelHash.Remove(ci->HashKey());
|
||||
mTunnelHash.Put(ci->HashKey(), newcount);
|
||||
LOG3(("Http2Stream::RegisterTunnel %p stream=%p tunnels=%d [%s]",
|
||||
this, aTunnel, newcount, ci->HashKey().get()));
|
||||
}
|
||||
|
||||
void
|
||||
Http2Session::UnRegisterTunnel(Http2Stream *aTunnel)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
nsHttpConnectionInfo *ci = aTunnel->Transaction()->ConnectionInfo();
|
||||
MOZ_ASSERT(FindTunnelCount(ci));
|
||||
uint32_t newcount = FindTunnelCount(ci) - 1;
|
||||
mTunnelHash.Remove(ci->HashKey());
|
||||
if (newcount) {
|
||||
mTunnelHash.Put(ci->HashKey(), newcount);
|
||||
}
|
||||
LOG3(("Http2Session::UnRegisterTunnel %p stream=%p tunnels=%d [%s]",
|
||||
this, aTunnel, newcount, ci->HashKey().get()));
|
||||
}
|
||||
|
||||
void
|
||||
Http2Session::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
|
||||
nsIInterfaceRequestor *aCallbacks)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
|
||||
nsHttpConnectionInfo *ci = aHttpTransaction->ConnectionInfo();
|
||||
MOZ_ASSERT(trans);
|
||||
|
||||
LOG3(("Http2Session::DispatchOnTunnel %p trans=%p", this, trans));
|
||||
|
||||
aHttpTransaction->SetConnection(nullptr);
|
||||
|
||||
// this transaction has done its work of setting up a tunnel, let
|
||||
// the connection manager queue it if necessary
|
||||
trans->SetDontRouteViaWildCard(true);
|
||||
|
||||
if (FindTunnelCount(ci) < gHttpHandler->MaxConnectionsPerOrigin()) {
|
||||
LOG3(("Http2Session::DispatchOnTunnel %p create on new tunnel %s",
|
||||
this, ci->HashKey().get()));
|
||||
nsRefPtr<SpdyConnectTransaction> connectTrans =
|
||||
new SpdyConnectTransaction(ci, aCallbacks,
|
||||
trans->Caps(), trans, this);
|
||||
AddStream(connectTrans, trans->Priority(),
|
||||
false, nullptr);
|
||||
Http2Stream *tunnel = mStreamTransactionHash.Get(connectTrans);
|
||||
MOZ_ASSERT(tunnel);
|
||||
RegisterTunnel(tunnel);
|
||||
}
|
||||
|
||||
// requeue it. The connection manager is responsible for actually putting
|
||||
// this on the tunnel connection with the specific ci now that it
|
||||
// has DontRouteViaWildCard set.
|
||||
trans->EnableKeepAlive();
|
||||
gHttpHandler->InitiateTransaction(trans, trans->Priority());
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
Http2Session::BufferOutput(const char *buf,
|
||||
uint32_t count,
|
||||
|
@ -2894,6 +2988,12 @@ Http2Session::TransactionHasDataToWrite(nsAHttpTransaction *caller)
|
|||
|
||||
mReadyForWrite.Push(stream);
|
||||
SetWriteCallbacks();
|
||||
|
||||
// NSPR poll will not poll the network if there are non system PR_FileDesc's
|
||||
// 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
|
||||
|
@ -2905,6 +3005,7 @@ Http2Session::TransactionHasDataToWrite(Http2Stream *stream)
|
|||
|
||||
mReadyForWrite.Push(stream);
|
||||
SetWriteCallbacks();
|
||||
ForceSend();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace net {
|
|||
|
||||
class Http2PushedStream;
|
||||
class Http2Stream;
|
||||
class nsHttpTransaction;
|
||||
|
||||
class Http2Session MOZ_FINAL : public ASpdySession
|
||||
, public nsAHttpConnection
|
||||
|
@ -445,6 +446,15 @@ private:
|
|||
// by the load group and the serial number can be used as part of the cache key
|
||||
// to make sure streams aren't shared across sessions.
|
||||
uint64_t mSerial;
|
||||
|
||||
private:
|
||||
/// connect tunnels
|
||||
void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);
|
||||
void RegisterTunnel(Http2Stream *);
|
||||
void UnRegisterTunnel(Http2Stream *);
|
||||
uint32_t FindTunnelCount(nsHttpConnectionInfo *);
|
||||
|
||||
nsDataHashtable<nsCStringHashKey, uint32_t> mTunnelHash;
|
||||
};
|
||||
|
||||
} // namespace mozilla::net
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "Http2Session.h"
|
||||
#include "Http2Stream.h"
|
||||
#include "Http2Push.h"
|
||||
#include "TunnelUtils.h"
|
||||
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsAlgorithm.h"
|
||||
|
@ -67,6 +68,7 @@ Http2Stream::Http2Stream(nsAHttpTransaction *httpTransaction,
|
|||
, mTotalSent(0)
|
||||
, mTotalRead(0)
|
||||
, mPushSource(nullptr)
|
||||
, mIsTunnel(false)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
|
||||
|
@ -96,6 +98,7 @@ Http2Stream::Http2Stream(nsAHttpTransaction *httpTransaction,
|
|||
|
||||
Http2Stream::~Http2Stream()
|
||||
{
|
||||
ClearTransactionsBlockedOnTunnel();
|
||||
mStreamID = Http2Session::kDeadStreamID;
|
||||
}
|
||||
|
||||
|
@ -141,6 +144,9 @@ Http2Stream::ReadSegments(nsAHttpSegmentReader *reader,
|
|||
rv = mTransaction->ReadSegments(this, count, countRead);
|
||||
mSegmentReader = nullptr;
|
||||
|
||||
LOG3(("Http2Stream::ReadSegments %p trans readsegments rv %x read=%d\n",
|
||||
this, rv, *countRead));
|
||||
|
||||
// Check to see if the transaction's request could be written out now.
|
||||
// If not, mark the stream for callback when writing can proceed.
|
||||
if (NS_SUCCEEDED(rv) &&
|
||||
|
@ -165,7 +171,7 @@ Http2Stream::ReadSegments(nsAHttpSegmentReader *reader,
|
|||
if (!mBlockedOnRwin &&
|
||||
!mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
|
||||
LOG3(("Http2Stream::ReadSegments %p 0x%X: Sending request data complete, "
|
||||
"mUpstreamState=%x",this, mStreamID, mUpstreamState));
|
||||
"mUpstreamState=%x\n",this, mStreamID, mUpstreamState));
|
||||
if (mSentFin) {
|
||||
ChangeState(UPSTREAM_COMPLETE);
|
||||
} else {
|
||||
|
@ -268,6 +274,7 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
|
|||
this, avail, mUpstreamState));
|
||||
|
||||
mFlatHttpRequestHeaders.Append(buf, avail);
|
||||
nsHttpRequestHead *head = mTransaction->RequestHead();
|
||||
|
||||
// We can use the simple double crlf because firefox is the
|
||||
// only client we are parsing
|
||||
|
@ -290,17 +297,17 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
|
|||
*countUsed = avail - (oldLen - endHeader) + 4;
|
||||
mAllHeadersSent = 1;
|
||||
|
||||
nsAutoCString hostHeader;
|
||||
nsAutoCString authorityHeader;
|
||||
nsAutoCString hashkey;
|
||||
mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
|
||||
head->GetHeader(nsHttp::Host, authorityHeader);
|
||||
|
||||
CreatePushHashKey(NS_LITERAL_CSTRING("https"),
|
||||
hostHeader, mSession->Serial(),
|
||||
mTransaction->RequestHead()->RequestURI(),
|
||||
CreatePushHashKey(nsDependentCString(head->IsHTTPS() ? "https" : "http"),
|
||||
authorityHeader, mSession->Serial(),
|
||||
head->RequestURI(),
|
||||
mOrigin, hashkey);
|
||||
|
||||
// check the push cache for GET
|
||||
if (mTransaction->RequestHead()->IsGet()) {
|
||||
if (head->IsGet()) {
|
||||
// from :scheme, :authority, :path
|
||||
nsILoadGroupConnectionInfo *loadGroupCI = mTransaction->LoadGroupConnectionInfo();
|
||||
SpdyPushCache *cache = nullptr;
|
||||
|
@ -347,7 +354,7 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
|
|||
MOZ_ASSERT(mStreamID & 1, "Http2 Stream Channel ID must be odd");
|
||||
LOG3(("Stream ID 0x%X [session=%p] for URI %s\n",
|
||||
mStreamID, mSession,
|
||||
nsCString(mTransaction->RequestHead()->RequestURI()).get()));
|
||||
nsCString(head->RequestURI()).get()));
|
||||
|
||||
if (mStreamID >= 0x80000000) {
|
||||
// streamID must fit in 31 bits. Evading This is theoretically possible
|
||||
|
@ -366,28 +373,46 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
|
|||
// of HTTP/2 headers by writing to mTxInlineFrame{sz}
|
||||
|
||||
nsCString compressedData;
|
||||
nsDependentCString scheme(head->IsHTTPS() ? "https" : "http");
|
||||
if (head->IsConnect()) {
|
||||
MOZ_ASSERT(mTransaction->QuerySpdyConnectTransaction());
|
||||
mIsTunnel = true;
|
||||
mRequestBodyLenRemaining = 0x0fffffffffffffffULL;
|
||||
|
||||
// Our normal authority has an implicit port, best to use an
|
||||
// explicit one with a tunnel
|
||||
nsHttpConnectionInfo *ci = mTransaction->ConnectionInfo();
|
||||
if (!ci) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
authorityHeader = ci->GetHost();
|
||||
authorityHeader.AppendLiteral(":");
|
||||
authorityHeader.AppendInt(ci->Port());
|
||||
}
|
||||
|
||||
mSession->Compressor()->EncodeHeaderBlock(mFlatHttpRequestHeaders,
|
||||
mTransaction->RequestHead()->Method(),
|
||||
mTransaction->RequestHead()->RequestURI(),
|
||||
hostHeader,
|
||||
NS_LITERAL_CSTRING("https"),
|
||||
head->Method(),
|
||||
head->RequestURI(),
|
||||
authorityHeader,
|
||||
scheme,
|
||||
head->IsConnect(),
|
||||
compressedData);
|
||||
|
||||
// Determine whether to put the fin bit on the header frame or whether
|
||||
// to wait for a data packet to put it on.
|
||||
uint8_t firstFrameFlags = Http2Session::kFlag_PRIORITY;
|
||||
|
||||
if (mTransaction->RequestHead()->IsGet() ||
|
||||
mTransaction->RequestHead()->IsConnect() ||
|
||||
mTransaction->RequestHead()->IsHead()) {
|
||||
// for GET, CONNECT, and HEAD place the fin bit right on the
|
||||
if (head->IsGet() ||
|
||||
head->IsHead()) {
|
||||
// for GET and HEAD place the fin bit right on the
|
||||
// header packet
|
||||
|
||||
SetSentFin(true);
|
||||
firstFrameFlags |= Http2Session::kFlag_END_STREAM;
|
||||
} else if (mTransaction->RequestHead()->IsPost() ||
|
||||
mTransaction->RequestHead()->IsPut() ||
|
||||
mTransaction->RequestHead()->IsOptions()) {
|
||||
} else if (head->IsPost() ||
|
||||
head->IsPut() ||
|
||||
head->IsConnect() ||
|
||||
head->IsOptions()) {
|
||||
// place fin in a data frame even for 0 length messages for iterop
|
||||
} else if (!mRequestBodyLenRemaining) {
|
||||
// for other HTTP extension methods, rely on the content-length
|
||||
|
@ -473,7 +498,7 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
|
|||
// The size of the input headers is approximate
|
||||
uint32_t ratio =
|
||||
compressedData.Length() * 100 /
|
||||
(11 + mTransaction->RequestHead()->RequestURI().Length() +
|
||||
(11 + head->RequestURI().Length() +
|
||||
mFlatHttpRequestHeaders.Length());
|
||||
|
||||
const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading();
|
||||
|
@ -599,6 +624,10 @@ void
|
|||
Http2Stream::UpdateTransportReadEvents(uint32_t count)
|
||||
{
|
||||
mTotalRead += count;
|
||||
if (!mSocketTransport) {
|
||||
return;
|
||||
}
|
||||
|
||||
mTransaction->OnTransportStatus(mSocketTransport,
|
||||
NS_NET_STATUS_RECEIVING_FROM,
|
||||
mTotalRead);
|
||||
|
@ -831,6 +860,14 @@ Http2Stream::ConvertResponseHeaders(Http2Decompressor *decompressor,
|
|||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
if (mIsTunnel) {
|
||||
nsresult errcode;
|
||||
if (status.ToInteger(&errcode) != 200) {
|
||||
LOG3(("Http2Stream %p Tunnel not 200", this));
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
}
|
||||
|
||||
if (aHeadersIn.Length() && aHeadersOut.Length()) {
|
||||
Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, aHeadersIn.Length());
|
||||
uint32_t ratio =
|
||||
|
@ -841,6 +878,11 @@ Http2Stream::ConvertResponseHeaders(Http2Decompressor *decompressor,
|
|||
aHeadersIn.Truncate();
|
||||
aHeadersOut.Append(NS_LITERAL_CSTRING("X-Firefox-Spdy: " NS_HTTP2_DRAFT_TOKEN "\r\n\r\n"));
|
||||
LOG (("decoded response headers are:\n%s", aHeadersOut.BeginReading()));
|
||||
if (mIsTunnel) {
|
||||
aHeadersOut.Truncate();
|
||||
LOG(("SpdyStream3::ConvertHeaders %p 0x%X headers removed for tunnel\n",
|
||||
this, mStreamID));
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -891,6 +933,13 @@ Http2Stream::Close(nsresult reason)
|
|||
mTransaction->Close(reason);
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2Stream::SetAllHeadersReceived(bool aStatus)
|
||||
{
|
||||
mAllHeadersReceived = aStatus ? 1 : 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
Http2Stream::AllowFlowControlledWrite()
|
||||
{
|
||||
|
@ -1057,11 +1106,15 @@ Http2Stream::OnReadSegment(const char *buf,
|
|||
mSession->DecrementServerSessionWindow(dataLength);
|
||||
mServerReceiveWindow -= dataLength;
|
||||
|
||||
LOG3(("Http2Stream %p id %x request len remaining %d, "
|
||||
"count avail %d, chunk used %d",
|
||||
LOG3(("Http2Stream %p id %x request len remaining %u, "
|
||||
"count avail %u, chunk used %u",
|
||||
this, mStreamID, mRequestBodyLenRemaining, count, dataLength));
|
||||
if (dataLength > mRequestBodyLenRemaining)
|
||||
if (!dataLength && mRequestBodyLenRemaining) {
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
if (dataLength > mRequestBodyLenRemaining) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
mRequestBodyLenRemaining -= dataLength;
|
||||
GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining);
|
||||
ChangeState(SENDING_BODY);
|
||||
|
@ -1127,6 +1180,28 @@ Http2Stream::OnWriteSegment(char *buf,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/// connect tunnels
|
||||
|
||||
void
|
||||
Http2Stream::ClearTransactionsBlockedOnTunnel()
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
|
||||
if (!mIsTunnel) {
|
||||
return;
|
||||
}
|
||||
gHttpHandler->ConnMgr()->ProcessPendingQ(mTransaction->ConnectionInfo());
|
||||
}
|
||||
|
||||
void
|
||||
Http2Stream::MapStreamToHttpConnection()
|
||||
{
|
||||
nsRefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
|
||||
MOZ_ASSERT(qiTrans);
|
||||
qiTrans->MapStreamToHttpConnection(mSocketTransport,
|
||||
mTransaction->ConnectionInfo());
|
||||
}
|
||||
|
||||
} // namespace mozilla::net
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -81,12 +81,15 @@ public:
|
|||
void SetCountAsActive(bool aStatus) { mCountAsActive = aStatus ? 1 : 0; }
|
||||
bool CountAsActive() { return mCountAsActive; }
|
||||
|
||||
void SetAllHeadersReceived(bool aStatus) { mAllHeadersReceived = aStatus ? 1 : 0; }
|
||||
// returns failure if stream cannot be made ready and stream
|
||||
// should be canceled
|
||||
nsresult SetAllHeadersReceived(bool aStatus);
|
||||
bool AllHeadersReceived() { return mAllHeadersReceived; }
|
||||
|
||||
void UpdateTransportSendEvents(uint32_t count);
|
||||
void UpdateTransportReadEvents(uint32_t count);
|
||||
|
||||
// NS_ERROR_ABORT terminates stream, other failure terminates session
|
||||
nsresult ConvertResponseHeaders(Http2Decompressor *, nsACString &, nsACString &);
|
||||
nsresult ConvertPushHeaders(Http2Decompressor *, nsACString &, nsACString &);
|
||||
|
||||
|
@ -234,7 +237,8 @@ private:
|
|||
// place the fin flag on the last data packet instead of waiting
|
||||
// for a stream closed indication. Relying on stream close results
|
||||
// in an extra 0-length runt packet and seems to have some interop
|
||||
// problems with the google servers.
|
||||
// problems with the google servers. Connect does rely on stream
|
||||
// close by setting this to the max value.
|
||||
int64_t mRequestBodyLenRemaining;
|
||||
|
||||
uint32_t mPriority;
|
||||
|
@ -267,6 +271,15 @@ private:
|
|||
|
||||
// For Http2Push
|
||||
Http2PushedStream *mPushSource;
|
||||
|
||||
/// connect tunnels
|
||||
public:
|
||||
bool IsTunnel() { return mIsTunnel; }
|
||||
private:
|
||||
void ClearTransactionsBlockedOnTunnel();
|
||||
void MapStreamToHttpConnection();
|
||||
|
||||
bool mIsTunnel;
|
||||
};
|
||||
|
||||
} // namespace mozilla::net
|
||||
|
|
|
@ -351,23 +351,23 @@ SpdySession31::AddStream(nsAHttpTransaction *aHttpTransaction,
|
|||
return false;
|
||||
}
|
||||
|
||||
// assert that
|
||||
// a] in the case we have a connection, that the new transaction connection
|
||||
// is either undefined or on the same connection
|
||||
// b] in the case we don't have a connection, that the new transaction
|
||||
// connection is defined so we can adopt it
|
||||
MOZ_ASSERT((mConnection && (!aHttpTransaction->Connection() ||
|
||||
mConnection == aHttpTransaction->Connection())) ||
|
||||
(!mConnection && aHttpTransaction->Connection()));
|
||||
|
||||
if (!mConnection) {
|
||||
mConnection = aHttpTransaction->Connection();
|
||||
}
|
||||
|
||||
aHttpTransaction->SetConnection(this);
|
||||
|
||||
if (aUseTunnel) {
|
||||
LOG3(("SpdySession31::AddStream session=%p trans=%p OnTunnel",
|
||||
this, aHttpTransaction));
|
||||
DispatchOnTunnel(aHttpTransaction, aCallbacks);
|
||||
return true;
|
||||
}
|
||||
|
||||
SpdyStream31 *stream = new SpdyStream31(aHttpTransaction, this, aPriority);
|
||||
|
||||
LOG3(("SpdySession31::AddStream session=%p stream=%p NextID=0x%X (tentative)",
|
||||
this, stream, mNextStreamID));
|
||||
LOG3(("SpdySession31::AddStream session=%p stream=%p serial=%u "
|
||||
"NextID=0x%X (tentative)", this, stream, mSerial, mNextStreamID));
|
||||
|
||||
mStreamTransactionHash.Put(aHttpTransaction, stream);
|
||||
|
||||
|
@ -946,6 +946,10 @@ SpdySession31::CloseStream(SpdyStream31 *aStream, nsresult aResult)
|
|||
|
||||
RemoveStreamFromQueues(aStream);
|
||||
|
||||
if (aStream->IsTunnel()) {
|
||||
UnRegisterTunnel(aStream);
|
||||
}
|
||||
|
||||
// Send the stream the close() indication
|
||||
aStream->Close(aResult);
|
||||
}
|
||||
|
@ -1067,7 +1071,13 @@ SpdySession31::HandleSynStream(SpdySession31 *self)
|
|||
self->mPushedStreams.AppendElement(pushedStream);
|
||||
|
||||
// The pushed stream is unidirectional so it is fully open immediately
|
||||
pushedStream->SetFullyOpen();
|
||||
rv = pushedStream->SetFullyOpen();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("SpdySession31::HandleSynStream pushedstream fully open failed\n"));
|
||||
self->CleanupStream(pushedStream, rv, RST_CANCEL);
|
||||
self->ResetDownstreamState();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Uncompress the response headers into a stream specific buffer, leaving them
|
||||
// in spdy format for the time being.
|
||||
|
@ -1140,10 +1150,10 @@ SpdySession31::HandleSynReply(SpdySession31 *self)
|
|||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
LOG3(("SpdySession31::HandleSynReply %p lookup via streamID in syn_reply.\n",
|
||||
self));
|
||||
uint32_t streamID =
|
||||
PR_ntohl(reinterpret_cast<uint32_t *>(self->mInputFrameBuffer.get())[2]);
|
||||
LOG3(("SpdySession31::HandleSynReply %p lookup via streamID 0x%X in syn_reply.\n",
|
||||
self, streamID));
|
||||
nsresult rv = self->SetInputFrameDataStream(streamID);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
@ -1206,7 +1216,19 @@ SpdySession31::HandleSynReply(SpdySession31 *self)
|
|||
self->ResetDownstreamState();
|
||||
return NS_OK;
|
||||
}
|
||||
self->mInputFrameDataStream->SetFullyOpen();
|
||||
|
||||
rv = self->mInputFrameDataStream->SetFullyOpen();
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("SpdySession31::HandleSynReply SetFullyOpen failed\n"));
|
||||
if (self->mInputFrameDataStream->IsTunnel()) {
|
||||
gHttpHandler->ConnMgr()->CancelTransactions(
|
||||
self->mInputFrameDataStream->Transaction()->ConnectionInfo(),
|
||||
NS_ERROR_CONNECTION_REFUSED);
|
||||
}
|
||||
self->CleanupStream(self->mInputFrameDataStream, rv, RST_CANCEL);
|
||||
self->ResetDownstreamState();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
self->mInputFrameDataLast = self->mInputFrameBuffer[4] & kFlag_Data_FIN;
|
||||
self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize);
|
||||
|
@ -2067,6 +2089,7 @@ SpdySession31::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||
MOZ_ASSERT(!mNeedsCleanup, "cleanup stream set unexpectedly");
|
||||
mNeedsCleanup = nullptr; /* just in case */
|
||||
|
||||
SpdyStream31 *stream = mInputFrameDataStream;
|
||||
mSegmentWriter = writer;
|
||||
rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
|
||||
mSegmentWriter = nullptr;
|
||||
|
@ -2077,7 +2100,6 @@ SpdySession31::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||
// This will happen when the transaction figures out it is EOF, generally
|
||||
// due to a content-length match being made. Return OK from this function
|
||||
// otherwise the whole session would be torn down.
|
||||
SpdyStream31 *stream = mInputFrameDataStream;
|
||||
|
||||
// if we were doing PROCESSING_COMPLETE_HEADERS need to pop the state
|
||||
// back to PROCESSING_DATA_FRAME where we came from
|
||||
|
@ -2091,8 +2113,8 @@ SpdySession31::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||
this, stream, stream ? stream->StreamID() : 0,
|
||||
mNeedsCleanup, rv));
|
||||
CleanupStream(stream, NS_OK, RST_CANCEL);
|
||||
MOZ_ASSERT(!mNeedsCleanup, "double cleanup out of data frame");
|
||||
mNeedsCleanup = nullptr; /* just in case */
|
||||
MOZ_ASSERT(!mNeedsCleanup || mNeedsCleanup == stream);
|
||||
mNeedsCleanup = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2579,6 +2601,79 @@ SpdySession31::ConnectPushedStream(SpdyStream31 *stream)
|
|||
ForceRecv();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
SpdySession31::FindTunnelCount(nsHttpConnectionInfo *aConnInfo)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
uint32_t rv = 0;
|
||||
mTunnelHash.Get(aConnInfo->HashKey(), &rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
SpdySession31::RegisterTunnel(SpdyStream31 *aTunnel)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
nsHttpConnectionInfo *ci = aTunnel->Transaction()->ConnectionInfo();
|
||||
uint32_t newcount = FindTunnelCount(ci) + 1;
|
||||
mTunnelHash.Remove(ci->HashKey());
|
||||
mTunnelHash.Put(ci->HashKey(), newcount);
|
||||
LOG3(("SpdySession31::RegisterTunnel %p stream=%p tunnels=%d [%s]",
|
||||
this, aTunnel, newcount, ci->HashKey().get()));
|
||||
}
|
||||
|
||||
void
|
||||
SpdySession31::UnRegisterTunnel(SpdyStream31 *aTunnel)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
nsHttpConnectionInfo *ci = aTunnel->Transaction()->ConnectionInfo();
|
||||
MOZ_ASSERT(FindTunnelCount(ci));
|
||||
uint32_t newcount = FindTunnelCount(ci) - 1;
|
||||
mTunnelHash.Remove(ci->HashKey());
|
||||
if (newcount) {
|
||||
mTunnelHash.Put(ci->HashKey(), newcount);
|
||||
}
|
||||
LOG3(("SpdySession31::UnRegisterTunnel %p stream=%p tunnels=%d [%s]",
|
||||
this, aTunnel, newcount, ci->HashKey().get()));
|
||||
}
|
||||
|
||||
void
|
||||
SpdySession31::DispatchOnTunnel(nsAHttpTransaction *aHttpTransaction,
|
||||
nsIInterfaceRequestor *aCallbacks)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
nsHttpTransaction *trans = aHttpTransaction->QueryHttpTransaction();
|
||||
nsHttpConnectionInfo *ci = aHttpTransaction->ConnectionInfo();
|
||||
MOZ_ASSERT(trans);
|
||||
|
||||
LOG3(("SpdySession31::DispatchOnTunnel %p trans=%p", this, trans));
|
||||
|
||||
aHttpTransaction->SetConnection(nullptr);
|
||||
|
||||
// this transaction has done its work of setting up a tunnel, let
|
||||
// the connection manager queue it if necessary
|
||||
trans->SetDontRouteViaWildCard(true);
|
||||
|
||||
if (FindTunnelCount(ci) < gHttpHandler->MaxConnectionsPerOrigin()) {
|
||||
LOG3(("SpdySession31::DispatchOnTunnel %p create on new tunnel %s",
|
||||
this, ci->HashKey().get()));
|
||||
nsRefPtr<SpdyConnectTransaction> connectTrans =
|
||||
new SpdyConnectTransaction(ci, aCallbacks,
|
||||
trans->Caps(), trans, this);
|
||||
AddStream(connectTrans, trans->Priority(),
|
||||
false, nullptr);
|
||||
SpdyStream31 *tunnel = mStreamTransactionHash.Get(connectTrans);
|
||||
MOZ_ASSERT(tunnel);
|
||||
RegisterTunnel(tunnel);
|
||||
}
|
||||
|
||||
// requeue it. The connection manager is responsible for actually putting
|
||||
// this on the tunnel connection with the specific ci now that it
|
||||
// has DontRouteViaWildCard set.
|
||||
trans->EnableKeepAlive();
|
||||
gHttpHandler->InitiateTransaction(trans, trans->Priority());
|
||||
}
|
||||
|
||||
nsresult
|
||||
SpdySession31::BufferOutput(const char *buf,
|
||||
uint32_t count,
|
||||
|
@ -2616,6 +2711,11 @@ SpdySession31::TransactionHasDataToWrite(nsAHttpTransaction *caller)
|
|||
|
||||
mReadyForWrite.Push(stream);
|
||||
SetWriteCallbacks();
|
||||
|
||||
// NSPR poll will not poll the network if there are non system PR_FileDesc's
|
||||
// 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
|
||||
|
@ -2627,6 +2727,7 @@ SpdySession31::TransactionHasDataToWrite(SpdyStream31 *stream)
|
|||
|
||||
mReadyForWrite.Push(stream);
|
||||
SetWriteCallbacks();
|
||||
ForceSend();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace mozilla { namespace net {
|
|||
|
||||
class SpdyPushedStream31;
|
||||
class SpdyStream31;
|
||||
class nsHttpTransaction;
|
||||
|
||||
class SpdySession31 MOZ_FINAL : public ASpdySession
|
||||
, public nsAHttpConnection
|
||||
|
@ -402,6 +403,15 @@ private:
|
|||
// by the load group and the serial number can be used as part of the cache key
|
||||
// to make sure streams aren't shared across sessions.
|
||||
uint64_t mSerial;
|
||||
|
||||
private:
|
||||
/// connect tunnels
|
||||
void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);
|
||||
void RegisterTunnel(SpdyStream31 *);
|
||||
void UnRegisterTunnel(SpdyStream31 *);
|
||||
uint32_t FindTunnelCount(nsHttpConnectionInfo *);
|
||||
|
||||
nsDataHashtable<nsCStringHashKey, uint32_t> mTunnelHash;
|
||||
};
|
||||
|
||||
}} // namespace mozilla::net
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "SpdyPush31.h"
|
||||
#include "SpdySession31.h"
|
||||
#include "SpdyStream31.h"
|
||||
#include "TunnelUtils.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -68,6 +69,7 @@ SpdyStream31::SpdyStream31(nsAHttpTransaction *httpTransaction,
|
|||
, mTotalSent(0)
|
||||
, mTotalRead(0)
|
||||
, mPushSource(nullptr)
|
||||
, mIsTunnel(false)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
|
||||
|
@ -82,6 +84,7 @@ SpdyStream31::SpdyStream31(nsAHttpTransaction *httpTransaction,
|
|||
|
||||
SpdyStream31::~SpdyStream31()
|
||||
{
|
||||
ClearTransactionsBlockedOnTunnel();
|
||||
mStreamID = SpdySession31::kDeadStreamID;
|
||||
}
|
||||
|
||||
|
@ -119,7 +122,8 @@ SpdyStream31::ReadSegments(nsAHttpSegmentReader *reader,
|
|||
mSegmentReader = reader;
|
||||
rv = mTransaction->ReadSegments(this, count, countRead);
|
||||
mSegmentReader = nullptr;
|
||||
|
||||
LOG3(("SpdyStream31::ReadSegments %p trans readsegments rv %x read=%d\n",
|
||||
this, rv, *countRead));
|
||||
// Check to see if the transaction's request could be written out now.
|
||||
// If not, mark the stream for callback when writing can proceed.
|
||||
if (NS_SUCCEEDED(rv) &&
|
||||
|
@ -144,7 +148,8 @@ SpdyStream31::ReadSegments(nsAHttpSegmentReader *reader,
|
|||
if (!mBlockedOnRwin &&
|
||||
!mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
|
||||
LOG3(("SpdyStream31::ReadSegments %p 0x%X: Sending request data complete, "
|
||||
"mUpstreamState=%x",this, mStreamID, mUpstreamState));
|
||||
"mUpstreamState=%x finondata=%d",this, mStreamID,
|
||||
mUpstreamState, mSentFinOnData));
|
||||
if (mSentFinOnData) {
|
||||
ChangeState(UPSTREAM_COMPLETE);
|
||||
}
|
||||
|
@ -464,24 +469,51 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf,
|
|||
// headers at this same level so it is not necessary to do so here.
|
||||
|
||||
const char *methodHeader = mTransaction->RequestHead()->Method().get();
|
||||
LOG3(("Stream method %p 0x%X %s\n", this, mStreamID, methodHeader));
|
||||
|
||||
// The header block length
|
||||
uint16_t count = hdrHash.Count() + 5; /* method, path, version, host, scheme */
|
||||
uint16_t count = hdrHash.Count() + 4; /* :method, :path, :version, :host */
|
||||
if (mTransaction->RequestHead()->IsConnect()) {
|
||||
mRequestBodyLenRemaining = 0x0fffffffffffffffULL;
|
||||
} else {
|
||||
++count; // :scheme used if not connect
|
||||
}
|
||||
CompressToFrame(count);
|
||||
|
||||
// :method, :path, :version comprise a HTTP/1 request line, so send those first
|
||||
// to make life easy for any gateways
|
||||
CompressToFrame(NS_LITERAL_CSTRING(":method"));
|
||||
CompressToFrame(methodHeader, strlen(methodHeader));
|
||||
|
||||
CompressToFrame(NS_LITERAL_CSTRING(":path"));
|
||||
CompressToFrame(mTransaction->RequestHead()->RequestURI());
|
||||
if (!mTransaction->RequestHead()->IsConnect()) {
|
||||
CompressToFrame(mTransaction->RequestHead()->RequestURI());
|
||||
} else {
|
||||
MOZ_ASSERT(mTransaction->QuerySpdyConnectTransaction());
|
||||
mIsTunnel = true;
|
||||
// Connect places host:port in :path. Don't use default port.
|
||||
nsHttpConnectionInfo *ci = mTransaction->ConnectionInfo();
|
||||
if (!ci) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
nsAutoCString route;
|
||||
route = ci->GetHost();
|
||||
route.AppendLiteral(":");
|
||||
route.AppendInt(ci->Port());
|
||||
CompressToFrame(route);
|
||||
}
|
||||
|
||||
CompressToFrame(NS_LITERAL_CSTRING(":version"));
|
||||
CompressToFrame(versionHeader);
|
||||
|
||||
CompressToFrame(NS_LITERAL_CSTRING(":host"));
|
||||
CompressToFrame(hostHeader);
|
||||
CompressToFrame(NS_LITERAL_CSTRING(":scheme"));
|
||||
CompressToFrame(nsDependentCString(mTransaction->RequestHead()->IsHTTPS() ? "https" : "http"));
|
||||
|
||||
if (!mTransaction->RequestHead()->IsConnect()) {
|
||||
// no :scheme with connect
|
||||
CompressToFrame(NS_LITERAL_CSTRING(":scheme"));
|
||||
CompressToFrame(nsDependentCString(mTransaction->RequestHead()->IsHTTPS() ? "https" : "http"));
|
||||
}
|
||||
|
||||
hdrHash.Enumerate(hdrHashEnumerate, this);
|
||||
CompressFlushFrame();
|
||||
|
@ -496,9 +528,8 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf,
|
|||
// to wait for a data packet to put it on.
|
||||
|
||||
if (mTransaction->RequestHead()->IsGet() ||
|
||||
mTransaction->RequestHead()->IsConnect() ||
|
||||
mTransaction->RequestHead()->IsHead()) {
|
||||
// for GET, CONNECT, and HEAD place the fin bit right on the
|
||||
// for GET and HEAD place the fin bit right on the
|
||||
// syn stream packet
|
||||
|
||||
mSentFinOnData = 1;
|
||||
|
@ -506,6 +537,7 @@ SpdyStream31::ParseHttpRequestHeaders(const char *buf,
|
|||
}
|
||||
else if (mTransaction->RequestHead()->IsPost() ||
|
||||
mTransaction->RequestHead()->IsPut() ||
|
||||
mTransaction->RequestHead()->IsConnect() ||
|
||||
mTransaction->RequestHead()->IsOptions()) {
|
||||
// place fin in a data frame even for 0 length messages, I've seen
|
||||
// the google gateway be unhappy with fin-on-syn for 0 length POST
|
||||
|
@ -600,6 +632,9 @@ void
|
|||
SpdyStream31::UpdateTransportReadEvents(uint32_t count)
|
||||
{
|
||||
mTotalRead += count;
|
||||
if (!mSocketTransport) {
|
||||
return;
|
||||
}
|
||||
|
||||
mTransaction->OnTransportStatus(mSocketTransport,
|
||||
NS_NET_STATUS_RECEIVING_FROM,
|
||||
|
@ -1262,6 +1297,12 @@ SpdyStream31::ConvertHeaders(nsACString &aHeadersOut)
|
|||
mDecompressBufferSize = 0;
|
||||
mDecompressBufferUsed = 0;
|
||||
|
||||
if (mIsTunnel) {
|
||||
aHeadersOut.Truncate();
|
||||
LOG(("SpdyStream31::ConvertHeaders %p 0x%X headers removed for tunnel\n",
|
||||
this, mStreamID));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1327,6 +1368,38 @@ SpdyStream31::CompressFlushFrame()
|
|||
ExecuteCompress(Z_SYNC_FLUSH);
|
||||
}
|
||||
|
||||
bool
|
||||
SpdyStream31::GetFullyOpen()
|
||||
{
|
||||
return mFullyOpen;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SpdyStream31::SetFullyOpen()
|
||||
{
|
||||
MOZ_ASSERT(!mFullyOpen);
|
||||
mFullyOpen = 1;
|
||||
if (mIsTunnel) {
|
||||
nsDependentCSubstring statusSubstring;
|
||||
nsresult rv = FindHeader(NS_LITERAL_CSTRING(":status"),
|
||||
statusSubstring);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsCString status(statusSubstring);
|
||||
nsresult errcode;
|
||||
|
||||
if (status.ToInteger(&errcode) != 200) {
|
||||
LOG3(("SpdyStream31::SetFullyOpen %p Tunnel not 200", this));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOG3(("SpdyStream31::SetFullyOpen %p Tunnel 200 OK", this));
|
||||
}
|
||||
|
||||
MapStreamToHttpConnection();
|
||||
ClearTransactionsBlockedOnTunnel();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream31::Close(nsresult reason)
|
||||
{
|
||||
|
@ -1420,11 +1493,15 @@ SpdyStream31::OnReadSegment(const char *buf,
|
|||
mRemoteWindow -= dataLength;
|
||||
mSession->DecrementRemoteSessionWindow(dataLength);
|
||||
|
||||
LOG3(("SpdyStream31 %p id %x request len remaining %d, "
|
||||
"count avail %d, chunk used %d",
|
||||
LOG3(("SpdyStream31 %p id %x request len remaining %u, "
|
||||
"count avail %u, chunk used %u",
|
||||
this, mStreamID, mRequestBodyLenRemaining, count, dataLength));
|
||||
if (dataLength > mRequestBodyLenRemaining)
|
||||
if (!dataLength && mRequestBodyLenRemaining) {
|
||||
return NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
if (dataLength > mRequestBodyLenRemaining) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
mRequestBodyLenRemaining -= dataLength;
|
||||
GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining);
|
||||
ChangeState(SENDING_REQUEST_BODY);
|
||||
|
@ -1490,6 +1567,27 @@ SpdyStream31::OnWriteSegment(char *buf,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/// connect tunnels
|
||||
|
||||
void
|
||||
SpdyStream31::ClearTransactionsBlockedOnTunnel()
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
|
||||
|
||||
if (!mIsTunnel) {
|
||||
return;
|
||||
}
|
||||
gHttpHandler->ConnMgr()->ProcessPendingQ(mTransaction->ConnectionInfo());
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream31::MapStreamToHttpConnection()
|
||||
{
|
||||
nsRefPtr<SpdyConnectTransaction> qiTrans(mTransaction->QuerySpdyConnectTransaction());
|
||||
MOZ_ASSERT(qiTrans);
|
||||
qiTrans->MapStreamToHttpConnection(mSocketTransport,
|
||||
mTransaction->ConnectionInfo());
|
||||
}
|
||||
|
||||
} // namespace mozilla::net
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -34,13 +34,10 @@ public:
|
|||
return static_cast<bool>(mRequestBlockedOnRead);
|
||||
}
|
||||
|
||||
// returns false if called more than once
|
||||
bool GetFullyOpen() {return mFullyOpen;}
|
||||
void SetFullyOpen()
|
||||
{
|
||||
MOZ_ASSERT(!mFullyOpen);
|
||||
mFullyOpen = 1;
|
||||
}
|
||||
bool GetFullyOpen();
|
||||
// returns failure if stream cannot be made ready and stream
|
||||
// should be canceled
|
||||
nsresult SetFullyOpen();
|
||||
|
||||
bool HasRegisteredID() { return mStreamID != 0; }
|
||||
|
||||
|
@ -215,7 +212,8 @@ private:
|
|||
// place the fin flag on the last data packet instead of waiting
|
||||
// for a stream closed indication. Relying on stream close results
|
||||
// in an extra 0-length runt packet and seems to have some interop
|
||||
// problems with the google servers.
|
||||
// problems with the google servers. Connect does rely on stream
|
||||
// close by setting this to the max value.
|
||||
int64_t mRequestBodyLenRemaining;
|
||||
|
||||
// based on nsISupportsPriority definitions
|
||||
|
@ -248,6 +246,15 @@ private:
|
|||
|
||||
// For SpdyPush
|
||||
SpdyPushedStream31 *mPushSource;
|
||||
|
||||
/// connect tunnels
|
||||
public:
|
||||
bool IsTunnel() { return mIsTunnel; }
|
||||
private:
|
||||
void ClearTransactionsBlockedOnTunnel();
|
||||
void MapStreamToHttpConnection();
|
||||
|
||||
bool mIsTunnel;
|
||||
};
|
||||
|
||||
}} // namespace mozilla::net
|
||||
|
|
Загрузка…
Ссылка в новой задаче