Bug 1248197 - Remove spdy/3.1 support. r=mcmanus

MozReview-Commit-ID: 1RgzxOY00Le

--HG--
extra : rebase_source : 6b37c087fcffcea7e5dd56d1e6ad5d099e8f5e49
This commit is contained in:
Nicholas Hurley 2016-07-15 15:13:49 -07:00
Родитель 067bab944a
Коммит c520425d6e
55 изменённых файлов: 18 добавлений и 12514 удалений

Просмотреть файл

@ -1500,7 +1500,6 @@ pref("network.http.bypass-cachelock-threshold", 250);
// Try and use SPDY when using SSL
pref("network.http.spdy.enabled", true);
pref("network.http.spdy.enabled.v3-1", false);
pref("network.http.spdy.enabled.http2", true);
pref("network.http.spdy.enabled.deps", true);
pref("network.http.spdy.enforce-tls-profile", true);

Просмотреть файл

@ -777,12 +777,8 @@ HttpConnInfo::SetHTTP1ProtocolVersion(uint8_t pv)
void
HttpConnInfo::SetHTTP2ProtocolVersion(uint8_t pv)
{
if (pv == SPDY_VERSION_31) {
protocolVersion.AssignLiteral(u"spdy/3.1");
} else {
MOZ_ASSERT (pv == HTTP_VERSION_2);
protocolVersion.Assign(u"h2");
}
MOZ_ASSERT (pv == HTTP_VERSION_2);
protocolVersion.Assign(u"h2");
}
NS_IMETHODIMP

Просмотреть файл

@ -8,7 +8,7 @@
#include "HttpLog.h"
/*
Currently supported are h2 and spdy/3.1
Currently supported is h2
*/
#include "nsHttp.h"
@ -16,9 +16,7 @@
#include "ASpdySession.h"
#include "PSpdyPush.h"
#include "SpdyPush31.h"
#include "Http2Push.h"
#include "SpdySession31.h"
#include "Http2Session.h"
#include "mozilla/Telemetry.h"
@ -40,8 +38,7 @@ ASpdySession::NewSpdySession(uint32_t version,
{
// This is a necko only interface, so we can enforce version
// requests as a precondition
MOZ_ASSERT(version == SPDY_VERSION_31 ||
version == HTTP_VERSION_2,
MOZ_ASSERT(version == HTTP_VERSION_2,
"Unsupported spdy version");
// Don't do a runtime check of IsSpdyV?Enabled() here because pref value
@ -51,30 +48,16 @@ ASpdySession::NewSpdySession(uint32_t version,
Telemetry::Accumulate(Telemetry::SPDY_VERSION2, version);
if (version == SPDY_VERSION_31) {
return new SpdySession31(aTransport);
} else if (version == HTTP_VERSION_2) {
return new Http2Session(aTransport, version);
}
return nullptr;
}
static bool SpdySessionTrue(nsISupports *securityInfo)
{
return true;
return new Http2Session(aTransport, version);
}
SpdyInformation::SpdyInformation()
{
// highest index of enabled protocols is the
// most preferred for ALPN negotiaton
Version[0] = SPDY_VERSION_31;
VersionString[0] = NS_LITERAL_CSTRING("spdy/3.1");
ALPNCallbacks[0] = SpdySessionTrue;
Version[1] = HTTP_VERSION_2;
VersionString[1] = NS_LITERAL_CSTRING("h2");
ALPNCallbacks[1] = Http2Session::ALPNCallback;
Version[0] = HTTP_VERSION_2;
VersionString[0] = NS_LITERAL_CSTRING("h2");
ALPNCallbacks[0] = Http2Session::ALPNCallback;
}
bool
@ -82,13 +65,7 @@ SpdyInformation::ProtocolEnabled(uint32_t index) const
{
MOZ_ASSERT(index < kCount, "index out of range");
switch (index) {
case 0:
return gHttpHandler->IsSpdyV31Enabled();
case 1:
return gHttpHandler->IsHttp2Enabled();
}
return false;
return gHttpHandler->IsHttp2Enabled();
}
nsresult
@ -118,36 +95,9 @@ SpdyPushCache::SpdyPushCache()
SpdyPushCache::~SpdyPushCache()
{
mHashSpdy31.Clear();
mHashHttp2.Clear();
}
bool
SpdyPushCache::RegisterPushedStreamSpdy31(nsCString key,
SpdyPushedStream31 *stream)
{
LOG3(("SpdyPushCache::RegisterPushedStreamSpdy31 %s 0x%X\n",
key.get(), stream->StreamID()));
if(mHashSpdy31.Get(key)) {
LOG3(("SpdyPushCache::RegisterPushedStreamSpdy31 %s 0x%X duplicate key\n",
key.get(), stream->StreamID()));
return false;
}
mHashSpdy31.Put(key, stream);
return true;
}
SpdyPushedStream31 *
SpdyPushCache::RemovePushedStreamSpdy31(nsCString key)
{
SpdyPushedStream31 *rv = mHashSpdy31.Get(key);
LOG3(("SpdyPushCache::RemovePushedStream %s 0x%X\n",
key.get(), rv ? rv->StreamID() : 0));
if (rv)
mHashSpdy31.Remove(key);
return rv;
}
bool
SpdyPushCache::RegisterPushedStreamHttp2(nsCString key,
Http2PushedStream *stream)

Просмотреть файл

@ -95,7 +95,7 @@ public:
SpdyInformation();
~SpdyInformation() {}
static const uint32_t kCount = 2;
static const uint32_t kCount = 1;
// determine the index (0..kCount-1) of the spdy information that
// correlates to the npn string. NS_FAILED() if no match is found.
@ -111,7 +111,6 @@ public:
// not to offer a particular protocol based on the known TLS information
// that we will offer in the client hello (such as version). There has
// not been a Server Hello received yet, so not much else can be considered.
// Stacks without restrictions can just use SpdySessionTrue()
ALPNCallback ALPNCallbacks[kCount];
};

Просмотреть файл

@ -318,10 +318,10 @@ public:
return;
}
// insist on spdy/3* or >= http/2
// insist on >= http/2
uint32_t version = mConnection->Version();
LOG(("AltSvcTransaction::MaybeValidate() %p version %d\n", this, version));
if ((version < HTTP_VERSION_2) && (version != SPDY_VERSION_31)) {
if (version < HTTP_VERSION_2) {
LOG(("AltSvcTransaction::MaybeValidate %p Failed due to protocol version", this));
return;
}

Просмотреть файл

@ -9,7 +9,6 @@
#include "nsHttpConnectionMgr.h"
#include "nsHttpConnection.h"
#include "SpdySession31.h"
#include "Http2Session.h"
#include "nsHttpHandler.h"
#include "nsIConsoleService.h"
@ -157,43 +156,6 @@ nsHttpConnection::PrintDiagnostics(nsCString &log)
mSpdySession->PrintDiagnostics(log);
}
void
SpdySession31::PrintDiagnostics(nsCString &log)
{
log.AppendPrintf(" ::: SPDY VERSION 3.1\n");
log.AppendPrintf(" shouldgoaway = %d mClosed = %d CanReuse = %d nextID=0x%X\n",
mShouldGoAway, mClosed, CanReuse(), mNextStreamID);
log.AppendPrintf(" concurrent = %d maxconcurrent = %d\n",
mConcurrent, mMaxConcurrent);
log.AppendPrintf(" roomformorestreams = %d roomformoreconcurrent = %d\n",
RoomForMoreStreams(), RoomForMoreConcurrent());
log.AppendPrintf(" transactionHashCount = %d streamIDHashCount = %d\n",
mStreamTransactionHash.Count(),
mStreamIDHash.Count());
log.AppendPrintf(" Queued Stream Size = %d\n", mQueuedStreams.GetSize());
PRIntervalTime now = PR_IntervalNow();
log.AppendPrintf(" Ping Threshold = %ums next ping id = 0x%X\n",
PR_IntervalToMilliseconds(mPingThreshold),
mNextPingID);
log.AppendPrintf(" Ping Timeout = %ums\n",
PR_IntervalToMilliseconds(gHttpHandler->SpdyPingTimeout()));
log.AppendPrintf(" Idle for Any Activity (ping) = %ums\n",
PR_IntervalToMilliseconds(now - mLastReadEpoch));
log.AppendPrintf(" Idle for Data Activity = %ums\n",
PR_IntervalToMilliseconds(now - mLastDataReadEpoch));
if (mPingSentEpoch)
log.AppendPrintf(" Ping Outstanding (ping) = %ums, expired = %d\n",
PR_IntervalToMilliseconds(now - mPingSentEpoch),
now - mPingSentEpoch >= gHttpHandler->SpdyPingTimeout());
else
log.AppendPrintf(" No Ping Outstanding\n");
}
void
Http2Session::PrintDiagnostics(nsCString &log)
{

Просмотреть файл

@ -34,7 +34,6 @@ class nsCString;
namespace mozilla {
namespace net {
class SpdyPushedStream31;
class Http2PushedStream;
// One cache per load group
@ -44,17 +43,6 @@ public:
// The cache holds only weak pointers - no references
SpdyPushCache();
virtual ~SpdyPushCache();
// for spdy/3.1
public:
bool RegisterPushedStreamSpdy31(nsCString key,
SpdyPushedStream31 *stream);
SpdyPushedStream31 *RemovePushedStreamSpdy31(nsCString key);
private:
nsDataHashtable<nsCStringHashKey, SpdyPushedStream31 *> mHashSpdy31;
// for http/2
public:
bool RegisterPushedStreamHttp2(nsCString key,
Http2PushedStream *stream);
Http2PushedStream *RemovePushedStreamHttp2(nsCString key);

Просмотреть файл

@ -1,378 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// HttpLog.h should generally be included first
#include "HttpLog.h"
// Log on level :5, instead of default :4.
#undef LOG
#define LOG(args) LOG5(args)
#undef LOG_ENABLED
#define LOG_ENABLED() LOG5_ENABLED()
#include <algorithm>
#include "nsDependentString.h"
#include "SpdyPush31.h"
namespace mozilla {
namespace net {
//////////////////////////////////////////
// SpdyPushedStream31
//////////////////////////////////////////
SpdyPushedStream31::SpdyPushedStream31(SpdyPush31TransactionBuffer *aTransaction,
SpdySession31 *aSession,
SpdyStream31 *aAssociatedStream,
uint32_t aID)
:SpdyStream31(aTransaction, aSession,
0 /* priority is only for sending, so ignore it on push */)
, mConsumerStream(nullptr)
, mBufferedPush(aTransaction)
, mStatus(NS_OK)
, mPushCompleted(false)
, mDeferCleanupOnSuccess(true)
{
LOG3(("SpdyPushedStream31 ctor this=%p id=0x%X\n", this, aID));
mStreamID = aID;
mBufferedPush->SetPushStream(this);
mRequestContext = aAssociatedStream->RequestContext();
mLastRead = TimeStamp::Now();
}
bool
SpdyPushedStream31::GetPushComplete()
{
return mPushCompleted;
}
nsresult
SpdyPushedStream31::WriteSegments(nsAHttpSegmentWriter *writer,
uint32_t count,
uint32_t *countWritten)
{
nsresult rv = SpdyStream31::WriteSegments(writer, count, countWritten);
if (NS_SUCCEEDED(rv) && *countWritten) {
mLastRead = TimeStamp::Now();
}
if (rv == NS_BASE_STREAM_CLOSED) {
mPushCompleted = true;
rv = NS_OK; // this is what a normal HTTP transaction would do
}
if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv))
mStatus = rv;
return rv;
}
nsresult
SpdyPushedStream31::ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *count)
{
// The SYN_STREAM for this has been processed, so we need to verify
// that :host, :scheme, and :path MUST be present
nsDependentCSubstring host, scheme, path;
nsresult rv;
rv = SpdyStream31::FindHeader(NS_LITERAL_CSTRING(":host"), host);
if (NS_FAILED(rv)) {
LOG3(("SpdyPushedStream31::ReadSegments session=%p ID 0x%X "
"push without required :host\n", mSession, mStreamID));
return rv;
}
rv = SpdyStream31::FindHeader(NS_LITERAL_CSTRING(":scheme"), scheme);
if (NS_FAILED(rv)) {
LOG3(("SpdyPushedStream31::ReadSegments session=%p ID 0x%X "
"push without required :scheme\n", mSession, mStreamID));
return rv;
}
rv = SpdyStream31::FindHeader(NS_LITERAL_CSTRING(":path"), path);
if (NS_FAILED(rv)) {
LOG3(("SpdyPushedStream31::ReadSegments session=%p ID 0x%X "
"push without required :host\n", mSession, mStreamID));
return rv;
}
CreatePushHashKey(nsCString(scheme), nsCString(host),
mSession->Serial(), path,
mOrigin, mHashKey);
LOG3(("SpdyPushStream31 0x%X hash key %s\n", mStreamID, mHashKey.get()));
// the write side of a pushed transaction just involves manipulating a little state
SpdyStream31::mSentFinOnData = 1;
SpdyStream31::mRequestHeadersDone = 1;
SpdyStream31::mSynFrameGenerated = 1;
SpdyStream31::ChangeState(UPSTREAM_COMPLETE);
*count = 0;
return NS_OK;
}
bool
SpdyPushedStream31::GetHashKey(nsCString &key)
{
if (mHashKey.IsEmpty())
return false;
key = mHashKey;
return true;
}
void
SpdyPushedStream31::ConnectPushedStream(SpdyStream31 *stream)
{
mSession->ConnectPushedStream(stream);
}
bool
SpdyPushedStream31::IsOrphaned(TimeStamp now)
{
MOZ_ASSERT(!now.IsNull());
// if spdy is not transmitting, and is also not connected to a consumer
// stream, and its been like that for too long then it is oprhaned
if (mConsumerStream)
return false;
bool rv = ((now - mLastRead).ToSeconds() > 30.0);
if (rv) {
LOG3(("SpdyPushedStream31::IsOrphaned 0x%X IsOrphaned %3.2f\n",
mStreamID, (now - mLastRead).ToSeconds()));
}
return rv;
}
nsresult
SpdyPushedStream31::GetBufferedData(char *buf,
uint32_t count,
uint32_t *countWritten)
{
if (NS_FAILED(mStatus))
return mStatus;
nsresult rv = mBufferedPush->GetBufferedData(buf, count, countWritten);
if (NS_FAILED(rv))
return rv;
if (!*countWritten)
rv = GetPushComplete() ? NS_BASE_STREAM_CLOSED : NS_BASE_STREAM_WOULD_BLOCK;
return rv;
}
//////////////////////////////////////////
// SpdyPush31TransactionBuffer
// This is the nsAHttpTransction owned by the stream when the pushed
// stream has not yet been matched with a pull request
//////////////////////////////////////////
NS_IMPL_ISUPPORTS0(SpdyPush31TransactionBuffer)
SpdyPush31TransactionBuffer::SpdyPush31TransactionBuffer()
: mStatus(NS_OK)
, mRequestHead(nullptr)
, mPushStream(nullptr)
, mIsDone(false)
, mBufferedHTTP1Size(kDefaultBufferSize)
, mBufferedHTTP1Used(0)
, mBufferedHTTP1Consumed(0)
{
mBufferedHTTP1 = MakeUnique<char[]>(mBufferedHTTP1Size);
}
SpdyPush31TransactionBuffer::~SpdyPush31TransactionBuffer()
{
delete mRequestHead;
}
void
SpdyPush31TransactionBuffer::SetConnection(nsAHttpConnection *conn)
{
}
nsAHttpConnection *
SpdyPush31TransactionBuffer::Connection()
{
return nullptr;
}
void
SpdyPush31TransactionBuffer::GetSecurityCallbacks(nsIInterfaceRequestor **outCB)
{
*outCB = nullptr;
}
void
SpdyPush31TransactionBuffer::OnTransportStatus(nsITransport* transport,
nsresult status, int64_t progress)
{
}
nsHttpConnectionInfo *
SpdyPush31TransactionBuffer::ConnectionInfo()
{
if (!mPushStream) {
return nullptr;
}
if (!mPushStream->Transaction()) {
return nullptr;
}
MOZ_ASSERT(mPushStream->Transaction() != this);
return mPushStream->Transaction()->ConnectionInfo();
}
bool
SpdyPush31TransactionBuffer::IsDone()
{
return mIsDone;
}
nsresult
SpdyPush31TransactionBuffer::Status()
{
return mStatus;
}
uint32_t
SpdyPush31TransactionBuffer::Caps()
{
return 0;
}
void
SpdyPush31TransactionBuffer::SetDNSWasRefreshed()
{
}
uint64_t
SpdyPush31TransactionBuffer::Available()
{
return mBufferedHTTP1Used - mBufferedHTTP1Consumed;
}
nsresult
SpdyPush31TransactionBuffer::ReadSegments(nsAHttpSegmentReader *reader,
uint32_t count, uint32_t *countRead)
{
*countRead = 0;
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
SpdyPush31TransactionBuffer::WriteSegments(nsAHttpSegmentWriter *writer,
uint32_t count, uint32_t *countWritten)
{
if ((mBufferedHTTP1Size - mBufferedHTTP1Used) < 20480) {
EnsureBuffer(mBufferedHTTP1, mBufferedHTTP1Size + kDefaultBufferSize,
mBufferedHTTP1Used, mBufferedHTTP1Size);
}
count = std::min(count, mBufferedHTTP1Size - mBufferedHTTP1Used);
nsresult rv = writer->OnWriteSegment(&mBufferedHTTP1[mBufferedHTTP1Used],
count, countWritten);
if (NS_SUCCEEDED(rv)) {
mBufferedHTTP1Used += *countWritten;
}
else if (rv == NS_BASE_STREAM_CLOSED) {
mIsDone = true;
}
if (Available() || mIsDone) {
SpdyStream31 *consumer = mPushStream->GetConsumerStream();
if (consumer) {
LOG3(("SpdyPush31TransactionBuffer::WriteSegments notifying connection "
"consumer data available 0x%X [%u] done=%d\n",
mPushStream->StreamID(), Available(), mIsDone));
mPushStream->ConnectPushedStream(consumer);
}
}
return rv;
}
uint32_t
SpdyPush31TransactionBuffer::Http1xTransactionCount()
{
return 0;
}
nsHttpRequestHead *
SpdyPush31TransactionBuffer::RequestHead()
{
if (!mRequestHead)
mRequestHead = new nsHttpRequestHead();
return mRequestHead;
}
nsresult
SpdyPush31TransactionBuffer::TakeSubTransactions(
nsTArray<RefPtr<nsAHttpTransaction> > &outTransactions)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
void
SpdyPush31TransactionBuffer::SetProxyConnectFailed()
{
}
void
SpdyPush31TransactionBuffer::Close(nsresult reason)
{
mStatus = reason;
mIsDone = true;
}
nsresult
SpdyPush31TransactionBuffer::AddTransaction(nsAHttpTransaction *trans)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
uint32_t
SpdyPush31TransactionBuffer::PipelineDepth()
{
return 0;
}
nsresult
SpdyPush31TransactionBuffer::SetPipelinePosition(int32_t position)
{
return NS_OK;
}
int32_t
SpdyPush31TransactionBuffer::PipelinePosition()
{
return 1;
}
nsresult
SpdyPush31TransactionBuffer::GetBufferedData(char *buf,
uint32_t count,
uint32_t *countWritten)
{
*countWritten = std::min(count, static_cast<uint32_t>(Available()));
if (*countWritten) {
memcpy(buf, &mBufferedHTTP1[mBufferedHTTP1Consumed], *countWritten);
mBufferedHTTP1Consumed += *countWritten;
}
// If all the data has been consumed then reset the buffer
if (mBufferedHTTP1Consumed == mBufferedHTTP1Used) {
mBufferedHTTP1Consumed = 0;
mBufferedHTTP1Used = 0;
}
return NS_OK;
}
} // namespace net
} // namespace mozilla

Просмотреть файл

@ -1,103 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// spdy/3.1
#ifndef mozilla_net_SpdyPush31_Internal_h
#define mozilla_net_SpdyPush31_Internal_h
#include "mozilla/Attributes.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "nsHttpRequestHead.h"
#include "nsIRequestContext.h"
#include "nsString.h"
#include "PSpdyPush.h"
#include "SpdySession31.h"
#include "SpdyStream31.h"
namespace mozilla {
namespace net {
class SpdyPush31TransactionBuffer;
class SpdyPushedStream31 final : public SpdyStream31
{
public:
SpdyPushedStream31(SpdyPush31TransactionBuffer *aTransaction,
SpdySession31 *aSession,
SpdyStream31 *aAssociatedStream,
uint32_t aID);
virtual ~SpdyPushedStream31() {}
bool GetPushComplete();
SpdyStream31 *GetConsumerStream() { return mConsumerStream; };
void SetConsumerStream(SpdyStream31 *aStream) { mConsumerStream = aStream; }
bool GetHashKey(nsCString &key);
// override of SpdyStream31
nsresult ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *);
nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *);
nsIRequestContext *RequestContext() { return mRequestContext; };
void ConnectPushedStream(SpdyStream31 *consumer);
bool DeferCleanupOnSuccess() { return mDeferCleanupOnSuccess; }
void SetDeferCleanupOnSuccess(bool val) { mDeferCleanupOnSuccess = val; }
bool IsOrphaned(TimeStamp now);
nsresult GetBufferedData(char *buf, uint32_t count, uint32_t *countWritten);
// overload of SpdyStream31
virtual bool HasSink() { return !!mConsumerStream; }
private:
SpdyStream31 *mConsumerStream; // paired request stream that consumes from
// real spdy one.. null until a match is made.
nsCOMPtr<nsIRequestContext> mRequestContext;
SpdyPush31TransactionBuffer *mBufferedPush;
TimeStamp mLastRead;
nsCString mHashKey;
nsresult mStatus;
bool mPushCompleted; // server push FIN received
bool mDeferCleanupOnSuccess;
};
class SpdyPush31TransactionBuffer final : public nsAHttpTransaction
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSAHTTPTRANSACTION
SpdyPush31TransactionBuffer();
nsresult GetBufferedData(char *buf, uint32_t count, uint32_t *countWritten);
void SetPushStream(SpdyPushedStream31 *stream) { mPushStream = stream; }
private:
virtual ~SpdyPush31TransactionBuffer();
const static uint32_t kDefaultBufferSize = 4096;
nsresult mStatus;
nsHttpRequestHead *mRequestHead;
SpdyPushedStream31 *mPushStream;
bool mIsDone;
UniquePtr<char[]> mBufferedHTTP1;
uint32_t mBufferedHTTP1Size;
uint32_t mBufferedHTTP1Used;
uint32_t mBufferedHTTP1Consumed;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_SpdyPush3_Internal_h

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,422 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_SpdySession31_h
#define mozilla_net_SpdySession31_h
// spdy/3.1
#include "ASpdySession.h"
#include "mozilla/Attributes.h"
#include "mozilla/UniquePtr.h"
#include "nsAHttpConnection.h"
#include "nsClassHashtable.h"
#include "nsDataHashtable.h"
#include "nsDeque.h"
#include "nsHashKeys.h"
#include "zlib.h"
class nsISocketTransport;
namespace mozilla { namespace net {
class SpdyPushedStream31;
class SpdyStream31;
class nsHttpTransaction;
class SpdySession31 final : public ASpdySession
, public nsAHttpConnection
, public nsAHttpSegmentReader
, public nsAHttpSegmentWriter
{
~SpdySession31();
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSAHTTPTRANSACTION
NS_DECL_NSAHTTPCONNECTION(mConnection)
NS_DECL_NSAHTTPSEGMENTREADER
NS_DECL_NSAHTTPSEGMENTWRITER
explicit SpdySession31(nsISocketTransport *);
bool AddStream(nsAHttpTransaction *, int32_t,
bool, nsIInterfaceRequestor *) override;
bool CanReuse() override { return !mShouldGoAway && !mClosed; }
bool RoomForMoreStreams() override;
// When the connection is active this is called up to once every 1 second
// return the interval (in seconds) that the connection next wants to
// have this invoked. It might happen sooner depending on the needs of
// other connections.
uint32_t ReadTimeoutTick(PRIntervalTime now) override;
// Idle time represents time since "goodput".. e.g. a data or header frame
PRIntervalTime IdleTime() override;
// Registering with a newID of 0 means pick the next available odd ID
uint32_t RegisterStreamID(SpdyStream31 *, uint32_t aNewID = 0);
const static uint8_t kVersion = 3;
const static uint8_t kFlag_Control = 0x80;
const static uint8_t kFlag_Data_FIN = 0x01;
const static uint8_t kFlag_Data_UNI = 0x02;
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, /* deprecated */
CONTROL_TYPE_PING = 6,
CONTROL_TYPE_GOAWAY = 7,
CONTROL_TYPE_HEADERS = 8,
CONTROL_TYPE_WINDOW_UPDATE = 9,
CONTROL_TYPE_CREDENTIAL = 10,
CONTROL_TYPE_LAST = 11
};
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_STREAM_IN_USE = 8,
RST_STREAM_ALREADY_CLOSED = 9,
RST_INVALID_CREDENTIALS = 10,
RST_FRAME_TOO_LARGE = 11
};
enum goawayReason
{
OK = 0,
PROTOCOL_ERROR = 1,
INTERNAL_ERROR = 2, // sometimes misdocumented as 11
NUM_STATUS_CODES = 3 // reserved by chromium but undocumented
};
enum settingsFlags
{
PERSIST_VALUE = 1,
PERSISTED_VALUE = 2
};
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 for flow control
SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE = 8
};
// 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 responses from SPDY google services seem to be > 1000
// with all less than 2000 when compression is enabled.
const static uint32_t kDefaultBufferSize = 2048;
// kDefaultQueueSize must be >= other queue size constants
const static uint32_t kDefaultQueueSize = 32768;
const static uint32_t kQueueMinimumCleanup = 24576;
const static uint32_t kQueueTailRoom = 4096;
const static uint32_t kQueueReserved = 1024;
const static uint32_t kMaxStreamID = 0x7800000;
// This is a sentinel for a deleted stream. It is not a valid
// 31 bit stream ID.
const static uint32_t kDeadStreamID = 0xffffdead;
// below the emergency threshold of local window we ack every received
// byte. Above that we coalesce bytes into the MinimumToAck size.
const static int32_t kEmergencyWindowThreshold = 1024 * 1024;
const static uint32_t kMinimumToAck = 64 * 1024;
// The default rwin is 64KB unless updated by a settings frame
const static uint32_t kDefaultRwin = 64 * 1024;
static nsresult HandleSynStream(SpdySession31 *);
static nsresult HandleSynReply(SpdySession31 *);
static nsresult HandleRstStream(SpdySession31 *);
static nsresult HandleSettings(SpdySession31 *);
static nsresult HandleNoop(SpdySession31 *);
static nsresult HandlePing(SpdySession31 *);
static nsresult HandleGoAway(SpdySession31 *);
static nsresult HandleHeaders(SpdySession31 *);
static nsresult HandleWindowUpdate(SpdySession31 *);
static nsresult HandleCredential(SpdySession31 *);
// For writing the SPDY data stream to LOG4
static void LogIO(SpdySession31 *, SpdyStream31 *, const char *,
const char *, uint32_t);
// an overload of nsAHttpConnection
void TransactionHasDataToWrite(nsAHttpTransaction *) override;
// a similar version for SpdyStream31
void TransactionHasDataToWrite(SpdyStream31 *);
// an overload of nsAHttpSegementReader
virtual nsresult CommitToSegmentSize(uint32_t size, bool forceCommitment) override;
nsresult BufferOutput(const char *, uint32_t, uint32_t *);
void FlushOutputQueue();
uint32_t AmountOfOutputBuffered() { return mOutputQueueUsed - mOutputQueueSent; }
uint32_t GetServerInitialStreamWindow() { return mServerInitialStreamWindow; }
bool TryToActivate(SpdyStream31 *stream);
void ConnectPushedStream(SpdyStream31 *stream);
void DecrementConcurrent(SpdyStream31 *stream);
uint64_t Serial() { return mSerial; }
void PrintDiagnostics (nsCString &log) override;
// Streams need access to these
uint32_t SendingChunkSize() { return mSendingChunkSize; }
uint32_t PushAllowance() { return mPushAllowance; }
z_stream *UpstreamZlib() { return &mUpstreamZlib; }
nsISocketTransport *SocketTransport() { return mSocketTransport; }
int64_t RemoteSessionWindow() { return mRemoteSessionWindow; }
void DecrementRemoteSessionWindow (uint32_t bytes) { mRemoteSessionWindow -= bytes; }
void SendPing() override;
bool MaybeReTunnel(nsAHttpTransaction *) override;
// overload of nsAHttpTransaction
nsresult ReadSegmentsAgain(nsAHttpSegmentReader *, uint32_t, uint32_t *, bool *) override final;
nsresult WriteSegmentsAgain(nsAHttpSegmentWriter *, uint32_t , uint32_t *, bool *) override final;
private:
enum stateType {
BUFFERING_FRAME_HEADER,
BUFFERING_CONTROL_FRAME,
PROCESSING_DATA_FRAME,
DISCARDING_DATA_FRAME,
PROCESSING_COMPLETE_HEADERS,
PROCESSING_CONTROL_RST_STREAM
};
nsresult ResponseHeadersComplete();
uint32_t GetWriteQueueSize();
void ChangeDownstreamState(enum stateType);
void ResetDownstreamState();
nsresult UncompressAndDiscard(uint32_t, uint32_t);
void zlibInit();
void GeneratePing(uint32_t);
void GenerateRstStream(uint32_t, uint32_t);
void GenerateGoAway(uint32_t);
void CleanupStream(SpdyStream31 *, nsresult, rstReason);
void CloseStream(SpdyStream31 *, nsresult);
void GenerateSettings();
void RemoveStreamFromQueues(SpdyStream31 *);
void SetWriteCallbacks();
void RealignOutputQueue();
void ProcessPending();
nsresult SetInputFrameDataStream(uint32_t);
bool VerifyStream(SpdyStream31 *, uint32_t);
void SetNeedsCleanup();
void UpdateLocalRwin(SpdyStream31 *stream, uint32_t bytes);
void UpdateLocalStreamWindow(SpdyStream31 *stream, uint32_t bytes);
void UpdateLocalSessionWindow(uint32_t bytes);
bool RoomForMoreConcurrent();
void IncrementConcurrent(SpdyStream31 *stream);
void QueueStream(SpdyStream31 *stream);
// a wrapper for all calls to the nshttpconnection level segment writer. Used
// to track network I/O for timeout purposes
nsresult NetworkRead(nsAHttpSegmentWriter *, char *, uint32_t, uint32_t *);
void Shutdown();
// This is intended to be nsHttpConnectionMgr:nsConnectionHandle taken
// from the first transaction on this session. That object contains the
// pointer to the real network-level nsHttpConnection object.
RefPtr<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;
uint32_t mSendingChunkSize; /* the transmission chunk size */
uint32_t mNextStreamID; /* 24 bits */
uint32_t mConcurrentHighWater; /* max parallelism on session */
uint32_t mPushAllowance; /* rwin for unmatched pushes */
stateType mDownstreamState; /* in frame, between frames, etc.. */
// Maintain 2 indexes - one by stream ID, one by transaction pointer.
// There are also several lists of streams: ready to write, queued due to
// max parallelism, streams that need to force a read for push, and the full
// set of pushed streams.
// The objects are not ref counted - they get destroyed
// by the nsClassHashtable implementation when they are removed from
// the transaction hash.
nsDataHashtable<nsUint32HashKey, SpdyStream31 *> mStreamIDHash;
nsClassHashtable<nsPtrHashKey<nsAHttpTransaction>,
SpdyStream31> mStreamTransactionHash;
nsDeque mReadyForWrite;
nsDeque mQueuedStreams;
nsDeque mReadyForRead;
nsTArray<SpdyPushedStream31 *> mPushedStreams;
// Compression contexts for header transport using deflate.
// SPDY compresses only HTTP headers and does not reset zlib in between
// frames. Even data that is not associated with a stream (e.g invalid
// stream ID) is passed through these contexts to keep the compression
// context correct.
z_stream mDownstreamZlib;
z_stream mUpstreamZlib;
// mInputFrameBuffer is used to store received control packets and the 8 bytes
// of header on data packets
uint32_t mInputFrameBufferSize;
uint32_t mInputFrameBufferUsed;
UniquePtr<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
uint32_t mInputFrameDataSize;
uint32_t 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.
SpdyStream31 *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.
SpdyStream31 *mNeedsCleanup;
// The CONTROL_TYPE value for a control frame
uint32_t mFrameControlType;
// This reason code in the last processed RESET frame
uint32_t mDownstreamRstReason;
// for the conversion of downstream http headers into spdy formatted headers
// The data here does not persist between frames
nsCString mFlatHTTPResponseHeaders;
uint32_t 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;
// indicates PROCESSING_COMPLETE_HEADERS state was pushed onto the stack
// over an active PROCESSING_DATA_FRAME, which should be restored when
// the processed headers are written to the stream
bool mDataPending;
// If a GoAway message was received this is the ID of the last valid
// stream. 0 otherwise. (0 is never a valid stream id.)
uint32_t 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.
uint32_t 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
uint32_t mConcurrent;
// The number of server initiated SYN-STREAMS, tracked for telemetry
uint32_t mServerPushedResources;
// The server rwin for new streams as determined from a SETTINGS frame
uint32_t mServerInitialStreamWindow;
// The Local Session window is how much data the server is allowed to send
// (across all streams) without getting a window update to stream 0. It is
// signed because asynchronous changes via SETTINGS can drive it negative.
int64_t mLocalSessionWindow;
// The Remote Session Window is how much data the client is allowed to send
// (across all streams) without receiving a window update to stream 0. It is
// signed because asynchronous changes via SETTINGS can drive it negative.
int64_t mRemoteSessionWindow;
// 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.
uint32_t mOutputQueueSize;
uint32_t mOutputQueueUsed;
uint32_t mOutputQueueSent;
UniquePtr<char[]> mOutputQueueBuffer;
PRIntervalTime mPingThreshold;
PRIntervalTime mLastReadEpoch; // used for ping timeouts
PRIntervalTime mLastDataReadEpoch; // used for IdleTime()
PRIntervalTime mPingSentEpoch;
uint32_t mNextPingID;
PRIntervalTime mPreviousPingThreshold; // backup for the former value
bool mPreviousUsed; // true when backup is used
// used as a temporary buffer while enumerating the stream hash during GoAway
nsDeque mGoAwayStreamsToRestart;
// Each session gets a unique serial number because the push cache is correlated
// by the load group and the serial number can be used as part of the cache key
// to make sure streams aren't shared across sessions.
uint64_t mSerial;
private:
/// connect tunnels
void DispatchOnTunnel(nsAHttpTransaction *, nsIInterfaceRequestor *);
void CreateTunnel(nsHttpTransaction *, nsHttpConnectionInfo *, nsIInterfaceRequestor *);
void RegisterTunnel(SpdyStream31 *);
void UnRegisterTunnel(SpdyStream31 *);
uint32_t FindTunnelCount(nsHttpConnectionInfo *);
nsDataHashtable<nsCStringHashKey, uint32_t> mTunnelHash;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_SpdySession31_h

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,282 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_SpdyStream31_h
#define mozilla_net_SpdyStream31_h
#include "mozilla/Attributes.h"
#include "mozilla/UniquePtr.h"
#include "nsAHttpTransaction.h"
namespace mozilla { namespace net {
class SpdyStream31 : public nsAHttpSegmentReader
, public nsAHttpSegmentWriter
{
public:
NS_DECL_NSAHTTPSEGMENTREADER
NS_DECL_NSAHTTPSEGMENTWRITER
SpdyStream31(nsAHttpTransaction *, SpdySession31 *, int32_t);
uint32_t StreamID() { return mStreamID; }
SpdyPushedStream31 *PushSource() { return mPushSource; }
virtual nsresult ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *);
virtual nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *);
virtual bool DeferCleanupOnSuccess() { return false; }
const nsAFlatCString &Origin() const { return mOrigin; }
bool RequestBlockedOnRead()
{
return static_cast<bool>(mRequestBlockedOnRead);
}
bool GetFullyOpen();
// returns failure if stream cannot be made ready and stream
// should be canceled
nsresult SetFullyOpen();
bool HasRegisteredID() { return mStreamID != 0; }
nsAHttpTransaction *Transaction() { return mTransaction; }
virtual nsIRequestContext *RequestContext()
{
return mTransaction ? mTransaction->RequestContext() : nullptr;
}
void Close(nsresult reason);
void SetResponseIsComplete();
void SetRecvdFin(bool aStatus) { mRecvdFin = aStatus ? 1 : 0; }
bool RecvdFin() { return mRecvdFin; }
void SetRecvdData(bool aStatus) { mReceivedData = aStatus ? 1 : 0; }
bool RecvdData() { return mReceivedData; }
void SetQueued(bool aStatus) { mQueued = aStatus ? 1 : 0; }
bool Queued() { return mQueued; }
void SetCountAsActive(bool aStatus) { mCountAsActive = aStatus ? 1 : 0; }
bool CountAsActive() { return mCountAsActive; }
void UpdateTransportSendEvents(uint32_t count);
void UpdateTransportReadEvents(uint32_t count);
// The zlib header compression dictionary defined by SPDY.
static const unsigned char kDictionary[1423];
nsresult Uncompress(z_stream *, char *, uint32_t);
nsresult ConvertHeaders(nsACString &);
void UpdateRemoteWindow(int32_t delta);
int64_t RemoteWindow() { return mRemoteWindow; }
void DecrementLocalWindow(uint32_t delta) {
mLocalWindow -= delta;
mLocalUnacked += delta;
}
void IncrementLocalWindow(uint32_t delta) {
mLocalWindow += delta;
mLocalUnacked -= delta;
}
uint64_t LocalUnAcked() { return mLocalUnacked; }
int64_t LocalWindow() { return mLocalWindow; }
bool BlockedOnRwin() { return mBlockedOnRwin; }
bool ChannelPipeFull();
// A pull stream has an implicit sink, a pushed stream has a sink
// once it is matched to a pull stream.
virtual bool HasSink() { return true; }
virtual ~SpdyStream31();
protected:
nsresult FindHeader(nsCString, nsDependentCSubstring &);
static void CreatePushHashKey(const nsCString &scheme,
const nsCString &hostHeader,
uint64_t serial,
const nsCSubstring &pathInfo,
nsCString &outOrigin,
nsCString &outKey);
enum stateType {
GENERATING_SYN_STREAM,
GENERATING_REQUEST_BODY,
SENDING_REQUEST_BODY,
SENDING_FIN_STREAM,
UPSTREAM_COMPLETE
};
uint32_t mStreamID;
// The session that this stream is a subset of
SpdySession31 *mSession;
nsCString mOrigin;
// 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;
// Flag is set when all http request headers have been read
uint32_t mRequestHeadersDone : 1;
// Flag is set when stream ID is stable
uint32_t mSynFrameGenerated : 1;
// Flag is set when a FIN has been placed on a data or syn packet
// (i.e after the client has closed)
uint32_t mSentFinOnData : 1;
// Flag is set when stream is queued inside the session due to
// concurrency limits being exceeded
uint32_t mQueued : 1;
void ChangeState(enum stateType);
private:
friend class nsAutoPtr<SpdyStream31>;
nsresult ParseHttpRequestHeaders(const char *, uint32_t, uint32_t *);
nsresult GenerateSynFrame();
void AdjustInitialWindow();
nsresult TransmitFrame(const char *, uint32_t *, bool forceCommitment);
void GenerateDataFrameHeader(uint32_t, bool);
void CompressToFrame(const nsACString &);
void CompressToFrame(const nsACString *);
void CompressToFrame(const char *, uint32_t);
void CompressToFrame(uint32_t);
void CompressFlushFrame();
void ExecuteCompress(uint32_t);
// The underlying HTTP transaction. This pointer is used as the key
// in the SpdySession31 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).
RefPtr<nsAHttpTransaction> mTransaction;
// 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 quanta upstream data frames are chopped into
uint32_t mChunkSize;
// Flag is set when the HTTP processor has more data to send
// but has blocked in doing so.
uint32_t mRequestBlockedOnRead : 1;
// Flag is set after the response frame bearing the fin bit has
// been processed. (i.e. after the server has closed).
uint32_t mRecvdFin : 1;
// Flag is set after syn reply received
uint32_t mFullyOpen : 1;
// Flag is set after the WAITING_FOR Transport event has been generated
uint32_t mSentWaitingFor : 1;
// Flag is set after 1st DATA frame has been passed to stream, after
// which additional HEADERS data is invalid
uint32_t mReceivedData : 1;
// Flag is set after TCP send autotuning has been disabled
uint32_t mSetTCPSocketBuffer : 1;
// Flag is set when stream is counted towards MAX_CONCURRENT streams in session
uint32_t mCountAsActive : 1;
// The InlineFrame and associated data is used for composing control
// frames and data frame headers.
UniquePtr<uint8_t[]> mTxInlineFrame;
uint32_t mTxInlineFrameSize;
uint32_t mTxInlineFrameUsed;
// mTxStreamFrameSize tracks the progress of
// transmitting a request body data frame. The data frame itself
// is never copied into the spdy layer.
uint32_t mTxStreamFrameSize;
// Compression context and buffer for request header compression.
// This is a copy of SpdySession31::mUpstreamZlib because it needs
// to remain the same in all streams of a session.
z_stream *mZlib;
nsCString mFlatHttpRequestHeaders;
// These are used for decompressing downstream spdy response headers
uint32_t mDecompressBufferSize;
uint32_t mDecompressBufferUsed;
uint32_t mDecompressedBytes;
UniquePtr<char[]> mDecompressBuffer;
// 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. Connect does rely on stream
// close by setting this to the max value.
int64_t mRequestBodyLenRemaining;
// based on nsISupportsPriority definitions
int32_t mPriority;
// mLocalWindow, mRemoteWindow, and mLocalUnacked are for flow control.
// *window are signed because the race conditions in asynchronous SETTINGS
// messages can force them temporarily negative.
// LocalWindow is how much data the server will send without getting a
// window update
int64_t mLocalWindow;
// RemoteWindow is how much data the client is allowed to send without
// getting a window update
int64_t mRemoteWindow;
// LocalUnacked is the number of bytes received by the client but not
// yet reflected in a window update. Sending that update will increment
// LocalWindow
uint64_t mLocalUnacked;
// True when sending is suspended becuase the remote flow control window is
// <= 0
bool mBlockedOnRwin;
// For Progress Events
uint64_t mTotalSent;
uint64_t mTotalRead;
// For SpdyPush
SpdyPushedStream31 *mPushSource;
/// connect tunnels
public:
bool IsTunnel() { return mIsTunnel; }
private:
void ClearTransactionsBlockedOnTunnel();
void MapStreamToPlainText();
void MapStreamToHttpConnection();
bool mIsTunnel;
bool mPlainTextTunnel;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_SpdyStream31_h

Просмотреть файл

@ -1,39 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SpdyZlibReporter.h"
namespace mozilla {
NS_IMPL_ISUPPORTS(SpdyZlibReporter, nsIMemoryReporter)
/* static */ Atomic<size_t> SpdyZlibReporter::sAmount;
/* static */ void*
SpdyZlibReporter::Alloc(void*, uInt items, uInt size)
{
void* p = moz_xmalloc(items * size);
sAmount += MallocSizeOfOnAlloc(p);
return p;
}
/* static */ void
SpdyZlibReporter::Free(void*, void* p)
{
sAmount -= MallocSizeOfOnFree(p);
free(p);
}
NS_IMETHODIMP
SpdyZlibReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize)
{
return MOZ_COLLECT_REPORT(
"explicit/network/spdy-zlib-buffers", KIND_HEAP, UNITS_BYTES, sAmount,
"Memory allocated for SPDY zlib send and receive buffers.");
}
} // namespace mozilla

Просмотреть файл

@ -1,58 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* A memory allocator for zlib use in SPDY that reports to about:memory. */
#ifndef mozilla_net_SpdyZlibReporter_h
#define mozilla_net_SpdyZlibReporter_h
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
#include "nsIMemoryReporter.h"
#include "zlib.h"
namespace mozilla {
class SpdyZlibReporter final : public nsIMemoryReporter
{
~SpdyZlibReporter() {}
public:
NS_DECL_ISUPPORTS
SpdyZlibReporter()
{
#ifdef DEBUG
// There must be only one instance of this class, due to |sAmount|
// being static.
static bool hasRun = false;
MOZ_ASSERT(!hasRun);
hasRun = true;
#endif
sAmount = 0;
}
static void* Alloc(void*, uInt items, uInt size);
static void Free(void*, void* p);
private:
// |sAmount| can be (implicitly) accessed by multiple threads, so it
// must be thread-safe.
static Atomic<size_t> sAmount;
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
NS_IMETHODIMP
CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
bool aAnonymize) override;
};
} // namespace mozilla
#endif // mozilla_net_SpdyZlibReporter_h

Просмотреть файл

@ -85,10 +85,6 @@ UNIFIED_SOURCES += [
'NullHttpTransaction.cpp',
'PackagedAppService.cpp',
'PackagedAppVerifier.cpp',
'SpdyPush31.cpp',
'SpdySession31.cpp',
'SpdyStream31.cpp',
'SpdyZlibReporter.cpp',
'TunnelUtils.cpp',
]

Просмотреть файл

@ -231,8 +231,6 @@ const char*
nsHttp::GetProtocolVersion(uint32_t pv)
{
switch (pv) {
case SPDY_VERSION_31:
return "spdy/3.1";
case HTTP_VERSION_2:
case NS_HTTP_VERSION_2_0:
return "h2";

Просмотреть файл

@ -30,7 +30,7 @@ namespace net {
enum {
// SPDY_VERSION_2 = 2, REMOVED
// SPDY_VERSION_3 = 3, REMOVED
SPDY_VERSION_31 = 4,
// SPDY_VERSION_31 = 4, REMOVED
HTTP_VERSION_2 = 5
// leave room for official versions. telem goes to 48

Просмотреть файл

@ -44,7 +44,6 @@
#include "nsIStreamConverterService.h"
#include "nsITimer.h"
#include "nsCRT.h"
#include "SpdyZlibReporter.h"
#include "nsIMemoryReporter.h"
#include "nsIParentalControlsService.h"
#include "nsPIDOMWindow.h"
@ -210,7 +209,6 @@ nsHttpHandler::nsHttpHandler()
, mAllowExperiments(true)
, mDebugObservations(false)
, mEnableSpdy(false)
, mSpdyV31(true)
, mHttp2Enabled(true)
, mUseH2Deps(true)
, mEnforceHttp2TlsProfile(true)
@ -243,8 +241,6 @@ nsHttpHandler::nsHttpHandler()
{
LOG(("Creating nsHttpHandler [this=%p].\n", this));
RegisterStrongMemoryReporter(new SpdyZlibReporter());
MOZ_ASSERT(!gHttpHandler, "HTTP handler already created!");
gHttpHandler = this;
}
@ -1319,12 +1315,6 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
mEnableSpdy = cVar;
}
if (PREF_CHANGED(HTTP_PREF("spdy.enabled.v3-1"))) {
rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled.v3-1"), &cVar);
if (NS_SUCCEEDED(rv))
mSpdyV31 = cVar;
}
if (PREF_CHANGED(HTTP_PREF("spdy.enabled.http2"))) {
rv = prefs->GetBoolPref(HTTP_PREF("spdy.enabled.http2"), &cVar);
if (NS_SUCCEEDED(rv))

Просмотреть файл

@ -108,7 +108,6 @@ public:
bool AllowExperiments() { return mTelemetryEnabled && mAllowExperiments; }
bool IsSpdyEnabled() { return mEnableSpdy; }
bool IsSpdyV31Enabled() { return mSpdyV31; }
bool IsHttp2Enabled() { return mHttp2Enabled; }
bool EnforceHttp2TlsProfile() { return mEnforceHttp2TlsProfile; }
bool CoalesceSpdy() { return mCoalesceSpdy; }
@ -513,7 +512,6 @@ private:
uint32_t mDebugObservations : 1;
uint32_t mEnableSpdy : 1;
uint32_t mSpdyV31 : 1;
uint32_t mHttp2Enabled : 1;
uint32_t mUseH2Deps : 1;
uint32_t mEnforceHttp2TlsProfile : 1;

Просмотреть файл

@ -1000,7 +1000,6 @@ function addCertOverride(host, port, bits) {
var prefs;
var spdypref;
var spdy3pref;
var spdypush;
var http2pref;
var tlspref;
@ -1013,7 +1012,6 @@ var speculativeLimit;
function resetPrefs() {
prefs.setIntPref("network.http.speculative-parallel-limit", speculativeLimit);
prefs.setBoolPref("network.http.spdy.enabled", spdypref);
prefs.setBoolPref("network.http.spdy.enabled.v3-1", spdy3pref);
prefs.setBoolPref("network.http.spdy.allow-push", spdypush);
prefs.setBoolPref("network.http.spdy.enabled.http2", http2pref);
prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlspref);
@ -1040,7 +1038,6 @@ function run_test() {
// Enable all versions of spdy to see that we auto negotiate http/2
spdypref = prefs.getBoolPref("network.http.spdy.enabled");
spdy3pref = prefs.getBoolPref("network.http.spdy.enabled.v3-1");
spdypush = prefs.getBoolPref("network.http.spdy.allow-push");
http2pref = prefs.getBoolPref("network.http.spdy.enabled.http2");
tlspref = prefs.getBoolPref("network.http.spdy.enforce-tls-profile");

Просмотреть файл

@ -1,476 +0,0 @@
// test spdy/3.1
Cu.import("resource://gre/modules/NetUtil.jsm");
// Generate a small and a large post with known pre-calculated md5 sums
function generateContent(size) {
var content = "";
for (var i = 0; i < size; i++) {
content += "0";
}
return content;
}
var posts = [];
posts.push(generateContent(10));
posts.push(generateContent(250000));
// pre-calculated md5sums (in hex) of the above posts
var md5s = ['f1b708bba17f1ce948dc979f4d7092bc',
'2ef8d3b6c8f329318eb1a119b12622b6'];
var bigListenerData = generateContent(128 * 1024);
var bigListenerMD5 = '8f607cfdd2c87d6a7eedb657dafbd836';
var hugeListenerData = generateContent(800 * 1024);
function checkIsSpdy(request) {
try {
if (request.getResponseHeader("X-Firefox-Spdy") == "3.1") {
if (request.getResponseHeader("X-Connection-Spdy") == "yes") {
return true;
}
return false; // Weird case, but the server disagrees with us
}
} catch (e) {
// Nothing to do here
}
return false;
}
var SpdyCheckListener = function() {};
SpdyCheckListener.prototype = {
onStartRequestFired: false,
onDataAvailableFired: false,
isSpdyConnection: false,
onStartRequest: function testOnStartRequest(request, ctx) {
this.onStartRequestFired = true;
if (!Components.isSuccessCode(request.status))
do_throw("Channel should have a success code! (" + request.status + ")");
do_check_true(request instanceof Components.interfaces.nsIHttpChannel);
do_check_eq(request.responseStatus, 200);
do_check_eq(request.requestSucceeded, true);
},
onDataAvailable: function testOnDataAvailable(request, ctx, stream, off, cnt) {
this.onDataAvailableFired = true;
this.isSpdyConnection = checkIsSpdy(request);
read_stream(stream, cnt);
},
onStopRequest: function testOnStopRequest(request, ctx, status) {
do_check_true(this.onStartRequestFired);
do_check_true(Components.isSuccessCode(status));
do_check_true(this.isSpdyConnection);
do_check_true(this.onDataAvailableFired);
run_next_test();
do_test_finished();
}
};
/*
* Support for testing valid multiplexing of streams
*/
var multiplexContent = generateContent(30*1024);
var completed_channels = [];
function register_completed_channel(listener) {
completed_channels.push(listener);
if (completed_channels.length == 2) {
do_check_neq(completed_channels[0].streamID, completed_channels[1].streamID);
run_next_test();
do_test_finished();
}
}
/* Listener class to control the testing of multiplexing */
var SpdyMultiplexListener = function() {};
SpdyMultiplexListener.prototype = new SpdyCheckListener();
SpdyMultiplexListener.prototype.streamID = 0;
SpdyMultiplexListener.prototype.buffer = "";
SpdyMultiplexListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) {
this.onDataAvailableFired = true;
this.isSpdyConnection = checkIsSpdy(request);
this.streamID = parseInt(request.getResponseHeader("X-Spdy-StreamID"));
var data = read_stream(stream, cnt);
this.buffer = this.buffer.concat(data);
};
SpdyMultiplexListener.prototype.onStopRequest = function(request, ctx, status) {
do_check_true(this.onStartRequestFired);
do_check_true(this.onDataAvailableFired);
do_check_true(this.isSpdyConnection);
do_check_true(this.buffer == multiplexContent);
// This is what does most of the hard work for us
register_completed_channel(this);
};
// Does the appropriate checks for header gatewaying
var SpdyHeaderListener = function(value) {
this.value = value
};
SpdyHeaderListener.prototype = new SpdyCheckListener();
SpdyHeaderListener.prototype.value = "";
SpdyHeaderListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) {
this.onDataAvailableFired = true;
this.isSpdyConnection = checkIsSpdy(request);
do_check_eq(request.getResponseHeader("X-Received-Test-Header"), this.value);
read_stream(stream, cnt);
};
var SpdyPushListener = function() {};
SpdyPushListener.prototype = new SpdyCheckListener();
SpdyPushListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) {
this.onDataAvailableFired = true;
this.isSpdyConnection = checkIsSpdy(request);
if (request.originalURI.spec == "https://localhost:" + serverPort + "/push.js" ||
request.originalURI.spec == "https://localhost:" + serverPort + "/push2.js") {
do_check_eq(request.getResponseHeader("pushed"), "yes");
}
read_stream(stream, cnt);
};
// Does the appropriate checks for a large GET response
var SpdyBigListener = function() {};
SpdyBigListener.prototype = new SpdyCheckListener();
SpdyBigListener.prototype.buffer = "";
SpdyBigListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) {
this.onDataAvailableFired = true;
this.isSpdyConnection = checkIsSpdy(request);
this.buffer = this.buffer.concat(read_stream(stream, cnt));
};
SpdyBigListener.prototype.onStopRequest = function(request, ctx, status) {
do_check_true(this.onStartRequestFired);
do_check_true(this.onDataAvailableFired);
do_check_true(this.isSpdyConnection);
// Don't want to flood output, so don't use do_check_eq
if (request.originalURI.spec == "https://localhost:" + serverPort + "/big") {
// We know the server should send us the same data as our big post will be,
// so the md5 should be the same
do_check_eq(bigListenerMD5, request.getResponseHeader("X-Expected-MD5"));
do_check_true(this.buffer == bigListenerData);
} else {
do_check_true(this.buffer == hugeListenerData);
}
run_next_test();
do_test_finished();
};
// Does the appropriate checks for POSTs
var SpdyPostListener = function(expected_md5) {
this.expected_md5 = expected_md5;
};
SpdyPostListener.prototype = new SpdyCheckListener();
SpdyPostListener.prototype.expected_md5 = "";
SpdyPostListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) {
this.onDataAvailableFired = true;
this.isSpdyConnection = checkIsSpdy(request);
read_stream(stream, cnt);
do_check_eq(this.expected_md5, request.getResponseHeader("X-Calculated-MD5"));
};
function makeChan(url) {
return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true})
.QueryInterface(Ci.nsIHttpChannel);
}
// Make sure we make a spdy connection and both us and the server mark it as such
function test_spdy_basic() {
var chan = makeChan("https://localhost:" + serverPort + "/");
var listener = new SpdyCheckListener();
chan.asyncOpen2(listener);
}
// Support for making sure XHR works over SPDY
function checkXhr(xhr) {
if (xhr.readyState != 4) {
return;
}
do_check_eq(xhr.status, 200);
do_check_eq(checkIsSpdy(xhr), true);
run_next_test();
do_test_finished();
}
// Fires off an XHR request over SPDY
function test_spdy_xhr() {
var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
req.open("GET", "https://localhost:" + serverPort + "/", true);
req.addEventListener("readystatechange", function (evt) { checkXhr(req); },
false);
req.send(null);
}
var concurrent_channels = [];
var SpdyConcurrentListener = function() {};
SpdyConcurrentListener.prototype = new SpdyCheckListener();
SpdyConcurrentListener.prototype.count = 0;
SpdyConcurrentListener.prototype.target = 0;
SpdyConcurrentListener.prototype.onStopRequest = function(request, ctx, status) {
this.count++;
do_check_true(this.isSpdyConnection);
if (this.count == this.target) {
run_next_test();
do_test_finished();
}
};
function test_spdy_concurrent() {
var concurrent_listener = new SpdyConcurrentListener();
concurrent_listener.target = 201;
for (var i = 0; i < concurrent_listener.target; i++) {
concurrent_channels[i] = makeChan("https://localhost:" + serverPort + "/750ms");
concurrent_channels[i].loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
concurrent_channels[i].asyncOpen2(concurrent_listener);
}
}
// Test to make sure we get multiplexing right
function test_spdy_multiplex() {
var chan1 = makeChan("https://localhost:" + serverPort + "/multiplex1");
var chan2 = makeChan("https://localhost:" + serverPort + "/multiplex2");
var listener1 = new SpdyMultiplexListener();
var listener2 = new SpdyMultiplexListener();
chan1.asyncOpen2(listener1);
chan2.asyncOpen2(listener2);
}
// Test to make sure we gateway non-standard headers properly
function test_spdy_header() {
var chan = makeChan("https://localhost:" + serverPort + "/header");
var hvalue = "Headers are fun";
var listener = new SpdyHeaderListener(hvalue);
chan.setRequestHeader("X-Test-Header", hvalue, false);
chan.asyncOpen2(listener);
}
function test_spdy_push1() {
var chan = makeChan("https://localhost:" + serverPort + "/push");
chan.loadGroup = loadGroup;
var listener = new SpdyPushListener();
chan.asyncOpen2(listener);
}
function test_spdy_push2() {
var chan = makeChan("https://localhost:" + serverPort + "/push.js");
chan.loadGroup = loadGroup;
var listener = new SpdyPushListener();
chan.asyncOpen2(listener);
}
function test_spdy_push3() {
var chan = makeChan("https://localhost:" + serverPort + "/push2");
chan.loadGroup = loadGroup;
var listener = new SpdyPushListener();
chan.asyncOpen2(listener);
}
function test_spdy_push4() {
var chan = makeChan("https://localhost:" + serverPort + "/push2.js");
chan.loadGroup = loadGroup;
var listener = new SpdyPushListener();
chan.asyncOpen2(listener);
}
// Make sure we handle GETs that cover more than 2 frames properly
function test_spdy_big() {
var chan = makeChan("https://localhost:" + serverPort + "/big");
var listener = new SpdyBigListener();
chan.asyncOpen2(listener);
}
// Make sure we handle big GETs with suspend/resume
function test_spdy_big_and_slow() {
var chan = makeChan("https://localhost:" + serverPort + "/huge");
var listener = new SpdyBigListener();
chan.asyncOpen2(listener);
chan.suspend();
do_timeout(750, chan.resume);
}
// Support for doing a POST
function do_post(content, chan, listener) {
var stream = Cc["@mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
stream.data = content;
var uchan = chan.QueryInterface(Ci.nsIUploadChannel);
uchan.setUploadStream(stream, "text/plain", stream.available());
chan.requestMethod = "POST";
chan.asyncOpen2(listener);
}
// Make sure we can do a simple POST
function test_spdy_post() {
var chan = makeChan("https://localhost:" + serverPort + "/post");
var listener = new SpdyPostListener(md5s[0]);
do_post(posts[0], chan, listener);
}
// Make sure we can do a POST that covers more than 2 frames
function test_spdy_post_big() {
var chan = makeChan("https://localhost:" + serverPort + "/post");
var listener = new SpdyPostListener(md5s[1]);
do_post(posts[1], chan, listener);
}
function test_complete() {
resetPrefs();
do_test_finished();
do_timeout(0,run_next_test);
}
// hack - the header test resets the multiplex object on the server,
// so make sure header is always run before the multiplex test.
//
// make sure post_big runs first to test race condition in restarting
// a stalled stream when a SETTINGS frame arrives
var tests = [ test_spdy_post_big
, test_spdy_basic
, test_spdy_concurrent
, test_spdy_push1
, test_spdy_push2
, test_spdy_push3
, test_spdy_push4
, test_spdy_xhr
, test_spdy_header
, test_spdy_multiplex
, test_spdy_big
, test_spdy_big_and_slow
, test_spdy_post
// cleanup
, test_complete
];
var current_test = 0;
function run_next_test() {
if (current_test < tests.length) {
tests[current_test]();
current_test++;
do_test_pending();
}
}
// Support for making sure we can talk to the invalid cert the server presents
var CertOverrideListener = function(host, port, bits) {
this.host = host;
if (port) {
this.port = port;
}
this.bits = bits;
};
CertOverrideListener.prototype = {
host: null,
port: -1,
bits: null,
getInterface: function(aIID) {
return this.QueryInterface(aIID);
},
QueryInterface: function(aIID) {
if (aIID.equals(Ci.nsIBadCertListener2) ||
aIID.equals(Ci.nsIInterfaceRequestor) ||
aIID.equals(Ci.nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
notifyCertProblem: function(socketInfo, sslStatus, targetHost) {
var cert = sslStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
var cos = Cc["@mozilla.org/security/certoverride;1"].
getService(Ci.nsICertOverrideService);
cos.rememberValidityOverride(this.host, this.port, cert, this.bits, false);
dump("Certificate Override in place\n");
return true;
},
};
function addCertOverride(host, port, bits) {
var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
try {
var url;
if (port) {
url = "https://" + host + ":" + port + "/";
} else {
url = "https://" + host + "/";
}
req.open("GET", url, false);
req.channel.notificationCallbacks = new CertOverrideListener(host, port, bits);
req.send(null);
} catch (e) {
// This will fail since the server is not trusted yet
}
}
var prefs;
var spdypref;
var spdy3pref;
var spdypush;
var loadGroup;
var serverPort;
function resetPrefs() {
prefs.setBoolPref("network.http.spdy.enabled", spdypref);
prefs.setBoolPref("network.http.spdy.enabled.v3-1", spdy3pref);
prefs.setBoolPref("network.http.spdy.allow-push", spdypush);
}
function run_test() {
var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
serverPort = env.get("MOZSPDY_PORT");
do_check_neq(serverPort, null);
dump("using port " + serverPort + "\n");
// Set to allow the cert presented by our SPDY server
do_get_profile();
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
var oldParallel = prefs.getIntPref("network.http.speculative-parallel-limit");
prefs.setIntPref("network.http.speculative-parallel-limit", 0);
addCertOverride("localhost", serverPort,
Ci.nsICertOverrideService.ERROR_UNTRUSTED |
Ci.nsICertOverrideService.ERROR_MISMATCH |
Ci.nsICertOverrideService.ERROR_TIME);
prefs.setIntPref("network.http.speculative-parallel-limit", oldParallel);
// Enable all versions of spdy to see that we auto negotiate spdy/3.1
spdypref = prefs.getBoolPref("network.http.spdy.enabled");
spdy3pref = prefs.getBoolPref("network.http.spdy.enabled.v3-1");
spdypush = prefs.getBoolPref("network.http.spdy.allow-push");
prefs.setBoolPref("network.http.spdy.enabled", true);
prefs.setBoolPref("network.http.spdy.enabled.v3-1", true);
prefs.setBoolPref("network.http.spdy.allow-push", true);
loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup);
run_next_test();
}

Просмотреть файл

@ -283,10 +283,7 @@ fail-if = os == "android"
[test_socks.js]
# Bug 675039: test fails consistently on Android
fail-if = os == "android"
# spdy and http2 unit tests require us to have node available to run the spdy and http2 server
[test_spdy.js]
skip-if = !hasNode
run-sequentially = node server exceptions dont replay well
# http2 unit tests require us to have node available to run the spdy and http2 server
[test_http2.js]
skip-if = !hasNode
run-sequentially = node server exceptions dont replay well

Просмотреть файл

@ -1,14 +0,0 @@
Test server for SPDY unit tests. To run it, you need node >= 0.7.0 (not provided)
and node-spdy (provided). Just run
node /path/to/moz-spdy.js
And you will get a SPDY server listening on port 4443, then you can run the
xpcshell unit tests in netwerk/test/unit/test_spdy.js
*** A NOTE ON TLS CERTIFICATES ***
The certificates used for this test (*.pem in this directory) are the ones
provided as examples by node-spdy, and are copied directly from keys/ under
its top-level source directory (slightly renamed to match the option names
in the options dictionary passed to spdy.createServer).

Просмотреть файл

@ -1,197 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var spdy = require('../node-spdy/lib/spdy.js');
var fs = require('fs');
var url = require('url');
var crypto = require('crypto');
function getHttpContent(path) {
var content = '<!doctype html>' +
'<html>' +
'<head><title>HOORAY!</title></head>' +
'<body>You Win! (by requesting' + path + ')</body>' +
'</html>';
return content;
}
function getHugeContent(size) {
var content = '';
for (var i = 0; i < size; i++) {
content += '0';
}
return content;
}
/* This takes care of responding to the multiplexed request for us */
var Multiplex = function() {};
Multiplex.prototype = {
mp1res: null,
mp2res: null,
buf: null,
mp1start: 0,
mp2start: 0,
checkReady: function() {
if (this.mp1res != null && this.mp2res != null) {
this.buf = getHugeContent(30*1024);
this.mp1start = 0;
this.mp2start = 0;
this.send(this.mp1res, 0);
setTimeout(function() { this.send(this.mp2res, 0); }.bind(this), 5);
}
},
send: function(res, start) {
var end = start + 1024;
if (end > this.buf.length)
end = this.buf.length;
var content = this.buf.substring(start, end);
if (end < this.buf.length) {
res.write(content);
setTimeout(function() { this.send(res, end); }.bind(this), 10);
} else {
res.end(content);
}
},
};
var m = new Multiplex();
var post_hash = null;
function receivePostData(chunk) {
post_hash.update(chunk.toString());
}
function finishPost(res, content) {
var md5 = post_hash.digest('hex');
res.setHeader('X-Calculated-MD5', md5);
res.writeHead(200);
res.end(content);
}
var runlater = function() {};
runlater.prototype = {
req : null,
resp : null,
onTimeout : function onTimeout() {
this.resp.writeHead(200);
this.resp.end("It's all good spdy 750ms.");
}
};
function executeRunLater(arg) {
arg.onTimeout();
}
function handleRequest(req, res) {
var u = url.parse(req.url);
var content = getHttpContent(u.pathname);
if (req.streamID) {
res.setHeader('X-Connection-Spdy', 'yes');
res.setHeader('X-Spdy-StreamId', '' + req.streamID);
} else {
res.setHeader('X-Connection-Spdy', 'no');
}
if (u.pathname === '/750ms') {
var rl = new runlater();
rl.req = req;
rl.resp = res;
setTimeout(executeRunLater, 750, rl);
return;
}
if (u.pathname == '/exit') {
res.setHeader('Content-Type', 'text/plain');
res.writeHead(200);
res.end('ok');
process.exit();
} else if (u.pathname == '/multiplex1' && req.streamID) {
res.setHeader('Content-Type', 'text/plain');
res.writeHead(200);
m.mp1res = res;
m.checkReady();
return;
} else if (u.pathname == '/multiplex2' && req.streamID) {
res.setHeader('Content-Type', 'text/plain');
res.writeHead(200);
m.mp2res = res;
m.checkReady();
return;
} else if (u.pathname == "/header") {
m = new Multiplex();
var val = req.headers["x-test-header"];
if (val) {
res.setHeader("X-Received-Test-Header", val);
}
} else if (u.pathname == "/push") {
var stream = res.push('/push.js',
{ 'content-type': 'application/javascript',
'pushed' : 'yes',
'content-length' : 11,
'X-Connection-Spdy': 'yes'});
stream.on('error', function(){});
stream.end('// comments');
content = '<head> <script src="push.js"/></head>body text';
} else if (u.pathname == "/push2") {
var stream = res.push('/push2.js',
{ 'content-type': 'application/javascript',
'pushed' : 'yes',
// no content-length
'X-Connection-Spdy': 'yes'});
stream.on('error', function(){});
stream.end('// comments');
content = '<head> <script src="push2.js"/></head>body text';
} else if (u.pathname == "/big") {
content = getHugeContent(128 * 1024);
var hash = crypto.createHash('md5');
hash.update(content);
var md5 = hash.digest('hex');
res.setHeader("X-Expected-MD5", md5);
} else if (u.pathname == "/huge") {
content = getHugeContent(800 * 1024);
} else if (u.pathname == "/post") {
if (req.method != "POST") {
res.writeHead(405);
res.end('Unexpected method: ' + req.method);
return;
}
post_hash = crypto.createHash('md5');
req.on('data', receivePostData);
req.on('end', function () { finishPost(res, content); });
return;
}
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.end(content);
}
// Set up the SSL certs for our server
var options = {
key: fs.readFileSync(__dirname + '/spdy-key.pem'),
cert: fs.readFileSync(__dirname + '/spdy-cert.pem'),
ca: fs.readFileSync(__dirname + '/spdy-ca.pem'),
windowSize: 16000000,
};
function listenok() {
console.log('SPDY server listening on port ' + webServer.address().port);
}
var webServer = spdy.createServer(options, handleRequest).listen(0, "0.0.0.0", 200, listenok);
// Set up to exit when the user finishes our stdin
process.stdin.resume();
process.stdin.on('end', function () {
process.exit();
});

Просмотреть файл

@ -1,11 +0,0 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBkzCB/QIBADBUMQswCQYDVQQGEwJSVTETMBEGA1UECBMKU29tZS1TdGF0ZTEN
MAsGA1UEBxMET21zazEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF
3/Gnld/wcOE9J/M01y0RFiyVdPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+je
i+mA1ZqyKXnMANZozVPSedXRGwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+
A3vk+3j3hjJjIm79rwIDAQABoAAwDQYJKoZIhvcNAQEFBQADgYEAiNWhz6EppIVa
FfUaB3sLeqfamb9tg9kBHtvqj/FJni0snqms0kPWaTySEPHZF0irIb7VVdq/sVCb
3gseMVSyoDvPJ4lHC3PXqGQ7kM1mIPhDnR/4HDA3BhlGhTXSDIHgZnvI+HMBdsyC
hC3dz5odyKqe4nmoofomALkBL9t4H8s=
-----END CERTIFICATE REQUEST-----

Просмотреть файл

@ -1,14 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICHzCCAYgCCQCPPSUAa8QZojANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJS
VTETMBEGA1UECBMKU29tZS1TdGF0ZTENMAsGA1UEBxMET21zazEhMB8GA1UEChMY
SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTExMDQwOTEwMDY0NVoXDTExMDUw
OTEwMDY0NVowVDELMAkGA1UEBhMCUlUxEzARBgNVBAgTClNvbWUtU3RhdGUxDTAL
BgNVBAcTBE9tc2sxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCB
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1bn25sPkv46wl70BffxradlkRd/x
p5Xf8HDhPSfzNNctERYslXT2fX7Dmfd5w1XTVqqGqJ4izp5VewoVOHA8uavo3ovp
gNWasil5zADWaM1T0nnV0RsFbZWzOTmm1U3D48K8rW3F5kOZ6f4yRq9QT1gF/gN7
5Pt494YyYyJu/a8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQBuRZisIViI2G/R+w79
vk21TzC/cJ+O7tKsseDqotXYTH8SuimEH5IWcXNgnWhNzczwN8s2362NixyvCipV
yd4wzMpPbjIhnWGM0hluWZiK2RxfcqimIBjDParTv6CMUIuwGQ257THKY8hXGg7j
Uws6Lif3P9UbsuRiYPxMgg98wg==
-----END CERTIFICATE-----

Просмотреть файл

@ -1,15 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDVufbmw+S/jrCXvQF9/Gtp2WRF3/Gnld/wcOE9J/M01y0RFiyV
dPZ9fsOZ93nDVdNWqoaoniLOnlV7ChU4cDy5q+jei+mA1ZqyKXnMANZozVPSedXR
GwVtlbM5OabVTcPjwrytbcXmQ5np/jJGr1BPWAX+A3vk+3j3hjJjIm79rwIDAQAB
AoGAAv2QI9h32epQND9TxwSCKD//dC7W/cZOFNovfKCTeZjNK6EIzKqPTGA6smvR
C1enFl5adf+IcyWqAoe4lkqTvurIj+2EhtXdQ8DBlVuXKr3xvEFdYxXPautdTCF6
KbXEyS/s1TZCRFjYftvCrXxc3pK45AQX/wg7z1K+YB5pyIECQQD0OJvLoxLYoXAc
FZraIOZiDsEbGuSHqoCReFXH75EC3+XGYkH2bQ/nSIZ0h1buuwQ/ylKXOlTPT3Qt
Xm1OQEBvAkEA4AjWsIO/rRpOm/Q2aCrynWMpoUXTZSbL2yGf8pxp/+8r2br5ier0
M1LeBb/OPY1+k39NWLXxQoo64xoSFYk2wQJAd2wDCwX4HkR7HNCXw1hZL9QFK6rv
20NN0VSlpboJD/3KT0MW/FiCcVduoCbaJK0Au+zEjDyy4hj5N4I4Mw6KMwJAXVAx
I+psTsxzS4/njXG+BgIEl/C+gRYsuMQDnAi8OebDq/et8l0Tg8ETSu++FnM18neG
ntmBeMacinUUbTXuwQJBAJp/onZdsMzeVulsGrqR1uS+Lpjc5Q1gt5ttt2cxj91D
rio48C/ZvWuKNE8EYj2ALtghcVKRvgaWfOxt2GPguGg=
-----END RSA PRIVATE KEY-----

Просмотреть файл

@ -1,262 +0,0 @@
# SPDY Server for node.js [![Build Status](https://secure.travis-ci.org/indutny/node-spdy.png)](http://travis-ci.org/indutny/node-spdy)
<a href="http://flattr.com/thing/758213/indutnynode-spdy-on-GitHub" target="_blank">
<img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a>
With this module you can create [SPDY](http://www.chromium.org/spdy) servers
in node.js with natural http module interface and fallback to regular https
(for browsers that don't support SPDY yet).
## Usage
Server:
```javascript
var spdy = require('spdy'),
fs = require('fs');
var options = {
key: fs.readFileSync(__dirname + '/keys/spdy-key.pem'),
cert: fs.readFileSync(__dirname + '/keys/spdy-cert.pem'),
ca: fs.readFileSync(__dirname + '/keys/spdy-ca.pem'),
// **optional** SPDY-specific options
windowSize: 1024 * 1024, // Server's window size
// **optional** if true - server will send 3.1 frames on 3.0 *plain* spdy
autoSpdy31: false
};
var server = spdy.createServer(options, function(req, res) {
res.writeHead(200);
res.end('hello world!');
});
server.listen(3000);
```
Client:
```javascript
var spdy = require('spdy');
var http = require('http');
var agent = spdy.createAgent({
host: 'www.google.com',
port: 443,
// Optional SPDY options
spdy: {
plain: false or true,
ssl: false or true,
version: 3 // Force SPDY version
}
});
http.get({
host: 'www.google.com',
agent: agent
}, function(response) {
console.log('yikes');
// Here it goes like with any other node.js HTTP request
// ...
// And once we're done - we may close TCP connection to server
// NOTE: All non-closed requests will die!
agent.close();
}).end();
```
And by popular demand - usage with
[express](https://github.com/visionmedia/express):
```javascript
var spdy = require('spdy'),
express = require('express'),
fs = require('fs');
var options = { /* the same as above */ };
var app = express();
app.use(/* your favorite middleware */);
var server = spdy.createServer(options, app);
server.listen(3000);
```
## API
API is compatible with `http` and `https` module, but you can use another
function as base class for SPDYServer.
```javascript
spdy.createServer(
[base class constructor, i.e. https.Server],
{ /* keys and options */ }, // <- the only one required argument
[request listener]
).listen([port], [host], [callback]);
```
Request listener will receive two arguments: `request` and `response`. They're
both instances of `http`'s `IncomingMessage` and `OutgoingMessage`. But three
custom properties are added to both of them: `streamID`, `isSpdy`,
`spdyVersion`. The first one indicates on which spdy stream are sitting request
and response. Second is always true and can be checked to ensure that incoming
request wasn't received by HTTPS fallback and last one is a number representing
used SPDY protocol version (2 or 3 for now).
### Push streams
It is possible to initiate 'push' streams to send content to clients _before_
the client requests it.
```javascript
spdy.createServer(options, function(req, res) {
var headers = { 'content-type': 'application/javascript' };
var stream = res.push('/main.js', headers);
stream.on('acknowledge', function() {
});
stream.on('error', function() {
});
stream.end('alert("hello from push stream!");');
res.end('<script src="/main.js"></script>');
}).listen(3000);
```
The method is also avaliable when using SPDY with an Express server:
```javascript
var app = express();
var server = spdy.createServer(options, app);
app.get('/', function(req, res) {
var headers = { 'content-type': 'application/javascript' };
res.push('/main.js', headers, function(err, stream) {
stream.on('acknowledge', function() {
});
stream.on('error', function() {
});
stream.end('alert("hello from push stream!");');
});
res.end('<script src="/main.js"></script>');
});
server.listen(3000);
```
Push is accomplished via the `push()` method invoked on the current response
object (this works for express.js response objects as well). The format of the
`push()` method is:
`.push('full or relative url', { ... headers ... }, optional priority, callback)`
You can use either full ( `http://host/path` ) or relative ( `/path` ) urls with
`.push()`. `headers` are the same as for regular response object. `callback`
will receive two arguments: `err` (if any error is happened) and `stream`
(stream object have API compatible with a
[net.Socket](http://nodejs.org/docs/latest/api/net.html#net.Socket) ).
Client usage:
```javascript
var agent = spdy.createAgent({ /* ... */ });
agent.on('push', function(stream) {
stream.on('error', function(err) {
// Handle error
});
// Read data from stream
// ...
// stream.associated points to associated client-initiated stream
});
```
NOTE: You're responsible for the `stream` object once given it in `.push()`
callback. Hence ignoring `error` events on it might result in uncaught
exceptions and crash your program.
### Trailing headers
Server usage:
```javascript
function (req, res) {
// Send trailing headers to client
res.addTrailers({ header1: 'value1', header2: 'value2' });
// On client's trailing headers
req.on('trailers', function(headers) {
// ...
});
}
```
Client usage:
```javascript
var req = http.request({ agent: spdyAgent, /* ... */ }).function (res) {
// On server's trailing headers
res.on('trailers', function(headers) {
// ...
});
});
req.write('stuff');
req.addTrailers({ /* ... */ });
req.end();
```
### Options
All options supported by
[tls](http://nodejs.org/docs/latest/api/tls.html#tls.createServer) are working
with node-spdy. In addition, `maxStreams` options is available. it allows you
controlling [maximum concurrent streams](http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft2#TOC-SETTINGS)
protocol option (if client will start more streams than that limit, RST_STREAM
will be sent for each additional stream).
Additional options:
* `plain` - if defined, server will ignore NPN and ALPN data and choose whether
to use spdy or plain http by looking at first data packet.
* `ssl` - if `false` and `options.plain` is `true`, `http.Server` will be used
as a `base` class for created server.
* `maxChunk` - if set and non-falsy, limits number of bytes sent in one DATA
chunk. Setting it to non-zero value is recommended if you care about
interleaving of outgoing data from multiple different streams.
(defaults to 8192)
#### Contributors
* [Fedor Indutny](https://github.com/indutny)
* [Chris Strom](https://github.com/eee-c)
* [François de Metz](https://github.com/francois2metz)
* [Ilya Grigorik](https://github.com/igrigorik)
* [Roberto Peon](https://github.com/grmocg)
* [Tatsuhiro Tsujikawa](https://github.com/tatsuhiro-t)
* [Jesse Cravens](https://github.com/jessecravens)
#### LICENSE
This software is licensed under the MIT License.
Copyright Fedor Indutny, 2014.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.

Просмотреть файл

@ -1,19 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDBjCCAe4CCQCC8qgopCwXKDANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTEzMDEwODEyMzYyM1oXDTIzMDEwNjEyMzYyM1owRTELMAkG
A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ALYWwPVTdWu+55Y9WKcZKi+iC/BVoV6uejF/7twOQZRAMAs2rFhCnxmds8d7jcX6
wBHOejKFYqbm9WPORckieOVmHRHYLhzCXHrhYMHSYW5NxwTCxdka+QEzoFJqBSwO
AkBQaF9ETA/GDa22TGN0DqUAtH6AiT3FAf4lSng1gLnMaa6+jYDmHmGHf2epRw+N
U8Kb4g8iNLPP10M3x1DIlVtB2MvXgHFY5/fz4BSD8QweqUGaoIuAcLdJ6qJmbztm
FWCWmr3uNHo2V6KxjHMMpziCa+280im6OgsdsgMuQO949hk+5k+PZ/WR+Ia4NylE
FI6cVXpZwALAVWxC9VO1oocCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAluAKUeNS
brX8+Jr7hOEftd+maTb8ERV4CU2wFSeCqupoSGh4Er37FO+eL1cAzaewpQzx8Uvp
q7oO+utrOKGA42P0bkdX6vqXZ73seorKl4Mv6Lnq1z1uTRTQyAbvJpv6mYrr+oz5
QgTGvPqUWqpv+OPf2R9J00IVLlfIjdKGLt0iZ3QEeS6SDkV/MHT4fEc9fImsg6Rq
xcb8kmcv9GAHo7YzxOw2F85h93epl6zUUlW2QCzUbvEF2S5NQowdkDQz0py6o4tv
S5Dh5XYLDm7gjcRElLNLaTmdhq9IGBEdbXy6a9kzJqZz4+AOn5+WPnA/KhVhWoTE
nJHIzjR3CQEkbA==
-----END CERTIFICATE-----

Просмотреть файл

@ -1,16 +0,0 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx
ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBALYWwPVTdWu+55Y9WKcZKi+iC/BVoV6uejF/7twO
QZRAMAs2rFhCnxmds8d7jcX6wBHOejKFYqbm9WPORckieOVmHRHYLhzCXHrhYMHS
YW5NxwTCxdka+QEzoFJqBSwOAkBQaF9ETA/GDa22TGN0DqUAtH6AiT3FAf4lSng1
gLnMaa6+jYDmHmGHf2epRw+NU8Kb4g8iNLPP10M3x1DIlVtB2MvXgHFY5/fz4BSD
8QweqUGaoIuAcLdJ6qJmbztmFWCWmr3uNHo2V6KxjHMMpziCa+280im6OgsdsgMu
QO949hk+5k+PZ/WR+Ia4NylEFI6cVXpZwALAVWxC9VO1oocCAwEAAaAAMA0GCSqG
SIb3DQEBBQUAA4IBAQCuMWhXWTpHDZTYFtkasd/b02D45RyyQTYNvw0qS67rBKnj
52YvHKXvdjXe+TFuEzq6G7QAa7cgcdv12ffzboa8l9E4hY4OTO/lysCB1gMxgpTg
ulDFRTQrIgCjrIm4rIGkFj47VL0ieSRhU4bf9npQydXdaYk0DHOxpLdGrs85LbZf
lt/5Bsls9bSsh+JYcgoOPYxcgKBzhingh/FV93HQ39JHxtcqkrpZqrfNfHSVr9gT
t+YyvUh0ctO5oGEfQj27Ewk3yt1nwgCETP55yCUhWIhJF5ATSFqm0nfeS1QYHMcU
27Hcp+20/4D4MbZ04gi4bi6Z92DawssfKfow/B4u
-----END CERTIFICATE REQUEST-----

Просмотреть файл

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAthbA9VN1a77nlj1YpxkqL6IL8FWhXq56MX/u3A5BlEAwCzas
WEKfGZ2zx3uNxfrAEc56MoVipub1Y85FySJ45WYdEdguHMJceuFgwdJhbk3HBMLF
2Rr5ATOgUmoFLA4CQFBoX0RMD8YNrbZMY3QOpQC0foCJPcUB/iVKeDWAucxprr6N
gOYeYYd/Z6lHD41TwpviDyI0s8/XQzfHUMiVW0HYy9eAcVjn9/PgFIPxDB6pQZqg
i4Bwt0nqomZvO2YVYJaave40ejZXorGMcwynOIJr7bzSKbo6Cx2yAy5A73j2GT7m
T49n9ZH4hrg3KUQUjpxVelnAAsBVbEL1U7WihwIDAQABAoIBACdvHBDFJ0vTRzI5
TOa7Q3CXZoCA+vaXUK1BqIgNqlQh5oW3LHHc07nndlTARD7ZBBmXHs2sJ2Y/5Grd
9C0QAyCjEa6Yo7vkt8SA5MR0/Fa4D17Pk6tl9QE2ngTbIw2cZw5om4HuN46+9J1n
OnnbW4SOd4hh69btwHW6u7r2007pQXme0AMlAxhgfIEiD6tHjqZDCtK4G0d+GSq6
Ks/IpXckLcrluqJ2gHVB2sbBbjh1m7dHCVzBKynCwaHrWbNla8P6j3SdabP4Umcb
tu3hP3nLigMJmA16KdkayUg05OvUTBAkReLcVZYZQ80LmM+NioB6UvwYy677h4ma
XFihvLkCgYEA3aLOY2rve6DxvTXDJe8D91V//+5B9XBcyLP2cMEXIP+3W9Gto9tz
vTIOcwj8MLKh22KBBMCP7OQlXFalBoG/d5Y9YKr6X2XHBcEw96Nm1HZctUPRPQdd
+C8S5ikwTre9QMfLmLGKzgfIpDRYyijv0ZQZw+8qlNATtVrAMQGu6D0CgYEA0lI9
zXK+4P28+SkkCUIG/Y31wF4KeM8V5s8eUYej8LV67GQIGs9XPCEIJY2cHZpn7qD8
H90lucr90rwAHN2rLUSdq28NVL0+RTYKNBFRRvPNloLlKOR9RvorsFz5U4z48i9x
yZ6gk+WL0ycc2oUFfihmQpHhRUiV46IB7deEXhMCgYEA1rW+3V8eC5VaOuOXXutS
20vwCX7GVUB6ElENICRfBK/V8NSLM98IG7QffV+p+H9E/+RIetMVWveWHgMuMcSG
ORLJ+RkKHlrZ2IBUsMKSfqb/nvbJACdf6GuqEmC6lLe5VsV3PkBY6MlvnWu8zHOm
CFFCOKc8iBef0CPPZmpsCD0CgYAOtS+bOWX9x+C6L9VUTGi+vHmuDSWAU0L91AgT
vX+KaraA53HlphA8pTazoZaEP3L7LgjTlZx4xKhBX2JGon3A+aZpAagV//Hl1ySZ
hYiAhLYgy2CJHolgOEhr2eSZoicakJTNe6lRDmFbz8Vlxp2et+aGyzrMpInO1Fp8
LnEUPwKBgExMk6THLz8CzjJhaKDrNn1fSs6jUxgK12+1cIJxb5JEgRJs4W+yFuLw
exCE5YKPwnZNrbji+bdkk9FwHPLuf20aH5fIB0UlMMVcvLGKyxYl7BG1pShN4k8C
kKXaClwkngJ2eSs/AsNAe9hhbEpFjAHt+3D7Sbg8EvgeCwdjnIxw
-----END RSA PRIVATE KEY-----

Просмотреть файл

@ -1,29 +0,0 @@
var spdy = exports;
// Exports utils
spdy.utils = require('./spdy/utils');
// Export parser&framer
spdy.protocol = require('./spdy/protocol');
// Export ServerResponse
spdy.response = require('./spdy/response');
// Export Scheduler
spdy.scheduler = require('./spdy/scheduler');
// Export ZlibPool
spdy.zlibpool = require('./spdy/zlib-pool');
// Export Connection and Stream
spdy.Stream = require('./spdy/stream').Stream;
spdy.Connection = require('./spdy/connection').Connection;
// Export server
spdy.server = require('./spdy/server');
spdy.Server = spdy.server.Server;
spdy.createServer = spdy.server.create;
// Export client
spdy.Agent = require('./spdy/client').Agent;
spdy.createAgent = require('./spdy/client').create;

Просмотреть файл

@ -1,230 +0,0 @@
var spdy = require('../spdy');
var assert = require('assert');
var util = require('util');
var net = require('net');
var https = require('https');
var EventEmitter = require('events').EventEmitter;
var proto = {};
function instantiate(base) {
function Agent(options) {
base.call(this, options);
if (!this.options.spdy)
this.options.spdy = {};
this._init();
// Hack for node.js v0.10
this.createConnection = proto.createConnection;
// No chunked encoding
this.keepAlive = false;
};
util.inherits(Agent, base);
// Copy prototype methods
Object.keys(proto).forEach(function(key) {
this[key] = proto[key];
}, Agent.prototype);
return Agent;
};
proto._init = function init() {
var self = this;
var state = {};
// Find super's `createConnection` method
var createConnection;
var cons = this.constructor.super_;
do {
createConnection = cons.prototype.createConnection;
if (cons.super_ === EventEmitter || !cons.super_)
break;
cons = cons.super_;
} while (!createConnection);
if (!createConnection)
createConnection = this.createConnection || net.createConnection;
// TODO(indutny): Think about falling back to http/https
var socket = createConnection.call(this, util._extend({
NPNProtocols: ['spdy/3.1', 'spdy/3', 'spdy/2'],
ALPNProtocols: ['spdy/3.1', 'spdy/3', 'spdy/2']
}, this.options));
state.socket = socket;
// Header compression is disabled by default in clients to prevent CRIME
// attacks. Don't enable it unless you know what you're doing.
if (this.options.spdy.headerCompression !== true)
this.options.spdy.headerCompression = false;
// Create SPDY connection using newly created socket
var connection = new spdy.Connection(socket,
util._extend(this.options.spdy, {
isServer: false
}));
state.connection = connection;
connection.on('error', function(err) {
self.emit('error', err);
});
// Select SPDY version
if (this.options.spdy.ssl !== false && !this.options.spdy.version) {
// SSL, wait for NPN or ALPN to happen
socket.once('secureConnect', function() {
var selectedProtocol = socket.npnProtocol || socket.alpnProtocol;
if (selectedProtocol === 'spdy/2')
connection._setVersion(2);
else if (selectedProtocol === 'spdy/3')
connection._setVersion(3);
else if (selectedProtocol === 'spdy/3.1')
connection._setVersion(3.1);
else
socket.emit('error', new Error('No supported SPDY version'));
});
} else {
// No SSL, use fixed version
connection._setVersion(this.options.spdy.version || 3);
}
// Destroy PUSH streams until the listener will be added
connection.on('stream', function(stream) {
stream.on('error', function() {});
stream.destroy();
});
state.pushServer = null;
this.on('newListener', this._onNewListener.bind(this));
this.on('removeListener', this._onRemoveListener.bind(this));
// Client has odd-numbered stream ids
state.id = 1;
this._spdyState = state;
};
//
// ### function onNewListener (type, listener)
// #### @type {String} Event type
// #### @listener {Function} Listener callback
//
proto._onNewListener = function onNewListener(type, listener) {
if (type !== 'push')
return;
var state = this._spdyState;
if (state.pushServer)
return state.pushServer.on('translated-request', listener);
state.pushServer = require('http').createServer(listener);
state.connection.removeAllListeners('stream');
state.connection.on('stream', function(stream) {
state.pushServer.emit('connection', stream);
});
state.pushServer.on('request', function(req) {
// Populate trailing headers
req.connection.on('headers', function(headers) {
Object.keys(headers).forEach(function(key) {
req.trailers[key] = headers[key];
});
req.emit('trailers', headers);
});
this.emit('translated-request', req);
});
};
//
// ### function onRemoveListener (type, listener)
// #### @type {String} Event type
// #### @listener {Function} Listener callback
//
proto._onRemoveListener = function onRemoveListener(type, listener) {
if (type !== 'push')
return;
var state = this._spdyState;
if (!state.pushServer)
return;
state.pushServer.removeListener('translated-request', listener);
};
//
// ### function createConnection (options)
//
proto.createConnection = function createConnection(options) {
if (!options)
options = {};
var state = this._spdyState;
var stream = new spdy.Stream(state.connection, {
id: state.id,
priority: options.priority || 7,
client: true,
decompress: options.spdy.decompress == undefined ? true :
options.spdy.decompress
});
state.id += 2;
return stream;
};
//
// ### function close (callback)
// #### @callback {Function}
// Close underlying socket and terminate all streams
//
proto.close = function close(callback) {
this._spdyState.socket.destroySoon();
if (callback)
this._spdyState.socket.once('close', callback);
};
//
// ### function ping (callback)
// #### @callback {Function}
// Send PING frame and invoke callback once received it back
//
proto.ping = function ping(callback) {
return this._spdyState.connection.ping(callback);
};
//
// Default Agent
//
exports.Agent = instantiate(https.Agent);
//
// ### function create (base, options)
// #### @base {Function} (optional) base server class (https.Server)
// #### @options {Object} tls server options
// @constructor wrapper
//
exports.create = function create(base, options) {
var agent;
if (typeof base === 'function') {
agent = instantiate(base);
} else {
agent = exports.Agent;
options = base;
base = null;
}
// Instantiate http server if `ssl: false`
if (!base &&
options &&
options.spdy &&
options.spdy.plain &&
options.spdy.ssl === false) {
return exports.create(require('http').Agent, options);
}
return new agent(options);
};

Просмотреть файл

@ -1,648 +0,0 @@
var util = require('util');
var assert = require('assert');
var spdy = require('../spdy');
var constants = spdy.protocol.constants;
var Stream = spdy.Stream;
//
// ### function Connection (socket, state, server)
// #### @socket {net.Socket} server's connection
// #### @options {Object} Connection options
// #### @server {net.Server} server
// Abstract connection @constructor
//
function Connection(socket, options, server) {
process.EventEmitter.call(this);
var state = {};
this._spdyState = state;
// NOTE: There's a big trick here. Connection is used as a `this` argument
// to the wrapped `connection` event listener.
// socket end doesn't necessarly mean connection drop
this.httpAllowHalfOpen = true;
// Socket timeout
this.timeout = server && server.timeout || 0;
// Defaults
state.maxStreams = options.maxStreams || Infinity;
state.initialSinkSize = constants.DEFAULT_WINDOW;
state.initialWindowSize = options.windowSize || 1 << 20;
state.autoSpdy31 = options.autoSpdy31;
// Connection-level flow control
state.sinkSize = state.initialSinkSize;
state.windowSize = constants.DEFAULT_WINDOW;
// Interleaving configuration
state.maxChunk = options.maxChunk === undefined ? 8 * 1024 : options.maxChunk;
// Various state info
state.closed = false;
state.pool = spdy.zlibpool.create(options.headerCompression);
state.counters = {
pushCount: 0,
streamCount: 0
};
state.socketBuffering = false;
state.version = null;
state.deflate = null;
state.inflate = null;
// Init streams list
state.isServer = options.isServer;
state.streams = {};
state.streamCount = 0;
state.lastId = 0;
state.pushId = 0;
state.pingId = state.isServer ? 0 : 1;
state.pings = {};
state.goaway = false;
// X-Forwarded feature
state.xForward = null;
// Initialize scheduler
state.scheduler = spdy.scheduler.create(this);
// Create parser and hole for framer
state.parser = spdy.protocol.parser.create(this);
state.framer = spdy.protocol.framer.create();
// Lock data
state.locked = false;
state.lockQueue = [];
this.socket = socket;
this.encrypted = socket.encrypted;
this.readable = true;
this.writable = true;
this._init();
}
util.inherits(Connection, process.EventEmitter);
exports.Connection = Connection;
Connection.prototype._init = function init() {
var self = this;
var state = this._spdyState;
var pool = state.pool;
var pair = null;
var sentSettings = false;
// Initialize parser
this._spdyState.parser.on('frame', this._handleFrame.bind(this));
this._spdyState.parser.on('version', function onversion(version) {
var prev = state.version;
var framer = state.framer;
state.version = version;
// Ignore transition to 3.1
if (!prev) {
pair = pool.get('spdy/' + version);
state.deflate = pair.deflate;
state.inflate = pair.inflate;
framer.setCompression(pair.deflate, pair.inflate);
}
framer.setVersion(version);
// Send settings frame (once)
if (!sentSettings) {
sentSettings = true;
framer.settingsFrame({
maxStreams: state.maxStreams,
windowSize: state.initialWindowSize
}, function(err, frame) {
if (err)
return self.emit('error', err);
self.write(frame);
});
// Send WINDOW_UPDATE for 3.1
self._sendWindowUpdate(true);
}
});
// Propagate parser errors
state.parser.on('error', function onParserError(err) {
self.emit('error', err);
});
this.socket.pipe(state.parser);
// 2 minutes socket timeout
this.socket.setTimeout(this.timeout);
this.socket.once('timeout', function ontimeout() {
self.socket.destroy();
});
// Allow high-level api to catch socket errors
this.socket.on('error', function onSocketError(e) {
self.emit('error', e);
});
this.socket.once('close', function onclose() {
var err = new Error('socket hang up');
err.code = 'ECONNRESET';
self._destroyStreams(err);
self.emit('close');
state.closed = true;
if (pair)
pool.put(pair);
});
// Do not allow half-open connections
this.socket.allowHalfOpen = false;
if (spdy.utils.isLegacy) {
this.socket.on('drain', function ondrain() {
self.emit('drain');
});
}
// for both legacy and non-legacy, when our socket is ready for writes again,
// drain the sinks in case they were queuing due to socketBuffering.
this.socket.on('drain', function () {
if (!state.socketBuffering)
return;
state.socketBuffering = false;
Object.keys(state.streams).forEach(function(id) {
state.streams[id]._drainSink(0);
});
});
};
//
// ### function handleFrame (frame)
// #### @frame {Object} SPDY frame
//
Connection.prototype._handleFrame = function handleFrame(frame) {
var state = this._spdyState;
if (state.closed)
return;
var stream;
// Create new stream
if (frame.type === 'SYN_STREAM') {
stream = this._handleSynStream(frame);
} else {
if (frame.id !== undefined) {
// Load created one
stream = state.streams[frame.id];
// Fail if not found
if (stream === undefined &&
!(frame.type === 'WINDOW_UPDATE' && frame.id === 0)) {
if (frame.type === 'RST_STREAM')
return;
this._rst(frame.id, constants.rst.INVALID_STREAM);
return;
}
}
// Emit 'data' event
if (frame.type === 'DATA') {
this._handleData(stream, frame);
// Reply for client stream
} else if (frame.type === 'SYN_REPLY') {
// If stream not client - send RST
if (!stream._spdyState.isClient) {
this._rst(frame.id, constants.rst.PROTOCOL_ERROR);
return;
}
stream._handleResponse(frame);
// Destroy stream if we were asked to do this
} else if (frame.type === 'RST_STREAM') {
stream._spdyState.rstCode = 0;
stream._spdyState.closedBy.us = true;
stream._spdyState.closedBy.them = true;
// Emit error on destroy
var err = new Error('Received rst: ' + frame.status);
err.code = 'RST_STREAM';
err.status = frame.status;
stream.destroy(err);
// Respond with same PING
} else if (frame.type === 'PING') {
this._handlePing(frame.pingId);
} else if (frame.type === 'SETTINGS') {
this._setDefaultWindow(frame.settings);
} else if (frame.type === 'GOAWAY') {
state.goaway = frame.lastId;
this.writable = false;
} else if (frame.type === 'WINDOW_UPDATE') {
if (stream)
stream._drainSink(frame.delta);
else
this._drainSink(frame.delta);
} else if (frame.type === 'HEADERS') {
stream.emit('headers', frame.headers);
} else if (frame.type === 'X_FORWARDED') {
state.xForward = frame.host;
} else {
console.error('Unknown type: ', frame.type);
}
}
// Handle half-closed
if (frame.fin) {
// Don't allow to close stream twice
if (stream._spdyState.closedBy.them) {
stream._spdyState.rstCode = constants.rst.PROTOCOL_ERROR;
stream.emit('error', 'Already half-closed');
} else {
stream._spdyState.closedBy.them = true;
// Receive FIN
stream._recvEnd();
stream._handleClose();
}
}
};
//
// ### function handleSynStream (frame)
// #### @frame {Object} SPDY frame
//
Connection.prototype._handleSynStream = function handleSynStream(frame) {
var state = this._spdyState;
var associated;
if (state.goaway && state.goaway < frame.id)
return this._rst(frame.id, constants.rst.REFUSED_STREAM);
// PUSH stream
if (!state.isServer) {
// Incorrect frame id
if (frame.id % 2 === 1 || frame.associated % 2 === 0)
return this._rst(frame.id, constants.rst.PROTOCOL_ERROR);
associated = state.streams[frame.associated];
// Fail if not found
if (associated === undefined) {
if (frame.type === 'RST_STREAM')
return;
this._rst(frame.id, constants.rst.INVALID_STREAM);
return;
}
}
var stream = new Stream(this, frame);
// Associate streams
if (associated) {
stream.associated = associated;
}
// TODO(indutny) handle stream limit
this.emit('stream', stream);
stream._start(frame.url, frame.headers);
return stream;
};
//
// ### function _handleData (stream, frame)
// #### @stream {Stream} SPDY Stream
// #### @frame {Object} SPDY frame
//
Connection.prototype._handleData = function handleData(stream, frame) {
var state = this._spdyState;
if (frame.data.length > 0) {
if (stream._spdyState.closedBy.them) {
stream._spdyState.rstCode = constants.rst.PROTOCOL_ERROR;
stream.emit('error', 'Writing to half-closed stream');
} else {
// Connection-level flow control
state.windowSize -= frame.data.length;
this._sendWindowUpdate();
stream._recv(frame.data);
}
}
};
//
// ### function sendWindowUpdate (force)
// #### @force {Boolean} send even if windowSize is positive
// Send WINDOW_UPDATE if needed
//
Connection.prototype._sendWindowUpdate = function sendWindowUpdate(force) {
var state = this._spdyState;
if (state.version < 3.1 && (!state.isServer || !state.autoSpdy31) ||
state.windowSize > state.initialWindowSize / 2 && !force) {
return;
}
var self = this;
var delta = state.initialWindowSize - state.windowSize;
if (delta === 0)
return;
state.windowSize += delta;
state.framer.windowUpdateFrame(0, delta, function(err, frame) {
if (err)
return self.emit('error', err);
self.write(frame);
});
};
//
// ### function _drainSink (delta)
// #### @delta {Number} WINDOW_UPDATE delta value
//
Connection.prototype._drainSink = function drainSink(delta) {
var state = this._spdyState;
// Switch to 3.1
if (state.version !== 3.1) {
this._setVersion(3.1);
this._sendWindowUpdate();
}
state.sinkSize += delta;
if (state.sinkSize <= 0)
return;
this.emit('drain');
// Try to write all pending data to the socket
Object.keys(state.streams).forEach(function(id) {
state.streams[id]._drainSink(0);
});
};
//
// ### function setVersion (version)
// #### @version {Number} Protocol version
// Set protocol version to use
//
Connection.prototype._setVersion = function setVersion(version) {
this._spdyState.parser.setVersion(version);
};
//
// ### function addStream (stream)
// #### @stream {Stream}
//
Connection.prototype._addStream = function addStream(stream) {
var state = this._spdyState;
var id = stream._spdyState.id;
if (state.streams[id])
return;
state.streams[id] = stream;
// Update lastId
state.lastId = Math.max(state.lastId, id);
var isClient = id % 2 == 1;
if (isClient && state.isServer || !isClient && !state.isServer)
state.streamCount++;
};
//
// ### function removeStream (stream)
// #### @stream {Stream}
//
Connection.prototype._removeStream = function removeStream(stream) {
var state = this._spdyState;
var id = stream._spdyState.id;
if (!state.streams[id])
return;
delete state.streams[id];
var isClient = id % 2 == 1;
if (isClient && state.isServer || !isClient && !state.isServer)
state.streamCount--;
if (!state.isServer &&
state.goaway &&
state.streamCount === 0 &&
this.socket) {
this.socket.destroySoon();
}
};
//
// ### function destroyStreams (err)
// #### @err {Error} *optional*
// Destroys all active streams
//
Connection.prototype._destroyStreams = function destroyStreams(err) {
var state = this._spdyState;
var streams = state.streams;
state.streams = {};
state.streamCount = 0;
Object.keys(streams).forEach(function(id) {
streams[id].destroy();
});
};
//
// ### function _rst (streamId, code, extra)
// #### @streamId {Number}
// #### @code {Number}
// #### @extra {String}
// Send RST frame
//
Connection.prototype._rst = function rst(streamId, code, extra) {
var self = this;
this._spdyState.framer.rstFrame(streamId, code, extra, function(err, frame) {
if (err)
return self.emit('error', err);
self.write(frame);
});
};
//
// ### function lock (callback)
// #### @callback {Function} continuation callback
// Acquire lock
//
Connection.prototype._lock = function lock(callback) {
if (!callback)
return;
var state = this._spdyState;
if (state.locked) {
state.lockQueue.push(callback);
} else {
state.locked = true;
callback(null);
}
};
//
// ### function unlock ()
// Release lock and call all buffered callbacks
//
Connection.prototype._unlock = function unlock() {
var state = this._spdyState;
if (state.locked) {
if (state.lockQueue.length) {
var cb = state.lockQueue.shift();
cb(null);
} else {
state.locked = false;
}
}
};
//
// ### function write (data, encoding)
// #### @data {String|Buffer} data
// #### @encoding {String} (optional) encoding
// Writes data to socket
//
Connection.prototype.write = function write(data, encoding) {
if (this.socket.writable) {
var wroteThrough = this.socket.write(data, encoding);
// if write returns false, the socket layer is buffering the data, so let's
// set a flag that we can use to create some backpressure
if (!wroteThrough)
this._spdyState.socketBuffering = true;
return wroteThrough;
}
};
//
// ### function _setDefaultWindow (settings)
// #### @settings {Object}
// Update the default transfer window -- in the connection and in the
// active streams
//
Connection.prototype._setDefaultWindow = function _setDefaultWindow(settings) {
if (!settings)
return;
if (!settings.initial_window_size ||
settings.initial_window_size.persisted) {
return;
}
var state = this._spdyState;
state.initialSinkSize = settings.initial_window_size.value;
Object.keys(state.streams).forEach(function(id) {
state.streams[id]._updateSinkSize(settings.initial_window_size.value);
});
};
//
// ### function handlePing (id)
// #### @id {Number} PING id
//
Connection.prototype._handlePing = function handlePing(id) {
var self = this;
var state = this._spdyState;
var ours = state.isServer && (id % 2 === 0) ||
!state.isServer && (id % 2 === 1);
// Handle incoming PING
if (!ours) {
state.framer.pingFrame(id, function(err, frame) {
if (err)
return self.emit('error', err);
self.emit('ping', id);
self.write(frame);
});
return;
}
// Handle reply PING
if (!state.pings[id])
return;
var ping = state.pings[id];
delete state.pings[id];
if (ping.cb)
ping.cb(null);
};
//
// ### function ping (callback)
// #### @callback {Function}
// Send PING frame and invoke callback once received it back
//
Connection.prototype.ping = function ping(callback) {
var self = this;
var state = this._spdyState;
var id = state.pingId;
state.pingId += 2;
state.framer.pingFrame(id, function(err, frame) {
if (err)
return self.emit('error', err);
state.pings[id] = { cb: callback };
self.write(frame);
});
};
//
// ### function getCounter (name)
// #### @name {String} Counter name
// Get counter value
//
Connection.prototype.getCounter = function getCounter(name) {
return this._spdyState.counters[name];
};
//
// ### function cork ()
// Accumulate data before writing out
//
Connection.prototype.cork = function cork() {
if (this.socket && this.socket.cork)
this.socket.cork();
};
//
// ### function uncork ()
// Write out accumulated data
//
Connection.prototype.uncork = function uncork() {
if (this.socket && this.socket.uncork)
this.socket.uncork();
};
Connection.prototype.end = function end() {
var self = this;
var state = this._spdyState;
state.framer.goawayFrame(state.lastId,
constants.goaway.OK,
function(err, frame) {
if (err)
return self.emit('error', err);
self.write(frame, function() {
state.goaway = state.lastId;
// Destroy socket if there are no streams
if (!state.isServer &&
state.goaway &&
state.streamCount === 0 &&
self.socket) {
self.socket.destroySoon();
}
});
});
};

Просмотреть файл

@ -1,35 +0,0 @@
exports.rst = {
PROTOCOL_ERROR: 1,
INVALID_STREAM: 2,
REFUSED_STREAM: 3,
UNSUPPORTED_VERSION: 4,
CANCEL: 5,
INTERNAL_ERROR: 6,
FLOW_CONTROL_ERROR: 7,
STREAM_IN_USE: 8,
STREAM_ALREADY_CLOSED: 9,
INVALID_CREDENTIALS: 10,
FRAME_TOO_LARGE: 11
};
exports.settings = {
FLAG_SETTINGS_PERSIST_VALUE: 1,
FLAG_SETTINGS_PERSISTED: 2,
SETTINGS_UPLOAD_BANDWIDTH: 1,
SETTINGS_DOWNLOAD_BANDWIDTH: 2,
SETTINGS_ROUND_TRIP_TIME: 3,
SETTINGS_MAX_CONCURRENT_STREAMS: 4,
SETTINGS_CURRENT_CWND: 5,
SETTINGS_DOWNLOAD_RETRANS_RATE: 6,
SETTINGS_INITIAL_WINDOW_SIZE: 7,
SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE: 8
};
exports.DEFAULT_WINDOW = 64 * 1024;
exports.goaway = {
OK: 0,
PROTOCOL_ERROR: 1,
INTERNAL_ERROR: 2
};

Просмотреть файл

@ -1,203 +0,0 @@
var Buffer = require('buffer').Buffer;
var dictionary = {};
module.exports = dictionary;
dictionary[2] = new Buffer([
'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\x00'
].join(''));
dictionary[3] = new Buffer([
0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // ....opti
0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // ons....h
0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // ead....p
0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // ost....p
0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // ut....de
0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // lete....
0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // trace...
0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // .accept.
0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep
0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t-charse
0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t....acc
0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ept-enco
0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // ding....
0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // accept-l
0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // anguage.
0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // ...accep
0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t-ranges
0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // ....age.
0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // ...allow
0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // ....auth
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // orizatio
0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n....cac
0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // he-contr
0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // ol....co
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // nnection
0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // ....cont
0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // ent-base
0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // ....cont
0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // ent-enco
0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // ding....
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // content-
0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // language
0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // ....cont
0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // ent-leng
0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // th....co
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // ntent-lo
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // cation..
0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten
0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t-md5...
0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // .content
0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // -range..
0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // ..conten
0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t-type..
0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // ..date..
0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // ..etag..
0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // ..expect
0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // ....expi
0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // res....f
0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // rom....h
0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // ost....i
0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f-match.
0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // ...if-mo
0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // dified-s
0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // ince....
0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // if-none-
0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // match...
0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // .if-rang
0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e....if-
0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // unmodifi
0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // ed-since
0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // ....last
0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // -modifie
0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d....loc
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // ation...
0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // .max-for
0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // wards...
0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // .pragma.
0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // ...proxy
0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // -authent
0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // icate...
0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // .proxy-a
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // uthoriza
0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // tion....
0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // range...
0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // .referer
0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // ....retr
0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y-after.
0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // ...serve
0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r....te.
0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // ...trail
0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // er....tr
0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // ansfer-e
0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // ncoding.
0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // ...upgra
0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // de....us
0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // er-agent
0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // ....vary
0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // ....via.
0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // ...warni
0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // ng....ww
0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w-authen
0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // ticate..
0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // ..method
0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // ....get.
0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // ...statu
0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s....200
0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // .OK....v
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // ersion..
0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // ..HTTP.1
0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // .1....ur
0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l....pub
0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // lic....s
0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // et-cooki
0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e....kee
0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p-alive.
0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // ...origi
0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n1001012
0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 01202205
0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 20630030
0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 23033043
0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 05306307
0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 40240540
0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 64074084
0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 09410411
0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 41241341
0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 44154164
0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 17502504
0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 505203.N
0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // on-Autho
0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // ritative
0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // .Informa
0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // tion204.
0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // No.Conte
0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // nt301.Mo
0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // ved.Perm
0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // anently4
0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 00.Bad.R
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // equest40
0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1.Unauth
0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // orized40
0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3.Forbid
0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // den404.N
0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // ot.Found
0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 500.Inte
0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // rnal.Ser
0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // ver.Erro
0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r501.Not
0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // .Impleme
0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // nted503.
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // Service.
0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // Unavaila
0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // bleJan.F
0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // eb.Mar.A
0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // pr.May.J
0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // un.Jul.A
0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // ug.Sept.
0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // Oct.Nov.
0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // Dec.00.0
0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0.00.Mon
0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // ..Tue..W
0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // ed..Thu.
0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // .Fri..Sa
0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t..Sun..
0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // GMTchunk
0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // ed.text.
0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // html.ima
0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // ge.png.i
0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // mage.jpg
0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // .image.g
0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // if.appli
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x
0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // ml.appli
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // cation.x
0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // html.xml
0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // .text.pl
0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // ain.text
0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // .javascr
0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // ipt.publ
0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // icprivat
0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // emax-age
0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // .gzip.de
0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // flate.sd
0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // chcharse
0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t.utf-8c
0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // harset.i
0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // so-8859-
0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1.utf-..
0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // .enq.0.
]);
dictionary[3.1] = dictionary[3];

Просмотреть файл

@ -1,497 +0,0 @@
var spdy = require('../../spdy');
var util = require('util');
var Buffer = require('buffer').Buffer;
var EventEmitter = require('events').EventEmitter;
var constants = require('./').constants;
var empty = new Buffer(0);
function Framer() {
EventEmitter.call(this);
this.version = null;
this.deflate = null;
this.inflate = null;
this.debug = false;
};
util.inherits(Framer, EventEmitter);
module.exports = Framer;
Framer.create = function create(version, deflate, inflate) {
return new Framer(version, deflate, inflate);
};
//
// ### function setCompression (deflate, inflate)
// #### @deflate {Deflate}
// #### @inflate {Inflate}
// Set framer compression
//
Framer.prototype.setCompression = function setCompresion(deflate, inflate) {
this.deflate = spdy.utils.zwrap(deflate);
this.inflate = spdy.utils.zwrap(inflate);
};
//
// ### function setCompression (deflate, inflate)
// #### @deflate {Deflate}
// #### @inflate {Inflate}
// Set framer compression
//
Framer.prototype.setVersion = function setVersion(version) {
this.version = version;
this.emit('version');
};
//
// internal, converts object into spdy dictionary
//
Framer.prototype.headersToDict = function headersToDict(headers, preprocess) {
function stringify(value) {
if (value !== undefined) {
if (Array.isArray(value)) {
return value.join('\x00');
} else if (typeof value === 'string') {
return value;
} else {
return value.toString();
}
} else {
return '';
}
}
// Lower case of all headers keys
var loweredHeaders = {};
Object.keys(headers || {}).map(function(key) {
loweredHeaders[key.toLowerCase()] = headers[key];
});
// Allow outer code to add custom headers or remove something
if (preprocess)
preprocess(loweredHeaders);
// Transform object into kv pairs
var size = this.version === 2 ? 2 : 4,
len = size,
pairs = Object.keys(loweredHeaders).filter(function(key) {
var lkey = key.toLowerCase();
return lkey !== 'connection' && lkey !== 'keep-alive' &&
lkey !== 'proxy-connection' && lkey !== 'transfer-encoding';
}).map(function(key) {
var klen = Buffer.byteLength(key),
value = stringify(loweredHeaders[key]),
vlen = Buffer.byteLength(value);
len += size * 2 + klen + vlen;
return [klen, key, vlen, value];
}),
result = new Buffer(len);
if (this.version === 2)
result.writeUInt16BE(pairs.length, 0, true);
else
result.writeUInt32BE(pairs.length, 0, true);
var offset = size;
pairs.forEach(function(pair) {
// Write key length
if (this.version === 2)
result.writeUInt16BE(pair[0], offset, true);
else
result.writeUInt32BE(pair[0], offset, true);
// Write key
result.write(pair[1], offset + size);
offset += pair[0] + size;
// Write value length
if (this.version === 2)
result.writeUInt16BE(pair[2], offset, true);
else
result.writeUInt32BE(pair[2], offset, true);
// Write value
result.write(pair[3], offset + size);
offset += pair[2] + size;
}, this);
return result;
};
Framer.prototype._synFrame = function _synFrame(type,
id,
assoc,
priority,
dict,
callback) {
var self = this;
// Compress headers
this.deflate(dict, function (err, chunks, size) {
if (err)
return callback(err);
var offset = type === 'SYN_STREAM' ? 18 : self.version === 2 ? 14 : 12,
total = offset - 8 + size,
frame = new Buffer(offset + size);
// Control + Version
frame.writeUInt16BE(0x8000 | self.version, 0, true);
// Type
frame.writeUInt16BE(type === 'SYN_STREAM' ? 1 : 2, 2, true);
// Size
frame.writeUInt32BE(total & 0x00ffffff, 4, true);
// Stream ID
frame.writeUInt32BE(id & 0x7fffffff, 8, true);
if (type === 'SYN_STREAM') {
// Unidirectional
if (assoc !== 0)
frame[4] = 2;
// Associated Stream-ID
frame.writeUInt32BE(assoc & 0x7fffffff, 12, true);
// Priority
var priorityValue;
if (self.version === 2)
priorityValue = Math.max(Math.min(priority, 3), 0) << 6;
else
priorityValue = Math.max(Math.min(priority, 7), 0) << 5;
frame.writeUInt8(priorityValue, 16, true);
}
for (var i = 0; i < chunks.length; i++) {
chunks[i].copy(frame, offset);
offset += chunks[i].length;
}
callback(null, frame);
});
};
//
// ### function replyFrame (id, code, reason, headers, callback)
// #### @id {Number} Stream ID
// #### @code {Number} HTTP Status Code
// #### @reason {String} (optional)
// #### @headers {Object|Array} (optional) HTTP headers
// #### @callback {Function} Continuation function
// Sends SYN_REPLY frame
//
Framer.prototype.replyFrame = function replyFrame(id,
code,
reason,
headers,
callback) {
if (!this.version) {
return this.on('version', function() {
this.replyFrame(id, code, reason, headers, callback);
});
}
var self = this;
var dict = this.headersToDict(headers, function(headers) {
if (self.version === 2) {
headers.status = code + ' ' + reason;
headers.version = 'HTTP/1.1';
} else {
headers[':status'] = code + ' ' + reason;
headers[':version'] = 'HTTP/1.1';
}
});
this._synFrame('SYN_REPLY', id, null, 0, dict, callback);
};
//
// ### function streamFrame (id, assoc, headers, callback)
// #### @id {Number} stream id
// #### @assoc {Number} associated stream id
// #### @meta {Object} meta headers ( method, scheme, url, version )
// #### @headers {Object} stream headers
// #### @callback {Function} continuation callback
// Create SYN_STREAM frame
// (needed for server push and testing)
//
Framer.prototype.streamFrame = function streamFrame(id,
assoc,
meta,
headers,
callback) {
if (!this.version) {
return this.on('version', function() {
this.streamFrame(id, assoc, meta, headers, callback);
});
}
var self = this;
var dict = this.headersToDict(headers, function(headers) {
if (self.version === 2) {
if (meta.status)
headers.status = meta.status;
headers.version = meta.version || 'HTTP/1.1';
headers.url = meta.url;
if (meta.method)
headers.method = meta.method;
} else {
if (meta.status)
headers[':status'] = meta.status;
headers[':version'] = meta.version || 'HTTP/1.1';
headers[':path'] = meta.path || meta.url;
headers[':scheme'] = meta.scheme || 'https';
headers[':host'] = meta.host;
if (meta.method)
headers[':method'] = meta.method;
}
});
this._synFrame('SYN_STREAM', id, assoc, meta.priority, dict, callback);
};
//
// ### function headersFrame (id, headers, callback)
// #### @id {Number} Stream id
// #### @headers {Object} Headers
// #### @callback {Function}
// Sends HEADERS frame
//
Framer.prototype.headersFrame = function headersFrame(id, headers, callback) {
if (!this.version) {
return this.on('version', function() {
this.headersFrame(id, headers, callback);
});
}
var self = this;
var dict = this.headersToDict(headers, function(headers) {});
this.deflate(dict, function (err, chunks, size) {
if (err)
return callback(err);
var offset = self.version === 2 ? 14 : 12,
total = offset - 8 + size,
frame = new Buffer(offset + size);
// Control + Version
frame.writeUInt16BE(0x8000 | self.version, 0, true);
// Type
frame.writeUInt16BE(8, 2, true);
// Length
frame.writeUInt32BE(total & 0x00ffffff, 4, true);
// Stream ID
frame.writeUInt32BE(id & 0x7fffffff, 8, true);
// Copy chunks
for (var i = 0; i < chunks.length; i++) {
chunks[i].copy(frame, offset);
offset += chunks[i].length;
}
callback(null, frame);
});
};
//
// ### function dataFrame (id, fin, data, callback)
// #### @id {Number} Stream id
// #### @fin {Bool} Is this data frame last frame
// #### @data {Buffer} Response data
// #### @callback {Function}
// Sends DATA frame
//
Framer.prototype.dataFrame = function dataFrame(id, fin, data, callback) {
if (!this.version) {
return this.on('version', function() {
this.dataFrame(id, fin, data, callback);
});
}
if (!fin && !data.length)
return callback(null, empty);
var frame = new Buffer(8 + data.length);
frame.writeUInt32BE(id & 0x7fffffff, 0, true);
frame.writeUInt32BE(data.length & 0x00ffffff, 4, true);
frame.writeUInt8(fin ? 0x01 : 0x0, 4, true);
if (data.length)
data.copy(frame, 8);
return callback(null, frame);
};
//
// ### function pingFrame (id)
// #### @id {Number} Ping ID
// Sends PING frame
//
Framer.prototype.pingFrame = function pingFrame(id, callback) {
if (!this.version) {
return this.on('version', function() {
this.pingFrame(id, callback);
});
}
var header = new Buffer(12);
// Version and type
header.writeUInt32BE(0x80000006 | (this.version << 16), 0, true);
// Length
header.writeUInt32BE(0x00000004, 4, true);
// ID
header.writeUInt32BE(id, 8, true);
return callback(null, header);
};
//
// ### function rstFrame (id, code, extra, callback)
// #### @id {Number} Stream ID
// #### @code {Number} RST Code
// #### @extra {String} Extra debugging info
// #### @callback {Function}
// Sends PING frame
//
Framer.prototype.rstFrame = function rstFrame(id, code, extra, callback) {
if (!this.version) {
return this.on('version', function() {
this.rstFrame(id, code, extra, callback);
});
}
var header = new Buffer(16 +
(this.debug ? Buffer.byteLength(extra || '') : 0));
// Version and type
header.writeUInt32BE(0x80000003 | (this.version << 16), 0, true);
// Length
header.writeUInt32BE(0x00000008, 4, true);
// Stream ID
header.writeUInt32BE(id & 0x7fffffff, 8, true);
// Status Code
header.writeUInt32BE(code, 12, true);
// Extra debugging information
if (this.debug && extra)
header.write(extra, 16);
return callback(null, header);
};
//
// ### function settingsFrame (options, callback)
// #### @options {Object} settings frame options
// #### @callback {Function}
// Sends SETTINGS frame with MAX_CONCURRENT_STREAMS and initial window
//
Framer.prototype.settingsFrame = function settingsFrame(options, callback) {
if (!this.version) {
return this.on('version', function() {
this.settingsFrame(options, callback);
});
}
var settings,
key = this.version === 2 ? '2/' + options.maxStreams :
'3/' + options.maxStreams + ':' +
options.windowSize;
if (!(settings = Framer.settingsCache[key])) {
var params = [];
if (isFinite(options.maxStreams)) {
params.push({
key: constants.settings.SETTINGS_MAX_CONCURRENT_STREAMS,
value: options.maxStreams
});
}
if (this.version > 2) {
params.push({
key: constants.settings.SETTINGS_INITIAL_WINDOW_SIZE,
value: options.windowSize
});
}
settings = new Buffer(12 + 8 * params.length);
// Version and type
settings.writeUInt32BE(0x80000004 | (this.version << 16), 0, true);
// Length
settings.writeUInt32BE((4 + 8 * params.length) & 0x00FFFFFF, 4, true);
// Count of entries
settings.writeUInt32BE(params.length, 8, true);
var offset = 12;
params.forEach(function(param) {
var flag = constants.settings.FLAG_SETTINGS_PERSIST_VALUE << 24;
if (this.version === 2)
settings.writeUInt32LE(flag | param.key, offset, true);
else
settings.writeUInt32BE(flag | param.key, offset, true);
offset += 4;
settings.writeUInt32BE(param.value & 0x7fffffff, offset, true);
offset += 4;
}, this);
Framer.settingsCache[key] = settings;
}
return callback(null, settings);
};
Framer.settingsCache = {};
//
// ### function windowUpdateFrame (id)
// #### @id {Buffer} WindowUpdate ID
// Sends WINDOW_UPDATE frame
//
Framer.prototype.windowUpdateFrame = function windowUpdateFrame(id, delta, cb) {
if (!this.version) {
return this.on('version', function() {
this.windowUpdateFrame(id, delta, cb);
});
}
var header = new Buffer(16);
// Version and type
header.writeUInt32BE(0x80000009 | (this.version << 16), 0, true);
// Length
header.writeUInt32BE(0x00000008, 4, true);
// ID
header.writeUInt32BE(id & 0x7fffffff, 8, true);
// Delta
if (delta > 0)
header.writeUInt32BE(delta & 0x7fffffff, 12, true);
else
header.writeUInt32BE(delta, 12, true);
return cb(null, header);
};
Framer.prototype.goawayFrame = function goawayFrame(lastId, status, cb) {
if (!this.version) {
return this.on('version', function() {
this.goawayFrame(lastId, status, cb);
});
}
var header = new Buffer(16);
// Version and type
header.writeUInt32BE(0x80000007 | (this.version << 16), 0, true);
// Length
header.writeUInt32BE(0x00000008, 4, true);
// Last-good-stream-ID
header.writeUInt32BE(lastId & 0x7fffffff, 8, true);
// Status
header.writeUInt32BE(status, 12, true);
return cb(null, header);
};

Просмотреть файл

@ -1,4 +0,0 @@
exports.dictionary = require('./dictionary');
exports.constants = require('./constants');
exports.parser = require('./parser');
exports.framer = require('./framer');

Просмотреть файл

@ -1,588 +0,0 @@
var parser = exports;
var spdy = require('../../spdy'),
utils = spdy.utils,
util = require('util'),
stream = require('stream'),
Buffer = require('buffer').Buffer;
var legacy = !stream.Duplex;
if (legacy) {
var DuplexStream = stream;
} else {
var DuplexStream = stream.Duplex;
}
//
// ### function Parser (connection)
// #### @connection {spdy.Connection} connection
// SPDY protocol frames parser's @constructor
//
function Parser(connection) {
DuplexStream.call(this);
this.paused = false;
this.buffer = [];
this.buffered = 0;
this.waiting = 8;
this.state = { type: 'frame-head' };
this.socket = connection.socket;
this.connection = connection;
this.version = null;
this.deflate = null;
this.inflate = null;
this.connection = connection;
if (legacy) {
this.readable = this.writable = true;
}
}
util.inherits(Parser, DuplexStream);
//
// ### function create (connection)
// #### @connection {spdy.Connection} connection
// @constructor wrapper
//
parser.create = function create(connection) {
return new Parser(connection);
};
//
// ### function destroy ()
// Just a stub.
//
Parser.prototype.destroy = function destroy() {
};
Parser.prototype._slice = function _slice(waiting) {
var buffer = new Buffer(waiting);
var sliced = 0;
var offset = 0;
while (waiting > offset && sliced < this.buffer.length) {
var chunk = this.buffer[sliced++],
overmatched = false;
// Copy chunk into `buffer`
if (chunk.length > waiting - offset) {
chunk.copy(buffer, offset, 0, waiting - offset);
this.buffer[--sliced] = chunk.slice(waiting - offset);
this.buffered += this.buffer[sliced].length;
overmatched = true;
} else {
chunk.copy(buffer, offset);
}
// Move offset and decrease amount of buffered data
offset += chunk.length;
this.buffered -= chunk.length;
if (overmatched) break;
}
// Remove used buffers
this.buffer = this.buffer.slice(sliced);
return buffer;
};
//
// ### function _write (data, encoding, cb)
// #### @data {Buffer} chunk of data
// #### @encoding {Null} encoding
// #### @cb {Function} callback
// Writes or buffers data to parser
//
Parser.prototype._write = function write(data, encoding, cb) {
// Legacy compatibility
if (!cb) cb = function() {};
if (data !== undefined) {
// Buffer data
this.buffer.push(data);
this.buffered += data.length;
}
// Notify caller about state (for piping)
if (this.paused) {
this.needDrain = true;
cb();
return false;
}
if (this.needDrain) {
// Mark parser as drained
this.needDrain = false;
this.emit('drain');
}
// We shall not do anything until we get all expected data
if (this.buffered < this.waiting) {
// Emit DATA by chunks as they come
if (this.buffered !== 0 &&
this.state.header &&
!this.state.header.control) {
var buffer = this._slice(this.buffered);
this.waiting -= buffer.length;
this.emit('frame', {
type: 'DATA',
id: this.state.header.id,
fin: false,
compressed: (this.state.header.flags & 0x02) === 0x02,
data: buffer
});
}
cb();
return;
}
var self = this;
var buffer = this._slice(this.waiting);
// Executed parser for buffered data
this.paused = true;
var sync = true;
this.execute(this.state, buffer, function (err, waiting) {
// Propagate errors
if (err) {
// And unpause once execution finished
self.paused = false;
cb();
return self.emit('error', err);
}
// Set new `waiting`
self.waiting = waiting;
if (sync) {
utils.nextTick(function() {
// Unpause right before entering new `_write()` call
self.paused = false;
self._write(undefined, null, cb);
});
} else {
// Unpause right before entering new `_write()` call
self.paused = false;
self._write(undefined, null, cb);
}
});
sync = false;
};
if (legacy) {
//
// ### function write (data, encoding, cb)
// #### @data {Buffer} chunk of data
// #### @encoding {Null} encoding
// #### @cb {Function} callback
// Legacy method
//
Parser.prototype.write = Parser.prototype._write;
//
// ### function end ()
// Stream's end() implementation
//
Parser.prototype.end = function end() {
this.emit('end');
};
}
//
// ### function setVersion (version)
// #### @version {Number} Protocol version
// Set protocol version to use
//
Parser.prototype.setVersion = function setVersion(version) {
this.version = version;
this.emit('version', version);
this.deflate = spdy.utils.zwrap(this.connection._spdyState.deflate);
this.inflate = spdy.utils.zwrap(this.connection._spdyState.inflate);
};
//
// ### function execute (state, data, callback)
// #### @state {Object} Parser's state
// #### @data {Buffer} Incoming data
// #### @callback {Function} continuation callback
// Parse buffered data
//
Parser.prototype.execute = function execute(state, data, callback) {
if (state.type === 'frame-head') {
var header = state.header = this.parseHeader(data);
if (this.version === null && header.control) {
if (header.version !== 2 && header.version !== 3) {
return callback(new Error('Unsupported spdy version: ' +
header.version));
}
this.setVersion(header.version);
}
state.type = 'frame-body';
callback(null, header.length);
} else if (state.type === 'frame-body') {
var self = this;
// Data frame
if (!state.header.control) {
return onFrame(null, {
type: 'DATA',
id: state.header.id,
fin: (state.header.flags & 0x01) === 0x01,
compressed: (state.header.flags & 0x02) === 0x02,
data: data
});
} else {
// Control frame
this.parseBody(state.header, data, onFrame);
}
function onFrame(err, frame) {
if (err) return callback(err);
self.emit('frame', frame);
state.type = 'frame-head';
state.header = null;
callback(null, 8);
};
}
};
//
// ### function parseHeader (data)
// ### @data {Buffer} incoming data
// Returns parsed SPDY frame header
//
Parser.prototype.parseHeader = function parseHeader(data) {
var header = {
control: (data.readUInt8(0) & 0x80) === 0x80 ? true : false,
version: null,
type: null,
id: null,
flags: data.readUInt8(4),
length: data.readUInt32BE(4) & 0x00ffffff
};
if (header.control) {
header.version = data.readUInt16BE(0) & 0x7fff;
header.type = data.readUInt16BE(2);
} else {
header.id = data.readUInt32BE(0) & 0x7fffffff;
}
return header;
};
//
// ### function execute (header, body, callback)
// #### @header {Object} Frame headers
// #### @body {Buffer} Frame's body
// #### @callback {Function} Continuation callback
// Parse frame (decompress data and create streams)
//
Parser.prototype.parseBody = function parseBody(header, body, callback) {
// SYN_STREAM or SYN_REPLY
if (header.type === 0x01 || header.type === 0x02)
this.parseSynHead(header.type, header.flags, body, callback);
// RST_STREAM
else if (header.type === 0x03)
this.parseRst(body, callback);
// SETTINGS
else if (header.type === 0x04)
this.parseSettings(body, callback);
else if (header.type === 0x05)
callback(null, { type: 'NOOP' });
// PING
else if (header.type === 0x06)
this.parsePing(body, callback);
// GOAWAY
else if (header.type === 0x07)
this.parseGoaway(body, callback);
// HEADERS
else if (header.type === 0x08)
this.parseHeaders(body, callback);
// WINDOW_UPDATE
else if (header.type === 0x09)
this.parseWindowUpdate(body, callback);
// X-FORWARDED
else if (header.type === 0xf000)
this.parseXForwarded(body, callback);
else
callback(null, { type: 'unknown: ' + header.type, body: body });
};
//
// ### function parseSynHead (type, flags, data)
// #### @type {Number} Frame type
// #### @flags {Number} Frame flags
// #### @data {Buffer} input data
// Returns parsed syn_* frame's head
//
Parser.prototype.parseSynHead = function parseSynHead(type,
flags,
data,
callback) {
var stream = type === 0x01;
var offset = stream ? 10 : this.version === 2 ? 6 : 4;
if (data.length < offset)
return callback(new Error('SynHead OOB'));
var kvs = data.slice(offset);
this.parseKVs(kvs, function(err, headers) {
if (err)
return callback(err);
if (stream === 'SYN_STREAM' &&
(!headers.method || !(headers.path || headers.url))) {
return callback(new Error('Missing `:method` and/or `:path` header'));
}
callback(null, {
type: stream ? 'SYN_STREAM' : 'SYN_REPLY',
id: data.readUInt32BE(0, true) & 0x7fffffff,
associated: stream ? data.readUInt32BE(4, true) & 0x7fffffff : 0,
priority: stream ? data[8] >> 5 : 0,
fin: (flags & 0x01) === 0x01,
unidir: (flags & 0x02) === 0x02,
headers: headers,
url: headers.path || headers.url || ''
});
});
};
//
// ### function parseHeaders (data, callback)
// #### @data {Buffer} input data
// #### @callback {Function} continuation
// Parse HEADERS
//
Parser.prototype.parseHeaders = function parseHeaders(data, callback) {
var offset = this.version === 2 ? 6 : 4;
if (data.length < offset)
return callback(new Error('HEADERS OOB'));
var streamId = data.readUInt32BE(0, true) & 0x7fffffff;
this.parseKVs(data.slice(offset), function(err, headers) {
if (err)
return callback(err);
callback(null, {
type: 'HEADERS',
id: streamId,
headers: headers
});
});
};
//
// ### function parseKVs (pairs, callback)
// #### @pairs {Buffer} header pairs
// #### @callback {Function} continuation
// Returns hashmap of parsed headers
//
Parser.prototype.parseKVs = function parseKVs(pairs, callback) {
var self = this;
this.inflate(pairs, function(err, chunks, length) {
if (err)
return callback(err);
var pairs = Buffer.concat(chunks, length);
var size = self.version === 2 ? 2 : 4;
if (pairs.length < size)
return callback(new Error('KV OOB'));
var count = size === 2 ? pairs.readUInt16BE(0, true) :
pairs.readUInt32BE(0, true),
headers = {};
pairs = pairs.slice(size);
function readString() {
if (pairs.length < size)
return null;
var len = size === 2 ? pairs.readUInt16BE(0, true) :
pairs.readUInt32BE(0, true);
if (pairs.length < size + len) {
return null;
}
var value = pairs.slice(size, size + len);
pairs = pairs.slice(size + len);
return value.toString();
}
while(count > 0) {
var key = readString(),
value = readString();
if (key === null || value === null)
return callback(new Error('Headers OOB'));
if (self.version >= 3)
headers[key.replace(/^:/, '')] = value;
else
headers[key] = value;
count--;
}
callback(null, headers);
});
};
//
// ### function parseRst (data, callback)
// #### @data {Buffer} input data
// #### @callback {Function} continuation
// Parse RST
//
Parser.prototype.parseRst = function parseRst(data, callback) {
if (data.length < 8)
return callback(new Error('RST OOB'));
callback(null, {
type: 'RST_STREAM',
id: data.readUInt32BE(0, true) & 0x7fffffff,
status: data.readUInt32BE(4, true),
extra: data.length > 8 ? data.slice(8) : null
});
};
//
// ### function parseSettings (data, callback)
// #### @data {Buffer} input data
// #### @callback {Function} continuation
// Parse SETTINGS
//
Parser.prototype.parseSettings = function parseSettings(data, callback) {
if (data.length < 4)
return callback(new Error('SETTINGS OOB'));
var settings = {},
number = data.readUInt32BE(0, true),
idMap = {
1: 'upload_bandwidth',
2: 'download_bandwidth',
3: 'round_trip_time',
4: 'max_concurrent_streams',
5: 'current_cwnd',
6: 'download_retrans_rate',
7: 'initial_window_size',
8: 'client_certificate_vector_size'
};
if (data.length < 4 + number * 8)
return callback(new Error('SETTINGS OOB#2'));
for (var i = 0; i < number; i++) {
var id = (this.version === 2 ? data.readUInt32LE(4 + i * 8, true) :
data.readUInt32BE(4 + i * 8, true)),
flags = (id >> 24) & 0xff;
id = id & 0xffffff;
var name = idMap[id];
settings[id] = settings[name] = {
persist: !!(flags & 0x1),
persisted: !!(flags & 0x2),
value: data.readUInt32BE(8 + (i*8), true)
};
}
callback(null, {
type: 'SETTINGS',
settings: settings
});
};
//
// ### function parseGoaway (data, callback)
// #### @data {Buffer} input data
// #### @callback {Function} continuation
// Parse PING
//
Parser.prototype.parsePing = function parsePing(body, callback) {
if (body.length < 4)
return callback(new Error('PING OOB'));
callback(null, { type: 'PING', pingId: body.readUInt32BE(0, true) });
};
//
// ### function parseGoaway (data, callback)
// #### @data {Buffer} input data
// #### @callback {Function} continuation
// Parse GOAWAY
//
Parser.prototype.parseGoaway = function parseGoaway(data, callback) {
if (data.length < 4)
return callback(new Error('GOAWAY OOB'));
callback(null, {
type: 'GOAWAY',
lastId: data.readUInt32BE(0, true) & 0x7fffffff
});
};
//
// ### function parseWindowUpdate (data, callback)
// #### @data {Buffer} input data
// #### @callback {Function} continuation
// Parse WINDOW_UPDATE
//
Parser.prototype.parseWindowUpdate = function parseWindowUpdate(data, callback) {
if (data.length < 8)
return callback(new Error('WINDOW_UPDATE OOB'));
callback(null, {
type: 'WINDOW_UPDATE',
id: data.readUInt32BE(0, true) & 0x7fffffff,
delta: data.readUInt32BE(4, true) & 0x7fffffff
});
};
//
// ### function parseXForwarded (data, callback)
// #### @data {Buffer} input data
// #### @callback {Function} continuation
// Parse X_FORWARDED
//
Parser.prototype.parseXForwarded = function parseXForwarded(data, callback) {
if (data.length < 4)
return callback(new Error('X_FORWARDED OOB'));
var len = data.readUInt32BE(0, true);
if (len + 4 > data.length)
return callback(new Error('X_FORWARDED host length OOB'));
callback(null, {
type: 'X_FORWARDED',
host: data.slice(4, 4 + len).toString()
});
};

Просмотреть файл

@ -1,208 +0,0 @@
var spdy = require('../spdy'),
utils = spdy.utils,
http = require('http'),
Stream = require('stream').Stream,
res = http.ServerResponse.prototype;
//
// ### function _renderHeaders ()
// Copy pasted from lib/http.js
// (added lowercase)
//
exports._renderHeaders = function renderHeaders() {
if (this._header)
throw new Error("Can't render headers after they are sent to the client.");
var keys = Object.keys(this._headerNames);
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
this._headerNames[key] = this._headerNames[key].toLowerCase();
}
return res._renderHeaders.call(this);
};
//
// ### function writeHead (statusCode)
// #### @statusCode {Number} HTTP Status code
// .writeHead() wrapper
// (Sorry, copy pasted from lib/http.js)
//
exports.writeHead = function writeHead(statusCode) {
if (this._headerSent)
return;
this._headerSent = true;
var reasonPhrase, headers = {}, headerIndex;
if (typeof arguments[1] == 'string') {
reasonPhrase = arguments[1];
headerIndex = 2;
} else {
reasonPhrase = http.STATUS_CODES[statusCode] || 'unknown';
headerIndex = 1;
}
this.statusCode = statusCode;
var obj = arguments[headerIndex];
if (obj && this._headers) {
// Slow-case: when progressive API and header fields are passed.
headers = this._renderHeaders();
// handle object case
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
var k = keys[i];
if (k)
headers[k] = obj[k];
}
} else if (this._headers) {
// only progressive api is used
headers = this._renderHeaders();
} else {
// only writeHead() called
headers = obj;
}
// cleanup
this._header = '';
// Do not send data to new connections after GOAWAY
if (this.socket._isGoaway())
return;
// Date header
if (this.sendDate === true) {
if (headers === undefined)
headers = {};
if (headers.date === undefined)
headers.date = new Date().toUTCString();
}
this.socket._lock(function() {
var socket = this;
this._spdyState.framer.replyFrame(
this._spdyState.id,
statusCode,
reasonPhrase,
headers,
function (err, frame) {
if (err) {
socket._unlock();
socket.emit('error', err);
return;
}
socket.connection.cork();
socket.connection.write(frame);
utils.nextTick(function() {
socket.connection.uncork();
});
socket._unlock();
}
);
});
};
//
// ### function end (data, encoding, cb)
// #### @data {Buffer|String} (optional) data
// #### @encoding {String} (optional) string encoding
// #### @cb {Function}
// Send final data
//
exports.end = function end(data, encoding, cb) {
if (this.socket)
this.socket._spdyState.ending = true;
this.constructor.prototype.end.call(this, data, encoding, cb);
};
//
// ### function push (url, headers, callback)
// #### @url {String} absolute or relative url (from root anyway)
// #### @headers {Object} response headers
// #### @callbacks {Function} continuation that will receive stream object
// Initiates push stream
//
exports.push = function push(url, headers, priority, callback) {
var socket = this.socket;
if (!callback && typeof priority === 'function') {
callback = priority;
priority = null;
}
if (!priority && typeof priority !== 'number')
priority = 7;
if (!callback)
callback = function() {};
if (!socket || socket._destroyed) {
var stub = new Stream();
var err = Error('Can\'t open push stream, parent socket destroyed');
utils.nextTick(function() {
if (stub.listeners('error').length !== 0)
stub.emit('error', err);
callback(err);
});
return stub;
}
var id = socket.connection._spdyState.pushId += 2,
scheme = socket._spdyState.scheme,
host = headers.host || socket._spdyState.host || 'localhost',
fullUrl = /^\//.test(url) ? scheme + '://' + host + url : url;
var stream = new spdy.Stream(socket.connection, {
type: 'SYN_STREAM',
id: id,
associated: socket._spdyState.id,
priority: priority,
headers: {}
});
stream.associated = socket;
socket._lock(function() {
this._spdyState.framer.streamFrame(
id,
this._spdyState.id,
{
method: 'GET',
path: url,
url: fullUrl,
scheme: scheme,
host: host,
version: 'HTTP/1.1',
priority: priority,
status: 200
},
headers,
function(err, frame) {
if (err) {
socket._unlock();
if (callback)
callback(err);
stream.destroy(err);
return;
} else {
socket.connection.cork();
socket.connection.write(frame);
utils.nextTick(function() {
socket.connection.uncork();
});
socket._unlock();
}
stream.emit('acknowledge');
if (callback)
callback(null, stream);
}
);
});
return stream;
};

Просмотреть файл

@ -1,101 +0,0 @@
var spdy = require('../spdy');
var utils = spdy.utils;
var scheduler = exports;
//
// ### function Scheduler (connection)
// #### @connection {spdy.Connection} active connection
// Connection's streams scheduler
//
function Scheduler(connection) {
this.connection = connection;
this.priorities = [[], [], [], [], [], [], [], []];
this._tickListener = this._tickListener.bind(this);
this._tickListening = false;
this._tickCallbacks = [];
this._corked = false;
}
//
// ### function create (connection)
// #### @connection {spdy.Connection} active connection
//
exports.create = function create(connection) {
return new Scheduler(connection);
};
//
// ### function schedule (stream, data)
// #### @stream {spdy.Stream} Source stream
// #### @data {Buffer} data to write on tick
// Use stream priority to invoke callbacks in right order
//
Scheduler.prototype.schedule = function schedule(stream, data) {
// Ignore data from destroyed stream
if (stream._spdyState.destroyed)
return;
this.scheduleLast(stream, data);
};
//
// ### function scheduleLast (stream, data)
// #### @stream {spdy.Stream} Source stream
// #### @data {Buffer} data to write on tick
// Use stream priority to invoke callbacks in right order
//
Scheduler.prototype.scheduleLast = function scheduleLast(stream, data) {
var priority = stream._spdyState.priority;
priority = Math.min(priority, 7);
priority = Math.max(priority, 0);
this.priorities[priority].push(data);
};
//
// ### function tickListener ()
//
Scheduler.prototype._tickListener = function tickListener() {
var priorities = this.priorities;
var tickCallbacks = this._tickCallbacks;
this._tickListening = false;
this.priorities = [[], [], [], [], [], [], [], []];
this._tickCallbacks = [];
// Run all priorities
for (var i = 0; i < 8; i++)
for (var j = 0; j < priorities[i].length; j++)
this.connection.write(priorities[i][j]);
// Invoke callbacks
for (var i = 0; i < tickCallbacks.length; i++)
tickCallbacks[i]();
if (this._corked) {
this.connection.uncork();
if (this._tickListening)
this.connection.cork();
else
this._corked = false;
}
};
//
// ### function tick ()
// Add .nextTick callback if not already present
//
Scheduler.prototype.tick = function tick(cb) {
if (cb)
this._tickCallbacks.push(cb);
if (this._tickListening)
return;
this._tickListening = true;
if (!this._corked) {
this.connection.cork();
this._corked = true;
}
if (!this.connection._spdyState.parser.needDrain) {
utils.nextTick(this._tickListener);
} else {
this.connection._spdyState.parser.once('drain', this._tickListener);
}
};

Просмотреть файл

@ -1,329 +0,0 @@
var spdy = require('../spdy'),
util = require('util'),
https = require('https');
var Connection = spdy.Connection;
//
// ### function instantiate (HTTPSServer)
// #### @HTTPSServer {https.Server|Function} Base server class
// Will return constructor for SPDY Server, based on the HTTPSServer class
//
function instantiate(HTTPSServer) {
//
// ### function Server (options, requestListener)
// #### @options {Object} tls server options
// #### @requestListener {Function} (optional) request callback
// SPDY Server @constructor
//
function Server(options, requestListener) {
// Initialize
this._init(HTTPSServer, options, requestListener);
// Wrap connection handler
this._wrap();
};
util.inherits(Server, HTTPSServer);
// Copy prototype methods
Object.keys(proto).forEach(function(key) {
this[key] = proto[key];
}, Server.prototype);
return Server;
}
exports.instantiate = instantiate;
// Common prototype for all servers
var proto = {};
//
// ### function _init(base, options, listener)
// #### @base {Function} (optional) base server class (https.Server)
// #### @options {Object} tls server options
// #### @handler {Function} (optional) request handler
// Initializer.
//
proto._init = function _init(base, options, handler) {
var state = {};
this._spdyState = state;
if (!options)
options = {};
// Copy user supplied options
options = util._extend({}, options);
var supportedProtocols = [
'spdy/3.1', 'spdy/3', 'spdy/2',
'http/1.1', 'http/1.0'
];
options.NPNProtocols = supportedProtocols;
options.ALPNProtocols = supportedProtocols;
options.isServer = true;
// Header compression is enabled by default in servers, in contrast to clients
// where it is disabled to prevent CRIME attacks.
// See: https://groups.google.com/d/msg/spdy-dev/6mVYRv-lbuc/qGcW2ldOpt8J
if (options.headerCompression !== false)
options.headerCompression = true;
state.options = options;
state.reqHandler = handler;
if (options.plain && !options.ssl)
base.call(this, handler);
else
base.call(this, options, handler);
// Use https if neither NPN or ALPN is supported
if (!process.features.tls_npn && !process.features.tls_alpn &&
!options.debug && !options.plain)
return;
};
//
// ### function _wrap()
// Wrap connection handler and add logic.
//
proto._wrap = function _wrap() {
var self = this,
state = this._spdyState;
// Wrap connection handler
var event = state.options.plain && !state.options.ssl ? 'connection' :
'secureConnection',
handler = this.listeners(event)[0];
state.handler = handler;
this.removeAllListeners(event);
// 2 minutes default timeout
if (state.options.timeout !== undefined)
this.timeout = state.options.timeout;
else
this.timeout = this.timeout || 2 * 60 * 1000;
// Normal mode, use NPN or ALPN to fallback to HTTPS
if (!state.options.plain)
return this.on(event, this._onConnection.bind(this));
// In case of plain connection, we must fallback to HTTPS if first byte
// is not equal to 0x80.
this.on(event, function(socket) {
var history = [],
_emit = socket.emit;
// Add 'data' listener, otherwise 'data' events won't be emitted
if (spdy.utils.isLegacy) {
function ondata() {};
socket.once('data', ondata);
}
// 2 minutes timeout, as http.js does by default
socket.setTimeout(self.timeout);
socket.emit = function emit(event, data) {
history.push(Array.prototype.slice.call(arguments));
if (event === 'data') {
// Legacy
if (spdy.utils.isLegacy)
onFirstByte.call(socket, data);
} else if (event === 'readable') {
// Streams
onReadable.call(socket);
} else if (event === 'end' ||
event === 'close' ||
event === 'error' ||
event === 'timeout') {
// We shouldn't get there if any data was received
fail();
}
};
function fail() {
socket.emit = _emit;
history = null;
socket.removeListener('readable', onReadable);
try {
socket.destroy();
} catch (e) {
}
}
function restore() {
var copy = history.slice();
history = null;
socket.removeListener('readable', onReadable);
if (spdy.utils.isLegacy)
socket.removeListener('data', ondata);
socket.emit = _emit;
for (var i = 0; i < copy.length; i++) {
if (copy[i][0] !== 'data' || spdy.utils.isLegacy)
socket.emit.apply(socket, copy[i]);
if (copy[i][0] === 'end' && socket.onend)
socket.onend();
}
}
function onFirstByte(data) {
// Ignore empty packets
if (data.length === 0)
return;
if (data[0] === 0x80)
self._onConnection(socket);
else
handler.call(self, socket);
// Fire events
restore();
// NOTE: If we came there - .ondata() will be called anyway in this tick,
// so there're no need to call it manually
};
// Hack to make streams2 work properly
if (!spdy.utils.isLegacy)
socket.on('readable', onReadable);
function onReadable() {
var data = socket.read(1);
// Ignore empty packets
if (!data)
return;
socket.removeListener('readable', onReadable);
// `.unshift()` emits `readable` event. Thus `emit` method should
// be restored before calling it.
socket.emit = _emit;
// Put packet back where it was before
socket.unshift(data);
if (data[0] === 0x80)
self._onConnection(socket);
else
handler.call(self, socket);
// Fire events
restore();
if (socket.ondata) {
data = socket.read(socket._readableState.length);
if (data)
socket.ondata(data, 0, data.length);
}
}
});
};
//
// ### function _onConnection (socket)
// #### @socket {Stream} incoming socket
// Server's connection handler wrapper.
//
proto._onConnection = function _onConnection(socket) {
var self = this,
state = this._spdyState;
// Fallback to HTTPS if needed
var selectedProtocol = socket.npnProtocol || socket.alpnProtocol;
if ((!selectedProtocol || !selectedProtocol.match(/spdy/)) &&
!state.options.debug && !state.options.plain) {
return state.handler.call(this, socket);
}
// Wrap incoming socket into abstract class
var connection = new Connection(socket, state.options, this);
if (selectedProtocol === 'spdy/3.1')
connection._setVersion(3.1);
else if (selectedProtocol === 'spdy/3')
connection._setVersion(3);
else if (selectedProtocol === 'spdy/2')
connection._setVersion(2);
// Emulate each stream like connection
connection.on('stream', state.handler);
connection.on('connect', function onconnect(req, socket) {
socket.streamID = req.streamID = req.socket._spdyState.id;
socket.isSpdy = req.isSpdy = true;
socket.spdyVersion = req.spdyVersion = req.socket._spdyState.framer.version;
socket.once('finish', function onfinish() {
req.connection.end();
});
self.emit('connect', req, socket);
});
connection.on('request', function onrequest(req, res) {
// Copy extension methods
res._renderHeaders = spdy.response._renderHeaders;
res.writeHead = spdy.response.writeHead;
res.end = spdy.response.end;
res.push = spdy.response.push;
res.streamID = req.streamID = req.socket._spdyState.id;
res.spdyVersion = req.spdyVersion = req.socket._spdyState.framer.version;
res.isSpdy = req.isSpdy = true;
res.addTrailers = function addTrailers(headers) {
res.socket.sendHeaders(headers);
};
// Make sure that keep-alive won't apply to the response
res._last = true;
// Chunked encoding is not supported in SPDY
res.useChunkedEncodingByDefault = false;
// Populate trailing headers
req.connection.on('headers', function(headers) {
Object.keys(headers).forEach(function(key) {
req.trailers[key] = headers[key];
});
req.emit('trailers', headers);
});
self.emit('request', req, res);
});
connection.on('error', function onerror(e) {
socket.destroy(e.errno === 'EPIPE' ? undefined : e);
});
};
// Export Server instantiated from https.Server
var Server = instantiate(https.Server);
exports.Server = Server;
//
// ### function create (base, options, requestListener)
// #### @base {Function} (optional) base server class (https.Server)
// #### @options {Object} tls server options
// #### @requestListener {Function} (optional) request callback
// @constructor wrapper
//
exports.create = function create(base, options, requestListener) {
var server;
if (typeof base === 'function') {
server = instantiate(base);
} else {
server = Server;
requestListener = options;
options = base;
base = null;
}
// Instantiate http server if `ssl: false`
if (!base && options && options.plain && options.ssl === false)
return exports.create(require('http').Server, options, requestListener);
return new server(options, requestListener);
};

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,142 +0,0 @@
var spdy = require('../spdy'),
utils = exports;
var stream = require('stream'),
zlib = require('zlib'),
Buffer = require('buffer').Buffer;
// Export streams related stuff
utils.isLegacy = !stream.Duplex;
if (utils.isLegacy)
utils.DuplexStream = stream;
else
utils.DuplexStream = stream.Duplex;
utils.isArgvParser = !/^v(0\.12|0\.11|0\.10|0\.8|0\.9)\./.test(process.version);
//
// ### function createDeflate (version, compression)
// #### @version {Number} SPDY version
// #### @compression {Boolean} whether to enable compression
// Creates deflate stream with SPDY dictionary
//
utils.createDeflate = function createDeflate(version, compression) {
var deflate = zlib.createDeflate({
dictionary: spdy.protocol.dictionary[version],
flush: zlib.Z_SYNC_FLUSH,
windowBits: 11,
level: compression ? zlib.Z_DEFAULT_COMPRESSION : zlib.Z_NO_COMPRESSION
});
// Define lock information early
deflate.locked = false;
deflate.lockQueue = [];
if (spdy.utils.isLegacy)
deflate._flush = zlib.Z_SYNC_FLUSH;
return deflate;
};
//
// ### function createInflate (version)
// #### @version {Number} SPDY version
// Creates inflate stream with SPDY dictionary
//
utils.createInflate = function createInflate(version) {
var inflate = zlib.createInflate({
dictionary: spdy.protocol.dictionary[version],
flush: zlib.Z_SYNC_FLUSH,
windowBits: 0
});
// Define lock information early
inflate.locked = false;
inflate.lockQueue = [];
if (spdy.utils.isLegacy)
inflate._flush = zlib.Z_SYNC_FLUSH;
return inflate;
};
//
// ### function resetZlibStream (stream)
// #### @stream {zlib.Stream} stream
// Resets zlib stream and associated locks
//
utils.resetZlibStream = function resetZlibStream(stream, callback) {
if (stream.locked) {
stream.lockQueue.push(function() {
resetZlibStream(stream, callback);
});
return;
}
stream.reset();
stream.lockQueue = [];
callback(null);
};
var delta = 0;
//
// ### function zstream (stream, buffer, callback)
// #### @stream {Deflate|Inflate} One of streams above
// #### @buffer {Buffer} Input data (to compress or to decompress)
// #### @callback {Function} Continuation to callback
// Compress/decompress data and pass it to callback
//
utils.zstream = function zstream(stream, buffer, callback) {
var chunks = [],
total = 0;
if (stream.locked) {
stream.lockQueue.push(function() {
zstream(stream, buffer, callback);
});
return;
}
stream.locked = true;
function collect(chunk) {
chunks.push(chunk);
total += chunk.length;
}
stream.on('data', collect);
stream.write(buffer, done);
function done() {
stream.removeAllListeners('data');
stream.removeAllListeners('error');
if (callback)
callback(null, chunks, total);
stream.locked = false;
var deferred = stream.lockQueue.shift();
if (deferred)
deferred();
};
stream.once('error', function(err) {
stream.removeAllListeners('data');
callback(err);
callback = null;
});
};
//
// ### function zwrap (stream)
// #### @stream {zlib.Stream} stream to wrap
// Wraps stream within function to allow simple deflate/inflate
//
utils.zwrap = function zwrap(stream) {
return function(data, callback) {
utils.zstream(stream, data, callback);
};
};
if (typeof setImmediate === 'undefined')
utils.nextTick = process.nextTick.bind(process);
else
utils.nextTick = setImmediate;

Просмотреть файл

@ -1,61 +0,0 @@
var zlibpool = exports,
spdy = require('../spdy');
//
// ### function Pool (compression)
// #### @compression {Boolean} whether to enable compression
// Zlib streams pool
//
function Pool(compression) {
this.compression = compression;
this.pool = {
'spdy/2': [],
'spdy/3': [],
'spdy/3.1': []
};
}
//
// ### function create (compression)
// #### @compression {Boolean} whether to enable compression
// Returns instance of Pool
//
zlibpool.create = function create(compression) {
return new Pool(compression);
};
var x = 0;
//
// ### function get ()
// Returns pair from pool or a new one
//
Pool.prototype.get = function get(version, callback) {
if (this.pool[version].length > 0) {
return this.pool[version].pop();
} else {
var id = version.split('/', 2)[1];
return {
version: version,
deflate: spdy.utils.createDeflate(id, this.compression),
inflate: spdy.utils.createInflate(id)
};
}
};
//
// ### function put (pair)
// Puts pair into pool
//
Pool.prototype.put = function put(pair) {
var self = this,
waiting = 2;
spdy.utils.resetZlibStream(pair.inflate, done);
spdy.utils.resetZlibStream(pair.deflate, done);
function done() {
if (--waiting === 0)
self.pool[pair.version].push(pair);
}
};

Просмотреть файл

@ -1,39 +0,0 @@
{
"name": "spdy",
"version": "1.32.5",
"description": "Implementation of the SPDY protocol on node.js.",
"license": "MIT",
"keywords": [
"spdy"
],
"repository": {
"type": "git",
"url": "git://github.com/indutny/node-spdy.git"
},
"homepage": "https://github.com/indutny/node-spdy",
"bugs": {
"email": "node-spdy+bugs@indutny.com",
"url": "https://github.com/indutny/node-spdy/issues"
},
"author": "Fedor Indutny <fedor.indutny@gmail.com>",
"contributors": [
"Chris Storm <github@eeecooks.com>",
"François de Metz <francois@2metz.fr>",
"Ilya Grigorik <ilya@igvita.com>",
"Roberto Peon",
"Tatsuhiro Tsujikawa",
"Jesse Cravens <jesse.cravens@gmail.com>"
],
"dependencies": {},
"devDependencies": {
"mocha": "1.3.x"
},
"scripts": {
"test": "mocha --ui tdd --growl --reporter spec test/unit/*-test.js"
},
"engines": [
"node >= 0.7.0"
],
"main": "./lib/spdy",
"optionalDependencies": {}
}

Просмотреть файл

@ -1,51 +0,0 @@
var tls = require('tls'),
frames = require('../fixtures/frames');
var uri = require('url').parse(process.argv[2]),
host = uri.hostname,
port = +uri.port,
url = uri.path;
frames.createSynStream(host, url, function(syn_stream) {
var start = +new Date,
num = 3000;
batch(port, host, syn_stream, 100, num, function() {
var end = +new Date;
console.log('requests/sec : %d', 1000 * num / (end - start));
});
});
function request(port, host, data, callback) {
var socket = tls.connect(port, host, {
NPNProtocols: ['spdy/2'],
ALPNProtocols: ['spdy/2']
}, function() {
socket.write(data);
socket.once('data', function() {
socket.destroy();
callback();
});
});
return socket;
};
function batch(port, host, data, parallel, num, callback) {
var left = num,
done = 0;
for (var i = 0; i < parallel; i++) {
run(i);
}
function run(i) {
left--;
if (left < 0) return;
request(port, host, data, function() {
if (++done === num) return callback();
run(i);
});
}
};

Просмотреть файл

@ -1,38 +0,0 @@
var spdy = require('../../lib/spdy'),
Buffer = require('buffer').Buffer;
exports.createSynStream = function(host, url, callback) {
var deflate = spdy.utils.createDeflate(),
chunks = [],
chunksTotal = 0,
syn_stream;
deflate.on('data', function(chunk) {
chunks.push(chunk);
chunksTotal += chunk.length;
});
deflate.write(new Buffer([ 0x00, 0x02, 0x00, 0x04 ]));
deflate.write('host');
deflate.write(new Buffer([ 0x00, host.length ]));
deflate.write(host);
deflate.write(new Buffer([ 0x00, 0x03 ]));
deflate.write('url');
deflate.write(new Buffer([ 0x00, url.length ]));
deflate.write(url);
deflate.flush(function() {
syn_stream = new Buffer(18 + chunksTotal);
syn_stream.writeUInt32BE(0x80020001, 0);
syn_stream.writeUInt32BE(chunksTotal + 8, 4);
syn_stream.writeUInt32BE(0x00000001, 8);
syn_stream.writeUInt32BE(0x00000000, 12);
var offset = 18;
chunks.forEach(function(chunk) {
chunk.copy(syn_stream, offset);
offset += chunk.length;
});
callback(syn_stream);
});
};

Просмотреть файл

@ -1,12 +0,0 @@
var fs = require('fs'),
path = require('path');
var keysDir = require('path').resolve(__dirname, '../../keys/'),
options = {
key: fs.readFileSync(keysDir + '/spdy-key.pem'),
cert: fs.readFileSync(keysDir + '/spdy-cert.pem'),
ca: fs.readFileSync(keysDir + '/spdy-csr.pem'),
rejectUnauthorized: false
};
module.exports = options;

Просмотреть файл

@ -1,239 +0,0 @@
var assert = require('assert'),
spdy = require('../../'),
zlib = require('zlib'),
keys = require('../fixtures/keys'),
https = require('https'),
tls = require('tls'),
Buffer = require('buffer').Buffer,
PORT = 8081;
suite('A SPDY Server / Connect', function() {
var server;
var fox = 'The quick brown fox jumps over the lazy dog';
setup(function(done) {
server = spdy.createServer(keys, function(req, res) {
var comp = req.url === '/gzip' ? zlib.createGzip() :
req.url === '/deflate' ? zlib.createDeflate() :
null;
if (req.url == '/headerTest') {
if (req.headers['accept-encoding'] == 'gzip, deflate')
return res.end(fox);
else
return res.end();
}
// Terminate connection gracefully
if (req.url === '/goaway')
req.socket.connection.end();
if (!comp)
return res.end(fox);
res.writeHead(200, { 'Content-Encoding' : req.url.slice(1) });
comp.on('data', function(chunk) {
res.write(chunk);
});
comp.once('end', function() {
res.end();
});
comp.end(fox);
});
server.listen(PORT, done);
});
teardown(function(done) {
server.close(done);
});
test('should respond on regular https requests', function(done) {
var req = https.request({
host: '127.0.0.1',
port: PORT,
path: '/',
method: 'GET',
agent: false,
rejectUnauthorized: false
}, function(res) {
var received = '';
res.on('data', function(chunk) {
received += chunk;
});
res.once('end', function() {
assert.equal(received, fox);
done();
});
assert.equal(res.statusCode, 200);
});
req.end();
});
function spdyReqTest(url) {
test('should respond on spdy requests on ' + url, function(done) {
var agent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
rejectUnauthorized: false
});
var req = https.request({
path: url,
method: 'GET',
agent: agent,
}, function(res) {
var received = '';
res.on('data', function(chunk) {
received += chunk;
});
res.once('end', function() {
assert.equal(received, fox);
agent.close();
done();
});
assert.equal(res.statusCode, 200);
});
req.end();
});
}
spdyReqTest('/');
spdyReqTest('/gzip');
spdyReqTest('/deflate');
test('should not fail at a lot of RSTs', function(done) {
var s = tls.connect({
host: '127.0.0.1',
port: PORT,
NPNProtocols: [ 'spdy/3' ],
rejectUnauthorized: false
}, function() {
var rst = new Buffer([
0x80, 0x03, 0x00, 0x03,
0x00, 0x00, 0x00, 0x08,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00
]);
var rsts = [];
for (var i = 0; i < 1000; i++)
rsts.push(rst);
rsts = Buffer.concat(rsts, rst.length * rsts.length);
for (var i = 0; i < 10; i++)
s.write(rsts);
s.write(rsts, function() {
s.destroy();
done();
});
});
});
test('should not decompress stream when decompress is false', function(done) {
var agent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
rejectUnauthorized: false,
spdy: {
decompress: false
}
});
var req = https.request({
path: '/gzip',
method: 'GET',
agent: agent,
}, function(res) {
var received = [];
res.on('data', function(chunk) {
received.push(chunk);
});
res.once('end', function() {
agent.close();
zlib.gunzip(Buffer.concat(received), function(err, decompressed) {
assert.equal(decompressed, fox);
done();
});
});
assert.equal(res.statusCode, 200);
assert.equal(res.headers['content-encoding'], 'gzip');
});
req.end();
});
test('should not create RangeErrors on client errors', function(done) {
// https://github.com/indutny/node-spdy/issues/147
var agent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
rejectUnauthorized: true
}).on('error', function (err) {
assert(err.message === 'self signed certificate' ||
err.message === 'DEPTH_ZERO_SELF_SIGNED_CERT');
});
var req = https.request({
path: '/',
method: 'GET',
agent: agent
});
req.on('error', function (err) {
assert.equal(err.code, 'ECONNRESET');
done();
});
req.end();
});
test('should not support GOAWAY', function(done) {
// https://github.com/indutny/node-spdy/issues/147
var agent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
rejectUnauthorized: false
});
var total = '';
var req = https.request({
path: '/goaway',
method: 'GET',
agent: agent
}, function(res) {
res.on('data', function(chunk) {
total += chunk;
});
});
req.end();
agent._spdyState.socket.once('close', function() {
assert.equal(total, fox);
done();
});
});
test('should add accept-encoding header to request headers, ' +
'if none present',
function(done) {
var agent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
rejectUnauthorized: false
});
var req = https.request({
path: '/headerTest',
method: 'GET',
agent: agent,
}, function(res) {
var total = '';
res.on('data', function(chunk){
total += chunk;
});
res.once('end', function() {
agent.close();
assert.equal(total, fox);
done();
});
});
req.end();
});
});

Просмотреть файл

@ -1,142 +0,0 @@
var assert = require('assert'),
spdy = require('../../'),
keys = require('../fixtures/keys'),
http = require('http'),
tls = require('tls'),
Buffer = require('buffer').Buffer,
PORT = 8081;
suite('A SPDY Server / Plain', function() {
var server;
setup(function(done) {
server = spdy.createServer({ plain: true, ssl: false }, function(req, res) {
res.end('ok');
});
server.listen(PORT, done);
});
teardown(function(done) {
server.close(done);
});
test('should respond on regular http requests', function(done) {
var req = http.request({
host: '127.0.0.1',
port: PORT,
path: '/',
method: 'GET',
agent: false,
rejectUnauthorized: false
}, function(res) {
res.on('data', function() {
// Ignore incoming data
});
assert.equal(res.statusCode, 200);
done();
});
req.end();
});
test('should respond on spdy requests', function(done) {
var agent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
spdy: {
ssl: false,
plain: true,
version: 3
}
});
var req = http.request({
path: '/',
method: 'GET',
agent: agent,
}, function(res) {
res.on('data', function() {
// Ignore incoming data
});
assert.equal(res.statusCode, 200);
agent.close();
done();
});
req.end();
});
test('should handle header values with colons', function(done) {
var agent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
spdy: {
ssl: false,
plain: true
}
});
var refererValue = 'http://127.0.0.1:' + PORT + '/header-with-colon';
server.on('request', function(req) {
assert.equal(req.headers.referer, refererValue);
});
http.request({
path: '/',
method: 'GET',
agent: agent,
headers: { 'referer': refererValue }
}, function(res) {
assert.equal(res.statusCode, 200);
agent.close();
done();
}).end();
});
test('should send date header as default', function(done) {
var agent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
spdy: {
ssl: false,
plain: true
}
});
http.request({
path: '/',
method: 'GET',
agent: agent
}, function(res) {
assert.equal(typeof res.headers.date, 'string');
agent.close();
done();
}).end();
});
test('should not send date header if res.sendDate is false', function(done) {
var agent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
spdy: {
ssl: false,
plain: true
}
});
server.removeAllListeners('request');
server.on('request', function(req, res) {
res.sendDate = false;
res.end('ok');
});
http.request({
path: '/',
method: 'GET',
agent: agent
}, function(res) {
assert.equal(typeof res.headers.date, 'undefined');
agent.close();
done();
}).end();
});
});

Просмотреть файл

@ -1,70 +0,0 @@
var assert = require('assert'),
https = require('https'),
spdy = require('../../'),
keys = require('../fixtures/keys'),
net = require('net'),
url = require('url'),
PORT = 8081;
suite('A SPDY server / Proxy', function() {
test('should emit connect event on CONNECT requests', function(done) {
var proxyServer = spdy.createServer(keys);
proxyServer.on('connect', function(req, socket) {
var srvUrl = url.parse('http://' + req.url);
var srvSocket = net.connect(srvUrl.port, srvUrl.hostname, function() {
socket._lock(function() {
var headers = {
'Connection': 'keep-alive',
'Proxy-Agent': 'SPDY Proxy'
}
socket._spdyState.framer.replyFrame(
socket._spdyState.id, 200, "Connection Established", headers,
function (err, frame) {
socket.connection.write(frame);
socket._unlock();
srvSocket.pipe(socket);
socket.pipe(srvSocket);
}
);
});
});
});
proxyServer.listen(PORT, '127.0.0.1', function() {
var spdyAgent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
rejectUnauthorized: false
});
var options = {
method: 'CONNECT',
path: 'www.google.com:80',
agent: spdyAgent
};
var req = https.request(options);
req.end();
req.on('connect', function(res, socket) {
var googlePage = "";
socket.write('GET / HTTP/1.1\r\n' +
'Host: www.google.com:80\r\n' +
'Connection: close\r\n' +
'\r\n');
socket.on('data', function(chunk) {
googlePage = googlePage + chunk.toString();
});
socket.on('end', function() {
assert.notEqual(googlePage.search('google'), -1,
"Google page should contain string 'google'");
spdyAgent.close(function() {
proxyServer.close(done);
});
});
});
});
});
});

Просмотреть файл

@ -1,411 +0,0 @@
var assert = require('assert'),
zlib = require('zlib'),
spdy = require('../../'),
keys = require('../fixtures/keys'),
https = require('https'),
util = require('util'),
Buffer = require('buffer').Buffer,
PORT = 8081;
suite('A SPDY Server / Stream', function() {
var server;
var agent;
var pair = null;
suite('normal', function() {
run({});
});
suite('maxChunk = false', function() {
run({ maxChunk: false });
});
function run(options) {
setup(function(done) {
var waiting = 2;
pair = { server: null, client: null };
server = spdy.createServer(util._extend(keys, options),
function(req, res) {
assert.equal(req.method, 'POST');
pair.server = { req: req, res: res };
// Just to remove junk from stream's buffer
if (spdy.utils.isLegacy)
req.once('data', function(data) {
assert.equal(data.toString(), 'shame');
if (--waiting === 0)
done();
});
else
req.once('readable', function() {
assert.equal(req.read().toString(), 'shame');
if (--waiting === 0)
done();
});
res.writeHead(200);
});
server.listen(PORT, function() {
agent = spdy.createAgent({
host: '127.0.0.1',
port: PORT,
rejectUnauthorized: false
});
pair.client = {
req: https.request({
path: '/',
method: 'POST',
agent: agent
}, function(res) {
pair.client.res = res;
if (--waiting === 0)
done();
}),
res: null
};
pair.client.req.write('shame');
});
});
teardown(function(done) {
pair = null;
agent.close(function() {
server.close(done);
});
});
test('should support PING from client', function(done) {
agent.ping(done);
});
test('should support PING from server', function(done) {
pair.server.res.socket.ping(done);
});
test('piping a lot of data', function(done) {
var big = new Buffer(2 * 1024 * 1024);
for (var i = 0; i < big.length; i++)
big[i] = ~~(Math.random() * 256);
var offset = 0;
if (spdy.utils.isLegacy) {
pair.client.res.on('data', function(chunk) {
for (var i = 0; i < chunk.length; i++) {
assert(i + offset < big.length);
assert.equal(big[i + offset], chunk[i]);
}
offset += i;
});
} else {
pair.client.res.on('readable', function() {
var bigEcho = pair.client.res.read(big.length);
if (bigEcho) {
assert.equal(big.length, bigEcho.length);
for (var i = 0; i < big.length; i += 256) {
assert.equal(big.slice(i, i + 256).toString('hex'),
bigEcho.slice(i, i + 256).toString('hex'));
}
offset += bigEcho.length;
}
});
}
pair.client.res.on('end', function() {
assert.equal(offset, big.length);
done();
});
pair.server.req.pipe(pair.server.res);
pair.client.req.end(big);
});
test('destroy in the middle', function(done) {
var big = new Buffer(2 * 1024 * 1024);
for (var i = 0; i < big.length; i++)
big[i] = ~~(Math.random() * 256);
var offset = 0;
pair.server.req.on('data', function(chunk) {
for (var i = 0; i < chunk.length; i++) {
assert(i + offset < big.length);
assert.equal(big[i + offset], chunk[i]);
}
offset += i;
});
pair.server.req.on('close', function() {
assert(offset < big.length);
done();
});
pair.client.req.write(big.slice(0, big.length >> 1));
pair.client.req.write(big.slice(big.length >> 1));
pair.client.req.socket.destroy();
});
test('destroySoon in the middle', function(done) {
var big = new Buffer(2 * 1024 * 1024);
for (var i = 0; i < big.length; i++)
big[i] = ~~(Math.random() * 256);
var offset = 0;
pair.server.req.on('data', function(chunk) {
for (var i = 0; i < chunk.length; i++) {
assert(i + offset < big.length);
assert.equal(big[i + offset], chunk[i]);
}
offset += i;
});
pair.server.req.on('end', function() {
assert.equal(offset, big.length);
done();
});
pair.client.req.write(big.slice(0, big.length >> 1));
pair.client.req.write(big.slice(big.length >> 1));
pair.client.req.socket.destroySoon();
});
test('ending', function(done) {
var data = '';
pair.server.req.on('data', function(chunk) {
data += chunk;
});
pair.server.req.on('end', function() {
assert.equal(data, 'hello');
pair.server.res.end();
done();
});
pair.client.req.end('hello');
});
test('trailing headers from client', function(done) {
pair.server.req.once('trailers', function(headers) {
assert.equal(headers.wtf, 'yes');
assert.equal(pair.server.req.trailers.wtf, 'yes');
done();
});
pair.client.req.addTrailers({ wtf: 'yes' });
});
test('trailing headers from server', function(done) {
pair.client.res.once('trailers', function(headers) {
assert.equal(headers.wtf, 'yes');
assert.equal(pair.client.res.trailers.wtf, 'yes');
done();
});
pair.server.res.addTrailers({ wtf: 'yes' });
});
test('push stream', function(done) {
agent.once('push', function(req) {
var gotTrailers = false;
assert.equal(req.headers.wtf, 'true');
var chunks = '';
req.once('data', function(chunk) {
chunks += chunk;
});
req.once('trailers', function(trailers) {
assert.equal(trailers.ok, 'yes');
gotTrailers = true;
});
req.once('end', function() {
assert(gotTrailers);
assert.equal(req.trailers.ok, 'yes');
assert.equal(chunks, 'yes, wtf');
done();
});
});
pair.server.res.push('/wtf', { wtf: true }, function(err, stream) {
assert(!err);
stream.on('error', function(err) {
throw err;
});
stream.sendHeaders({ ok: 'yes' });
stream.end('yes, wtf');
});
});
test('push stream with compression', function(done) {
agent.once('push', function(req) {
req.once('data', function(chunk) {
assert.equal(chunk.toString(), 'yes, wtf');
done();
});
});
pair.server.res.push('/wtf', {
'Content-Encoding': 'gzip'
}, function(err, stream) {
assert(!err);
stream.on('error', function(err) {
throw err;
});
var gzip = zlib.createGzip();
gzip.on('data', function(data) {
stream.write(data);
});
gzip.on('end', function() {
stream.end();
});
gzip.end('yes, wtf');
});
});
test('push stream - big chunks', function(done) {
var count = 10;
var chunk = new Buffer(256 * 1024 - 7);
for (var i = 0; i < chunk.length; i++)
chunk[i] = ~~(Math.random() * 256);
agent.once('push', function(req) {
assert.equal(req.headers.wtf, 'true');
var offset = 0;
var total = 0;
req.on('data', function(data) {
for (var i = 0; i < data.length; i++) {
assert.equal(data[i],
chunk[(offset + i) % chunk.length],
'Mismatch at: ' + (offset + i));
}
offset = (offset + i) % chunk.length;
total += i;
});
req.once('end', function() {
assert.equal(total, count * chunk.length);
done();
});
});
pair.server.res.push('/wtf', { wtf: true }, function(err, stream) {
assert(!err);
stream.on('error', function(err) {
throw err;
});
function start(count) {
if (count === 1)
return stream.end(chunk);
stream.write(chunk);
setTimeout(function() {
start(count - 1);
}, 5);
}
start(count);
});
});
test('push stream - early close', function(done) {
agent.once('push', function(req) {
var chunks = '';
req.on('data', function(chunk) {
chunks += chunk;
});
req.once('end', function() {
assert.equal(chunks, 'yes, wtf');
done();
});
});
var stream = pair.server.res.push('/wtf', {});
pair.client.res.on('data', function() {});
pair.client.res.once('end', function() {
stream.end('yes, wtf');
});
pair.client.req.end();
pair.server.res.end();
});
test('timing out', function(done) {
var data = '';
pair.server.req.socket.setTimeout(300);
pair.client.req.on('error', function() {
done();
});
});
test('timing out after write', function(done) {
var data = '';
var chunks = 0;
pair.server.req.socket.setTimeout(150);
setTimeout(function() {
pair.server.res.write('ok1');
setTimeout(function() {
pair.server.res.write('ok2');
}, 100);
}, 100);
pair.client.res.on('data', function(chunk) {
chunk = chunk.toString();
assert(chunks === 0 && chunk === 'ok1' ||
chunks === 1 && chunk === 'ok2');
chunks++;
});
pair.client.req.on('error', function() {
assert.equal(chunks, 2);
done();
});
});
test('req[spdyVersion/streamID]', function(done) {
var data = '';
assert.equal(pair.server.req.spdyVersion, 3.1);
assert(pair.server.req.streamID > 0);
pair.server.req.resume();
pair.server.req.on('end', function() {
pair.server.res.end();
done();
});
pair.client.req.end();
});
}
test('it should not attempt to send empty arrays', function(done) {
var server = spdy.createServer(keys);
var agent;
server.on('request', function(req, res) {
setTimeout(function() {
res.end();
done();
server.close();
agent.close();
}, 100);
});
server.listen(PORT, function() {
agent = spdy.createAgent({
port: PORT,
rejectUnauthorized: false
}).on('error', function(err) {
done(err);
});
var body = new Buffer([1,2,3,4]);
var opts = {
method: 'POST',
headers: {
'Host': 'localhost',
'Content-Length': body.length
},
path: '/some-url',
agent: agent
};
var req = https.request(opts, function(res) {
}).on('error', function(err) {
});
req.end(body);
});
});
});

Просмотреть файл

@ -984,7 +984,7 @@ class XPCShellTests(object):
def trySetupNode(self):
"""
Run node for SPDY tests, if available, and updates mozinfo as appropriate.
Run node for HTTP/2 tests, if available, and updates mozinfo as appropriate.
"""
nodeMozInfo = {'hasNode': False} # Assume the worst
nodeBin = None
@ -1000,10 +1000,10 @@ class XPCShellTests(object):
def startServer(name, serverJs):
if os.path.exists(serverJs):
# OK, we found our SPDY server, let's try to get it running
# OK, we found our server, let's try to get it running
self.log.info('Found %s at %s' % (name, serverJs))
try:
# We pipe stdin to node because the spdy server will exit when its
# We pipe stdin to node because the server will exit when its
# stdin reaches EOF
process = Popen([nodeBin, serverJs], stdin=PIPE, stdout=PIPE,
stderr=PIPE, env=self.env, cwd=os.getcwd())
@ -1014,9 +1014,6 @@ class XPCShellTests(object):
msg = process.stdout.readline()
if 'server listening' in msg:
nodeMozInfo['hasNode'] = True
searchObj = re.search( r'SPDY server listening on port (.*)', msg, 0)
if searchObj:
self.env["MOZSPDY_PORT"] = searchObj.group(1)
searchObj = re.search( r'HTTP2 server listening on port (.*)', msg, 0)
if searchObj:
self.env["MOZHTTP2_PORT"] = searchObj.group(1)
@ -1025,7 +1022,6 @@ class XPCShellTests(object):
self.log.error('Could not run %s server: %s' % (name, str(e)))
myDir = os.path.split(os.path.abspath(__file__))[0]
startServer('moz-spdy', os.path.join(myDir, 'moz-spdy', 'moz-spdy.js'))
startServer('moz-http2', os.path.join(myDir, 'moz-http2', 'moz-http2.js'))
elif os.getenv('MOZ_ASSUME_NODE_RUNNING', None):
self.log.info('Assuming required node servers are already running')
@ -1237,7 +1233,7 @@ class XPCShellTests(object):
appDirKey = self.mozInfo["appname"] + "-appdir"
# We have to do this before we build the test list so we know whether or
# not to run tests that depend on having the node spdy server
# not to run tests that depend on having the node http/2 server
self.trySetupNode()
pStdout, pStderr = self.getPipes()