2011-12-13 07:55:50 -08:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set sw=2 ts=8 et tw=80 : */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Mozilla Foundation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Patrick McManus <mcmanus@ducksong.com>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "nsHttp.h"
|
|
|
|
#include "SpdySession.h"
|
|
|
|
#include "SpdyStream.h"
|
|
|
|
#include "nsHttpConnection.h"
|
2012-01-25 21:15:26 -08:00
|
|
|
#include "nsHttpHandler.h"
|
2011-12-13 07:55:50 -08:00
|
|
|
#include "prnetdb.h"
|
|
|
|
#include "mozilla/Telemetry.h"
|
|
|
|
#include "mozilla/Preferences.h"
|
|
|
|
#include "prprf.h"
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// defined by the socket transport service while active
|
|
|
|
extern PRThread *gSocketThread;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace net {
|
|
|
|
|
|
|
|
// SpdySession has multiple inheritance of things that implement
|
|
|
|
// nsISupports, so this magic is taken from nsHttpPipeline that
|
|
|
|
// implements some of the same abstract classes.
|
|
|
|
NS_IMPL_THREADSAFE_ADDREF(SpdySession)
|
|
|
|
NS_IMPL_THREADSAFE_RELEASE(SpdySession)
|
|
|
|
NS_INTERFACE_MAP_BEGIN(SpdySession)
|
|
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsAHttpConnection)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
SpdySession::SpdySession(nsAHttpTransaction *aHttpTransaction,
|
|
|
|
nsISocketTransport *aSocketTransport,
|
|
|
|
PRInt32 firstPriority)
|
|
|
|
: mSocketTransport(aSocketTransport),
|
|
|
|
mSegmentReader(nsnull),
|
|
|
|
mSegmentWriter(nsnull),
|
|
|
|
mSendingChunkSize(kSendingChunkSize),
|
|
|
|
mNextStreamID(1),
|
|
|
|
mConcurrentHighWater(0),
|
|
|
|
mDownstreamState(BUFFERING_FRAME_HEADER),
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameBufferSize(kDefaultBufferSize),
|
|
|
|
mInputFrameBufferUsed(0),
|
|
|
|
mInputFrameDataLast(false),
|
|
|
|
mInputFrameDataStream(nsnull),
|
2011-12-13 07:55:50 -08:00
|
|
|
mNeedsCleanup(nsnull),
|
|
|
|
mDecompressBufferSize(kDefaultBufferSize),
|
|
|
|
mDecompressBufferUsed(0),
|
|
|
|
mShouldGoAway(false),
|
|
|
|
mClosed(false),
|
|
|
|
mCleanShutdown(false),
|
|
|
|
mGoAwayID(0),
|
|
|
|
mMaxConcurrent(kDefaultMaxConcurrent),
|
|
|
|
mConcurrent(0),
|
|
|
|
mServerPushedResources(0),
|
|
|
|
mOutputQueueSize(kDefaultQueueSize),
|
|
|
|
mOutputQueueUsed(0),
|
2012-02-23 17:58:43 -08:00
|
|
|
mOutputQueueSent(0),
|
|
|
|
mLastReadEpoch(PR_IntervalNow()),
|
|
|
|
mPingSentEpoch(0),
|
2012-02-27 07:32:09 -08:00
|
|
|
mNextPingID(1),
|
|
|
|
mPingThresholdExperiment(false)
|
2011-12-13 07:55:50 -08:00
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
|
|
|
LOG3(("SpdySession::SpdySession %p transaction 1 = %p",
|
|
|
|
this, aHttpTransaction));
|
|
|
|
|
|
|
|
mStreamIDHash.Init();
|
|
|
|
mStreamTransactionHash.Init();
|
|
|
|
mConnection = aHttpTransaction->Connection();
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameBuffer = new char[mInputFrameBufferSize];
|
2011-12-13 07:55:50 -08:00
|
|
|
mDecompressBuffer = new char[mDecompressBufferSize];
|
|
|
|
mOutputQueueBuffer = new char[mOutputQueueSize];
|
|
|
|
zlibInit();
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
|
2011-12-13 07:55:50 -08:00
|
|
|
AddStream(aHttpTransaction, firstPriority);
|
2012-02-23 17:58:43 -08:00
|
|
|
mLastDataReadEpoch = mLastReadEpoch;
|
2012-02-27 07:32:09 -08:00
|
|
|
|
|
|
|
DeterminePingThreshold();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::DeterminePingThreshold()
|
|
|
|
{
|
|
|
|
mPingThreshold = gHttpHandler->SpdyPingThreshold();
|
|
|
|
|
|
|
|
if (!mPingThreshold || !gHttpHandler->AllowExperiments())
|
|
|
|
return;
|
|
|
|
|
|
|
|
PRUint32 randomVal = gHttpHandler->Get32BitsOfPseudoRandom();
|
|
|
|
|
|
|
|
// Use the lower 10 bits to select 1 in 1024 sessions for the
|
|
|
|
// ping threshold experiment. Somewhat less than that will actually be
|
|
|
|
// used because random values greater than the total http idle timeout
|
|
|
|
// for the session are discarded.
|
|
|
|
if ((randomVal & 0x3ff) != 1) // lottery
|
|
|
|
return;
|
|
|
|
|
|
|
|
randomVal = randomVal >> 10; // those bits are used up
|
|
|
|
|
|
|
|
// This session has been selected - use a random ping threshold of 10 +
|
|
|
|
// a random number from 0 to 255, based on the next 8 bits of the
|
|
|
|
// random buffer
|
|
|
|
PRIntervalTime randomThreshold =
|
|
|
|
PR_SecondsToInterval((randomVal & 0xff) + 10);
|
|
|
|
if (randomThreshold > gHttpHandler->IdleTimeout())
|
|
|
|
return;
|
|
|
|
|
|
|
|
mPingThreshold = randomThreshold;
|
|
|
|
mPingThresholdExperiment = true;
|
|
|
|
LOG3(("SpdySession %p Ping Threshold Experimental Selection : %dsec\n",
|
|
|
|
this, PR_IntervalToSeconds(mPingThreshold)));
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
PLDHashOperator
|
2012-01-25 21:15:26 -08:00
|
|
|
SpdySession::ShutdownEnumerator(nsAHttpTransaction *key,
|
|
|
|
nsAutoPtr<SpdyStream> &stream,
|
|
|
|
void *closure)
|
2011-12-13 07:55:50 -08:00
|
|
|
{
|
|
|
|
SpdySession *self = static_cast<SpdySession *>(closure);
|
2012-01-25 21:15:26 -08:00
|
|
|
|
|
|
|
// On a clean server hangup the server sets the GoAwayID to be the ID of
|
|
|
|
// the last transaction it processed. If the ID of stream in the
|
|
|
|
// local session is greater than that it can safely be restarted because the
|
|
|
|
// server guarantees it was not partially processed.
|
|
|
|
if (self->mCleanShutdown && (stream->StreamID() > self->mGoAwayID))
|
2011-12-13 07:55:50 -08:00
|
|
|
stream->Close(NS_ERROR_NET_RESET); // can be restarted
|
|
|
|
else
|
|
|
|
stream->Close(NS_ERROR_ABORT);
|
|
|
|
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
SpdySession::~SpdySession()
|
|
|
|
{
|
|
|
|
LOG3(("SpdySession::~SpdySession %p mDownstreamState=%X",
|
|
|
|
this, mDownstreamState));
|
|
|
|
|
|
|
|
inflateEnd(&mDownstreamZlib);
|
|
|
|
deflateEnd(&mUpstreamZlib);
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
|
2011-12-13 07:55:50 -08:00
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater);
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2);
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS,
|
|
|
|
mServerPushedResources);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::LogIO(SpdySession *self, SpdyStream *stream, const char *label,
|
|
|
|
const char *data, PRUint32 datalen)
|
|
|
|
{
|
|
|
|
if (!LOG4_ENABLED())
|
|
|
|
return;
|
|
|
|
|
|
|
|
LOG4(("SpdySession::LogIO %p stream=%p id=0x%X [%s]",
|
|
|
|
self, stream, stream ? stream->StreamID() : 0, label));
|
|
|
|
|
|
|
|
// Max line is (16 * 3) + 10(prefix) + newline + null
|
|
|
|
char linebuf[128];
|
|
|
|
PRUint32 index;
|
|
|
|
char *line = linebuf;
|
|
|
|
|
|
|
|
linebuf[127] = 0;
|
|
|
|
|
|
|
|
for (index = 0; index < datalen; ++index) {
|
|
|
|
if (!(index % 16)) {
|
|
|
|
if (index) {
|
|
|
|
*line = 0;
|
|
|
|
LOG4(("%s", linebuf));
|
|
|
|
}
|
|
|
|
line = linebuf;
|
|
|
|
PR_snprintf(line, 128, "%08X: ", index);
|
|
|
|
line += 10;
|
|
|
|
}
|
|
|
|
PR_snprintf(line, 128 - (line - linebuf), "%02X ",
|
|
|
|
((unsigned char *)data)[index]);
|
|
|
|
line += 3;
|
|
|
|
}
|
|
|
|
if (index) {
|
|
|
|
*line = 0;
|
|
|
|
LOG4(("%s", linebuf));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef nsresult (*Control_FX) (SpdySession *self);
|
|
|
|
static Control_FX sControlFunctions[] =
|
|
|
|
{
|
|
|
|
nsnull,
|
|
|
|
SpdySession::HandleSynStream,
|
|
|
|
SpdySession::HandleSynReply,
|
|
|
|
SpdySession::HandleRstStream,
|
|
|
|
SpdySession::HandleSettings,
|
|
|
|
SpdySession::HandleNoop,
|
|
|
|
SpdySession::HandlePing,
|
|
|
|
SpdySession::HandleGoAway,
|
|
|
|
SpdySession::HandleHeaders,
|
|
|
|
SpdySession::HandleWindowUpdate
|
|
|
|
};
|
|
|
|
|
|
|
|
bool
|
|
|
|
SpdySession::RoomForMoreConcurrent()
|
|
|
|
{
|
2012-01-25 21:15:26 -08:00
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
return (mConcurrent < mMaxConcurrent);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
SpdySession::RoomForMoreStreams()
|
|
|
|
{
|
|
|
|
if (mNextStreamID + mStreamTransactionHash.Count() * 2 > kMaxStreamID)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return !mShouldGoAway;
|
|
|
|
}
|
|
|
|
|
2012-02-23 17:58:43 -08:00
|
|
|
PRIntervalTime
|
|
|
|
SpdySession::IdleTime()
|
|
|
|
{
|
|
|
|
return PR_IntervalNow() - mLastDataReadEpoch;
|
|
|
|
}
|
|
|
|
|
2012-02-23 17:56:57 -08:00
|
|
|
void
|
|
|
|
SpdySession::ReadTimeoutTick(PRIntervalTime now)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
2012-02-23 17:58:43 -08:00
|
|
|
NS_ABORT_IF_FALSE(mNextPingID & 1, "Ping Counter Not Odd");
|
|
|
|
|
2012-02-27 07:32:09 -08:00
|
|
|
if (!mPingThreshold)
|
2012-02-23 17:58:43 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
LOG(("SpdySession::ReadTimeoutTick %p delta since last read %ds\n",
|
|
|
|
this, PR_IntervalToSeconds(now - mLastReadEpoch)));
|
|
|
|
|
2012-02-27 07:32:09 -08:00
|
|
|
if ((now - mLastReadEpoch) < mPingThreshold) {
|
2012-02-23 17:58:43 -08:00
|
|
|
// recent activity means ping is not an issue
|
2012-02-27 07:32:09 -08:00
|
|
|
if (mPingSentEpoch)
|
|
|
|
ClearPing(true);
|
2012-02-23 17:58:43 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mPingSentEpoch) {
|
|
|
|
LOG(("SpdySession::ReadTimeoutTick %p handle outstanding ping\n"));
|
|
|
|
if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) {
|
|
|
|
LOG(("SpdySession::ReadTimeoutTick %p Ping Timer Exhaustion\n",
|
|
|
|
this));
|
2012-02-27 07:32:09 -08:00
|
|
|
ClearPing(false);
|
2012-02-23 17:58:43 -08:00
|
|
|
Close(NS_ERROR_NET_TIMEOUT);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(("SpdySession::ReadTimeoutTick %p generating ping 0x%x\n",
|
|
|
|
this, mNextPingID));
|
|
|
|
|
|
|
|
if (mNextPingID == 0xffffffff) {
|
|
|
|
LOG(("SpdySession::ReadTimeoutTick %p cannot form ping - ids exhausted\n",
|
|
|
|
this));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mPingSentEpoch = PR_IntervalNow();
|
|
|
|
if (!mPingSentEpoch)
|
|
|
|
mPingSentEpoch = 1; // avoid the 0 sentinel value
|
|
|
|
GeneratePing(mNextPingID);
|
|
|
|
mNextPingID += 2;
|
|
|
|
|
|
|
|
if (mNextPingID == 0xffffffff) {
|
|
|
|
LOG(("SpdySession::ReadTimeoutTick %p "
|
|
|
|
"ping ids exhausted marking goaway\n", this));
|
|
|
|
mShouldGoAway = true;
|
|
|
|
}
|
2012-02-23 17:56:57 -08:00
|
|
|
}
|
|
|
|
|
2012-02-27 07:32:09 -08:00
|
|
|
void
|
|
|
|
SpdySession::ClearPing(bool pingOK)
|
|
|
|
{
|
|
|
|
mPingSentEpoch = 0;
|
|
|
|
|
|
|
|
if (mPingThresholdExperiment) {
|
|
|
|
LOG3(("SpdySession::ClearPing %p mPingThresholdExperiment %dsec %s\n",
|
|
|
|
this, PR_IntervalToSeconds(mPingThreshold),
|
|
|
|
pingOK ? "pass" :"fail"));
|
|
|
|
|
|
|
|
if (pingOK)
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_PING_EXPERIMENT_PASS,
|
|
|
|
PR_IntervalToSeconds(mPingThreshold));
|
|
|
|
else
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_PING_EXPERIMENT_FAIL,
|
|
|
|
PR_IntervalToSeconds(mPingThreshold));
|
|
|
|
mPingThreshold = gHttpHandler->SpdyPingThreshold();
|
|
|
|
mPingThresholdExperiment = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
PRUint32
|
|
|
|
SpdySession::RegisterStreamID(SpdyStream *stream)
|
|
|
|
{
|
2012-01-25 21:15:26 -08:00
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::RegisterStreamID session=%p stream=%p id=0x%X "
|
|
|
|
"concurrent=%d",this, stream, mNextStreamID, mConcurrent));
|
|
|
|
|
|
|
|
NS_ABORT_IF_FALSE(mNextStreamID < 0xfffffff0,
|
|
|
|
"should have stopped admitting streams");
|
|
|
|
|
|
|
|
PRUint32 result = mNextStreamID;
|
|
|
|
mNextStreamID += 2;
|
|
|
|
|
|
|
|
// We've used up plenty of ID's on this session. Start
|
|
|
|
// moving to a new one before there is a crunch involving
|
|
|
|
// server push streams or concurrent non-registered submits
|
|
|
|
if (mNextStreamID >= kMaxStreamID)
|
|
|
|
mShouldGoAway = true;
|
|
|
|
|
|
|
|
mStreamIDHash.Put(result, stream);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
SpdySession::AddStream(nsAHttpTransaction *aHttpTransaction,
|
|
|
|
PRInt32 aPriority)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
NS_ABORT_IF_FALSE(!mStreamTransactionHash.Get(aHttpTransaction),
|
|
|
|
"AddStream duplicate transaction pointer");
|
|
|
|
|
|
|
|
aHttpTransaction->SetConnection(this);
|
|
|
|
SpdyStream *stream = new SpdyStream(aHttpTransaction,
|
|
|
|
this,
|
|
|
|
mSocketTransport,
|
|
|
|
mSendingChunkSize,
|
|
|
|
&mUpstreamZlib,
|
|
|
|
aPriority);
|
|
|
|
|
|
|
|
|
|
|
|
LOG3(("SpdySession::AddStream session=%p stream=%p NextID=0x%X (tentative)",
|
|
|
|
this, stream, mNextStreamID));
|
|
|
|
|
|
|
|
mStreamTransactionHash.Put(aHttpTransaction, stream);
|
|
|
|
|
|
|
|
if (RoomForMoreConcurrent()) {
|
|
|
|
LOG3(("SpdySession::AddStream %p stream %p activated immediately.",
|
|
|
|
this, stream));
|
|
|
|
ActivateStream(stream);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
LOG3(("SpdySession::AddStream %p stream %p queued.",
|
|
|
|
this, stream));
|
|
|
|
mQueuedStreams.Push(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::ActivateStream(SpdyStream *stream)
|
|
|
|
{
|
2012-01-25 21:15:26 -08:00
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
mConcurrent++;
|
|
|
|
if (mConcurrent > mConcurrentHighWater)
|
|
|
|
mConcurrentHighWater = mConcurrent;
|
|
|
|
LOG3(("SpdySession::AddStream %p activating stream %p Currently %d "
|
|
|
|
"streams in session, high water mark is %d",
|
|
|
|
this, stream, mConcurrent, mConcurrentHighWater));
|
|
|
|
|
|
|
|
mReadyForWrite.Push(stream);
|
2012-01-25 21:15:26 -08:00
|
|
|
SetWriteCallbacks();
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
// Kick off the SYN transmit without waiting for the poll loop
|
2012-05-16 06:05:34 -07:00
|
|
|
// This won't work for stream id=1 because there is no segment reader
|
|
|
|
// yet.
|
|
|
|
if (mSegmentReader) {
|
|
|
|
PRUint32 countRead;
|
|
|
|
ReadSegments(nsnull, kDefaultBufferSize, &countRead);
|
|
|
|
}
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::ProcessPending()
|
|
|
|
{
|
2012-01-25 21:15:26 -08:00
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
while (RoomForMoreConcurrent()) {
|
|
|
|
SpdyStream *stream = static_cast<SpdyStream *>(mQueuedStreams.PopFront());
|
|
|
|
if (!stream)
|
|
|
|
return;
|
|
|
|
LOG3(("SpdySession::ProcessPending %p stream %p activated from queue.",
|
|
|
|
this, stream));
|
|
|
|
ActivateStream(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-23 17:58:43 -08:00
|
|
|
nsresult
|
|
|
|
SpdySession::NetworkRead(nsAHttpSegmentWriter *writer, char *buf,
|
|
|
|
PRUint32 count, PRUint32 *countWritten)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
|
|
|
nsresult rv = writer->OnWriteSegment(buf, count, countWritten);
|
|
|
|
if (NS_SUCCEEDED(rv) && *countWritten > 0)
|
|
|
|
mLastReadEpoch = PR_IntervalNow();
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
void
|
2012-01-25 21:15:26 -08:00
|
|
|
SpdySession::SetWriteCallbacks()
|
2011-12-13 07:55:50 -08:00
|
|
|
{
|
2012-01-25 21:15:26 -08:00
|
|
|
if (mConnection && (GetWriteQueueSize() || mOutputQueueUsed))
|
|
|
|
mConnection->ResumeSend();
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::FlushOutputQueue()
|
|
|
|
{
|
|
|
|
if (!mSegmentReader || !mOutputQueueUsed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
PRUint32 countRead;
|
|
|
|
PRUint32 avail = mOutputQueueUsed - mOutputQueueSent;
|
|
|
|
|
|
|
|
rv = mSegmentReader->
|
|
|
|
OnReadSegment(mOutputQueueBuffer.get() + mOutputQueueSent, avail,
|
|
|
|
&countRead);
|
|
|
|
LOG3(("SpdySession::FlushOutputQueue %p sz=%d rv=%x actual=%d",
|
|
|
|
this, avail, rv, countRead));
|
|
|
|
|
|
|
|
// Dont worry about errors on write, we will pick this up as a read error too
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (countRead == avail) {
|
|
|
|
mOutputQueueUsed = 0;
|
|
|
|
mOutputQueueSent = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mOutputQueueSent += countRead;
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
// If the output queue is close to filling up and we have sent out a good
|
|
|
|
// chunk of data from the beginning then realign it.
|
|
|
|
|
|
|
|
if ((mOutputQueueSent >= kQueueMinimumCleanup) &&
|
|
|
|
((mOutputQueueSize - mOutputQueueUsed) < kQueueTailRoom)) {
|
2011-12-13 07:55:50 -08:00
|
|
|
mOutputQueueUsed -= mOutputQueueSent;
|
|
|
|
memmove(mOutputQueueBuffer.get(),
|
|
|
|
mOutputQueueBuffer.get() + mOutputQueueSent,
|
|
|
|
mOutputQueueUsed);
|
|
|
|
mOutputQueueSent = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::DontReuse()
|
|
|
|
{
|
|
|
|
mShouldGoAway = true;
|
2012-01-25 21:15:26 -08:00
|
|
|
if (!mStreamTransactionHash.Count())
|
2011-12-13 07:55:50 -08:00
|
|
|
Close(NS_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint32
|
2012-01-25 21:15:26 -08:00
|
|
|
SpdySession::GetWriteQueueSize()
|
2011-12-13 07:55:50 -08:00
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
return mUrgentForWrite.GetSize() + mReadyForWrite.GetSize();
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::ChangeDownstreamState(enum stateType newState)
|
|
|
|
{
|
2012-01-25 21:15:26 -08:00
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdyStream::ChangeDownstreamState() %p from %X to %X",
|
|
|
|
this, mDownstreamState, newState));
|
|
|
|
mDownstreamState = newState;
|
2012-01-25 21:15:26 -08:00
|
|
|
}
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
void
|
|
|
|
SpdySession::ResetDownstreamState()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
|
|
|
LOG3(("SpdyStream::ResetDownstreamState() %p", this));
|
|
|
|
ChangeDownstreamState(BUFFERING_FRAME_HEADER);
|
|
|
|
|
|
|
|
if (mInputFrameDataLast && mInputFrameDataStream) {
|
|
|
|
mInputFrameDataLast = false;
|
|
|
|
if (!mInputFrameDataStream->RecvdFin()) {
|
|
|
|
mInputFrameDataStream->SetRecvdFin(true);
|
|
|
|
--mConcurrent;
|
|
|
|
ProcessPending();
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
}
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameBufferUsed = 0;
|
|
|
|
mInputFrameDataStream = nsnull;
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::EnsureBuffer(nsAutoArrayPtr<char> &buf,
|
|
|
|
PRUint32 newSize,
|
|
|
|
PRUint32 preserve,
|
|
|
|
PRUint32 &objSize)
|
|
|
|
{
|
|
|
|
if (objSize >= newSize)
|
2012-01-25 21:15:26 -08:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Leave a little slop on the new allocation - add 2KB to
|
|
|
|
// what we need and then round the result up to a 4KB (page)
|
|
|
|
// boundary.
|
|
|
|
|
|
|
|
objSize = (newSize + 2048 + 4095) & ~4095;
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
nsAutoArrayPtr<char> tmp(new char[objSize]);
|
2012-01-25 21:15:26 -08:00
|
|
|
memcpy(tmp, buf, preserve);
|
2011-12-13 07:55:50 -08:00
|
|
|
buf = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::zlibInit()
|
|
|
|
{
|
|
|
|
mDownstreamZlib.zalloc = SpdyStream::zlib_allocator;
|
|
|
|
mDownstreamZlib.zfree = SpdyStream::zlib_destructor;
|
|
|
|
mDownstreamZlib.opaque = Z_NULL;
|
|
|
|
|
|
|
|
inflateInit(&mDownstreamZlib);
|
|
|
|
|
|
|
|
mUpstreamZlib.zalloc = SpdyStream::zlib_allocator;
|
|
|
|
mUpstreamZlib.zfree = SpdyStream::zlib_destructor;
|
|
|
|
mUpstreamZlib.opaque = Z_NULL;
|
|
|
|
|
|
|
|
deflateInit(&mUpstreamZlib, Z_DEFAULT_COMPRESSION);
|
|
|
|
deflateSetDictionary(&mUpstreamZlib,
|
|
|
|
reinterpret_cast<const unsigned char *>
|
|
|
|
(SpdyStream::kDictionary),
|
|
|
|
strlen(SpdyStream::kDictionary) + 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::DownstreamUncompress(char *blockStart, PRUint32 blockLen)
|
|
|
|
{
|
|
|
|
mDecompressBufferUsed = 0;
|
|
|
|
|
|
|
|
mDownstreamZlib.avail_in = blockLen;
|
|
|
|
mDownstreamZlib.next_in = reinterpret_cast<unsigned char *>(blockStart);
|
|
|
|
|
|
|
|
do {
|
|
|
|
mDownstreamZlib.next_out =
|
|
|
|
reinterpret_cast<unsigned char *>(mDecompressBuffer.get()) +
|
|
|
|
mDecompressBufferUsed;
|
|
|
|
mDownstreamZlib.avail_out = mDecompressBufferSize - mDecompressBufferUsed;
|
|
|
|
int zlib_rv = inflate(&mDownstreamZlib, Z_NO_FLUSH);
|
|
|
|
|
|
|
|
if (zlib_rv == Z_NEED_DICT)
|
|
|
|
inflateSetDictionary(&mDownstreamZlib,
|
|
|
|
reinterpret_cast<const unsigned char *>
|
|
|
|
(SpdyStream::kDictionary),
|
|
|
|
strlen(SpdyStream::kDictionary) + 1);
|
|
|
|
|
|
|
|
if (zlib_rv == Z_DATA_ERROR || zlib_rv == Z_MEM_ERROR)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
mDecompressBufferUsed += mDecompressBufferSize - mDecompressBufferUsed -
|
|
|
|
mDownstreamZlib.avail_out;
|
|
|
|
|
|
|
|
// When there is no more output room, but input still available then
|
|
|
|
// increase the output space
|
|
|
|
if (zlib_rv == Z_OK &&
|
|
|
|
!mDownstreamZlib.avail_out && mDownstreamZlib.avail_in) {
|
|
|
|
LOG3(("SpdySession::DownstreamUncompress %p Large Headers - so far %d",
|
|
|
|
this, mDecompressBufferSize));
|
|
|
|
EnsureBuffer(mDecompressBuffer,
|
|
|
|
mDecompressBufferSize + 4096,
|
|
|
|
mDecompressBufferUsed,
|
|
|
|
mDecompressBufferSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (mDownstreamZlib.avail_in);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::FindHeader(nsCString name,
|
|
|
|
nsDependentCSubstring &value)
|
|
|
|
{
|
|
|
|
const unsigned char *nvpair = reinterpret_cast<unsigned char *>
|
|
|
|
(mDecompressBuffer.get()) + 2;
|
|
|
|
const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
|
|
|
|
(mDecompressBuffer.get()) + mDecompressBufferUsed;
|
|
|
|
if (lastHeaderByte < nvpair)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
PRUint16 numPairs =
|
|
|
|
PR_ntohs(reinterpret_cast<PRUint16 *>(mDecompressBuffer.get())[0]);
|
|
|
|
for (PRUint16 index = 0; index < numPairs; ++index) {
|
|
|
|
if (lastHeaderByte < nvpair + 2)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
PRUint32 nameLen = (nvpair[0] << 8) + nvpair[1];
|
|
|
|
if (lastHeaderByte < nvpair + 2 + nameLen)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
nsDependentCSubstring nameString =
|
2012-01-25 21:15:26 -08:00
|
|
|
Substring(reinterpret_cast<const char *>(nvpair) + 2,
|
|
|
|
reinterpret_cast<const char *>(nvpair) + 2 + nameLen);
|
2011-12-13 07:55:50 -08:00
|
|
|
if (lastHeaderByte < nvpair + 4 + nameLen)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
PRUint16 valueLen = (nvpair[2 + nameLen] << 8) + nvpair[3 + nameLen];
|
|
|
|
if (lastHeaderByte < nvpair + 4 + nameLen + valueLen)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
if (nameString.Equals(name)) {
|
|
|
|
value.Assign(((char *)nvpair) + 4 + nameLen, valueLen);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
nvpair += 4 + nameLen + valueLen;
|
|
|
|
}
|
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::ConvertHeaders(nsDependentCSubstring &status,
|
|
|
|
nsDependentCSubstring &version)
|
|
|
|
{
|
|
|
|
mFlatHTTPResponseHeaders.Truncate();
|
|
|
|
mFlatHTTPResponseHeadersOut = 0;
|
|
|
|
mFlatHTTPResponseHeaders.SetCapacity(mDecompressBufferUsed + 64);
|
|
|
|
|
|
|
|
// Connection, Keep-Alive and chunked transfer encodings are to be
|
|
|
|
// removed.
|
|
|
|
|
|
|
|
// Content-Length is 'advisory'.. we will not strip it because it can
|
|
|
|
// create UI feedback.
|
|
|
|
|
|
|
|
mFlatHTTPResponseHeaders.Append(version);
|
|
|
|
mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING(" "));
|
|
|
|
mFlatHTTPResponseHeaders.Append(status);
|
|
|
|
mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
|
|
|
|
|
|
|
|
const unsigned char *nvpair = reinterpret_cast<unsigned char *>
|
|
|
|
(mDecompressBuffer.get()) + 2;
|
|
|
|
const unsigned char *lastHeaderByte = reinterpret_cast<unsigned char *>
|
|
|
|
(mDecompressBuffer.get()) + mDecompressBufferUsed;
|
|
|
|
|
|
|
|
if (lastHeaderByte < nvpair)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
|
|
|
|
PRUint16 numPairs =
|
|
|
|
PR_ntohs(reinterpret_cast<PRUint16 *>(mDecompressBuffer.get())[0]);
|
|
|
|
|
|
|
|
for (PRUint16 index = 0; index < numPairs; ++index) {
|
|
|
|
if (lastHeaderByte < nvpair + 2)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
|
|
|
|
PRUint32 nameLen = (nvpair[0] << 8) + nvpair[1];
|
|
|
|
if (lastHeaderByte < nvpair + 2 + nameLen)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
|
|
|
|
nsDependentCSubstring nameString =
|
2012-01-25 21:15:26 -08:00
|
|
|
Substring(reinterpret_cast<const char *>(nvpair) + 2,
|
|
|
|
reinterpret_cast<const char *>(nvpair) + 2 + nameLen);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
if (lastHeaderByte < nvpair + 4 + nameLen)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
2012-01-25 21:15:26 -08:00
|
|
|
|
|
|
|
// Look for illegal characters in the nameString.
|
|
|
|
// This includes upper case characters and nulls (as they will
|
|
|
|
// break the fixup-nulls-in-value-string algorithm)
|
2011-12-13 07:55:50 -08:00
|
|
|
// Look for upper case characters in the name. They are illegal.
|
|
|
|
for (char *cPtr = nameString.BeginWriting();
|
|
|
|
cPtr && cPtr < nameString.EndWriting();
|
|
|
|
++cPtr) {
|
|
|
|
if (*cPtr <= 'Z' && *cPtr >= 'A') {
|
|
|
|
nsCString toLog(nameString);
|
|
|
|
|
|
|
|
LOG3(("SpdySession::ConvertHeaders session=%p stream=%p "
|
|
|
|
"upper case response header found. [%s]\n",
|
2012-01-25 21:15:26 -08:00
|
|
|
this, mInputFrameDataStream, toLog.get()));
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
2012-01-25 21:15:26 -08:00
|
|
|
|
|
|
|
// check for null characters
|
|
|
|
if (*cPtr == '\0')
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// HTTP Chunked responses are not legal over spdy. We do not need
|
|
|
|
// to look for chunked specifically because it is the only HTTP
|
|
|
|
// allowed default encoding and we did not negotiate further encodings
|
|
|
|
// via TE
|
|
|
|
if (nameString.Equals(NS_LITERAL_CSTRING("transfer-encoding"))) {
|
|
|
|
LOG3(("SpdySession::ConvertHeaders session=%p stream=%p "
|
|
|
|
"transfer-encoding found. Chunked is invalid and no TE sent.",
|
2012-01-25 21:15:26 -08:00
|
|
|
this, mInputFrameDataStream));
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
PRUint16 valueLen = (nvpair[2 + nameLen] << 8) + nvpair[3 + nameLen];
|
|
|
|
if (lastHeaderByte < nvpair + 4 + nameLen + valueLen)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
if (!nameString.Equals(NS_LITERAL_CSTRING("version")) &&
|
|
|
|
!nameString.Equals(NS_LITERAL_CSTRING("status")) &&
|
|
|
|
!nameString.Equals(NS_LITERAL_CSTRING("connection")) &&
|
|
|
|
!nameString.Equals(NS_LITERAL_CSTRING("keep-alive"))) {
|
|
|
|
nsDependentCSubstring valueString =
|
2012-01-25 21:15:26 -08:00
|
|
|
Substring(reinterpret_cast<const char *>(nvpair) + 4 + nameLen,
|
|
|
|
reinterpret_cast<const char *>(nvpair) + 4 + nameLen +
|
|
|
|
valueLen);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
mFlatHTTPResponseHeaders.Append(nameString);
|
|
|
|
mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING(": "));
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
// expand NULL bytes in the value string
|
|
|
|
for (char *cPtr = valueString.BeginWriting();
|
|
|
|
cPtr && cPtr < valueString.EndWriting();
|
|
|
|
++cPtr) {
|
|
|
|
if (*cPtr != 0) {
|
|
|
|
mFlatHTTPResponseHeaders.Append(*cPtr);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NULLs are really "\r\nhdr: "
|
|
|
|
mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
|
|
|
|
mFlatHTTPResponseHeaders.Append(nameString);
|
|
|
|
mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING(": "));
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
mFlatHTTPResponseHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
|
|
|
|
}
|
|
|
|
nvpair += 4 + nameLen + valueLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
mFlatHTTPResponseHeaders.Append(
|
|
|
|
NS_LITERAL_CSTRING("X-Firefox-Spdy: 1\r\n\r\n"));
|
|
|
|
LOG (("decoded response headers are:\n%s",
|
|
|
|
mFlatHTTPResponseHeaders.get()));
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::GeneratePing(PRUint32 aID)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
LOG3(("SpdySession::GeneratePing %p 0x%X\n", this, aID));
|
|
|
|
|
|
|
|
EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 12,
|
|
|
|
mOutputQueueUsed, mOutputQueueSize);
|
|
|
|
char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
|
|
|
|
mOutputQueueUsed += 12;
|
|
|
|
|
|
|
|
packet[0] = kFlag_Control;
|
|
|
|
packet[1] = 2; /* version 2 */
|
|
|
|
packet[2] = 0;
|
|
|
|
packet[3] = CONTROL_TYPE_PING;
|
|
|
|
packet[4] = 0; /* flags */
|
|
|
|
packet[5] = 0;
|
|
|
|
packet[6] = 0;
|
|
|
|
packet[7] = 4; /* length */
|
|
|
|
|
|
|
|
aID = PR_htonl(aID);
|
2012-01-25 21:15:26 -08:00
|
|
|
memcpy(packet + 8, &aID, 4);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
FlushOutputQueue();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::GenerateRstStream(PRUint32 aStatusCode, PRUint32 aID)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
LOG3(("SpdySession::GenerateRst %p 0x%X %d\n", this, aID, aStatusCode));
|
|
|
|
|
|
|
|
EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 16,
|
|
|
|
mOutputQueueUsed, mOutputQueueSize);
|
|
|
|
char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
|
|
|
|
mOutputQueueUsed += 16;
|
|
|
|
|
|
|
|
packet[0] = kFlag_Control;
|
|
|
|
packet[1] = 2; /* version 2 */
|
|
|
|
packet[2] = 0;
|
|
|
|
packet[3] = CONTROL_TYPE_RST_STREAM;
|
|
|
|
packet[4] = 0; /* flags */
|
|
|
|
packet[5] = 0;
|
|
|
|
packet[6] = 0;
|
|
|
|
packet[7] = 8; /* length */
|
|
|
|
|
|
|
|
aID = PR_htonl(aID);
|
2012-01-25 21:15:26 -08:00
|
|
|
memcpy(packet + 8, &aID, 4);
|
2011-12-13 07:55:50 -08:00
|
|
|
aStatusCode = PR_htonl(aStatusCode);
|
2012-01-25 21:15:26 -08:00
|
|
|
memcpy(packet + 12, &aStatusCode, 4);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
FlushOutputQueue();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::GenerateGoAway()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
LOG3(("SpdySession::GenerateGoAway %p\n", this));
|
|
|
|
|
|
|
|
EnsureBuffer(mOutputQueueBuffer, mOutputQueueUsed + 12,
|
|
|
|
mOutputQueueUsed, mOutputQueueSize);
|
|
|
|
char *packet = mOutputQueueBuffer.get() + mOutputQueueUsed;
|
|
|
|
mOutputQueueUsed += 12;
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
memset(packet, 0, 12);
|
2011-12-13 07:55:50 -08:00
|
|
|
packet[0] = kFlag_Control;
|
|
|
|
packet[1] = 2; /* version 2 */
|
|
|
|
packet[3] = CONTROL_TYPE_GOAWAY;
|
|
|
|
packet[7] = 4; /* data length */
|
|
|
|
|
|
|
|
// last-good-stream-id are bytes 8-11, when we accept server push this will
|
|
|
|
// need to be set non zero
|
|
|
|
|
|
|
|
FlushOutputQueue();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-02-19 11:28:58 -08:00
|
|
|
SpdySession::CleanupStream(SpdyStream *aStream, nsresult aResult,
|
|
|
|
rstReason aResetCode)
|
2011-12-13 07:55:50 -08:00
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
LOG3(("SpdySession::CleanupStream %p %p 0x%x %X\n",
|
|
|
|
this, aStream, aStream->StreamID(), aResult));
|
|
|
|
|
|
|
|
if (!aStream->RecvdFin() && aStream->StreamID()) {
|
2012-02-19 11:28:58 -08:00
|
|
|
LOG3(("Stream had not processed recv FIN, sending RST code %X\n",
|
|
|
|
aResetCode));
|
|
|
|
GenerateRstStream(aResetCode, aStream->StreamID());
|
2011-12-13 07:55:50 -08:00
|
|
|
--mConcurrent;
|
|
|
|
ProcessPending();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if partial frame reader
|
2012-01-25 21:15:26 -08:00
|
|
|
if (aStream == mInputFrameDataStream) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("Stream had active partial read frame on close"));
|
2012-01-25 21:15:26 -08:00
|
|
|
ChangeDownstreamState(DISCARDING_DATA_FRAME);
|
|
|
|
mInputFrameDataStream = nsnull;
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// check the streams blocked on write, this is linear but the list
|
|
|
|
// should be pretty short.
|
|
|
|
PRUint32 size = mReadyForWrite.GetSize();
|
|
|
|
for (PRUint32 count = 0; count < size; ++count) {
|
|
|
|
SpdyStream *stream = static_cast<SpdyStream *>(mReadyForWrite.PopFront());
|
|
|
|
if (stream != aStream)
|
|
|
|
mReadyForWrite.Push(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the streams blocked on urgent (i.e. window update) writing.
|
|
|
|
// This should also be short.
|
|
|
|
size = mUrgentForWrite.GetSize();
|
|
|
|
for (PRUint32 count = 0; count < size; ++count) {
|
|
|
|
SpdyStream *stream = static_cast<SpdyStream *>(mUrgentForWrite.PopFront());
|
|
|
|
if (stream != aStream)
|
|
|
|
mUrgentForWrite.Push(stream);
|
|
|
|
}
|
|
|
|
|
2011-12-19 13:36:26 -08:00
|
|
|
// Check the streams queued for activation. Because we normally accept a high
|
|
|
|
// level of parallelization this should also be short.
|
|
|
|
size = mQueuedStreams.GetSize();
|
|
|
|
for (PRUint32 count = 0; count < size; ++count) {
|
|
|
|
SpdyStream *stream = static_cast<SpdyStream *>(mQueuedStreams.PopFront());
|
|
|
|
if (stream != aStream)
|
|
|
|
mQueuedStreams.Push(stream);
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
// Remove the stream from the ID hash table. (this one isn't short, which is
|
|
|
|
// why it is hashed.)
|
|
|
|
mStreamIDHash.Remove(aStream->StreamID());
|
|
|
|
|
|
|
|
// Send the stream the close() indication
|
|
|
|
aStream->Close(aResult);
|
|
|
|
|
|
|
|
// removing from the stream transaction hash will
|
|
|
|
// delete the SpdyStream and drop the reference to
|
|
|
|
// its transaction
|
|
|
|
mStreamTransactionHash.Remove(aStream->Transaction());
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (mShouldGoAway && !mStreamTransactionHash.Count())
|
2011-12-13 07:55:50 -08:00
|
|
|
Close(NS_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::HandleSynStream(SpdySession *self)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SYN_STREAM,
|
|
|
|
"wrong control type");
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (self->mInputFrameDataSize < 18) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandleSynStream %p SYN_STREAM too short data=%d",
|
2012-01-25 21:15:26 -08:00
|
|
|
self, self->mInputFrameDataSize));
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint32 streamID =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
|
2012-01-14 19:46:30 -08:00
|
|
|
PRUint32 associatedID =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[3]);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-14 19:46:30 -08:00
|
|
|
LOG3(("SpdySession::HandleSynStream %p recv SYN_STREAM (push) "
|
|
|
|
"for ID 0x%X associated with 0x%X.",
|
|
|
|
self, streamID, associatedID));
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
if (streamID & 0x01) { // test for odd stream ID
|
|
|
|
LOG3(("SpdySession::HandleSynStream %p recvd SYN_STREAM id must be even.",
|
|
|
|
self));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
++(self->mServerPushedResources);
|
|
|
|
|
|
|
|
// Anytime we start using the high bit of stream ID (either client or server)
|
|
|
|
// begin to migrate to a new session.
|
|
|
|
if (streamID >= kMaxStreamID)
|
|
|
|
self->mShouldGoAway = true;
|
|
|
|
|
2012-01-14 19:46:30 -08:00
|
|
|
// Need to decompress the headers even though we aren't using them yet in
|
|
|
|
// order to keep the compression context consistent for other syn_reply frames
|
2012-01-25 21:15:26 -08:00
|
|
|
nsresult rv = self->DownstreamUncompress(self->mInputFrameBuffer + 18,
|
|
|
|
self->mInputFrameDataSize - 10);
|
2012-01-14 19:46:30 -08:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOG(("SpdySession::HandleSynStream uncompress failed\n"));
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
// todo populate cache. For now, just reject server push p3
|
|
|
|
self->GenerateRstStream(RST_REFUSED_STREAM, streamID);
|
2012-01-25 21:15:26 -08:00
|
|
|
self->ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::HandleSynReply(SpdySession *self)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SYN_REPLY,
|
|
|
|
"wrong control type");
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (self->mInputFrameDataSize < 8) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandleSynReply %p SYN REPLY too short data=%d",
|
2012-01-25 21:15:26 -08:00
|
|
|
self, self->mInputFrameDataSize));
|
2012-02-19 11:28:58 -08:00
|
|
|
// A framing error is a session wide error that cannot be recovered
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
2012-02-19 11:28:58 -08:00
|
|
|
// Uncompress the headers into mDecompressBuffer, leaving them in
|
|
|
|
// spdy format for the time being. Make certain to do this
|
|
|
|
// step before any error handling that might abort the stream but not
|
|
|
|
// the session becuase the session compression context will become
|
|
|
|
// inconsistent if all of the compressed data is not processed.
|
|
|
|
if (NS_FAILED(self->DownstreamUncompress(self->mInputFrameBuffer + 14,
|
|
|
|
self->mInputFrameDataSize - 6))) {
|
|
|
|
LOG(("SpdySession::HandleSynReply uncompress failed\n"));
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
PRUint32 streamID =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
|
|
|
|
self->mInputFrameDataStream = self->mStreamIDHash.Get(streamID);
|
|
|
|
if (!self->mInputFrameDataStream) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandleSynReply %p lookup streamID in syn_reply "
|
|
|
|
"0x%X failed. NextStreamID = 0x%x", self, streamID,
|
|
|
|
self->mNextStreamID));
|
|
|
|
if (streamID >= self->mNextStreamID)
|
|
|
|
self->GenerateRstStream(RST_INVALID_STREAM, streamID);
|
2012-02-19 11:28:58 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
self->ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2012-01-25 12:21:13 -08:00
|
|
|
|
2012-02-19 11:28:58 -08:00
|
|
|
nsresult rv = self->HandleSynReplyForValidStream();
|
|
|
|
if (rv == NS_ERROR_ILLEGAL_VALUE) {
|
|
|
|
LOG3(("SpdySession::HandleSynReply %p PROTOCOL_ERROR detected 0x%X\n",
|
|
|
|
self, streamID));
|
|
|
|
self->CleanupStream(self->mInputFrameDataStream, rv, RST_PROTOCOL_ERROR);
|
|
|
|
self->ResetDownstreamState();
|
|
|
|
rv = NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
2012-01-25 21:15:26 -08:00
|
|
|
|
2012-02-19 11:28:58 -08:00
|
|
|
// HandleSynReplyForValidStream() returns NS_ERROR_ILLEGAL_VALUE when the stream
|
|
|
|
// should be reset with a PROTOCOL_ERROR, NS_OK when the SYN_REPLY was
|
|
|
|
// fine, and any other error is fatal to the session.
|
|
|
|
nsresult
|
|
|
|
SpdySession::HandleSynReplyForValidStream()
|
|
|
|
{
|
|
|
|
if (mInputFrameDataStream->GetFullyOpen()) {
|
2011-12-13 07:55:50 -08:00
|
|
|
// "If an endpoint receives multiple SYN_REPLY frames for the same active
|
|
|
|
// stream ID, it must drop the stream, and send a RST_STREAM for the
|
|
|
|
// stream with the error PROTOCOL_ERROR."
|
|
|
|
//
|
2012-02-19 11:28:58 -08:00
|
|
|
// If the stream is open then just RST_STREAM and move on, otherwise
|
|
|
|
// abort the session
|
|
|
|
return mInputFrameDataStream->RecvdFin() ?
|
|
|
|
NS_ERROR_ALREADY_OPENED : NS_ERROR_ILLEGAL_VALUE;
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
2012-02-19 11:28:58 -08:00
|
|
|
mInputFrameDataStream->SetFullyOpen();
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-02-19 11:28:58 -08:00
|
|
|
mInputFrameDataLast = mInputFrameBuffer[4] & kFlag_Data_FIN;
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-02-19 11:28:58 -08:00
|
|
|
if (mInputFrameBuffer[4] & kFlag_Data_UNI) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SynReply had unidirectional flag set on it - nonsensical"));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
2012-02-19 11:28:58 -08:00
|
|
|
LOG3(("SpdySession::HandleSynReplyForValidStream %p SYN_REPLY for 0x%X "
|
|
|
|
"fin=%d",
|
|
|
|
this, mInputFrameDataStream->StreamID(), mInputFrameDataLast));
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE,
|
2012-02-19 11:28:58 -08:00
|
|
|
mInputFrameDataSize - 6);
|
|
|
|
if (mDecompressBufferUsed) {
|
2012-01-23 19:46:37 -08:00
|
|
|
PRUint32 ratio =
|
2012-02-19 11:28:58 -08:00
|
|
|
(mInputFrameDataSize - 6) * 100 / mDecompressBufferUsed;
|
2012-01-23 19:46:37 -08:00
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio);
|
|
|
|
}
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
// status and version are required.
|
|
|
|
nsDependentCSubstring status, version;
|
2012-02-19 11:28:58 -08:00
|
|
|
nsresult rv = FindHeader(NS_LITERAL_CSTRING("status"), status);
|
2011-12-13 07:55:50 -08:00
|
|
|
if (NS_FAILED(rv))
|
2012-02-19 11:28:58 -08:00
|
|
|
return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv;
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-02-19 11:28:58 -08:00
|
|
|
rv = FindHeader(NS_LITERAL_CSTRING("version"), version);
|
2011-12-13 07:55:50 -08:00
|
|
|
if (NS_FAILED(rv))
|
2012-02-19 11:28:58 -08:00
|
|
|
return (rv == NS_ERROR_NOT_AVAILABLE) ? NS_ERROR_ILLEGAL_VALUE : rv;
|
|
|
|
|
|
|
|
// The spdystream needs to see flattened http headers
|
|
|
|
// Uncompressed spdy format headers currently live in
|
|
|
|
// mDeccompressBuffer - convert that to HTTP format in
|
|
|
|
// mFlatHTTPResponseHeaders in ConvertHeaders()
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-02-19 11:28:58 -08:00
|
|
|
rv = ConvertHeaders(status, version);
|
2011-12-13 07:55:50 -08:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
2012-02-19 11:28:58 -08:00
|
|
|
mInputFrameDataStream->UpdateTransportReadEvents(mInputFrameDataSize);
|
2012-02-23 17:58:43 -08:00
|
|
|
mLastDataReadEpoch = mLastReadEpoch;
|
2012-02-19 11:28:58 -08:00
|
|
|
ChangeDownstreamState(PROCESSING_CONTROL_SYN_REPLY);
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::HandleRstStream(SpdySession *self)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_RST_STREAM,
|
|
|
|
"wrong control type");
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (self->mInputFrameDataSize != 8) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandleRstStream %p RST_STREAM wrong length data=%d",
|
2012-01-25 21:15:26 -08:00
|
|
|
self, self->mInputFrameDataSize));
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
PRUint8 flags = reinterpret_cast<PRUint8 *>(self->mInputFrameBuffer.get())[4];
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
PRUint32 streamID =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
self->mDownstreamRstReason =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[3]);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
LOG3(("SpdySession::HandleRstStream %p RST_STREAM Reason Code %u ID %x "
|
|
|
|
"flags %x", self, self->mDownstreamRstReason, streamID, flags));
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (flags != 0) {
|
|
|
|
LOG3(("SpdySession::HandleRstStream %p RST_STREAM with flags is illegal",
|
|
|
|
self));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
if (self->mDownstreamRstReason == RST_INVALID_STREAM ||
|
|
|
|
self->mDownstreamRstReason == RST_FLOW_CONTROL_ERROR) {
|
|
|
|
// basically just ignore this
|
2012-01-25 21:15:26 -08:00
|
|
|
self->ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
self->mInputFrameDataStream = self->mStreamIDHash.Get(streamID);
|
|
|
|
if (!self->mInputFrameDataStream) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandleRstStream %p lookup streamID for RST Frame "
|
|
|
|
"0x%X failed", self, streamID));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
2012-01-25 21:15:26 -08:00
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
self->ChangeDownstreamState(PROCESSING_CONTROL_RST_STREAM);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::HandleSettings(SpdySession *self)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_SETTINGS,
|
|
|
|
"wrong control type");
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (self->mInputFrameDataSize < 4) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandleSettings %p SETTINGS wrong length data=%d",
|
2012-01-25 21:15:26 -08:00
|
|
|
self, self->mInputFrameDataSize));
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint32 numEntries =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
// Ensure frame is large enough for supplied number of entries
|
|
|
|
// Each entry is 8 bytes, frame data is reduced by 4 to account for
|
|
|
|
// the NumEntries value.
|
2012-01-25 21:15:26 -08:00
|
|
|
if ((self->mInputFrameDataSize - 4) < (numEntries * 8)) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandleSettings %p SETTINGS wrong length data=%d",
|
2012-01-25 21:15:26 -08:00
|
|
|
self, self->mInputFrameDataSize));
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG3(("SpdySession::HandleSettings %p SETTINGS Control Frame with %d entries",
|
|
|
|
self, numEntries));
|
|
|
|
|
|
|
|
for (PRUint32 index = 0; index < numEntries; ++index) {
|
|
|
|
// To clarify the v2 spec:
|
|
|
|
// Each entry is a 24 bits of a little endian id
|
|
|
|
// followed by 8 bits of flags
|
|
|
|
// followed by a 32 bit big endian value
|
|
|
|
|
|
|
|
unsigned char *setting = reinterpret_cast<unsigned char *>
|
2012-01-25 21:15:26 -08:00
|
|
|
(self->mInputFrameBuffer.get()) + 12 + index * 8;
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
PRUint32 id = (setting[2] << 16) + (setting[1] << 8) + setting[0];
|
|
|
|
PRUint32 flags = setting[3];
|
|
|
|
PRUint32 value = PR_ntohl(reinterpret_cast<PRUint32 *>(setting)[1]);
|
|
|
|
|
|
|
|
LOG3(("Settings ID %d, Flags %X, Value %d", id, flags, value));
|
|
|
|
|
|
|
|
switch (id)
|
|
|
|
{
|
|
|
|
case SETTINGS_TYPE_UPLOAD_BW:
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_UL_BW, value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SETTINGS_TYPE_DOWNLOAD_BW:
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_DL_BW, value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SETTINGS_TYPE_RTT:
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RTT, value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SETTINGS_TYPE_MAX_CONCURRENT:
|
|
|
|
self->mMaxConcurrent = value;
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_MAX_STREAMS, value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SETTINGS_TYPE_CWND:
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_CWND, value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE:
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_RETRANS, value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SETTINGS_TYPE_INITIAL_WINDOW:
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_SETTINGS_IW, value >> 10);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
self->ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::HandleNoop(SpdySession *self)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_NOOP,
|
|
|
|
"wrong control type");
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (self->mInputFrameDataSize != 0) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandleNoop %p NOP had data %d",
|
2012-01-25 21:15:26 -08:00
|
|
|
self, self->mInputFrameDataSize));
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG3(("SpdySession::HandleNoop %p NOP.", self));
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
self->ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::HandlePing(SpdySession *self)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_PING,
|
|
|
|
"wrong control type");
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (self->mInputFrameDataSize != 4) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandlePing %p PING had wrong amount of data %d",
|
2012-01-25 21:15:26 -08:00
|
|
|
self, self->mInputFrameDataSize));
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint32 pingID =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
LOG3(("SpdySession::HandlePing %p PING ID 0x%X.", self, pingID));
|
|
|
|
|
|
|
|
if (pingID & 0x01) {
|
2012-02-23 17:58:43 -08:00
|
|
|
// presumably a reply to our timeout ping
|
2012-02-27 07:32:09 -08:00
|
|
|
self->ClearPing(true);
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
else {
|
2012-02-23 17:58:43 -08:00
|
|
|
// Servers initiate even numbered pings, go ahead and echo it back
|
2011-12-13 07:55:50 -08:00
|
|
|
self->GeneratePing(pingID);
|
|
|
|
}
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
self->ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::HandleGoAway(SpdySession *self)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_GOAWAY,
|
|
|
|
"wrong control type");
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (self->mInputFrameDataSize != 4) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandleGoAway %p GOAWAY had wrong amount of data %d",
|
2012-01-25 21:15:26 -08:00
|
|
|
self, self->mInputFrameDataSize));
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
self->mShouldGoAway = true;
|
|
|
|
self->mGoAwayID =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
|
2011-12-13 07:55:50 -08:00
|
|
|
self->mCleanShutdown = true;
|
|
|
|
|
|
|
|
LOG3(("SpdySession::HandleGoAway %p GOAWAY Last-Good-ID 0x%X.",
|
|
|
|
self, self->mGoAwayID));
|
2012-01-25 21:15:26 -08:00
|
|
|
self->ResumeRecv();
|
|
|
|
self->ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::HandleHeaders(SpdySession *self)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_HEADERS,
|
|
|
|
"wrong control type");
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (self->mInputFrameDataSize < 10) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandleHeaders %p HEADERS had wrong amount of data %d",
|
2012-01-25 21:15:26 -08:00
|
|
|
self, self->mInputFrameDataSize));
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint32 streamID =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohl(reinterpret_cast<PRUint32 *>(self->mInputFrameBuffer.get())[2]);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
// this is actually not legal in the HTTP mapping of SPDY. All
|
|
|
|
// headers are in the syn or syn reply. Log and ignore it.
|
|
|
|
|
2012-01-25 12:21:13 -08:00
|
|
|
// in v3 this will be legal and we must remember to note
|
|
|
|
// NS_NET_STATUS_RECEIVING_FROM from it
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::HandleHeaders %p HEADERS for Stream 0x%X. "
|
|
|
|
"They are ignored in the HTTP/SPDY mapping.",
|
|
|
|
self, streamID));
|
2012-02-23 17:58:43 -08:00
|
|
|
self->mLastDataReadEpoch = self->mLastReadEpoch;
|
2012-01-25 21:15:26 -08:00
|
|
|
self->ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::HandleWindowUpdate(SpdySession *self)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(self->mFrameControlType == CONTROL_TYPE_WINDOW_UPDATE,
|
|
|
|
"wrong control type");
|
|
|
|
LOG3(("SpdySession::HandleWindowUpdate %p WINDOW UPDATE was "
|
|
|
|
"received. WINDOW UPDATE is no longer defined in v2. Ignoring.",
|
|
|
|
self));
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
self->ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsAHttpTransaction. It is expected that nsHttpConnection is the caller
|
|
|
|
// of these methods
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::OnTransportStatus(nsITransport* aTransport,
|
|
|
|
nsresult aStatus,
|
|
|
|
PRUint64 aProgress)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
2012-01-25 12:21:13 -08:00
|
|
|
switch (aStatus) {
|
|
|
|
// These should appear only once, deliver to the first
|
|
|
|
// transaction on the session.
|
|
|
|
case NS_NET_STATUS_RESOLVING_HOST:
|
|
|
|
case NS_NET_STATUS_RESOLVED_HOST:
|
|
|
|
case NS_NET_STATUS_CONNECTING_TO:
|
|
|
|
case NS_NET_STATUS_CONNECTED_TO:
|
|
|
|
{
|
|
|
|
SpdyStream *target = mStreamIDHash.Get(1);
|
|
|
|
if (target)
|
|
|
|
target->Transaction()->OnTransportStatus(aTransport, aStatus, aProgress);
|
|
|
|
break;
|
|
|
|
}
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 12:21:13 -08:00
|
|
|
default:
|
|
|
|
// The other transport events are ignored here because there is no good
|
|
|
|
// way to map them to the right transaction in spdy. Instead, the events
|
|
|
|
// are generated again from the spdy code and passed directly to the
|
|
|
|
// correct transaction.
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 12:21:13 -08:00
|
|
|
// NS_NET_STATUS_SENDING_TO:
|
|
|
|
// This is generated by the socket transport when (part) of
|
|
|
|
// a transaction is written out
|
|
|
|
//
|
|
|
|
// There is no good way to map it to the right transaction in spdy,
|
|
|
|
// so it is ignored here and generated separately when the SYN_STREAM
|
|
|
|
// is sent from SpdyStream::TransmitFrame
|
|
|
|
|
|
|
|
// NS_NET_STATUS_WAITING_FOR:
|
|
|
|
// Created by nsHttpConnection when the request has been totally sent.
|
|
|
|
// There is no good way to map it to the right transaction in spdy,
|
|
|
|
// so it is ignored here and generated separately when the same
|
|
|
|
// condition is complete in SpdyStream when there is no more
|
|
|
|
// request body left to be transmitted.
|
|
|
|
|
|
|
|
// NS_NET_STATUS_RECEIVING_FROM
|
|
|
|
// Generated in spdysession whenever we read a data frame or a syn_reply
|
|
|
|
// that can be attributed to a particular stream/transaction
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// ReadSegments() is used to write data to the network. Generally, HTTP
|
|
|
|
// request data is pulled from the approriate transaction and
|
|
|
|
// converted to SPDY data. Sometimes control data like window-update are
|
|
|
|
// generated instead.
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::ReadSegments(nsAHttpSegmentReader *reader,
|
|
|
|
PRUint32 count,
|
|
|
|
PRUint32 *countRead)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
2012-05-16 06:05:34 -07:00
|
|
|
NS_ABORT_IF_FALSE(!mSegmentReader || !reader || (mSegmentReader == reader),
|
|
|
|
"Inconsistent Write Function Callback");
|
|
|
|
|
|
|
|
if (reader)
|
|
|
|
mSegmentReader = reader;
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
nsresult rv;
|
|
|
|
*countRead = 0;
|
|
|
|
|
|
|
|
// First priority goes to frames that were writing to the network but were
|
|
|
|
// blocked part way through. Then to frames that have no streams (e.g ping
|
|
|
|
// reply) and then third to streams marked urgent (generally they have
|
|
|
|
// window updates), and finally to streams generally
|
|
|
|
// ready to send data frames (http requests).
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
LOG3(("SpdySession::ReadSegments %p", this));
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
SpdyStream *stream;
|
|
|
|
|
|
|
|
stream = static_cast<SpdyStream *>(mUrgentForWrite.PopFront());
|
2011-12-13 07:55:50 -08:00
|
|
|
if (!stream)
|
|
|
|
stream = static_cast<SpdyStream *>(mReadyForWrite.PopFront());
|
|
|
|
if (!stream) {
|
|
|
|
LOG3(("SpdySession %p could not identify a stream to write; suspending.",
|
|
|
|
this));
|
|
|
|
FlushOutputQueue();
|
2012-01-25 21:15:26 -08:00
|
|
|
SetWriteCallbacks();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG3(("SpdySession %p will write from SpdyStream %p", this, stream));
|
|
|
|
|
|
|
|
rv = stream->ReadSegments(this, count, countRead);
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
// Not every permutation of stream->ReadSegents produces data (and therefore
|
|
|
|
// tries to flush the output queue) - SENDING_FIN_STREAM can be an example
|
|
|
|
// of that. But we might still have old data buffered that would be good
|
|
|
|
// to flush.
|
2011-12-13 07:55:50 -08:00
|
|
|
FlushOutputQueue();
|
|
|
|
|
|
|
|
if (stream->RequestBlockedOnRead()) {
|
|
|
|
|
|
|
|
// We are blocked waiting for input - either more http headers or
|
|
|
|
// any request body data. When more data from the request stream
|
|
|
|
// becomes available the httptransaction will call conn->ResumeSend().
|
|
|
|
|
|
|
|
LOG3(("SpdySession::ReadSegments %p dealing with block on read", this));
|
|
|
|
|
|
|
|
// call readsegments again if there are other streams ready
|
|
|
|
// to run in this session
|
2012-01-25 21:15:26 -08:00
|
|
|
if (GetWriteQueueSize())
|
2011-12-13 07:55:50 -08:00
|
|
|
rv = NS_OK;
|
|
|
|
else
|
|
|
|
rv = NS_BASE_STREAM_WOULD_BLOCK;
|
2012-01-25 21:15:26 -08:00
|
|
|
SetWriteCallbacks();
|
2011-12-13 07:55:50 -08:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOG3(("SpdySession::ReadSegments %p returning FAIL code %X",
|
|
|
|
this, rv));
|
2012-05-16 06:05:34 -07:00
|
|
|
if (rv != NS_BASE_STREAM_WOULD_BLOCK)
|
|
|
|
CleanupStream(stream, rv, RST_CANCEL);
|
2011-12-13 07:55:50 -08:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*countRead > 0) {
|
|
|
|
LOG3(("SpdySession::ReadSegments %p stream=%p generated end of frame %d",
|
|
|
|
this, stream, *countRead));
|
|
|
|
mReadyForWrite.Push(stream);
|
2012-01-25 21:15:26 -08:00
|
|
|
SetWriteCallbacks();
|
2011-12-13 07:55:50 -08:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG3(("SpdySession::ReadSegments %p stream=%p stream send complete",
|
|
|
|
this, stream));
|
|
|
|
|
|
|
|
/* we now want to recv data */
|
2012-01-25 21:15:26 -08:00
|
|
|
ResumeRecv();
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
// call readsegments again if there are other streams ready
|
|
|
|
// to go in this session
|
2012-01-25 21:15:26 -08:00
|
|
|
SetWriteCallbacks();
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteSegments() is used to read data off the socket. Generally this is
|
|
|
|
// just the SPDY frame header and from there the appropriate SPDYStream
|
|
|
|
// is identified from the Stream-ID. The http transaction associated with
|
|
|
|
// that read then pulls in the data directly, which it will feed to
|
|
|
|
// OnWriteSegment(). That function will gateway it into http and feed
|
|
|
|
// it to the appropriate transaction.
|
|
|
|
|
2012-02-23 17:58:43 -08:00
|
|
|
// we call writer->OnWriteSegment via NetworkRead() to get a spdy header..
|
|
|
|
// and decide if it is data or control.. if it is control, just deal with it.
|
2011-12-13 07:55:50 -08:00
|
|
|
// if it is data, identify the spdy stream
|
|
|
|
// call stream->WriteSegemnts which can call this::OnWriteSegment to get the
|
|
|
|
// data. It always gets full frames if they are part of the stream
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::WriteSegments(nsAHttpSegmentWriter *writer,
|
|
|
|
PRUint32 count,
|
|
|
|
PRUint32 *countWritten)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
*countWritten = 0;
|
|
|
|
|
|
|
|
if (mClosed)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
SetWriteCallbacks();
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
// We buffer all control frames and act on them in this layer.
|
|
|
|
// We buffer the first 8 bytes of data frames (the header) but
|
|
|
|
// the actual data is passed through unprocessed.
|
|
|
|
|
|
|
|
if (mDownstreamState == BUFFERING_FRAME_HEADER) {
|
|
|
|
// The first 8 bytes of every frame is header information that
|
|
|
|
// we are going to want to strip before passing to http. That is
|
|
|
|
// true of both control and data packets.
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
NS_ABORT_IF_FALSE(mInputFrameBufferUsed < 8,
|
2011-12-13 07:55:50 -08:00
|
|
|
"Frame Buffer Used Too Large for State");
|
|
|
|
|
2012-02-23 17:58:43 -08:00
|
|
|
rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed,
|
|
|
|
8 - mInputFrameBufferUsed, countWritten);
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOG3(("SpdySession %p buffering frame header read failure %x\n",
|
|
|
|
this, rv));
|
|
|
|
// maybe just blocked reading from network
|
|
|
|
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
|
2012-01-25 21:15:26 -08:00
|
|
|
ResumeRecv();
|
2011-12-13 07:55:50 -08:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
LogIO(this, nsnull, "Reading Frame Header",
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameBuffer + mInputFrameBufferUsed, *countWritten);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameBufferUsed += *countWritten;
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (mInputFrameBufferUsed < 8)
|
2011-12-13 07:55:50 -08:00
|
|
|
{
|
|
|
|
LOG3(("SpdySession::WriteSegments %p "
|
|
|
|
"BUFFERING FRAME HEADER incomplete size=%d",
|
2012-01-25 21:15:26 -08:00
|
|
|
this, mInputFrameBufferUsed));
|
2011-12-13 07:55:50 -08:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For both control and data frames the second 32 bit word of the header
|
|
|
|
// is 8-flags, 24-length. (network byte order)
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameDataSize =
|
|
|
|
PR_ntohl(reinterpret_cast<PRUint32 *>(mInputFrameBuffer.get())[1]);
|
|
|
|
mInputFrameDataSize &= 0x00ffffff;
|
|
|
|
mInputFrameDataRead = 0;
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (mInputFrameBuffer[0] & kFlag_Control) {
|
|
|
|
EnsureBuffer(mInputFrameBuffer, mInputFrameDataSize + 8, 8,
|
|
|
|
mInputFrameBufferSize);
|
2011-12-13 07:55:50 -08:00
|
|
|
ChangeDownstreamState(BUFFERING_CONTROL_FRAME);
|
|
|
|
|
|
|
|
// The first 32 bit word of the header is
|
|
|
|
// 1 ctrl - 15 version - 16 type
|
|
|
|
PRUint16 version =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohs(reinterpret_cast<PRUint16 *>(mInputFrameBuffer.get())[0]);
|
2011-12-13 07:55:50 -08:00
|
|
|
version &= 0x7fff;
|
|
|
|
|
|
|
|
mFrameControlType =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohs(reinterpret_cast<PRUint16 *>(mInputFrameBuffer.get())[1]);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
LOG3(("SpdySession::WriteSegments %p - Control Frame Identified "
|
|
|
|
"type %d version %d data len %d",
|
2012-01-25 21:15:26 -08:00
|
|
|
this, mFrameControlType, version, mInputFrameDataSize));
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
if (mFrameControlType >= CONTROL_TYPE_LAST ||
|
|
|
|
mFrameControlType <= CONTROL_TYPE_FIRST)
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
|
|
|
|
// The protocol document says this value must be 1 even though this
|
|
|
|
// is known as version 2.. Testing interop indicates that is a typo
|
|
|
|
// in the protocol document
|
|
|
|
if (version != 2) {
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ChangeDownstreamState(PROCESSING_DATA_FRAME);
|
|
|
|
|
|
|
|
PRUint32 streamID =
|
2012-01-25 21:15:26 -08:00
|
|
|
PR_ntohl(reinterpret_cast<PRUint32 *>(mInputFrameBuffer.get())[0]);
|
|
|
|
mInputFrameDataStream = mStreamIDHash.Get(streamID);
|
|
|
|
if (!mInputFrameDataStream) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("SpdySession::WriteSegments %p lookup streamID 0x%X failed. "
|
|
|
|
"Next = 0x%x", this, streamID, mNextStreamID));
|
|
|
|
if (streamID >= mNextStreamID)
|
|
|
|
GenerateRstStream(RST_INVALID_STREAM, streamID);
|
2012-01-25 21:15:26 -08:00
|
|
|
ChangeDownstreamState(DISCARDING_DATA_FRAME);
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameDataLast = (mInputFrameBuffer[4] & kFlag_Data_FIN);
|
|
|
|
Telemetry::Accumulate(Telemetry::SPDY_CHUNK_RECVD,
|
|
|
|
mInputFrameDataSize >> 10);
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("Start Processing Data Frame. "
|
|
|
|
"Session=%p Stream ID 0x%x Stream Ptr %p Fin=%d Len=%d",
|
2012-01-25 21:15:26 -08:00
|
|
|
this, streamID, mInputFrameDataStream, mInputFrameDataLast,
|
|
|
|
mInputFrameDataSize));
|
2012-02-23 17:58:43 -08:00
|
|
|
mLastDataReadEpoch = mLastReadEpoch;
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (mInputFrameBuffer[4] & kFlag_Data_ZLIB) {
|
2011-12-13 07:55:50 -08:00
|
|
|
LOG3(("Data flag has ZLIB flag set which is not valid >=2 spdy"));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mDownstreamState == PROCESSING_CONTROL_RST_STREAM) {
|
|
|
|
if (mDownstreamRstReason == RST_REFUSED_STREAM)
|
|
|
|
rv = NS_ERROR_NET_RESET; //we can retry this 100% safely
|
|
|
|
else if (mDownstreamRstReason == RST_CANCEL ||
|
|
|
|
mDownstreamRstReason == RST_PROTOCOL_ERROR ||
|
|
|
|
mDownstreamRstReason == RST_INTERNAL_ERROR ||
|
|
|
|
mDownstreamRstReason == RST_UNSUPPORTED_VERSION)
|
|
|
|
rv = NS_ERROR_NET_INTERRUPT;
|
|
|
|
else
|
|
|
|
rv = NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
|
|
|
|
if (mDownstreamRstReason != RST_REFUSED_STREAM &&
|
|
|
|
mDownstreamRstReason != RST_CANCEL)
|
|
|
|
mShouldGoAway = true;
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
// mInputFrameDataStream is reset by ChangeDownstreamState
|
|
|
|
SpdyStream *stream = mInputFrameDataStream;
|
|
|
|
ResetDownstreamState();
|
2012-02-19 11:28:58 -08:00
|
|
|
CleanupStream(stream, rv, RST_CANCEL);
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mDownstreamState == PROCESSING_DATA_FRAME ||
|
|
|
|
mDownstreamState == PROCESSING_CONTROL_SYN_REPLY) {
|
|
|
|
|
|
|
|
mSegmentWriter = writer;
|
2012-01-25 21:15:26 -08:00
|
|
|
rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
|
2011-12-13 07:55:50 -08:00
|
|
|
mSegmentWriter = nsnull;
|
|
|
|
|
2012-02-23 17:58:43 -08:00
|
|
|
mLastDataReadEpoch = mLastReadEpoch;
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
if (rv == NS_BASE_STREAM_CLOSED) {
|
|
|
|
// This will happen when the transaction figures out it is EOF, generally
|
|
|
|
// due to a content-length match being made
|
2012-01-25 21:15:26 -08:00
|
|
|
SpdyStream *stream = mInputFrameDataStream;
|
|
|
|
if (mInputFrameDataRead == mInputFrameDataSize)
|
|
|
|
ResetDownstreamState();
|
2012-02-19 11:28:58 -08:00
|
|
|
CleanupStream(stream, NS_OK, RST_CANCEL);
|
2011-12-13 07:55:50 -08:00
|
|
|
NS_ABORT_IF_FALSE(!mNeedsCleanup, "double cleanup out of data frame");
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mNeedsCleanup) {
|
2012-02-19 11:28:58 -08:00
|
|
|
CleanupStream(mNeedsCleanup, NS_OK, RST_CANCEL);
|
2011-12-13 07:55:50 -08:00
|
|
|
mNeedsCleanup = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In v3 this is where we would generate a window update
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (mDownstreamState == DISCARDING_DATA_FRAME) {
|
2011-12-13 07:55:50 -08:00
|
|
|
char trash[4096];
|
2012-01-25 21:15:26 -08:00
|
|
|
PRUint32 count = NS_MIN(4096U, mInputFrameDataSize - mInputFrameDataRead);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
if (!count) {
|
2012-01-25 21:15:26 -08:00
|
|
|
ResetDownstreamState();
|
|
|
|
ResumeRecv();
|
|
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
2012-02-23 17:58:43 -08:00
|
|
|
rv = NetworkRead(writer, trash, count, countWritten);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOG3(("SpdySession %p discard frame read failure %x\n", this, rv));
|
|
|
|
// maybe just blocked reading from network
|
|
|
|
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
|
2012-01-25 21:15:26 -08:00
|
|
|
ResumeRecv();
|
2011-12-13 07:55:50 -08:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
LogIO(this, nsnull, "Discarding Frame", trash, *countWritten);
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameDataRead += *countWritten;
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (mInputFrameDataRead == mInputFrameDataSize)
|
|
|
|
ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (mDownstreamState != BUFFERING_CONTROL_FRAME) {
|
|
|
|
// this cannot happen
|
|
|
|
NS_ABORT_IF_FALSE(false, "Not in Bufering Control Frame State");
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ABORT_IF_FALSE(mInputFrameBufferUsed == 8,
|
2011-12-13 07:55:50 -08:00
|
|
|
"Frame Buffer Header Not Present");
|
|
|
|
|
2012-02-23 17:58:43 -08:00
|
|
|
rv = NetworkRead(writer, mInputFrameBuffer + 8 + mInputFrameDataRead,
|
|
|
|
mInputFrameDataSize - mInputFrameDataRead, countWritten);
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOG3(("SpdySession %p buffering control frame read failure %x\n",
|
|
|
|
this, rv));
|
|
|
|
// maybe just blocked reading from network
|
|
|
|
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
|
2012-01-25 21:15:26 -08:00
|
|
|
ResumeRecv();
|
2011-12-13 07:55:50 -08:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
LogIO(this, nsnull, "Reading Control Frame",
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameBuffer + 8 + mInputFrameDataRead, *countWritten);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameDataRead += *countWritten;
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (mInputFrameDataRead != mInputFrameDataSize)
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
// This check is actually redundant, the control type was previously
|
|
|
|
// checked to make sure it was in range, but we will check it again
|
|
|
|
// at time of use to make sure a regression doesn't creep in.
|
|
|
|
if (mFrameControlType >= CONTROL_TYPE_LAST ||
|
|
|
|
mFrameControlType <= CONTROL_TYPE_FIRST)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(false, "control type out of range");
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
2011-12-13 07:55:50 -08:00
|
|
|
rv = sControlFunctions[mFrameControlType](this);
|
|
|
|
|
|
|
|
NS_ABORT_IF_FALSE(NS_FAILED(rv) ||
|
|
|
|
mDownstreamState != BUFFERING_CONTROL_FRAME,
|
|
|
|
"Control Handler returned OK but did not change state");
|
|
|
|
|
|
|
|
if (mShouldGoAway && !mStreamTransactionHash.Count())
|
|
|
|
Close(NS_OK);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::Close(nsresult aReason)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
|
|
|
if (mClosed)
|
|
|
|
return;
|
|
|
|
|
|
|
|
LOG3(("SpdySession::Close %p %X", this, aReason));
|
|
|
|
|
|
|
|
mClosed = true;
|
2012-01-25 21:15:26 -08:00
|
|
|
mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
|
|
|
|
if (NS_SUCCEEDED(aReason))
|
|
|
|
GenerateGoAway();
|
2011-12-13 07:55:50 -08:00
|
|
|
mConnection = nsnull;
|
2012-01-25 21:15:26 -08:00
|
|
|
mSegmentReader = nsnull;
|
|
|
|
mSegmentWriter = nsnull;
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::CloseTransaction(nsAHttpTransaction *aTransaction,
|
|
|
|
nsresult aResult)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
LOG3(("SpdySession::CloseTransaction %p %p %x", this, aTransaction, aResult));
|
|
|
|
|
|
|
|
// Generally this arrives as a cancel event from the connection manager.
|
|
|
|
|
|
|
|
// need to find the stream and call CleanupStream() on it.
|
|
|
|
SpdyStream *stream = mStreamTransactionHash.Get(aTransaction);
|
|
|
|
if (!stream) {
|
|
|
|
LOG3(("SpdySession::CloseTransaction %p %p %x - not found.",
|
|
|
|
this, aTransaction, aResult));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
LOG3(("SpdySession::CloseTranscation probably a cancel. "
|
|
|
|
"this=%p, trans=%p, result=%x, streamID=0x%X stream=%p",
|
|
|
|
this, aTransaction, aResult, stream->StreamID(), stream));
|
2012-02-19 11:28:58 -08:00
|
|
|
CleanupStream(stream, aResult, RST_CANCEL);
|
2012-01-25 21:15:26 -08:00
|
|
|
ResumeRecv();
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsAHttpSegmentReader
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::OnReadSegment(const char *buf,
|
|
|
|
PRUint32 count,
|
|
|
|
PRUint32 *countRead)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
// If we can release old queued data then we can try and write the new
|
|
|
|
// data directly to the network without using the output queue at all
|
|
|
|
if (mOutputQueueUsed)
|
|
|
|
FlushOutputQueue();
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (!mOutputQueueUsed && mSegmentReader) {
|
2011-12-13 07:55:50 -08:00
|
|
|
// try and write directly without output queue
|
|
|
|
rv = mSegmentReader->OnReadSegment(buf, count, countRead);
|
2012-01-25 21:15:26 -08:00
|
|
|
|
|
|
|
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
|
|
|
|
*countRead = 0;
|
|
|
|
else if (NS_FAILED(rv))
|
2011-12-13 07:55:50 -08:00
|
|
|
return rv;
|
2012-01-25 21:15:26 -08:00
|
|
|
|
|
|
|
if (*countRead < count) {
|
|
|
|
PRUint32 required = count - *countRead;
|
|
|
|
// assuming a commitment() happened, this ensurebuffer is a nop
|
|
|
|
// but just in case the queuesize is too small for the required data
|
|
|
|
// call ensurebuffer().
|
|
|
|
EnsureBuffer(mOutputQueueBuffer, required, 0, mOutputQueueSize);
|
|
|
|
memcpy(mOutputQueueBuffer.get(), buf + *countRead, required);
|
|
|
|
mOutputQueueUsed = required;
|
|
|
|
}
|
|
|
|
|
|
|
|
*countRead = count;
|
|
|
|
return NS_OK;
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
// At this point we are going to buffer the new data in the output
|
|
|
|
// queue if it fits. By coalescing multiple small submissions into one larger
|
2012-01-25 21:15:26 -08:00
|
|
|
// buffer we can get larger writes out to the network later on.
|
|
|
|
|
|
|
|
// This routine should not be allowed to fill up the output queue
|
|
|
|
// all on its own - at least kQueueReserved bytes are always left
|
2012-01-25 21:15:26 -08:00
|
|
|
// for other routines to use - but this is an all-or-nothing function,
|
|
|
|
// so if it will not all fit just return WOULD_BLOCK
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if ((mOutputQueueUsed + count) > (mOutputQueueSize - kQueueReserved))
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
|
|
|
|
|
|
memcpy(mOutputQueueBuffer.get() + mOutputQueueUsed, buf, count);
|
|
|
|
mOutputQueueUsed += count;
|
|
|
|
*countRead = count;
|
|
|
|
|
|
|
|
FlushOutputQueue();
|
2012-01-25 21:15:26 -08:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::CommitToSegmentSize(PRUint32 count)
|
|
|
|
{
|
|
|
|
if (mOutputQueueUsed)
|
|
|
|
FlushOutputQueue();
|
|
|
|
|
|
|
|
// would there be enough room to buffer this if needed?
|
|
|
|
if ((mOutputQueueUsed + count) <= (mOutputQueueSize - kQueueReserved))
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// if we are using part of our buffers already, try again later
|
|
|
|
if (mOutputQueueUsed)
|
|
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
|
|
|
|
|
|
// not enough room to buffer even with completely empty buffers.
|
|
|
|
// normal frames are max 4kb, so the only case this can really happen
|
|
|
|
// is a SYN_STREAM with technically unbounded headers. That is highly
|
|
|
|
// unlikely, but possible. Create enough room for it because the buffers
|
|
|
|
// will be necessary - SSL does not absorb writes of very large sizes
|
|
|
|
// in single sends.
|
|
|
|
|
|
|
|
EnsureBuffer(mOutputQueueBuffer, count + kQueueReserved, 0, mOutputQueueSize);
|
|
|
|
|
|
|
|
NS_ABORT_IF_FALSE((mOutputQueueUsed + count) <=
|
|
|
|
(mOutputQueueSize - kQueueReserved),
|
|
|
|
"buffer not as large as expected");
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// nsAHttpSegmentWriter
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::OnWriteSegment(char *buf,
|
|
|
|
PRUint32 count,
|
|
|
|
PRUint32 *countWritten)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
nsresult rv;
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (!mSegmentWriter) {
|
|
|
|
// the only way this could happen would be if Close() were called on the
|
|
|
|
// stack with WriteSegments()
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
if (mDownstreamState == PROCESSING_DATA_FRAME) {
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
if (mInputFrameDataLast &&
|
|
|
|
mInputFrameDataRead == mInputFrameDataSize) {
|
2011-12-13 07:55:50 -08:00
|
|
|
// This will result in Close() being called
|
2012-01-25 21:15:26 -08:00
|
|
|
NS_ABORT_IF_FALSE(!mNeedsCleanup, "mNeedsCleanup unexpectedly set");
|
|
|
|
mNeedsCleanup = mInputFrameDataStream;
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
LOG3(("SpdySession::OnWriteSegment %p - recorded downstream fin of "
|
2012-01-25 21:15:26 -08:00
|
|
|
"stream %p 0x%X", this, mInputFrameDataStream,
|
|
|
|
mInputFrameDataStream->StreamID()));
|
2011-12-13 07:55:50 -08:00
|
|
|
*countWritten = 0;
|
2012-01-25 21:15:26 -08:00
|
|
|
ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_BASE_STREAM_CLOSED;
|
|
|
|
}
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
count = NS_MIN(count, mInputFrameDataSize - mInputFrameDataRead);
|
2012-02-23 17:58:43 -08:00
|
|
|
rv = NetworkRead(mSegmentWriter, buf, count, countWritten);
|
2011-12-13 07:55:50 -08:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
LogIO(this, mInputFrameDataStream, "Reading Data Frame",
|
|
|
|
buf, *countWritten);
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameDataRead += *countWritten;
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameDataStream->UpdateTransportReadEvents(*countWritten);
|
|
|
|
if ((mInputFrameDataRead == mInputFrameDataSize) && !mInputFrameDataLast)
|
|
|
|
ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mDownstreamState == PROCESSING_CONTROL_SYN_REPLY) {
|
|
|
|
|
|
|
|
if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
|
2012-01-25 21:15:26 -08:00
|
|
|
mInputFrameDataLast) {
|
2011-12-13 07:55:50 -08:00
|
|
|
*countWritten = 0;
|
2012-01-25 21:15:26 -08:00
|
|
|
ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_BASE_STREAM_CLOSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = NS_MIN(count,
|
|
|
|
mFlatHTTPResponseHeaders.Length() -
|
|
|
|
mFlatHTTPResponseHeadersOut);
|
|
|
|
memcpy(buf,
|
|
|
|
mFlatHTTPResponseHeaders.get() + mFlatHTTPResponseHeadersOut,
|
|
|
|
count);
|
|
|
|
mFlatHTTPResponseHeadersOut += count;
|
|
|
|
*countWritten = count;
|
|
|
|
|
|
|
|
if (mFlatHTTPResponseHeaders.Length() == mFlatHTTPResponseHeadersOut &&
|
2012-01-25 21:15:26 -08:00
|
|
|
!mInputFrameDataLast)
|
|
|
|
ResetDownstreamState();
|
2011-12-13 07:55:50 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Modified methods of nsAHttpConnection
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
nsresult
|
2012-01-25 21:15:26 -08:00
|
|
|
SpdySession::ResumeSend()
|
2011-12-13 07:55:50 -08:00
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
2012-01-25 21:15:26 -08:00
|
|
|
LOG3(("SpdySession::ResumeSend %p", this));
|
2011-12-13 07:55:50 -08:00
|
|
|
|
|
|
|
if (!mConnection)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
return mConnection->ResumeSend();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::TransactionHasDataToWrite(nsAHttpTransaction *caller)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
LOG3(("SpdySession::TransactionHasDataToWrite %p trans=%p", this, caller));
|
|
|
|
|
|
|
|
// a trapped signal from the http transaction to the connection that
|
|
|
|
// it is no longer blocked on read.
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
SpdyStream *stream = mStreamTransactionHash.Get(caller);
|
2012-01-25 21:15:26 -08:00
|
|
|
if (!stream) {
|
|
|
|
LOG3(("SpdySession::TransactionHasDataToWrite %p caller %p not found",
|
|
|
|
this, caller));
|
|
|
|
return;
|
|
|
|
}
|
2011-12-13 07:55:50 -08:00
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
LOG3(("SpdySession::TransactionHasDataToWrite %p ID is %x",
|
|
|
|
this, stream->StreamID()));
|
|
|
|
|
|
|
|
mReadyForWrite.Push(stream);
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
void
|
|
|
|
SpdySession::TransactionHasDataToWrite(SpdyStream *stream)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
LOG3(("SpdySession::TransactionHasDataToWrite %p stream=%p ID=%x",
|
|
|
|
this, stream, stream->StreamID()));
|
|
|
|
|
|
|
|
mReadyForWrite.Push(stream);
|
|
|
|
SetWriteCallbacks();
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
nsresult
|
2012-01-25 21:15:26 -08:00
|
|
|
SpdySession::ResumeRecv()
|
2011-12-13 07:55:50 -08:00
|
|
|
{
|
|
|
|
if (!mConnection)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2012-01-25 21:15:26 -08:00
|
|
|
return mConnection->ResumeRecv();
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
SpdySession::IsPersistent()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::TakeTransport(nsISocketTransport **,
|
|
|
|
nsIAsyncInputStream **,
|
|
|
|
nsIAsyncOutputStream **)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(false, "TakeTransport of SpdySession");
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsHttpConnection *
|
|
|
|
SpdySession::TakeHttpConnection()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(false, "TakeHttpConnection of SpdySession");
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsISocketTransport *
|
|
|
|
SpdySession::Transport()
|
|
|
|
{
|
2012-01-25 21:15:26 -08:00
|
|
|
if (!mConnection)
|
|
|
|
return nsnull;
|
|
|
|
return mConnection->Transport();
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
2012-03-22 16:39:31 -07:00
|
|
|
PRUint32
|
|
|
|
SpdySession::CancelPipeline(nsresult reason)
|
|
|
|
{
|
|
|
|
// we don't pipeline inside spdy, so this isn't an issue
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAHttpTransaction::Classifier
|
|
|
|
SpdySession::Classification()
|
|
|
|
{
|
|
|
|
if (!mConnection)
|
|
|
|
return nsAHttpTransaction::CLASS_GENERAL;
|
|
|
|
return mConnection->Classification();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::Classify(nsAHttpTransaction::Classifier newclass)
|
|
|
|
{
|
|
|
|
if (!mConnection)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mConnection->Classify(newclass);
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// unused methods of nsAHttpTransaction
|
|
|
|
// We can be sure of this because SpdySession is only constructed in
|
|
|
|
// nsHttpConnection and is never passed out of that object
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::SetConnection(nsAHttpConnection *)
|
|
|
|
{
|
|
|
|
// This is unexpected
|
|
|
|
NS_ABORT_IF_FALSE(false, "SpdySession::SetConnection()");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::GetSecurityCallbacks(nsIInterfaceRequestor **,
|
|
|
|
nsIEventTarget **)
|
|
|
|
{
|
|
|
|
// This is unexpected
|
|
|
|
NS_ABORT_IF_FALSE(false, "SpdySession::GetSecurityCallbacks()");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::SetSSLConnectFailed()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(false, "SpdySession::SetSSLConnectFailed()");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
SpdySession::IsDone()
|
|
|
|
{
|
2012-03-22 16:39:31 -07:00
|
|
|
return !mStreamTransactionHash.Count();
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::Status()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(false, "SpdySession::Status()");
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
2012-03-22 16:39:31 -07:00
|
|
|
PRUint8
|
|
|
|
SpdySession::Caps()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(false, "SpdySession::Caps()");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
PRUint32
|
|
|
|
SpdySession::Available()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(false, "SpdySession::Available()");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsHttpRequestHead *
|
|
|
|
SpdySession::RequestHead()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
NS_ABORT_IF_FALSE(false,
|
|
|
|
"SpdySession::RequestHead() "
|
|
|
|
"should not be called after SPDY is setup");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint32
|
|
|
|
SpdySession::Http1xTransactionCount()
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-15 13:41:18 -08:00
|
|
|
// used as an enumerator by TakeSubTransactions()
|
|
|
|
static PLDHashOperator
|
|
|
|
TakeStream(nsAHttpTransaction *key,
|
|
|
|
nsAutoPtr<SpdyStream> &stream,
|
|
|
|
void *closure)
|
|
|
|
{
|
|
|
|
nsTArray<nsRefPtr<nsAHttpTransaction> > *list =
|
|
|
|
static_cast<nsTArray<nsRefPtr<nsAHttpTransaction> > *>(closure);
|
|
|
|
|
|
|
|
list->AppendElement(key);
|
|
|
|
|
|
|
|
// removing the stream from the hash will delete the stream
|
|
|
|
// and drop the transaction reference the hash held
|
|
|
|
return PL_DHASH_REMOVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::TakeSubTransactions(
|
|
|
|
nsTArray<nsRefPtr<nsAHttpTransaction> > &outTransactions)
|
|
|
|
{
|
|
|
|
// Generally this cannot be done with spdy as transactions are
|
|
|
|
// started right away.
|
|
|
|
|
|
|
|
LOG3(("SpdySession::TakeSubTransactions %p\n", this));
|
|
|
|
|
|
|
|
if (mConcurrentHighWater > 0)
|
|
|
|
return NS_ERROR_ALREADY_OPENED;
|
|
|
|
|
|
|
|
LOG3((" taking %d\n", mStreamTransactionHash.Count()));
|
|
|
|
|
|
|
|
mStreamTransactionHash.Enumerate(TakeStream, &outTransactions);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2012-03-22 16:39:31 -07:00
|
|
|
nsresult
|
|
|
|
SpdySession::AddTransaction(nsAHttpTransaction *)
|
|
|
|
{
|
|
|
|
// This API is meant for pipelining, SpdySession's should be
|
|
|
|
// extended with AddStream()
|
|
|
|
|
|
|
|
NS_ABORT_IF_FALSE(false,
|
|
|
|
"SpdySession::AddTransaction() should not be called");
|
|
|
|
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
2012-03-22 16:39:31 -07:00
|
|
|
PRUint32
|
|
|
|
SpdySession::PipelineDepth()
|
2012-03-22 16:39:31 -07:00
|
|
|
{
|
2012-03-22 16:39:31 -07:00
|
|
|
return IsDone() ? 0 : 1;
|
2012-03-22 16:39:31 -07:00
|
|
|
}
|
|
|
|
|
2012-03-22 16:39:31 -07:00
|
|
|
nsresult
|
|
|
|
SpdySession::SetPipelinePosition(PRInt32 position)
|
|
|
|
{
|
|
|
|
// This API is meant for pipelining, SpdySession's should be
|
|
|
|
// extended with AddStream()
|
|
|
|
|
|
|
|
NS_ABORT_IF_FALSE(false,
|
|
|
|
"SpdySession::SetPipelinePosition() should not be called");
|
|
|
|
|
|
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32
|
|
|
|
SpdySession::PipelinePosition()
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Pass through methods of nsAHttpConnection
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
nsAHttpConnection *
|
|
|
|
SpdySession::Connection()
|
|
|
|
{
|
2012-01-25 21:15:26 -08:00
|
|
|
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
return mConnection;
|
2011-12-13 07:55:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::OnHeadersAvailable(nsAHttpTransaction *transaction,
|
|
|
|
nsHttpRequestHead *requestHead,
|
|
|
|
nsHttpResponseHead *responseHead,
|
|
|
|
bool *reset)
|
|
|
|
{
|
|
|
|
return mConnection->OnHeadersAvailable(transaction,
|
|
|
|
requestHead,
|
|
|
|
responseHead,
|
|
|
|
reset);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::GetConnectionInfo(nsHttpConnectionInfo **connInfo)
|
|
|
|
{
|
|
|
|
mConnection->GetConnectionInfo(connInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::GetSecurityInfo(nsISupports **supports)
|
|
|
|
{
|
|
|
|
mConnection->GetSecurityInfo(supports);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
SpdySession::IsReused()
|
|
|
|
{
|
|
|
|
return mConnection->IsReused();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
SpdySession::PushBack(const char *buf, PRUint32 len)
|
|
|
|
{
|
|
|
|
return mConnection->PushBack(buf, len);
|
|
|
|
}
|
|
|
|
|
2012-03-22 16:39:31 -07:00
|
|
|
bool
|
|
|
|
SpdySession::IsProxyConnectInProgress()
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(mConnection, "no connection");
|
|
|
|
return mConnection->IsProxyConnectInProgress();
|
|
|
|
}
|
|
|
|
|
2011-12-13 07:55:50 -08:00
|
|
|
bool
|
|
|
|
SpdySession::LastTransactionExpectedNoContent()
|
|
|
|
{
|
|
|
|
return mConnection->LastTransactionExpectedNoContent();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
SpdySession::SetLastTransactionExpectedNoContent(bool val)
|
|
|
|
{
|
|
|
|
mConnection->SetLastTransactionExpectedNoContent(val);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace mozilla::net
|
|
|
|
} // namespace mozilla
|
|
|
|
|