mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 05:15:45 +00:00
bug 737470 patch 2 clone spdy/2 into spdy/3 r=honzab
--HG-- rename : netwerk/protocol/http/SpdySession2.cpp => netwerk/protocol/http/SpdySession3.cpp rename : netwerk/protocol/http/SpdySession2.h => netwerk/protocol/http/SpdySession3.h rename : netwerk/protocol/http/SpdyStream2.cpp => netwerk/protocol/http/SpdyStream3.cpp rename : netwerk/protocol/http/SpdyStream2.h => netwerk/protocol/http/SpdyStream3.h
This commit is contained in:
parent
ba5ffc3931
commit
e68b6e9d07
@ -829,6 +829,8 @@ pref("network.http.fast-fallback-to-IPv4", true);
|
||||
|
||||
// Try and use SPDY when using SSL
|
||||
pref("network.http.spdy.enabled", true);
|
||||
pref("network.http.spdy.enabled.v2", true);
|
||||
pref("network.http.spdy.enabled.v3", false);
|
||||
pref("network.http.spdy.chunk-size", 4096);
|
||||
pref("network.http.spdy.timeout", 180);
|
||||
pref("network.http.spdy.coalesce-hostnames", true);
|
||||
|
@ -38,8 +38,11 @@
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsHttp.h"
|
||||
#include "nsHttpHandler.h"
|
||||
|
||||
#include "ASpdySession.h"
|
||||
#include "SpdySession2.h"
|
||||
#include "SpdySession3.h"
|
||||
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
@ -54,38 +57,44 @@ ASpdySession::NewSpdySession(PRUint32 version,
|
||||
{
|
||||
// This is a necko only interface, so we can enforce version
|
||||
// requests as a precondition
|
||||
NS_ABORT_IF_FALSE(version == SpdyInformation::SPDY_VERSION_2,
|
||||
"Only version 2 implemented");
|
||||
NS_ABORT_IF_FALSE(version == SpdyInformation::SPDY_VERSION_2 ||
|
||||
version == SpdyInformation::SPDY_VERSION_3,
|
||||
"Unsupported spdy version");
|
||||
|
||||
// Don't do a runtime check of IsSpdyV?Enabled() here because pref value
|
||||
// may have changed since starting negotiation. The selected protocol comes
|
||||
// from a list provided in the SERVER HELLO filtered by our acceptable
|
||||
// versions, so there is no risk of the server ignoring our prefs.
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SPDY_VERSION, version);
|
||||
|
||||
return new SpdySession2(aTransaction,
|
||||
aTransport,
|
||||
aPriority);
|
||||
if (version == SpdyInformation::SPDY_VERSION_2)
|
||||
return new SpdySession2(aTransaction, aTransport, aPriority);
|
||||
|
||||
return new SpdySession3(aTransaction, aTransport, aPriority);
|
||||
}
|
||||
|
||||
SpdyInformation::SpdyInformation()
|
||||
{
|
||||
Version[0] = SPDY_VERSION_2;
|
||||
VersionString[0] = NS_LITERAL_CSTRING("spdy/2");
|
||||
AlternateProtocolString[0] = NS_LITERAL_CSTRING("443:npn-spdy/2");
|
||||
// list the preferred version first
|
||||
Version[0] = SPDY_VERSION_3;
|
||||
VersionString[0] = NS_LITERAL_CSTRING("spdy/3");
|
||||
AlternateProtocolString[0] = NS_LITERAL_CSTRING("443:npn-spdy/3");
|
||||
|
||||
Version[1] = 0;
|
||||
VersionString[1] = EmptyCString();
|
||||
AlternateProtocolString[1] = EmptyCString();
|
||||
Version[1] = SPDY_VERSION_2;
|
||||
VersionString[1] = NS_LITERAL_CSTRING("spdy/2");
|
||||
AlternateProtocolString[1] = NS_LITERAL_CSTRING("443:npn-spdy/2");
|
||||
}
|
||||
|
||||
bool
|
||||
SpdyInformation::ProtocolEnabled(PRUint32 index)
|
||||
{
|
||||
// A future patch will make a spdy v2 specific pref
|
||||
if (index == 0)
|
||||
return true;
|
||||
return gHttpHandler->IsSpdyV3Enabled();
|
||||
|
||||
// Right now there is no second protocol version
|
||||
if (index == 1)
|
||||
return false;
|
||||
|
||||
return gHttpHandler->IsSpdyV2Enabled();
|
||||
|
||||
NS_ABORT_IF_FALSE(false, "index out of range");
|
||||
return false;
|
||||
}
|
||||
|
@ -49,8 +49,12 @@ class nsISocketTransport;
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
// This is designed to handle 1 or 2 concrete protocol levels
|
||||
// This is designed to handle up to 2 concrete protocol levels
|
||||
// simultaneously
|
||||
//
|
||||
// Currently supported are v3 (preferred), and v2
|
||||
// network.protocol.http.spdy.enabled.v2 (and v3) prefs can enable/disable
|
||||
// them.
|
||||
|
||||
class ASpdySession : public nsAHttpTransaction
|
||||
{
|
||||
@ -93,7 +97,8 @@ public:
|
||||
PRUint8 *result);
|
||||
|
||||
enum {
|
||||
SPDY_VERSION_2 = 2
|
||||
SPDY_VERSION_2 = 2,
|
||||
SPDY_VERSION_3 = 3
|
||||
};
|
||||
|
||||
PRUint8 Version[2];
|
||||
|
@ -81,6 +81,8 @@ CPPSRCS = \
|
||||
ASpdySession.cpp \
|
||||
SpdySession2.cpp \
|
||||
SpdyStream2.cpp \
|
||||
SpdySession3.cpp \
|
||||
SpdyStream3.cpp \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
|
2402
netwerk/protocol/http/SpdySession3.cpp
Normal file
2402
netwerk/protocol/http/SpdySession3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
373
netwerk/protocol/http/SpdySession3.h
Normal file
373
netwerk/protocol/http/SpdySession3.h
Normal file
@ -0,0 +1,373 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
/* ***** 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) 2012
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef mozilla_net_SpdySession3_h
|
||||
#define mozilla_net_SpdySession3_h
|
||||
|
||||
// SPDY as defined by
|
||||
// http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2
|
||||
|
||||
#include "ASpdySession.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsDeque.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "zlib.h"
|
||||
|
||||
class nsHttpConnection;
|
||||
class nsISocketTransport;
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
class SpdyStream3;
|
||||
|
||||
class SpdySession3 : public ASpdySession
|
||||
, public nsAHttpConnection
|
||||
, public nsAHttpSegmentReader
|
||||
, public nsAHttpSegmentWriter
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSAHTTPTRANSACTION
|
||||
NS_DECL_NSAHTTPCONNECTION(mConnection)
|
||||
NS_DECL_NSAHTTPSEGMENTREADER
|
||||
NS_DECL_NSAHTTPSEGMENTWRITER
|
||||
|
||||
SpdySession3(nsAHttpTransaction *, nsISocketTransport *, PRInt32);
|
||||
~SpdySession3();
|
||||
|
||||
bool AddStream(nsAHttpTransaction *, PRInt32);
|
||||
bool CanReuse() { return !mShouldGoAway && !mClosed; }
|
||||
bool RoomForMoreStreams();
|
||||
|
||||
// When the connection is active this is called every 1 second
|
||||
void ReadTimeoutTick(PRIntervalTime now);
|
||||
|
||||
// Idle time represents time since "goodput".. e.g. a data or header frame
|
||||
PRIntervalTime IdleTime();
|
||||
|
||||
PRUint32 RegisterStreamID(SpdyStream3 *);
|
||||
|
||||
const static PRUint8 kFlag_Control = 0x80;
|
||||
|
||||
const static PRUint8 kFlag_Data_FIN = 0x01;
|
||||
const static PRUint8 kFlag_Data_UNI = 0x02;
|
||||
const static PRUint8 kFlag_Data_ZLIB = 0x02;
|
||||
|
||||
// The protocol document for v2 specifies that the
|
||||
// highest value (3) is the highest priority, but in
|
||||
// reality 0 is the highest priority.
|
||||
//
|
||||
// Draft 3 notes here https://sites.google.com/a/chromium.org/dev/spdy/spdy-protocol/
|
||||
// are the best guide to the mistake. Also see
|
||||
// GetLowestPriority() and GetHighestPriority() in spdy_framer.h of
|
||||
// chromium source.
|
||||
|
||||
const static PRUint8 kPri00 = 0 << 6; // highest
|
||||
const static PRUint8 kPri01 = 1 << 6;
|
||||
const static PRUint8 kPri02 = 2 << 6;
|
||||
const static PRUint8 kPri03 = 3 << 6; // lowest
|
||||
|
||||
enum
|
||||
{
|
||||
CONTROL_TYPE_FIRST = 0,
|
||||
CONTROL_TYPE_SYN_STREAM = 1,
|
||||
CONTROL_TYPE_SYN_REPLY = 2,
|
||||
CONTROL_TYPE_RST_STREAM = 3,
|
||||
CONTROL_TYPE_SETTINGS = 4,
|
||||
CONTROL_TYPE_NOOP = 5,
|
||||
CONTROL_TYPE_PING = 6,
|
||||
CONTROL_TYPE_GOAWAY = 7,
|
||||
CONTROL_TYPE_HEADERS = 8,
|
||||
CONTROL_TYPE_WINDOW_UPDATE = 9, /* no longer in v2 */
|
||||
CONTROL_TYPE_LAST = 10
|
||||
};
|
||||
|
||||
enum rstReason
|
||||
{
|
||||
RST_PROTOCOL_ERROR = 1,
|
||||
RST_INVALID_STREAM = 2,
|
||||
RST_REFUSED_STREAM = 3,
|
||||
RST_UNSUPPORTED_VERSION = 4,
|
||||
RST_CANCEL = 5,
|
||||
RST_INTERNAL_ERROR = 6,
|
||||
RST_FLOW_CONTROL_ERROR = 7,
|
||||
RST_BAD_ASSOC_STREAM = 8
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SETTINGS_TYPE_UPLOAD_BW = 1, // kb/s
|
||||
SETTINGS_TYPE_DOWNLOAD_BW = 2, // kb/s
|
||||
SETTINGS_TYPE_RTT = 3, // ms
|
||||
SETTINGS_TYPE_MAX_CONCURRENT = 4, // streams
|
||||
SETTINGS_TYPE_CWND = 5, // packets
|
||||
SETTINGS_TYPE_DOWNLOAD_RETRANS_RATE = 6, // percentage
|
||||
SETTINGS_TYPE_INITIAL_WINDOW = 7 // bytes. Not used in v2.
|
||||
};
|
||||
|
||||
// This should be big enough to hold all of your control packets,
|
||||
// but if it needs to grow for huge headers it can do so dynamically.
|
||||
// About 1% of requests to SPDY google services seem to be > 1000
|
||||
// with all less than 2000.
|
||||
const static PRUint32 kDefaultBufferSize = 2048;
|
||||
|
||||
// kDefaultQueueSize must be >= other queue size constants
|
||||
const static PRUint32 kDefaultQueueSize = 16384;
|
||||
const static PRUint32 kQueueMinimumCleanup = 8192;
|
||||
const static PRUint32 kQueueTailRoom = 4096;
|
||||
const static PRUint32 kQueueReserved = 1024;
|
||||
|
||||
const static PRUint32 kDefaultMaxConcurrent = 100;
|
||||
const static PRUint32 kMaxStreamID = 0x7800000;
|
||||
|
||||
// This is a sentinel for a deleted stream. It is not a valid
|
||||
// 31 bit stream ID.
|
||||
const static PRUint32 kDeadStreamID = 0xffffdead;
|
||||
|
||||
static nsresult HandleSynStream(SpdySession3 *);
|
||||
static nsresult HandleSynReply(SpdySession3 *);
|
||||
static nsresult HandleRstStream(SpdySession3 *);
|
||||
static nsresult HandleSettings(SpdySession3 *);
|
||||
static nsresult HandleNoop(SpdySession3 *);
|
||||
static nsresult HandlePing(SpdySession3 *);
|
||||
static nsresult HandleGoAway(SpdySession3 *);
|
||||
static nsresult HandleHeaders(SpdySession3 *);
|
||||
static nsresult HandleWindowUpdate(SpdySession3 *);
|
||||
|
||||
static void EnsureBuffer(nsAutoArrayPtr<char> &,
|
||||
PRUint32, PRUint32, PRUint32 &);
|
||||
|
||||
// For writing the SPDY data stream to LOG4
|
||||
static void LogIO(SpdySession3 *, SpdyStream3 *, const char *,
|
||||
const char *, PRUint32);
|
||||
|
||||
// an overload of nsAHttpConnection
|
||||
void TransactionHasDataToWrite(nsAHttpTransaction *);
|
||||
|
||||
// a similar version for SpdyStream3
|
||||
void TransactionHasDataToWrite(SpdyStream3 *);
|
||||
|
||||
// an overload of nsAHttpSegementReader
|
||||
virtual nsresult CommitToSegmentSize(PRUint32 size);
|
||||
|
||||
private:
|
||||
|
||||
enum stateType {
|
||||
BUFFERING_FRAME_HEADER,
|
||||
BUFFERING_CONTROL_FRAME,
|
||||
PROCESSING_DATA_FRAME,
|
||||
DISCARDING_DATA_FRAME,
|
||||
PROCESSING_CONTROL_SYN_REPLY,
|
||||
PROCESSING_CONTROL_RST_STREAM
|
||||
};
|
||||
|
||||
void DeterminePingThreshold();
|
||||
nsresult HandleSynReplyForValidStream();
|
||||
PRUint32 GetWriteQueueSize();
|
||||
void ChangeDownstreamState(enum stateType);
|
||||
void ResetDownstreamState();
|
||||
nsresult DownstreamUncompress(char *, PRUint32);
|
||||
void zlibInit();
|
||||
nsresult FindHeader(nsCString, nsDependentCSubstring &);
|
||||
nsresult ConvertHeaders(nsDependentCSubstring &,
|
||||
nsDependentCSubstring &);
|
||||
void GeneratePing(PRUint32);
|
||||
void ClearPing(bool);
|
||||
void GenerateRstStream(PRUint32, PRUint32);
|
||||
void GenerateGoAway();
|
||||
void CleanupStream(SpdyStream3 *, nsresult, rstReason);
|
||||
|
||||
void SetWriteCallbacks();
|
||||
void FlushOutputQueue();
|
||||
|
||||
bool RoomForMoreConcurrent();
|
||||
void ActivateStream(SpdyStream3 *);
|
||||
void ProcessPending();
|
||||
nsresult SetInputFrameDataStream(PRUint32);
|
||||
bool VerifyStream(SpdyStream3 *, PRUint32);
|
||||
void SetNeedsCleanup();
|
||||
|
||||
// a wrapper for all calls to the nshttpconnection level segment writer. Used
|
||||
// to track network I/O for timeout purposes
|
||||
nsresult NetworkRead(nsAHttpSegmentWriter *, char *, PRUint32, PRUint32 *);
|
||||
|
||||
static PLDHashOperator ShutdownEnumerator(nsAHttpTransaction *,
|
||||
nsAutoPtr<SpdyStream3> &,
|
||||
void *);
|
||||
|
||||
// This is intended to be nsHttpConnectionMgr:nsHttpConnectionHandle taken
|
||||
// from the first transaction on this session. That object contains the
|
||||
// pointer to the real network-level nsHttpConnection object.
|
||||
nsRefPtr<nsAHttpConnection> mConnection;
|
||||
|
||||
// The underlying socket transport object is needed to propogate some events
|
||||
nsISocketTransport *mSocketTransport;
|
||||
|
||||
// These are temporary state variables to hold the argument to
|
||||
// Read/WriteSegments so it can be accessed by On(read/write)segment
|
||||
// further up the stack.
|
||||
nsAHttpSegmentReader *mSegmentReader;
|
||||
nsAHttpSegmentWriter *mSegmentWriter;
|
||||
|
||||
PRUint32 mSendingChunkSize; /* the transmission chunk size */
|
||||
PRUint32 mNextStreamID; /* 24 bits */
|
||||
PRUint32 mConcurrentHighWater; /* max parallelism on session */
|
||||
|
||||
stateType mDownstreamState; /* in frame, between frames, etc.. */
|
||||
|
||||
// Maintain 5 indexes - one by stream ID, one by transaction ptr,
|
||||
// one list of streams ready to write, one list of streams that are queued
|
||||
// due to max parallelism settings, and one list of streams
|
||||
// that must be given priority to write for window updates. The objects
|
||||
// are not ref counted - they get destroyed
|
||||
// by the nsClassHashtable implementation when they are removed from
|
||||
// there.
|
||||
nsDataHashtable<nsUint32HashKey, SpdyStream3 *> mStreamIDHash;
|
||||
nsClassHashtable<nsPtrHashKey<nsAHttpTransaction>,
|
||||
SpdyStream3> mStreamTransactionHash;
|
||||
nsDeque mReadyForWrite;
|
||||
nsDeque mQueuedStreams;
|
||||
|
||||
// UrgentForWrite is meant to carry window updates. They were defined in
|
||||
// the v2 spec but apparently never implemented so are now scheduled to
|
||||
// be removed. But they will be reintroduced for v3, so we will leave
|
||||
// this queue in place to ease that transition.
|
||||
nsDeque mUrgentForWrite;
|
||||
|
||||
// Compression contexts for header transport using deflate.
|
||||
// SPDY compresses only HTTP headers and does not reset zlib in between
|
||||
// frames.
|
||||
z_stream mDownstreamZlib;
|
||||
z_stream mUpstreamZlib;
|
||||
|
||||
// mInputFrameBuffer is used to store received control packets and the 8 bytes
|
||||
// of header on data packets
|
||||
PRUint32 mInputFrameBufferSize;
|
||||
PRUint32 mInputFrameBufferUsed;
|
||||
nsAutoArrayPtr<char> mInputFrameBuffer;
|
||||
|
||||
// mInputFrameDataSize/Read are used for tracking the amount of data consumed
|
||||
// in a data frame. the data itself is not buffered in spdy
|
||||
// The frame size is mInputFrameDataSize + the constant 8 byte header
|
||||
PRUint32 mInputFrameDataSize;
|
||||
PRUint32 mInputFrameDataRead;
|
||||
bool mInputFrameDataLast; // This frame was marked FIN
|
||||
|
||||
// When a frame has been received that is addressed to a particular stream
|
||||
// (e.g. a data frame after the stream-id has been decoded), this points
|
||||
// to the stream.
|
||||
SpdyStream3 *mInputFrameDataStream;
|
||||
|
||||
// mNeedsCleanup is a state variable to defer cleanup of a closed stream
|
||||
// If needed, It is set in session::OnWriteSegments() and acted on and
|
||||
// cleared when the stack returns to session::WriteSegments(). The stream
|
||||
// cannot be destroyed directly out of OnWriteSegments because
|
||||
// stream::writeSegments() is on the stack at that time.
|
||||
SpdyStream3 *mNeedsCleanup;
|
||||
|
||||
// The CONTROL_TYPE value for a control frame
|
||||
PRUint32 mFrameControlType;
|
||||
|
||||
// This reason code in the last processed RESET frame
|
||||
PRUint32 mDownstreamRstReason;
|
||||
|
||||
// These are used for decompressing downstream spdy response headers
|
||||
// This is done at the session level because sometimes the stream
|
||||
// has already been canceled but the decompression still must happen
|
||||
// to keep the zlib state correct for the next state of headers.
|
||||
PRUint32 mDecompressBufferSize;
|
||||
PRUint32 mDecompressBufferUsed;
|
||||
nsAutoArrayPtr<char> mDecompressBuffer;
|
||||
|
||||
// for the conversion of downstream http headers into spdy formatted headers
|
||||
nsCString mFlatHTTPResponseHeaders;
|
||||
PRUint32 mFlatHTTPResponseHeadersOut;
|
||||
|
||||
// when set, the session will go away when it reaches 0 streams. This flag
|
||||
// is set when: the stream IDs are running out (at either the client or the
|
||||
// server), when DontReuse() is called, a RST that is not specific to a
|
||||
// particular stream is received, a GOAWAY frame has been received from
|
||||
// the server.
|
||||
bool mShouldGoAway;
|
||||
|
||||
// the session has received a nsAHttpTransaction::Close() call
|
||||
bool mClosed;
|
||||
|
||||
// the session received a GoAway frame with a valid GoAwayID
|
||||
bool mCleanShutdown;
|
||||
|
||||
// If a GoAway message was received this is the ID of the last valid
|
||||
// stream. 0 otherwise. (0 is never a valid stream id.)
|
||||
PRUint32 mGoAwayID;
|
||||
|
||||
// The limit on number of concurrent streams for this session. Normally it
|
||||
// is basically unlimited, but the SETTINGS control message from the
|
||||
// server might bring it down.
|
||||
PRUint32 mMaxConcurrent;
|
||||
|
||||
// The actual number of concurrent streams at this moment. Generally below
|
||||
// mMaxConcurrent, but the max can be lowered in real time to a value
|
||||
// below the current value
|
||||
PRUint32 mConcurrent;
|
||||
|
||||
// The number of server initiated SYN-STREAMS, tracked for telemetry
|
||||
PRUint32 mServerPushedResources;
|
||||
|
||||
// This is a output queue of bytes ready to be written to the SSL stream.
|
||||
// When that streams returns WOULD_BLOCK on direct write the bytes get
|
||||
// coalesced together here. This results in larger writes to the SSL layer.
|
||||
// The buffer is not dynamically grown to accomodate stream writes, but
|
||||
// does expand to accept infallible session wide frames like GoAway and RST.
|
||||
PRUint32 mOutputQueueSize;
|
||||
PRUint32 mOutputQueueUsed;
|
||||
PRUint32 mOutputQueueSent;
|
||||
nsAutoArrayPtr<char> mOutputQueueBuffer;
|
||||
|
||||
PRIntervalTime mPingThreshold;
|
||||
PRIntervalTime mLastReadEpoch; // used for ping timeouts
|
||||
PRIntervalTime mLastDataReadEpoch; // used for IdleTime()
|
||||
PRIntervalTime mPingSentEpoch;
|
||||
PRUint32 mNextPingID;
|
||||
bool mPingThresholdExperiment;
|
||||
};
|
||||
|
||||
}} // namespace mozilla::net
|
||||
|
||||
#endif // mozilla_net_SpdySession3_h
|
926
netwerk/protocol/http/SpdyStream3.cpp
Normal file
926
netwerk/protocol/http/SpdyStream3.cpp
Normal file
@ -0,0 +1,926 @@
|
||||
/* -*- 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) 2012
|
||||
* 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 "SpdySession3.h"
|
||||
#include "SpdyStream3.h"
|
||||
#include "nsAlgorithm.h"
|
||||
#include "prnetdb.h"
|
||||
#include "nsHttpRequestHead.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsISupportsPriority.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
// defined by the socket transport service while active
|
||||
extern PRThread *gSocketThread;
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
SpdyStream3::SpdyStream3(nsAHttpTransaction *httpTransaction,
|
||||
SpdySession3 *spdySession,
|
||||
nsISocketTransport *socketTransport,
|
||||
PRUint32 chunkSize,
|
||||
z_stream *compressionContext,
|
||||
PRInt32 priority)
|
||||
: mUpstreamState(GENERATING_SYN_STREAM),
|
||||
mTransaction(httpTransaction),
|
||||
mSession(spdySession),
|
||||
mSocketTransport(socketTransport),
|
||||
mSegmentReader(nsnull),
|
||||
mSegmentWriter(nsnull),
|
||||
mStreamID(0),
|
||||
mChunkSize(chunkSize),
|
||||
mSynFrameComplete(0),
|
||||
mRequestBlockedOnRead(0),
|
||||
mSentFinOnData(0),
|
||||
mRecvdFin(0),
|
||||
mFullyOpen(0),
|
||||
mSentWaitingFor(0),
|
||||
mTxInlineFrameSize(SpdySession3::kDefaultBufferSize),
|
||||
mTxInlineFrameUsed(0),
|
||||
mTxStreamFrameSize(0),
|
||||
mZlib(compressionContext),
|
||||
mRequestBodyLenRemaining(0),
|
||||
mPriority(priority),
|
||||
mTotalSent(0),
|
||||
mTotalRead(0)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
LOG3(("SpdyStream3::SpdyStream3 %p", this));
|
||||
|
||||
mTxInlineFrame = new char[mTxInlineFrameSize];
|
||||
}
|
||||
|
||||
SpdyStream3::~SpdyStream3()
|
||||
{
|
||||
mStreamID = SpdySession3::kDeadStreamID;
|
||||
}
|
||||
|
||||
// ReadSegments() is used to write data down the socket. Generally, HTTP
|
||||
// request data is pulled from the approriate transaction and
|
||||
// converted to SPDY data. Sometimes control data like a window-update is
|
||||
// generated instead.
|
||||
|
||||
nsresult
|
||||
SpdyStream3::ReadSegments(nsAHttpSegmentReader *reader,
|
||||
PRUint32 count,
|
||||
PRUint32 *countRead)
|
||||
{
|
||||
LOG3(("SpdyStream3 %p ReadSegments reader=%p count=%d state=%x",
|
||||
this, reader, count, mUpstreamState));
|
||||
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
nsresult rv = NS_ERROR_UNEXPECTED;
|
||||
mRequestBlockedOnRead = 0;
|
||||
|
||||
switch (mUpstreamState) {
|
||||
case GENERATING_SYN_STREAM:
|
||||
case GENERATING_REQUEST_BODY:
|
||||
case SENDING_REQUEST_BODY:
|
||||
// Call into the HTTP Transaction to generate the HTTP request
|
||||
// stream. That stream will show up in OnReadSegment().
|
||||
mSegmentReader = reader;
|
||||
rv = mTransaction->ReadSegments(this, count, countRead);
|
||||
mSegmentReader = nsnull;
|
||||
|
||||
// Check to see if the transaction's request could be written out now.
|
||||
// If not, mark the stream for callback when writing can proceed.
|
||||
if (NS_SUCCEEDED(rv) &&
|
||||
mUpstreamState == GENERATING_SYN_STREAM &&
|
||||
!mSynFrameComplete)
|
||||
mSession->TransactionHasDataToWrite(this);
|
||||
|
||||
// mTxinlineFrameUsed represents any queued un-sent frame. It might
|
||||
// be 0 if there is no such frame, which is not a gurantee that we
|
||||
// don't have more request body to send - just that any data that was
|
||||
// sent comprised a complete SPDY frame. Likewise, a non 0 value is
|
||||
// a queued, but complete, spdy frame length.
|
||||
|
||||
// Mark that we are blocked on read if the http transaction needs to
|
||||
// provide more of the request message body and there is nothing queued
|
||||
// for writing
|
||||
if (rv == NS_BASE_STREAM_WOULD_BLOCK && !mTxInlineFrameUsed)
|
||||
mRequestBlockedOnRead = 1;
|
||||
|
||||
if (!mTxInlineFrameUsed && NS_SUCCEEDED(rv) && (!*countRead)) {
|
||||
LOG3(("ReadSegments %p: Sending request data complete, mUpstreamState=%x",
|
||||
this, mUpstreamState));
|
||||
if (mSentFinOnData) {
|
||||
ChangeState(UPSTREAM_COMPLETE);
|
||||
}
|
||||
else {
|
||||
GenerateDataFrameHeader(0, true);
|
||||
ChangeState(SENDING_FIN_STREAM);
|
||||
mSession->TransactionHasDataToWrite(this);
|
||||
rv = NS_BASE_STREAM_WOULD_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SENDING_SYN_STREAM:
|
||||
// We were trying to send the SYN-STREAM but were blocked from trying
|
||||
// to transmit it the first time(s).
|
||||
mSegmentReader = reader;
|
||||
rv = TransmitFrame(nsnull, nsnull);
|
||||
mSegmentReader = nsnull;
|
||||
*countRead = 0;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
NS_ABORT_IF_FALSE(!mTxInlineFrameUsed,
|
||||
"Transmit Frame should be all or nothing");
|
||||
|
||||
if (mSentFinOnData) {
|
||||
ChangeState(UPSTREAM_COMPLETE);
|
||||
rv = NS_OK;
|
||||
}
|
||||
else {
|
||||
rv = NS_BASE_STREAM_WOULD_BLOCK;
|
||||
ChangeState(GENERATING_REQUEST_BODY);
|
||||
mSession->TransactionHasDataToWrite(this);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SENDING_FIN_STREAM:
|
||||
// We were trying to send the FIN-STREAM but were blocked from
|
||||
// sending it out - try again.
|
||||
if (!mSentFinOnData) {
|
||||
mSegmentReader = reader;
|
||||
rv = TransmitFrame(nsnull, nsnull);
|
||||
mSegmentReader = nsnull;
|
||||
NS_ABORT_IF_FALSE(NS_FAILED(rv) || !mTxInlineFrameUsed,
|
||||
"Transmit Frame should be all or nothing");
|
||||
if (NS_SUCCEEDED(rv))
|
||||
ChangeState(UPSTREAM_COMPLETE);
|
||||
}
|
||||
else {
|
||||
rv = NS_OK;
|
||||
mTxInlineFrameUsed = 0; // cancel fin data packet
|
||||
ChangeState(UPSTREAM_COMPLETE);
|
||||
}
|
||||
|
||||
*countRead = 0;
|
||||
|
||||
// don't change OK to WOULD BLOCK. we are really done sending if OK
|
||||
break;
|
||||
|
||||
case UPSTREAM_COMPLETE:
|
||||
*countRead = 0;
|
||||
rv = NS_OK;
|
||||
break;
|
||||
|
||||
default:
|
||||
NS_ABORT_IF_FALSE(false, "SpdyStream3::ReadSegments unknown state");
|
||||
break;
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
nsresult
|
||||
SpdyStream3::WriteSegments(nsAHttpSegmentWriter *writer,
|
||||
PRUint32 count,
|
||||
PRUint32 *countWritten)
|
||||
{
|
||||
LOG3(("SpdyStream3::WriteSegments %p count=%d state=%x",
|
||||
this, count, mUpstreamState));
|
||||
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
NS_ABORT_IF_FALSE(!mSegmentWriter, "segment writer in progress");
|
||||
|
||||
mSegmentWriter = writer;
|
||||
nsresult rv = mTransaction->WriteSegments(writer, count, countWritten);
|
||||
mSegmentWriter = nsnull;
|
||||
return rv;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
SpdyStream3::hdrHashEnumerate(const nsACString &key,
|
||||
nsAutoPtr<nsCString> &value,
|
||||
void *closure)
|
||||
{
|
||||
SpdyStream3 *self = static_cast<SpdyStream3 *>(closure);
|
||||
|
||||
self->CompressToFrame(key);
|
||||
self->CompressToFrame(value.get());
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SpdyStream3::ParseHttpRequestHeaders(const char *buf,
|
||||
PRUint32 avail,
|
||||
PRUint32 *countUsed)
|
||||
{
|
||||
// Returns NS_OK even if the headers are incomplete
|
||||
// set mSynFrameComplete flag if they are complete
|
||||
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
NS_ABORT_IF_FALSE(mUpstreamState == GENERATING_SYN_STREAM, "wrong state");
|
||||
|
||||
LOG3(("SpdyStream3::ParseHttpRequestHeaders %p avail=%d state=%x",
|
||||
this, avail, mUpstreamState));
|
||||
|
||||
mFlatHttpRequestHeaders.Append(buf, avail);
|
||||
|
||||
// We can use the simple double crlf because firefox is the
|
||||
// only client we are parsing
|
||||
PRInt32 endHeader = mFlatHttpRequestHeaders.Find("\r\n\r\n");
|
||||
|
||||
if (endHeader == kNotFound) {
|
||||
// We don't have all the headers yet
|
||||
LOG3(("SpdyStream3::ParseHttpRequestHeaders %p "
|
||||
"Need more header bytes. Len = %d",
|
||||
this, mFlatHttpRequestHeaders.Length()));
|
||||
*countUsed = avail;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We have recvd all the headers, trim the local
|
||||
// buffer of the final empty line, and set countUsed to reflect
|
||||
// the whole header has been consumed.
|
||||
PRUint32 oldLen = mFlatHttpRequestHeaders.Length();
|
||||
mFlatHttpRequestHeaders.SetLength(endHeader + 2);
|
||||
*countUsed = avail - (oldLen - endHeader) + 4;
|
||||
mSynFrameComplete = 1;
|
||||
|
||||
// It is now OK to assign a streamID that we are assured will
|
||||
// be monotonically increasing amongst syn-streams on this
|
||||
// session
|
||||
mStreamID = mSession->RegisterStreamID(this);
|
||||
NS_ABORT_IF_FALSE(mStreamID & 1,
|
||||
"Spdy Stream Channel ID must be odd");
|
||||
|
||||
if (mStreamID >= 0x80000000) {
|
||||
// streamID must fit in 31 bits. This is theoretically possible
|
||||
// because stream ID assignment is asynchronous to stream creation
|
||||
// because of the protocol requirement that the ID in syn-stream
|
||||
// be monotonically increasing. In reality this is really not possible
|
||||
// because new streams stop being added to a session with 0x10000000 / 2
|
||||
// IDs still available and no race condition is going to bridge that gap,
|
||||
// so we can be comfortable on just erroring out for correctness in that
|
||||
// case.
|
||||
LOG3(("Stream assigned out of range ID: 0x%X", mStreamID));
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Now we need to convert the flat http headers into a set
|
||||
// of SPDY headers.. writing to mTxInlineFrame{sz}
|
||||
|
||||
mTxInlineFrame[0] = SpdySession3::kFlag_Control;
|
||||
mTxInlineFrame[1] = 2; /* version */
|
||||
mTxInlineFrame[2] = 0;
|
||||
mTxInlineFrame[3] = SpdySession3::CONTROL_TYPE_SYN_STREAM;
|
||||
// 4 to 7 are length and flags, we'll fill that in later
|
||||
|
||||
PRUint32 networkOrderID = PR_htonl(mStreamID);
|
||||
memcpy(mTxInlineFrame + 8, &networkOrderID, 4);
|
||||
|
||||
// this is the associated-to field, which is not used sending
|
||||
// from the client in the http binding
|
||||
memset (mTxInlineFrame + 12, 0, 4);
|
||||
|
||||
// Priority flags are the C0 mask of byte 16.
|
||||
//
|
||||
// The other 6 bits of 16 are unused. Spdy/3 will expand
|
||||
// priority to 4 bits.
|
||||
//
|
||||
// When Spdy/3 implements WINDOW_UPDATE the lowest priority
|
||||
// streams over a threshold (32?) should be given tiny
|
||||
// receive windows, separate from their spdy priority
|
||||
//
|
||||
if (mPriority >= nsISupportsPriority::PRIORITY_LOW)
|
||||
mTxInlineFrame[16] = SpdySession3::kPri03;
|
||||
else if (mPriority >= nsISupportsPriority::PRIORITY_NORMAL)
|
||||
mTxInlineFrame[16] = SpdySession3::kPri02;
|
||||
else if (mPriority >= nsISupportsPriority::PRIORITY_HIGH)
|
||||
mTxInlineFrame[16] = SpdySession3::kPri01;
|
||||
else
|
||||
mTxInlineFrame[16] = SpdySession3::kPri00;
|
||||
|
||||
mTxInlineFrame[17] = 0; /* unused */
|
||||
|
||||
const char *methodHeader = mTransaction->RequestHead()->Method().get();
|
||||
|
||||
nsCString hostHeader;
|
||||
mTransaction->RequestHead()->GetHeader(nsHttp::Host, hostHeader);
|
||||
|
||||
nsCString versionHeader;
|
||||
if (mTransaction->RequestHead()->Version() == NS_HTTP_VERSION_1_1)
|
||||
versionHeader = NS_LITERAL_CSTRING("HTTP/1.1");
|
||||
else
|
||||
versionHeader = NS_LITERAL_CSTRING("HTTP/1.0");
|
||||
|
||||
nsClassHashtable<nsCStringHashKey, nsCString> hdrHash;
|
||||
|
||||
// use mRequestHead() to get a sense of how big to make the hash,
|
||||
// even though we are parsing the actual text stream because
|
||||
// it is legit to append headers.
|
||||
hdrHash.Init(1 + (mTransaction->RequestHead()->Headers().Count() * 2));
|
||||
|
||||
const char *beginBuffer = mFlatHttpRequestHeaders.BeginReading();
|
||||
|
||||
// need to hash all the headers together to remove duplicates, special
|
||||
// headers, etc..
|
||||
|
||||
PRInt32 crlfIndex = mFlatHttpRequestHeaders.Find("\r\n");
|
||||
while (true) {
|
||||
PRInt32 startIndex = crlfIndex + 2;
|
||||
|
||||
crlfIndex = mFlatHttpRequestHeaders.Find("\r\n", false, startIndex);
|
||||
if (crlfIndex == -1)
|
||||
break;
|
||||
|
||||
PRInt32 colonIndex = mFlatHttpRequestHeaders.Find(":", false, startIndex,
|
||||
crlfIndex - startIndex);
|
||||
if (colonIndex == -1)
|
||||
break;
|
||||
|
||||
nsDependentCSubstring name = Substring(beginBuffer + startIndex,
|
||||
beginBuffer + colonIndex);
|
||||
// all header names are lower case in spdy
|
||||
ToLowerCase(name);
|
||||
|
||||
if (name.Equals("method") ||
|
||||
name.Equals("version") ||
|
||||
name.Equals("scheme") ||
|
||||
name.Equals("keep-alive") ||
|
||||
name.Equals("accept-encoding") ||
|
||||
name.Equals("te") ||
|
||||
name.Equals("connection") ||
|
||||
name.Equals("proxy-connection") ||
|
||||
name.Equals("url"))
|
||||
continue;
|
||||
|
||||
nsCString *val = hdrHash.Get(name);
|
||||
if (!val) {
|
||||
val = new nsCString();
|
||||
hdrHash.Put(name, val);
|
||||
}
|
||||
|
||||
PRInt32 valueIndex = colonIndex + 1;
|
||||
while (valueIndex < crlfIndex && beginBuffer[valueIndex] == ' ')
|
||||
++valueIndex;
|
||||
|
||||
nsDependentCSubstring v = Substring(beginBuffer + valueIndex,
|
||||
beginBuffer + crlfIndex);
|
||||
if (!val->IsEmpty())
|
||||
val->Append(static_cast<char>(0));
|
||||
val->Append(v);
|
||||
|
||||
if (name.Equals("content-length")) {
|
||||
PRInt64 len;
|
||||
if (nsHttp::ParseInt64(val->get(), nsnull, &len))
|
||||
mRequestBodyLenRemaining = len;
|
||||
}
|
||||
}
|
||||
|
||||
mTxInlineFrameUsed = 18;
|
||||
|
||||
// Do not naively log the request headers here beacuse they might
|
||||
// contain auth. The http transaction already logs the sanitized request
|
||||
// headers at this same level so it is not necessary to do so here.
|
||||
|
||||
// The header block length
|
||||
PRUint16 count = hdrHash.Count() + 4; /* method, scheme, url, version */
|
||||
CompressToFrame(count);
|
||||
|
||||
// method, scheme, url, and version headers for request line
|
||||
|
||||
CompressToFrame(NS_LITERAL_CSTRING("method"));
|
||||
CompressToFrame(methodHeader, strlen(methodHeader));
|
||||
CompressToFrame(NS_LITERAL_CSTRING("scheme"));
|
||||
CompressToFrame(NS_LITERAL_CSTRING("https"));
|
||||
CompressToFrame(NS_LITERAL_CSTRING("url"));
|
||||
CompressToFrame(mTransaction->RequestHead()->RequestURI());
|
||||
CompressToFrame(NS_LITERAL_CSTRING("version"));
|
||||
CompressToFrame(versionHeader);
|
||||
|
||||
hdrHash.Enumerate(hdrHashEnumerate, this);
|
||||
CompressFlushFrame();
|
||||
|
||||
// 4 to 7 are length and flags, which we can now fill in
|
||||
(reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[1] =
|
||||
PR_htonl(mTxInlineFrameUsed - 8);
|
||||
|
||||
NS_ABORT_IF_FALSE(!mTxInlineFrame[4],
|
||||
"Size greater than 24 bits");
|
||||
|
||||
// Determine whether to put the fin bit on the syn stream frame or whether
|
||||
// to wait for a data packet to put it on.
|
||||
|
||||
if (mTransaction->RequestHead()->Method() == nsHttp::Get ||
|
||||
mTransaction->RequestHead()->Method() == nsHttp::Connect ||
|
||||
mTransaction->RequestHead()->Method() == nsHttp::Head) {
|
||||
// for GET, CONNECT, and HEAD place the fin bit right on the
|
||||
// syn stream packet
|
||||
|
||||
mSentFinOnData = 1;
|
||||
mTxInlineFrame[4] = SpdySession3::kFlag_Data_FIN;
|
||||
}
|
||||
else if (mTransaction->RequestHead()->Method() == nsHttp::Post ||
|
||||
mTransaction->RequestHead()->Method() == nsHttp::Put ||
|
||||
mTransaction->RequestHead()->Method() == nsHttp::Options) {
|
||||
// place fin in a data frame even for 0 length messages, I've seen
|
||||
// the google gateway be unhappy with fin-on-syn for 0 length POST
|
||||
}
|
||||
else if (!mRequestBodyLenRemaining) {
|
||||
// for other HTTP extension methods, rely on the content-length
|
||||
// to determine whether or not to put fin on syn
|
||||
mSentFinOnData = 1;
|
||||
mTxInlineFrame[4] = SpdySession3::kFlag_Data_FIN;
|
||||
}
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SPDY_SYN_SIZE, mTxInlineFrameUsed - 18);
|
||||
|
||||
// The size of the input headers is approximate
|
||||
PRUint32 ratio =
|
||||
(mTxInlineFrameUsed - 18) * 100 /
|
||||
(11 + mTransaction->RequestHead()->RequestURI().Length() +
|
||||
mFlatHttpRequestHeaders.Length());
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SPDY_SYN_RATIO, ratio);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream3::UpdateTransportReadEvents(PRUint32 count)
|
||||
{
|
||||
mTotalRead += count;
|
||||
|
||||
mTransaction->OnTransportStatus(mSocketTransport,
|
||||
NS_NET_STATUS_RECEIVING_FROM,
|
||||
mTotalRead);
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream3::UpdateTransportSendEvents(PRUint32 count)
|
||||
{
|
||||
mTotalSent += count;
|
||||
|
||||
if (mUpstreamState != SENDING_FIN_STREAM)
|
||||
mTransaction->OnTransportStatus(mSocketTransport,
|
||||
NS_NET_STATUS_SENDING_TO,
|
||||
mTotalSent);
|
||||
|
||||
if (!mSentWaitingFor && !mRequestBodyLenRemaining) {
|
||||
mSentWaitingFor = 1;
|
||||
mTransaction->OnTransportStatus(mSocketTransport,
|
||||
NS_NET_STATUS_WAITING_FOR,
|
||||
LL_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
SpdyStream3::TransmitFrame(const char *buf,
|
||||
PRUint32 *countUsed)
|
||||
{
|
||||
// If TransmitFrame returns SUCCESS than all the data is sent (or at least
|
||||
// buffered at the session level), if it returns WOULD_BLOCK then none of
|
||||
// the data is sent.
|
||||
|
||||
// You can call this function with no data and no out parameter in order to
|
||||
// flush internal buffers that were previously blocked on writing. You can
|
||||
// of course feed new data to it as well.
|
||||
|
||||
NS_ABORT_IF_FALSE(mTxInlineFrameUsed, "empty stream frame in transmit");
|
||||
NS_ABORT_IF_FALSE(mSegmentReader, "TransmitFrame with null mSegmentReader");
|
||||
NS_ABORT_IF_FALSE((buf && countUsed) || (!buf && !countUsed),
|
||||
"TransmitFrame arguments inconsistent");
|
||||
|
||||
PRUint32 transmittedCount;
|
||||
nsresult rv;
|
||||
|
||||
LOG3(("SpdyStream3::TransmitFrame %p inline=%d stream=%d",
|
||||
this, mTxInlineFrameUsed, mTxStreamFrameSize));
|
||||
if (countUsed)
|
||||
*countUsed = 0;
|
||||
|
||||
// In the (relatively common) event that we have a small amount of data
|
||||
// split between the inlineframe and the streamframe, then move the stream
|
||||
// data into the inlineframe via copy in order to coalesce into one write.
|
||||
// Given the interaction with ssl this is worth the small copy cost.
|
||||
if (mTxStreamFrameSize && mTxInlineFrameUsed &&
|
||||
mTxStreamFrameSize < SpdySession3::kDefaultBufferSize &&
|
||||
mTxInlineFrameUsed + mTxStreamFrameSize < mTxInlineFrameSize) {
|
||||
LOG3(("Coalesce Transmit"));
|
||||
memcpy (mTxInlineFrame + mTxInlineFrameUsed,
|
||||
buf, mTxStreamFrameSize);
|
||||
if (countUsed)
|
||||
*countUsed += mTxStreamFrameSize;
|
||||
mTxInlineFrameUsed += mTxStreamFrameSize;
|
||||
mTxStreamFrameSize = 0;
|
||||
}
|
||||
|
||||
rv = mSegmentReader->CommitToSegmentSize(mTxStreamFrameSize +
|
||||
mTxInlineFrameUsed);
|
||||
if (rv == NS_BASE_STREAM_WOULD_BLOCK)
|
||||
mSession->TransactionHasDataToWrite(this);
|
||||
if (NS_FAILED(rv)) // this will include WOULD_BLOCK
|
||||
return rv;
|
||||
|
||||
// This function calls mSegmentReader->OnReadSegment to report the actual SPDY
|
||||
// bytes through to the SpdySession3 and then the HttpConnection which calls
|
||||
// the socket write function. It will accept all of the inline and stream
|
||||
// data because of the above 'commitment' even if it has to buffer
|
||||
|
||||
rv = mSegmentReader->OnReadSegment(mTxInlineFrame, mTxInlineFrameUsed,
|
||||
&transmittedCount);
|
||||
LOG3(("SpdyStream3::TransmitFrame for inline session=%p "
|
||||
"stream=%p result %x len=%d",
|
||||
mSession, this, rv, transmittedCount));
|
||||
|
||||
NS_ABORT_IF_FALSE(rv != NS_BASE_STREAM_WOULD_BLOCK,
|
||||
"inconsistent inline commitment result");
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
NS_ABORT_IF_FALSE(transmittedCount == mTxInlineFrameUsed,
|
||||
"inconsistent inline commitment count");
|
||||
|
||||
SpdySession3::LogIO(mSession, this, "Writing from Inline Buffer",
|
||||
mTxInlineFrame, transmittedCount);
|
||||
|
||||
if (mTxStreamFrameSize) {
|
||||
if (!buf) {
|
||||
// this cannot happen
|
||||
NS_ABORT_IF_FALSE(false, "Stream transmit with null buf argument to "
|
||||
"TransmitFrame()");
|
||||
LOG(("Stream transmit with null buf argument to TransmitFrame()\n"));
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
rv = mSegmentReader->OnReadSegment(buf, mTxStreamFrameSize,
|
||||
&transmittedCount);
|
||||
|
||||
LOG3(("SpdyStream3::TransmitFrame for regular session=%p "
|
||||
"stream=%p result %x len=%d",
|
||||
mSession, this, rv, transmittedCount));
|
||||
|
||||
NS_ABORT_IF_FALSE(rv != NS_BASE_STREAM_WOULD_BLOCK,
|
||||
"inconsistent stream commitment result");
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
NS_ABORT_IF_FALSE(transmittedCount == mTxStreamFrameSize,
|
||||
"inconsistent stream commitment count");
|
||||
|
||||
SpdySession3::LogIO(mSession, this, "Writing from Transaction Buffer",
|
||||
buf, transmittedCount);
|
||||
|
||||
*countUsed += mTxStreamFrameSize;
|
||||
}
|
||||
|
||||
// calling this will trigger waiting_for if mRequestBodyLenRemaining is 0
|
||||
UpdateTransportSendEvents(mTxInlineFrameUsed + mTxStreamFrameSize);
|
||||
|
||||
mTxInlineFrameUsed = 0;
|
||||
mTxStreamFrameSize = 0;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream3::ChangeState(enum stateType newState)
|
||||
{
|
||||
LOG3(("SpdyStream3::ChangeState() %p from %X to %X",
|
||||
this, mUpstreamState, newState));
|
||||
mUpstreamState = newState;
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream3::GenerateDataFrameHeader(PRUint32 dataLength, bool lastFrame)
|
||||
{
|
||||
LOG3(("SpdyStream3::GenerateDataFrameHeader %p len=%d last=%d",
|
||||
this, dataLength, lastFrame));
|
||||
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
NS_ABORT_IF_FALSE(!mTxInlineFrameUsed, "inline frame not empty");
|
||||
NS_ABORT_IF_FALSE(!mTxStreamFrameSize, "stream frame not empty");
|
||||
NS_ABORT_IF_FALSE(!(dataLength & 0xff000000), "datalength > 24 bits");
|
||||
|
||||
(reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[0] = PR_htonl(mStreamID);
|
||||
(reinterpret_cast<PRUint32 *>(mTxInlineFrame.get()))[1] =
|
||||
PR_htonl(dataLength);
|
||||
|
||||
NS_ABORT_IF_FALSE(!(mTxInlineFrame[0] & 0x80),
|
||||
"control bit set unexpectedly");
|
||||
NS_ABORT_IF_FALSE(!mTxInlineFrame[4], "flag bits set unexpectedly");
|
||||
|
||||
mTxInlineFrameUsed = 8;
|
||||
mTxStreamFrameSize = dataLength;
|
||||
|
||||
if (lastFrame) {
|
||||
mTxInlineFrame[4] |= SpdySession3::kFlag_Data_FIN;
|
||||
if (dataLength)
|
||||
mSentFinOnData = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream3::CompressToFrame(const nsACString &str)
|
||||
{
|
||||
CompressToFrame(str.BeginReading(), str.Length());
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream3::CompressToFrame(const nsACString *str)
|
||||
{
|
||||
CompressToFrame(str->BeginReading(), str->Length());
|
||||
}
|
||||
|
||||
// Dictionary taken from
|
||||
// http://dev.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2
|
||||
// Name/Value Header Block Format
|
||||
// spec indicates that the compression dictionary is not null terminated
|
||||
// but in reality it is. see:
|
||||
// https://groups.google.com/forum/#!topic/spdy-dev/2pWxxOZEIcs
|
||||
|
||||
const char *SpdyStream3::kDictionary =
|
||||
"optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-"
|
||||
"languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi"
|
||||
"f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser"
|
||||
"-agent10010120020120220320420520630030130230330430530630740040140240340440"
|
||||
"5406407408409410411412413414415416417500501502503504505accept-rangesageeta"
|
||||
"glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic"
|
||||
"ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran"
|
||||
"sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati"
|
||||
"oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo"
|
||||
"ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe"
|
||||
"pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic"
|
||||
"ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1"
|
||||
".1statusversionurl";
|
||||
|
||||
// use for zlib data types
|
||||
void *
|
||||
SpdyStream3::zlib_allocator(void *opaque, uInt items, uInt size)
|
||||
{
|
||||
return moz_xmalloc(items * size);
|
||||
}
|
||||
|
||||
// use for zlib data types
|
||||
void
|
||||
SpdyStream3::zlib_destructor(void *opaque, void *addr)
|
||||
{
|
||||
moz_free(addr);
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream3::ExecuteCompress(PRUint32 flushMode)
|
||||
{
|
||||
// Expect mZlib->avail_in and mZlib->next_in to be set.
|
||||
// Append the compressed version of next_in to mTxInlineFrame
|
||||
|
||||
do
|
||||
{
|
||||
PRUint32 avail = mTxInlineFrameSize - mTxInlineFrameUsed;
|
||||
if (avail < 1) {
|
||||
SpdySession3::EnsureBuffer(mTxInlineFrame,
|
||||
mTxInlineFrameSize + 2000,
|
||||
mTxInlineFrameUsed,
|
||||
mTxInlineFrameSize);
|
||||
avail = mTxInlineFrameSize - mTxInlineFrameUsed;
|
||||
}
|
||||
|
||||
mZlib->next_out = reinterpret_cast<unsigned char *> (mTxInlineFrame.get()) +
|
||||
mTxInlineFrameUsed;
|
||||
mZlib->avail_out = avail;
|
||||
deflate(mZlib, flushMode);
|
||||
mTxInlineFrameUsed += avail - mZlib->avail_out;
|
||||
} while (mZlib->avail_in > 0 || !mZlib->avail_out);
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream3::CompressToFrame(PRUint16 data)
|
||||
{
|
||||
// convert the data to network byte order and write that
|
||||
// to the compressed stream
|
||||
|
||||
data = PR_htons(data);
|
||||
|
||||
mZlib->next_in = reinterpret_cast<unsigned char *> (&data);
|
||||
mZlib->avail_in = 2;
|
||||
ExecuteCompress(Z_NO_FLUSH);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SpdyStream3::CompressToFrame(const char *data, PRUint32 len)
|
||||
{
|
||||
// Format calls for a network ordered 16 bit length
|
||||
// followed by the utf8 string
|
||||
|
||||
// for now, silently truncate headers greater than 64KB. Spdy/3 will
|
||||
// fix this by making the len a 32 bit quantity
|
||||
if (len > 0xffff)
|
||||
len = 0xffff;
|
||||
|
||||
PRUint16 networkLen = PR_htons(len);
|
||||
|
||||
// write out the length
|
||||
mZlib->next_in = reinterpret_cast<unsigned char *> (&networkLen);
|
||||
mZlib->avail_in = 2;
|
||||
ExecuteCompress(Z_NO_FLUSH);
|
||||
|
||||
// write out the data
|
||||
mZlib->next_in = (unsigned char *)data;
|
||||
mZlib->avail_in = len;
|
||||
ExecuteCompress(Z_NO_FLUSH);
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream3::CompressFlushFrame()
|
||||
{
|
||||
mZlib->next_in = (unsigned char *) "";
|
||||
mZlib->avail_in = 0;
|
||||
ExecuteCompress(Z_SYNC_FLUSH);
|
||||
}
|
||||
|
||||
void
|
||||
SpdyStream3::Close(nsresult reason)
|
||||
{
|
||||
mTransaction->Close(reason);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsAHttpSegmentReader
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
SpdyStream3::OnReadSegment(const char *buf,
|
||||
PRUint32 count,
|
||||
PRUint32 *countRead)
|
||||
{
|
||||
LOG3(("SpdyStream3::OnReadSegment %p count=%d state=%x",
|
||||
this, count, mUpstreamState));
|
||||
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
NS_ABORT_IF_FALSE(mSegmentReader, "OnReadSegment with null mSegmentReader");
|
||||
|
||||
nsresult rv = NS_ERROR_UNEXPECTED;
|
||||
PRUint32 dataLength;
|
||||
|
||||
switch (mUpstreamState) {
|
||||
case GENERATING_SYN_STREAM:
|
||||
// The buffer is the HTTP request stream, including at least part of the
|
||||
// HTTP request header. This state's job is to build a SYN_STREAM frame
|
||||
// from the header information. count is the number of http bytes available
|
||||
// (which may include more than the header), and in countRead we return
|
||||
// the number of those bytes that we consume (i.e. the portion that are
|
||||
// header bytes)
|
||||
|
||||
rv = ParseHttpRequestHeaders(buf, count, countRead);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
LOG3(("ParseHttpRequestHeaders %p used %d of %d. complete = %d",
|
||||
this, *countRead, count, mSynFrameComplete));
|
||||
if (mSynFrameComplete) {
|
||||
NS_ABORT_IF_FALSE(mTxInlineFrameUsed,
|
||||
"OnReadSegment SynFrameComplete 0b");
|
||||
rv = TransmitFrame(nsnull, nsnull);
|
||||
NS_ABORT_IF_FALSE(NS_FAILED(rv) || !mTxInlineFrameUsed,
|
||||
"Transmit Frame should be all or nothing");
|
||||
|
||||
// normalize a blocked write into an ok one if we have consumed the data
|
||||
// while parsing headers as some code will take WOULD_BLOCK to mean an
|
||||
// error with nothing processed.
|
||||
// (e.g. nsHttpTransaction::ReadRequestSegment())
|
||||
if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
|
||||
rv = NS_OK;
|
||||
|
||||
// mTxInlineFrameUsed > 0 means the current frame is in progress
|
||||
// of sending. mTxInlineFrameUsed is dropped to 0 after both the frame
|
||||
// and its payload (if any) are completely sent out. Here during
|
||||
// GENERATING_SYN_STREAM state we are sending just the http headers.
|
||||
// Only when the frame is completely sent out do we proceed to
|
||||
// GENERATING_REQUEST_BODY state.
|
||||
|
||||
if (mTxInlineFrameUsed)
|
||||
ChangeState(SENDING_SYN_STREAM);
|
||||
else
|
||||
ChangeState(GENERATING_REQUEST_BODY);
|
||||
break;
|
||||
}
|
||||
NS_ABORT_IF_FALSE(*countRead == count,
|
||||
"Header parsing not complete but unused data");
|
||||
break;
|
||||
|
||||
case GENERATING_REQUEST_BODY:
|
||||
dataLength = NS_MIN(count, mChunkSize);
|
||||
LOG3(("SpdyStream3 %p id %x request len remaining %d, "
|
||||
"count avail %d, chunk used %d",
|
||||
this, mStreamID, mRequestBodyLenRemaining, count, dataLength));
|
||||
if (dataLength > mRequestBodyLenRemaining)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
mRequestBodyLenRemaining -= dataLength;
|
||||
GenerateDataFrameHeader(dataLength, !mRequestBodyLenRemaining);
|
||||
ChangeState(SENDING_REQUEST_BODY);
|
||||
// NO BREAK
|
||||
|
||||
case SENDING_REQUEST_BODY:
|
||||
NS_ABORT_IF_FALSE(mTxInlineFrameUsed, "OnReadSegment Send Data Header 0b");
|
||||
rv = TransmitFrame(buf, countRead);
|
||||
NS_ABORT_IF_FALSE(NS_FAILED(rv) || !mTxInlineFrameUsed,
|
||||
"Transmit Frame should be all or nothing");
|
||||
|
||||
LOG3(("TransmitFrame() rv=%x returning %d data bytes. "
|
||||
"Header is %d Body is %d.",
|
||||
rv, *countRead, mTxInlineFrameUsed, mTxStreamFrameSize));
|
||||
|
||||
// normalize a partial write with a WOULD_BLOCK into just a partial write
|
||||
// as some code will take WOULD_BLOCK to mean an error with nothing
|
||||
// written (e.g. nsHttpTransaction::ReadRequestSegment()
|
||||
if (rv == NS_BASE_STREAM_WOULD_BLOCK && *countRead)
|
||||
rv = NS_OK;
|
||||
|
||||
// If that frame was all sent, look for another one
|
||||
if (!mTxInlineFrameUsed)
|
||||
ChangeState(GENERATING_REQUEST_BODY);
|
||||
break;
|
||||
|
||||
case SENDING_SYN_STREAM:
|
||||
rv = NS_BASE_STREAM_WOULD_BLOCK;
|
||||
break;
|
||||
|
||||
case SENDING_FIN_STREAM:
|
||||
NS_ABORT_IF_FALSE(false,
|
||||
"resuming partial fin stream out of OnReadSegment");
|
||||
break;
|
||||
|
||||
default:
|
||||
NS_ABORT_IF_FALSE(false, "SpdyStream3::OnReadSegment non-write state");
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsAHttpSegmentWriter
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
SpdyStream3::OnWriteSegment(char *buf,
|
||||
PRUint32 count,
|
||||
PRUint32 *countWritten)
|
||||
{
|
||||
LOG3(("SpdyStream3::OnWriteSegment %p count=%d state=%x",
|
||||
this, count, mUpstreamState));
|
||||
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
NS_ABORT_IF_FALSE(mSegmentWriter, "OnWriteSegment with null mSegmentWriter");
|
||||
|
||||
return mSegmentWriter->OnWriteSegment(buf, count, countWritten);
|
||||
}
|
||||
|
||||
} // namespace mozilla::net
|
||||
} // namespace mozilla
|
||||
|
212
netwerk/protocol/http/SpdyStream3.h
Normal file
212
netwerk/protocol/http/SpdyStream3.h
Normal file
@ -0,0 +1,212 @@
|
||||
/* -*- 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) 2012
|
||||
* 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 ***** */
|
||||
|
||||
#ifndef mozilla_net_SpdyStream3_h
|
||||
#define mozilla_net_SpdyStream3_h
|
||||
|
||||
#include "nsAHttpTransaction.h"
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
class SpdyStream3 : public nsAHttpSegmentReader
|
||||
, public nsAHttpSegmentWriter
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSAHTTPSEGMENTREADER
|
||||
NS_DECL_NSAHTTPSEGMENTWRITER
|
||||
|
||||
SpdyStream3(nsAHttpTransaction *,
|
||||
SpdySession3 *, nsISocketTransport *,
|
||||
PRUint32, z_stream *, PRInt32);
|
||||
|
||||
PRUint32 StreamID() { return mStreamID; }
|
||||
|
||||
nsresult ReadSegments(nsAHttpSegmentReader *, PRUint32, PRUint32 *);
|
||||
nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *);
|
||||
|
||||
bool RequestBlockedOnRead()
|
||||
{
|
||||
return static_cast<bool>(mRequestBlockedOnRead);
|
||||
}
|
||||
|
||||
// returns false if called more than once
|
||||
bool GetFullyOpen() {return mFullyOpen;}
|
||||
void SetFullyOpen()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!mFullyOpen, "SetFullyOpen already open");
|
||||
mFullyOpen = 1;
|
||||
}
|
||||
|
||||
nsAHttpTransaction *Transaction()
|
||||
{
|
||||
return mTransaction;
|
||||
}
|
||||
|
||||
void Close(nsresult reason);
|
||||
|
||||
void SetRecvdFin(bool aStatus) { mRecvdFin = aStatus ? 1 : 0; }
|
||||
bool RecvdFin() { return mRecvdFin; }
|
||||
|
||||
void UpdateTransportSendEvents(PRUint32 count);
|
||||
void UpdateTransportReadEvents(PRUint32 count);
|
||||
|
||||
// The zlib header compression dictionary defined by SPDY,
|
||||
// and hooks to the mozilla allocator for zlib to use.
|
||||
static const char *kDictionary;
|
||||
static void *zlib_allocator(void *, uInt, uInt);
|
||||
static void zlib_destructor(void *, void *);
|
||||
|
||||
private:
|
||||
|
||||
// a SpdyStream3 object is only destroyed by being removed from the
|
||||
// SpdySession3 mStreamTransactionHash - make the dtor private to
|
||||
// just the AutoPtr implementation needed for that hash.
|
||||
friend class nsAutoPtr<SpdyStream3>;
|
||||
~SpdyStream3();
|
||||
|
||||
enum stateType {
|
||||
GENERATING_SYN_STREAM,
|
||||
SENDING_SYN_STREAM,
|
||||
GENERATING_REQUEST_BODY,
|
||||
SENDING_REQUEST_BODY,
|
||||
SENDING_FIN_STREAM,
|
||||
UPSTREAM_COMPLETE
|
||||
};
|
||||
|
||||
static PLDHashOperator hdrHashEnumerate(const nsACString &,
|
||||
nsAutoPtr<nsCString> &,
|
||||
void *);
|
||||
|
||||
void ChangeState(enum stateType);
|
||||
nsresult ParseHttpRequestHeaders(const char *, PRUint32, PRUint32 *);
|
||||
nsresult TransmitFrame(const char *, PRUint32 *);
|
||||
void GenerateDataFrameHeader(PRUint32, bool);
|
||||
|
||||
void CompressToFrame(const nsACString &);
|
||||
void CompressToFrame(const nsACString *);
|
||||
void CompressToFrame(const char *, PRUint32);
|
||||
void CompressToFrame(PRUint16);
|
||||
void CompressFlushFrame();
|
||||
void ExecuteCompress(PRUint32);
|
||||
|
||||
// Each stream goes from syn_stream to upstream_complete, perhaps
|
||||
// looping on multiple instances of generating_request_body and
|
||||
// sending_request_body for each SPDY chunk in the upload.
|
||||
enum stateType mUpstreamState;
|
||||
|
||||
// The underlying HTTP transaction. This pointer is used as the key
|
||||
// in the SpdySession3 mStreamTransactionHash so it is important to
|
||||
// keep a reference to it as long as this stream is a member of that hash.
|
||||
// (i.e. don't change it or release it after it is set in the ctor).
|
||||
nsRefPtr<nsAHttpTransaction> mTransaction;
|
||||
|
||||
// The session that this stream is a subset of
|
||||
SpdySession3 *mSession;
|
||||
|
||||
// The underlying socket transport object is needed to propogate some events
|
||||
nsISocketTransport *mSocketTransport;
|
||||
|
||||
// These are temporary state variables to hold the argument to
|
||||
// Read/WriteSegments so it can be accessed by On(read/write)segment
|
||||
// further up the stack.
|
||||
nsAHttpSegmentReader *mSegmentReader;
|
||||
nsAHttpSegmentWriter *mSegmentWriter;
|
||||
|
||||
// The 24 bit SPDY stream ID
|
||||
PRUint32 mStreamID;
|
||||
|
||||
// The quanta upstream data frames are chopped into
|
||||
PRUint32 mChunkSize;
|
||||
|
||||
// Flag is set when all http request headers have been read
|
||||
PRUint32 mSynFrameComplete : 1;
|
||||
|
||||
// Flag is set when the HTTP processor has more data to send
|
||||
// but has blocked in doing so.
|
||||
PRUint32 mRequestBlockedOnRead : 1;
|
||||
|
||||
// Flag is set when a FIN has been placed on a data or syn packet
|
||||
// (i.e after the client has closed)
|
||||
PRUint32 mSentFinOnData : 1;
|
||||
|
||||
// Flag is set after the response frame bearing the fin bit has
|
||||
// been processed. (i.e. after the server has closed).
|
||||
PRUint32 mRecvdFin : 1;
|
||||
|
||||
// Flag is set after syn reply received
|
||||
PRUint32 mFullyOpen : 1;
|
||||
|
||||
// Flag is set after the WAITING_FOR Transport event has been generated
|
||||
PRUint32 mSentWaitingFor : 1;
|
||||
|
||||
// The InlineFrame and associated data is used for composing control
|
||||
// frames and data frame headers.
|
||||
nsAutoArrayPtr<char> mTxInlineFrame;
|
||||
PRUint32 mTxInlineFrameSize;
|
||||
PRUint32 mTxInlineFrameUsed;
|
||||
|
||||
// mTxStreamFrameSize tracks the progress of
|
||||
// transmitting a request body data frame. The data frame itself
|
||||
// is never copied into the spdy layer.
|
||||
PRUint32 mTxStreamFrameSize;
|
||||
|
||||
// Compression context and buffer for request header compression.
|
||||
// This is a copy of SpdySession3::mUpstreamZlib because it needs
|
||||
// to remain the same in all streams of a session.
|
||||
z_stream *mZlib;
|
||||
nsCString mFlatHttpRequestHeaders;
|
||||
|
||||
// Track the content-length of a request body so that we can
|
||||
// place the fin flag on the last data packet instead of waiting
|
||||
// for a stream closed indication. Relying on stream close results
|
||||
// in an extra 0-length runt packet and seems to have some interop
|
||||
// problems with the google servers.
|
||||
PRInt64 mRequestBodyLenRemaining;
|
||||
|
||||
// based on nsISupportsPriority definitions
|
||||
PRInt32 mPriority;
|
||||
|
||||
// For Progress Events
|
||||
PRUint64 mTotalSent;
|
||||
PRUint64 mTotalRead;
|
||||
};
|
||||
|
||||
}} // namespace mozilla::net
|
||||
|
||||
#endif // mozilla_net_SpdyStream3_h
|
@ -169,6 +169,8 @@ nsHttpHandler::nsHttpHandler()
|
||||
, mTelemetryEnabled(false)
|
||||
, mAllowExperiments(true)
|
||||
, mEnableSpdy(false)
|
||||
, mSpdyV2(true)
|
||||
, mSpdyV3(true)
|
||||
, mCoalesceSpdy(true)
|
||||
, mUseAlternateProtocol(false)
|
||||
, mSpdySendingChunkSize(ASpdySession::kSendingChunkSize)
|
||||
@ -1132,6 +1134,18 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
|
||||
mEnableSpdy = cVar;
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("spdy.enabled.v2"))) {
|
||||
rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled.v2"), &cVar);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mSpdyV2 = cVar;
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("spdy.enabled.v3"))) {
|
||||
rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled.v3"), &cVar);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mSpdyV3 = cVar;
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("spdy.coalesce-hostnames"))) {
|
||||
rv = prefs->GetBoolPref(HTTP_PREF("spdy.coalesce-hostnames"), &cVar);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
|
@ -87,6 +87,8 @@ public:
|
||||
bool AllowExperiments() { return mTelemetryEnabled && mAllowExperiments; }
|
||||
|
||||
bool IsSpdyEnabled() { return mEnableSpdy; }
|
||||
bool IsSpdyV2Enabled() { return mSpdyV2; }
|
||||
bool IsSpdyV3Enabled() { return mSpdyV3; }
|
||||
bool CoalesceSpdy() { return mCoalesceSpdy; }
|
||||
bool UseAlternateProtocol() { return mUseAlternateProtocol; }
|
||||
PRUint32 SpdySendingChunkSize() { return mSpdySendingChunkSize; }
|
||||
@ -357,6 +359,8 @@ private:
|
||||
// Try to use SPDY features instead of HTTP/1.1 over SSL
|
||||
mozilla::net::SpdyInformation mSpdyInfo;
|
||||
bool mEnableSpdy;
|
||||
bool mSpdyV2;
|
||||
bool mSpdyV3;
|
||||
bool mCoalesceSpdy;
|
||||
bool mUseAlternateProtocol;
|
||||
PRUint32 mSpdySendingChunkSize;
|
||||
|
Loading…
Reference in New Issue
Block a user