зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1248197 - Remove spdy/3.1 support. r=mcmanus
MozReview-Commit-ID: 1RgzxOY00Le --HG-- extra : rebase_source : 6b37c087fcffcea7e5dd56d1e6ad5d099e8f5e49
This commit is contained in:
Родитель
067bab944a
Коммит
c520425d6e
|
@ -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()
|
||||
|
|
Загрузка…
Ссылка в новой задаче