зеркало из https://github.com/mozilla/gecko-dev.git
bug 1024730 - nsIHttpPushListener r=hurley
co-author: ben brittain <ben@brittain.org>
This commit is contained in:
Родитель
f5046854b9
Коммит
bc4244f5a9
|
@ -43,6 +43,7 @@ XPIDL_SOURCES += [
|
|||
'nsIFileStreams.idl',
|
||||
'nsIFileURL.idl',
|
||||
'nsIForcePendingChannel.idl',
|
||||
'nsIHttpPushListener.idl',
|
||||
'nsIIncrementalDownload.idl',
|
||||
'nsIInputStreamChannel.idl',
|
||||
'nsIInputStreamPump.idl',
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/* 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 "nsISupports.idl"
|
||||
interface nsIHttpChannel;
|
||||
|
||||
/**
|
||||
* nsIHttpPushListener
|
||||
*
|
||||
* Used for triggering when a HTTP/2 push is received.
|
||||
*
|
||||
*/
|
||||
[scriptable, uuid(0d6ce59c-ad5d-4520-b4d3-09664868f279)]
|
||||
interface nsIHttpPushListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* When provided as a notificationCallback to an httpChannel, this.onPush()
|
||||
* will be invoked when there is a >= Http2 push to that
|
||||
* channel. The push may be in progress.
|
||||
*
|
||||
* The consumer must start the new channel in the usual way by calling
|
||||
* pushChannel.AsyncOpen with a nsIStreamListener object that
|
||||
* will receive the normal sequence of OnStartRequest(),
|
||||
* 0 to N OnDataAvailable(), and onStopRequest().
|
||||
*
|
||||
* The new channel can be canceled after the AsyncOpen if it is not wanted.
|
||||
*
|
||||
* @param associatedChannel
|
||||
* the monitor channel that was recieved on
|
||||
* @param pushChannel
|
||||
* a channel to the resource which is being pushed
|
||||
*/
|
||||
void onPush(in nsIHttpChannel associatedChannel,
|
||||
in nsIHttpChannel pushChannel);
|
||||
};
|
|
@ -579,6 +579,18 @@
|
|||
{0xb5, 0x27, 0x8a, 0x64, 0x30, 0x56, 0xab, 0xbd} \
|
||||
}
|
||||
|
||||
// component implementing nsIHttpPushListener.
|
||||
#define NS_HTTPPUSHLISTENER_CONTRACTID \
|
||||
"@mozilla.org/network/push-listener;1"
|
||||
#define NS_HTTPPUSHLISTENER_CID \
|
||||
{ \
|
||||
0x73cf4430, \
|
||||
0x5877, \
|
||||
0x4c6b, \
|
||||
{0xb8, 0x78, 0x3e, 0xde, 0x5b, 0xc8, 0xef, 0xf1} \
|
||||
}
|
||||
|
||||
|
||||
#define NS_HTTPACTIVITYDISTRIBUTOR_CONTRACTID \
|
||||
"@mozilla.org/network/http-activity-distributor;1"
|
||||
#define NS_HTTPACTIVITYDISTRIBUTOR_CID \
|
||||
|
|
|
@ -147,8 +147,11 @@ SpdyPushCache::RegisterPushedStreamSpdy3(nsCString key,
|
|||
{
|
||||
LOG3(("SpdyPushCache::RegisterPushedStreamSpdy3 %s 0x%X\n",
|
||||
key.get(), stream->StreamID()));
|
||||
if(mHashSpdy3.Get(key))
|
||||
if(mHashSpdy3.Get(key)) {
|
||||
LOG3(("SpdyPushCache::RegisterPushedStreamSpdy3 %s 0x%X duplicate key\n",
|
||||
key.get(), stream->StreamID()));
|
||||
return false;
|
||||
}
|
||||
mHashSpdy3.Put(key, stream);
|
||||
return true;
|
||||
}
|
||||
|
@ -170,8 +173,11 @@ SpdyPushCache::RegisterPushedStreamSpdy31(nsCString key,
|
|||
{
|
||||
LOG3(("SpdyPushCache::RegisterPushedStreamSpdy31 %s 0x%X\n",
|
||||
key.get(), stream->StreamID()));
|
||||
if(mHashSpdy31.Get(key))
|
||||
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;
|
||||
}
|
||||
|
@ -193,8 +199,11 @@ SpdyPushCache::RegisterPushedStreamHttp2(nsCString key,
|
|||
{
|
||||
LOG3(("SpdyPushCache::RegisterPushedStreamHttp2 %s 0x%X\n",
|
||||
key.get(), stream->StreamID()));
|
||||
if(mHashHttp2.Get(key))
|
||||
if(mHashHttp2.Get(key)) {
|
||||
LOG3(("SpdyPushCache::RegisterPushedStreamHttp2 %s 0x%X duplicate key\n",
|
||||
key.get(), stream->StreamID()));
|
||||
return false;
|
||||
}
|
||||
mHashHttp2.Put(key, stream);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -16,12 +16,45 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "Http2Push.h"
|
||||
|
||||
#include "nsDependentString.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "nsIHttpPushListener.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class CallChannelOnPush MOZ_FINAL : public nsRunnable {
|
||||
public:
|
||||
CallChannelOnPush(nsIHttpChannelInternal *associatedChannel,
|
||||
const nsACString &pushedURI,
|
||||
Http2PushedStream *pushStream)
|
||||
: mAssociatedChannel(associatedChannel)
|
||||
, mPushedURI(pushedURI)
|
||||
, mPushedStream(pushStream)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<nsHttpChannel> channel;
|
||||
CallQueryInterface(mAssociatedChannel, channel.StartAssignment());
|
||||
MOZ_ASSERT(channel);
|
||||
if (channel && NS_SUCCEEDED(channel->OnPush(mPushedURI, mPushedStream))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
LOG3(("Http2PushedStream Orphan %p failed OnPush\n", this));
|
||||
mPushedStream->OnPushFailed();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIHttpChannelInternal> mAssociatedChannel;
|
||||
const nsCString mPushedURI;
|
||||
Http2PushedStream *mPushedStream;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////
|
||||
// Http2PushedStream
|
||||
//////////////////////////////////////////
|
||||
|
@ -32,10 +65,13 @@ Http2PushedStream::Http2PushedStream(Http2PushTransactionBuffer *aTransaction,
|
|||
uint32_t aID)
|
||||
:Http2Stream(aTransaction, aSession, 0)
|
||||
, mConsumerStream(nullptr)
|
||||
, mAssociatedTransaction(aAssociatedStream->Transaction())
|
||||
, mBufferedPush(aTransaction)
|
||||
, mStatus(NS_OK)
|
||||
, mPushCompleted(false)
|
||||
, mDeferCleanupOnSuccess(true)
|
||||
, mDeferCleanupOnPush(false)
|
||||
, mOnPushFailed(false)
|
||||
{
|
||||
LOG3(("Http2PushedStream ctor this=%p 0x%X\n", this, aID));
|
||||
mStreamID = aID;
|
||||
|
@ -70,6 +106,51 @@ Http2PushedStream::WriteSegments(nsAHttpSegmentWriter *writer,
|
|||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
Http2PushedStream::DeferCleanup(nsresult status)
|
||||
{
|
||||
LOG3(("Http2PushedStream::DeferCleanup Query %p %x\n", this, status));
|
||||
|
||||
if (NS_SUCCEEDED(status) && mDeferCleanupOnSuccess) {
|
||||
LOG3(("Http2PushedStream::DeferCleanup %p %x defer on success\n", this, status));
|
||||
return true;
|
||||
}
|
||||
if (mDeferCleanupOnPush) {
|
||||
LOG3(("Http2PushedStream::DeferCleanup %p %x defer onPush ref\n", this, status));
|
||||
return true;
|
||||
}
|
||||
if (mConsumerStream) {
|
||||
LOG3(("Http2PushedStream::DeferCleanup %p %x defer active consumer\n", this, status));
|
||||
return true;
|
||||
}
|
||||
LOG3(("Http2PushedStream::DeferCleanup Query %p %x not deferred\n", this, status));
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true if channel implements nsIHttpPushListener
|
||||
bool
|
||||
Http2PushedStream::TryOnPush()
|
||||
{
|
||||
nsHttpTransaction *trans = mAssociatedTransaction->QueryHttpTransaction();
|
||||
if (!trans) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> associatedChannel = do_QueryInterface(trans->HttpChannel());
|
||||
if (!associatedChannel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(trans->Caps() & NS_HTTP_ONPUSH_LISTENER)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mDeferCleanupOnPush = true;
|
||||
nsCString uri = Origin() + Path();
|
||||
NS_DispatchToMainThread(new CallChannelOnPush(associatedChannel, uri, this));
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Http2PushedStream::ReadSegments(nsAHttpSegmentReader *,
|
||||
uint32_t, uint32_t *count)
|
||||
|
@ -91,6 +172,13 @@ Http2PushedStream::ReadSegments(nsAHttpSegmentReader *,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
Http2PushedStream::SetConsumerStream(Http2Stream *consumer)
|
||||
{
|
||||
mConsumerStream = consumer;
|
||||
mDeferCleanupOnPush = false;
|
||||
}
|
||||
|
||||
bool
|
||||
Http2PushedStream::GetHashKey(nsCString &key)
|
||||
{
|
||||
|
@ -115,8 +203,13 @@ Http2PushedStream::IsOrphaned(TimeStamp now)
|
|||
// if session 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)
|
||||
if (mConsumerStream || mDeferCleanupOnPush) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mOnPushFailed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rv = ((now - mLastRead).ToSeconds() > 30.0);
|
||||
if (rv) {
|
||||
|
|
|
@ -31,8 +31,11 @@ public:
|
|||
virtual ~Http2PushedStream() {}
|
||||
|
||||
bool GetPushComplete();
|
||||
Http2Stream *GetConsumerStream() { return mConsumerStream; };
|
||||
void SetConsumerStream(Http2Stream *aStream) { mConsumerStream = aStream; }
|
||||
|
||||
// The consumer stream is the synthetic pull stream hooked up to this push
|
||||
virtual Http2Stream *GetConsumerStream() MOZ_OVERRIDE { return mConsumerStream; };
|
||||
|
||||
void SetConsumerStream(Http2Stream *aStream);
|
||||
bool GetHashKey(nsCString &key);
|
||||
|
||||
// override of Http2Stream
|
||||
|
@ -42,16 +45,21 @@ public:
|
|||
nsILoadGroupConnectionInfo *LoadGroupConnectionInfo() { return mLoadGroupCI; };
|
||||
void ConnectPushedStream(Http2Stream *consumer);
|
||||
|
||||
bool DeferCleanupOnSuccess() { return mDeferCleanupOnSuccess; }
|
||||
bool TryOnPush();
|
||||
|
||||
virtual bool DeferCleanup(nsresult status) MOZ_OVERRIDE;
|
||||
void SetDeferCleanupOnSuccess(bool val) { mDeferCleanupOnSuccess = val; }
|
||||
|
||||
bool IsOrphaned(TimeStamp now);
|
||||
void OnPushFailed() { mDeferCleanupOnPush = false; mOnPushFailed = true; }
|
||||
|
||||
nsresult GetBufferedData(char *buf, uint32_t count, uint32_t *countWritten);
|
||||
|
||||
// overload of Http2Stream
|
||||
virtual bool HasSink() { return !!mConsumerStream; }
|
||||
|
||||
nsCString &GetRequestString() { return mRequestString; }
|
||||
|
||||
private:
|
||||
|
||||
Http2Stream *mConsumerStream; // paired request stream that consumes from
|
||||
|
@ -59,6 +67,8 @@ private:
|
|||
|
||||
nsCOMPtr<nsILoadGroupConnectionInfo> mLoadGroupCI;
|
||||
|
||||
nsAHttpTransaction *mAssociatedTransaction;
|
||||
|
||||
Http2PushTransactionBuffer *mBufferedPush;
|
||||
mozilla::TimeStamp mLastRead;
|
||||
|
||||
|
@ -66,6 +76,17 @@ private:
|
|||
nsresult mStatus;
|
||||
bool mPushCompleted; // server push FIN received
|
||||
bool mDeferCleanupOnSuccess;
|
||||
|
||||
// mDeferCleanupOnPush prevents Http2Session::CleanupStream() from
|
||||
// destroying the push stream on an error code during the period between
|
||||
// when we need to do OnPush() on another thread and the time it takes
|
||||
// for that event to create a synthetic pull stream attached to this
|
||||
// object. That synthetic pull will become mConsuemerStream.
|
||||
// Ths is essentially a delete protecting reference.
|
||||
bool mDeferCleanupOnPush;
|
||||
bool mOnPushFailed;
|
||||
nsCString mRequestString;
|
||||
|
||||
};
|
||||
|
||||
class Http2PushTransactionBuffer MOZ_FINAL : public nsAHttpTransaction
|
||||
|
|
|
@ -833,7 +833,7 @@ Http2Session::SendHello()
|
|||
}
|
||||
|
||||
// Advertise the Push RWIN for the session, and on each new pull stream
|
||||
// send a window update with END_FLOW_CONTROL
|
||||
// send a window update
|
||||
CopyAsNetwork16(packet + kFrameHeaderBytes + (6 * numberOfEntries), SETTINGS_TYPE_INITIAL_WINDOW);
|
||||
CopyAsNetwork32(packet + kFrameHeaderBytes + (6 * numberOfEntries) + 2, mPushAllowance);
|
||||
numberOfEntries++;
|
||||
|
@ -942,9 +942,7 @@ Http2Session::CleanupStream(Http2Stream *aStream, nsresult aResult,
|
|||
return;
|
||||
}
|
||||
|
||||
Http2PushedStream *pushSource = nullptr;
|
||||
|
||||
if (NS_SUCCEEDED(aResult) && aStream->DeferCleanupOnSuccess()) {
|
||||
if (aStream->DeferCleanup(aResult)) {
|
||||
LOG3(("Http2Session::CleanupStream 0x%X deferred\n", aStream->StreamID()));
|
||||
return;
|
||||
}
|
||||
|
@ -954,11 +952,17 @@ Http2Session::CleanupStream(Http2Stream *aStream, nsresult aResult,
|
|||
return;
|
||||
}
|
||||
|
||||
pushSource = aStream->PushSource();
|
||||
Http2PushedStream *pushSource = aStream->PushSource();
|
||||
if (pushSource) {
|
||||
// aStream is a synthetic attached to an even push
|
||||
MOZ_ASSERT(pushSource->GetConsumerStream() == aStream);
|
||||
MOZ_ASSERT(!aStream->StreamID());
|
||||
MOZ_ASSERT(!(pushSource->StreamID() & 0x1));
|
||||
pushSource->SetConsumerStream(nullptr);
|
||||
}
|
||||
|
||||
if (!aStream->RecvdFin() && !aStream->RecvdReset() && aStream->StreamID()) {
|
||||
LOG3(("Stream had not processed recv FIN, sending RST code %X\n",
|
||||
aResetCode));
|
||||
LOG3(("Stream had not processed recv FIN, sending RST code %X\n", aResetCode));
|
||||
GenerateRstStream(aResetCode, aStream->StreamID());
|
||||
}
|
||||
|
||||
|
@ -1577,8 +1581,24 @@ Http2Session::RecvPushPromise(Http2Session *self)
|
|||
new Http2PushTransactionBuffer();
|
||||
transactionBuffer->SetConnection(self);
|
||||
Http2PushedStream *pushedStream =
|
||||
new Http2PushedStream(transactionBuffer, self,
|
||||
associatedStream, promisedID);
|
||||
new Http2PushedStream(transactionBuffer, self, associatedStream, promisedID);
|
||||
|
||||
self->mDecompressBuffer.Append(self->mInputFrameBuffer + kFrameHeaderBytes + paddingControlBytes + promiseLen,
|
||||
self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength);
|
||||
|
||||
rv = pushedStream->ConvertPushHeaders(&self->mDecompressor,
|
||||
self->mDecompressBuffer,
|
||||
pushedStream->GetRequestString());
|
||||
|
||||
if (rv == NS_ERROR_NOT_IMPLEMENTED) {
|
||||
LOG3(("Http2Session::PushPromise Semantics not Implemented\n"));
|
||||
self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
|
||||
delete pushedStream;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// Ownership of the pushed stream is by the transaction hash, just as it
|
||||
// is for a client initiated stream. Errors that aren't fatal to the
|
||||
|
@ -1587,22 +1607,6 @@ Http2Session::RecvPushPromise(Http2Session *self)
|
|||
self->mStreamTransactionHash.Put(transactionBuffer, pushedStream);
|
||||
self->mPushedStreams.AppendElement(pushedStream);
|
||||
|
||||
self->mDecompressBuffer.Append(self->mInputFrameBuffer + kFrameHeaderBytes + paddingControlBytes + promiseLen,
|
||||
self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength);
|
||||
|
||||
nsAutoCString requestHeaders;
|
||||
rv = pushedStream->ConvertPushHeaders(&self->mDecompressor,
|
||||
self->mDecompressBuffer, requestHeaders);
|
||||
|
||||
if (rv == NS_ERROR_NOT_IMPLEMENTED) {
|
||||
LOG3(("Http2Session::PushPromise Semantics not Implemented\n"));
|
||||
self->GenerateRstStream(REFUSED_STREAM_ERROR, promisedID);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (self->RegisterStreamID(pushedStream, promisedID) == kDeadStreamID) {
|
||||
LOG3(("Http2Session::RecvPushPromise registerstreamid failed\n"));
|
||||
self->mGoAwayReason = INTERNAL_ERROR;
|
||||
|
@ -1626,19 +1630,28 @@ Http2Session::RecvPushPromise(Http2Session *self)
|
|||
}
|
||||
|
||||
if (!associatedStream->Origin().Equals(pushedStream->Origin())) {
|
||||
LOG3(("Http2Session::RecvPushPromise pushed stream mismatched origin\n"));
|
||||
LOG3(("Http2Session::RecvPushPromise %p pushed stream mismatched origin "
|
||||
"associated origin %s .. pushed origin %s\n", self,
|
||||
associatedStream->Origin().get(), pushedStream->Origin().get()));
|
||||
self->CleanupStream(pushedStream, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR);
|
||||
self->ResetDownstreamState();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!cache->RegisterPushedStreamHttp2(key, pushedStream)) {
|
||||
LOG3(("Http2Session::RecvPushPromise registerPushedStream Failed\n"));
|
||||
self->CleanupStream(pushedStream, NS_ERROR_FAILURE, INTERNAL_ERROR);
|
||||
self->ResetDownstreamState();
|
||||
return NS_OK;
|
||||
if (pushedStream->TryOnPush()) {
|
||||
LOG3(("Http2Session::RecvPushPromise %p channel implements nsIHttpPushListener "
|
||||
"stream %p will not be placed into session cache.\n", self, pushedStream));
|
||||
} else {
|
||||
LOG3(("Http2Session::RecvPushPromise %p place stream into session cache\n", self));
|
||||
if (!cache->RegisterPushedStreamHttp2(key, pushedStream)) {
|
||||
LOG3(("Http2Session::RecvPushPromise registerPushedStream Failed\n"));
|
||||
self->CleanupStream(pushedStream, NS_ERROR_FAILURE, INTERNAL_ERROR);
|
||||
self->ResetDownstreamState();
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
pushedStream->SetHTTPState(Http2Stream::RESERVED_BY_REMOTE);
|
||||
static_assert(Http2Stream::kWorstPriority >= 0,
|
||||
"kWorstPriority out of range");
|
||||
uint8_t priorityWeight = (nsISupportsPriority::PRIORITY_LOWEST + 1) -
|
||||
|
|
|
@ -312,15 +312,32 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
|
|||
// from :scheme, :authority, :path
|
||||
nsILoadGroupConnectionInfo *loadGroupCI = mTransaction->LoadGroupConnectionInfo();
|
||||
SpdyPushCache *cache = nullptr;
|
||||
if (loadGroupCI)
|
||||
if (loadGroupCI) {
|
||||
loadGroupCI->GetSpdyPushCache(&cache);
|
||||
}
|
||||
|
||||
Http2PushedStream *pushedStream = nullptr;
|
||||
|
||||
// If a push stream is attached to the transaction via onPush, match only with that
|
||||
// one. This occurs when a push was made with in conjunction with a nsIHttpPushListener
|
||||
nsHttpTransaction *trans = mTransaction->QueryHttpTransaction();
|
||||
if (trans && (pushedStream = trans->TakePushedStream())) {
|
||||
if (pushedStream->mSession == mSession) {
|
||||
LOG3(("Pushed Stream match based on OnPush correlation %p", pushedStream));
|
||||
} else {
|
||||
LOG3(("Pushed Stream match failed due to stream mismatch %p %d %d\n", pushedStream,
|
||||
pushedStream->mSession->Serial(), mSession->Serial()));
|
||||
pushedStream->OnPushFailed();
|
||||
pushedStream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// we remove the pushedstream from the push cache so that
|
||||
// it will not be used for another GET. This does not destroy the
|
||||
// stream itself - that is done when the transactionhash is done with it.
|
||||
if (cache)
|
||||
pushedStream = cache->RemovePushedStreamHttp2(hashkey);
|
||||
if (cache && !pushedStream){
|
||||
pushedStream = cache->RemovePushedStreamHttp2(hashkey);
|
||||
}
|
||||
|
||||
LOG3(("Pushed Stream Lookup "
|
||||
"session=%p key=%s loadgroupci=%p cache=%p hit=%p\n",
|
||||
|
@ -569,6 +586,17 @@ Http2Stream::AdjustInitialWindow()
|
|||
return;
|
||||
}
|
||||
|
||||
if (stream->mState == RESERVED_BY_REMOTE) {
|
||||
// h2-14 prevents sending a window update in this state
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mClientReceiveWindow <= ASpdySession::kInitialRwin);
|
||||
uint32_t bump = ASpdySession::kInitialRwin - mClientReceiveWindow;
|
||||
if (!bump) { // nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t *packet = mTxInlineFrame.get() + mTxInlineFrameUsed;
|
||||
EnsureBuffer(mTxInlineFrame, mTxInlineFrameUsed + Http2Session::kFrameHeaderBytes + 4,
|
||||
mTxInlineFrameUsed, mTxInlineFrameSize);
|
||||
|
@ -578,8 +606,6 @@ Http2Stream::AdjustInitialWindow()
|
|||
Http2Session::FRAME_TYPE_WINDOW_UPDATE,
|
||||
0, stream->mStreamID);
|
||||
|
||||
MOZ_ASSERT(mClientReceiveWindow <= ASpdySession::kInitialRwin);
|
||||
uint32_t bump = ASpdySession::kInitialRwin - mClientReceiveWindow;
|
||||
mClientReceiveWindow += bump;
|
||||
bump = PR_htonl(bump);
|
||||
memcpy(packet + Http2Session::kFrameHeaderBytes, &bump, 4);
|
||||
|
@ -835,7 +861,7 @@ Http2Stream::GenerateDataFrameHeader(uint32_t dataLength, bool lastFrame)
|
|||
mTxStreamFrameSize = dataLength;
|
||||
}
|
||||
|
||||
// ConvertHeaders is used to convert the response headers
|
||||
// ConvertResponseHeaders is used to convert the response headers
|
||||
// into HTTP/1 format and report some telemetry
|
||||
nsresult
|
||||
Http2Stream::ConvertResponseHeaders(Http2Decompressor *decompressor,
|
||||
|
@ -851,14 +877,14 @@ Http2Stream::ConvertResponseHeaders(Http2Decompressor *decompressor,
|
|||
aHeadersIn.Length(),
|
||||
aHeadersOut, false);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG3(("Http2Stream::ConvertHeaders %p decode Error\n", this));
|
||||
LOG3(("Http2Stream::ConvertResponseHeaders %p decode Error\n", this));
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
nsAutoCString statusString;
|
||||
decompressor->GetStatus(statusString);
|
||||
if (statusString.IsEmpty()) {
|
||||
LOG3(("Http2Stream::ConvertHeaders %p Error - no status\n", this));
|
||||
LOG3(("Http2Stream::ConvertResponseHeaders %p Error - no status\n", this));
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
|
@ -873,7 +899,7 @@ Http2Stream::ConvertResponseHeaders(Http2Decompressor *decompressor,
|
|||
|
||||
if (httpResponseCode == 101) {
|
||||
// 8.1.1 of h2 disallows 101.. throw PROTOCOL_ERROR on stream
|
||||
LOG3(("Http2Stream::ConvertHeaders %p Error - status == 101\n", this));
|
||||
LOG3(("Http2Stream::ConvertResponseHeaders %p Error - status == 101\n", this));
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
|
@ -901,7 +927,7 @@ Http2Stream::ConvertResponseHeaders(Http2Decompressor *decompressor,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// ConvertHeaders is used to convert the response headers
|
||||
// ConvertPushHeaders is used to convert the pushed request headers
|
||||
// into HTTP/1 format and report some telemetry
|
||||
nsresult
|
||||
Http2Stream::ConvertPushHeaders(Http2Decompressor *decompressor,
|
||||
|
@ -909,6 +935,7 @@ Http2Stream::ConvertPushHeaders(Http2Decompressor *decompressor,
|
|||
nsACString &aHeadersOut)
|
||||
{
|
||||
aHeadersOut.Truncate();
|
||||
aHeadersOut.SetCapacity(aHeadersIn.Length() + 512);
|
||||
nsresult rv =
|
||||
decompressor->DecodeHeaderBlock(reinterpret_cast<const uint8_t *>(aHeadersIn.BeginReading()),
|
||||
aHeadersIn.Length(),
|
||||
|
@ -955,6 +982,14 @@ Http2Stream::SetAllHeadersReceived()
|
|||
return;
|
||||
}
|
||||
|
||||
if (mState == RESERVED_BY_REMOTE) {
|
||||
// pushed streams needs to wait until headers have
|
||||
// arrived to open up their window
|
||||
LOG3(("Http2Stream::SetAllHeadersReceived %p state OPEN from reserved\n", this));
|
||||
mState = OPEN;
|
||||
AdjustInitialWindow();
|
||||
}
|
||||
|
||||
mAllHeadersReceived = 1;
|
||||
if (mIsTunnel) {
|
||||
MapStreamToHttpConnection();
|
||||
|
|
|
@ -47,9 +47,15 @@ public:
|
|||
|
||||
virtual nsresult ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *);
|
||||
virtual nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *);
|
||||
virtual bool DeferCleanupOnSuccess() { return false; }
|
||||
virtual bool DeferCleanup(nsresult status) { return false; }
|
||||
|
||||
// The consumer stream is the synthetic pull stream hooked up to this stream
|
||||
// http2PushedStream overrides it
|
||||
virtual Http2Stream *GetConsumerStream() { return nullptr; };
|
||||
|
||||
const nsAFlatCString &Origin() const { return mOrigin; }
|
||||
const nsAFlatCString &Host() const { return mHeaderHost; }
|
||||
const nsAFlatCString &Path() const { return mHeaderPath; }
|
||||
|
||||
bool RequestBlockedOnRead()
|
||||
{
|
||||
|
@ -125,6 +131,8 @@ public:
|
|||
|
||||
virtual ~Http2Stream();
|
||||
|
||||
Http2Session *Session() { return mSession; }
|
||||
|
||||
protected:
|
||||
static void CreatePushHashKey(const nsCString &scheme,
|
||||
const nsCString &hostHeader,
|
||||
|
|
|
@ -86,6 +86,9 @@ typedef uint8_t nsHttpVersion;
|
|||
// weaker security profiles based on past history
|
||||
#define NS_HTTP_ALLOW_RSA_FALSESTART (1<<9)
|
||||
|
||||
// This flag indicates the transaction should accept associated pushes
|
||||
#define NS_HTTP_ONPUSH_LISTENER (1<<10)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// some default values
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "nsICacheEntryDescriptor.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIHttpChannelAuthProvider.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsIHttpEventSink.h"
|
||||
#include "nsIPrompt.h"
|
||||
#include "nsInputStreamPump.h"
|
||||
|
@ -67,6 +68,7 @@
|
|||
#include "mozilla/Telemetry.h"
|
||||
#include "AlternateServices.h"
|
||||
#include "InterceptedChannel.h"
|
||||
#include "nsIHttpPushListener.h"
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
|
@ -232,6 +234,7 @@ nsHttpChannel::nsHttpChannel()
|
|||
, mConcurentCacheAccess(0)
|
||||
, mIsPartialRequest(0)
|
||||
, mHasAutoRedirectVetoNotifier(0)
|
||||
, mPushedStream(nullptr)
|
||||
, mDidReval(false)
|
||||
{
|
||||
LOG(("Creating nsHttpChannel [this=%p]\n", this));
|
||||
|
@ -813,6 +816,20 @@ nsHttpChannel::SetupTransaction()
|
|||
mCaps |= NS_HTTP_DISALLOW_SPDY;
|
||||
}
|
||||
|
||||
if (mPushedStream) {
|
||||
mTransaction->SetPushedStream(mPushedStream);
|
||||
mPushedStream = nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpPushListener> pushListener;
|
||||
NS_QueryNotificationCallbacks(mCallbacks,
|
||||
mLoadGroup,
|
||||
NS_GET_IID(nsIHttpPushListener),
|
||||
getter_AddRefs(pushListener));
|
||||
if (pushListener) {
|
||||
mCaps |= NS_HTTP_ONPUSH_LISTENER;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> responseStream;
|
||||
rv = mTransaction->Init(mCaps, mConnectionInfo, &mRequestHead,
|
||||
mUploadStream, mUploadStreamHasHeaders,
|
||||
|
@ -4593,6 +4610,12 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
// we have no macro that covers this case.
|
||||
if (aIID.Equals(NS_GET_IID(nsHttpChannel)) ) {
|
||||
AddRef();
|
||||
*aInstancePtr = this;
|
||||
return NS_OK;
|
||||
} else
|
||||
NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -6484,4 +6507,76 @@ nsHttpChannel::AwaitingCacheCallbacks()
|
|||
return mCacheEntriesToWaitFor != 0;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpChannel::SetPushedStream(Http2PushedStream *stream)
|
||||
{
|
||||
MOZ_ASSERT(stream);
|
||||
MOZ_ASSERT(!mPushedStream);
|
||||
mPushedStream = stream;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpChannel::OnPush(const nsACString &url, Http2PushedStream *pushedStream)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG(("nsHttpChannel::OnPush [this=%p]\n", this));
|
||||
|
||||
MOZ_ASSERT(mCaps & NS_HTTP_ONPUSH_LISTENER);
|
||||
nsCOMPtr<nsIHttpPushListener> pushListener;
|
||||
NS_QueryNotificationCallbacks(mCallbacks,
|
||||
mLoadGroup,
|
||||
NS_GET_IID(nsIHttpPushListener),
|
||||
getter_AddRefs(pushListener));
|
||||
|
||||
MOZ_ASSERT(pushListener);
|
||||
if (!pushListener) {
|
||||
LOG(("nsHttpChannel::OnPush [this=%p] notification callbacks do not "
|
||||
"implement nsIHttpPushListener\n", this));
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> pushResource;
|
||||
nsresult rv;
|
||||
|
||||
// Create a Channel for the Push Resource
|
||||
rv = NS_NewURI(getter_AddRefs(pushResource), url);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIIOService> ioService;
|
||||
rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIChannel> pushChannel;
|
||||
rv = ioService->NewChannelFromURI(pushResource, getter_AddRefs(pushChannel));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> pushHttpChannel = do_QueryInterface(pushChannel);
|
||||
MOZ_ASSERT(pushHttpChannel);
|
||||
if (!pushHttpChannel) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsRefPtr<nsHttpChannel> channel;
|
||||
CallQueryInterface(pushHttpChannel, channel.StartAssignment());
|
||||
MOZ_ASSERT(channel);
|
||||
if (!channel) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// new channel needs mrqeuesthead and headers from pushedStream
|
||||
channel->mRequestHead.ParseHeaderSet(
|
||||
pushedStream->GetRequestString().BeginWriting());
|
||||
|
||||
channel->mLoadGroup = mLoadGroup;
|
||||
channel->mLoadInfo = mLoadInfo;
|
||||
channel->mCallbacks = mCallbacks;
|
||||
|
||||
// Link the pushed stream with the new channel and call listener
|
||||
channel->SetPushedStream(pushedStream);
|
||||
rv = pushListener->OnPush(this, pushHttpChannel);
|
||||
return rv;
|
||||
}
|
||||
|
||||
} } // namespace mozilla::net
|
||||
|
|
|
@ -32,10 +32,20 @@ class nsISSLStatus;
|
|||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
class Http2PushedStream;
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Use to support QI nsIChannel to nsHttpChannel
|
||||
#define NS_HTTPCHANNEL_IID \
|
||||
{ \
|
||||
0x301bf95b, \
|
||||
0x7bb3, \
|
||||
0x4ae1, \
|
||||
{0xa9, 0x71, 0x40, 0xbc, 0xfa, 0x81, 0xde, 0x12} \
|
||||
}
|
||||
|
||||
class nsHttpChannel MOZ_FINAL : public HttpBaseChannel
|
||||
, public HttpAsyncAborter<nsHttpChannel>
|
||||
, public nsIStreamListener
|
||||
|
@ -67,6 +77,7 @@ public:
|
|||
NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
|
||||
NS_DECL_NSITHREADRETARGETABLEREQUEST
|
||||
NS_DECL_NSIDNSLISTENER
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_HTTPCHANNEL_IID)
|
||||
|
||||
// nsIHttpAuthenticableChannel. We can't use
|
||||
// NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates cancel() and
|
||||
|
@ -95,6 +106,8 @@ public:
|
|||
uint32_t aProxyResolveFlags,
|
||||
nsIURI *aProxyURI);
|
||||
|
||||
nsresult OnPush(const nsACString &uri, Http2PushedStream *pushedStream);
|
||||
|
||||
// Methods HttpBaseChannel didn't implement for us or that we override.
|
||||
//
|
||||
// nsIRequest
|
||||
|
@ -345,6 +358,8 @@ private:
|
|||
nsresult OpenCacheInputStream(nsICacheEntry* cacheEntry, bool startBuffering,
|
||||
bool checkingAppCacheEntry);
|
||||
|
||||
void SetPushedStream(Http2PushedStream *stream);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISupports> mSecurityInfo;
|
||||
nsCOMPtr<nsICancelable> mProxyRequest;
|
||||
|
@ -438,6 +453,8 @@ private:
|
|||
// Needed for accurate DNS timing
|
||||
nsRefPtr<nsDNSPrefetch> mDNSPrefetch;
|
||||
|
||||
Http2PushedStream *mPushedStream;
|
||||
|
||||
nsresult WaitForRedirectCallback();
|
||||
void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
|
||||
void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
|
||||
|
@ -451,6 +468,7 @@ private: // cache telemetry
|
|||
bool mDidReval;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsHttpChannel, NS_HTTPCHANNEL_IID)
|
||||
} } // namespace mozilla::net
|
||||
|
||||
#endif // nsHttpChannel_h__
|
||||
|
|
|
@ -1882,8 +1882,8 @@ nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
|
|||
nsresult rv;
|
||||
|
||||
LOG(("nsHttpConnectionMgr::DispatchTransaction "
|
||||
"[ent-ci=%s trans=%p caps=%x conn=%p priority=%d]\n",
|
||||
ent->mConnInfo->HashKey().get(), trans, caps, conn, priority));
|
||||
"[ent-ci=%s %p trans=%p caps=%x conn=%p priority=%d]\n",
|
||||
ent->mConnInfo->HashKey().get(), ent, trans, caps, conn, priority));
|
||||
|
||||
// It is possible for a rate-paced transaction to be dispatched independent
|
||||
// of the token bucket when the amount of parallelization has changed or
|
||||
|
@ -2046,6 +2046,13 @@ nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
|
|||
|
||||
trans->SetPendingTime();
|
||||
|
||||
Http2PushedStream *pushedStream = trans->GetPushedStream();
|
||||
if (pushedStream) {
|
||||
return pushedStream->Session()->
|
||||
AddStream(trans, trans->Priority(), false, nullptr) ?
|
||||
NS_OK : NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
nsHttpConnectionInfo *ci = trans->ConnectionInfo();
|
||||
MOZ_ASSERT(ci);
|
||||
|
|
|
@ -184,6 +184,25 @@ nsHttpHeaderArray::ParseHeaderLine(const char *line,
|
|||
return SetHeaderFromNet(atom, nsDependentCString(p, p2 - p));
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpHeaderArray::ParseHeaderSet(char *buffer)
|
||||
{
|
||||
nsHttpAtom hdr;
|
||||
char *val;
|
||||
while (buffer) {
|
||||
char *eof = strchr(buffer, '\r');
|
||||
if (!eof) {
|
||||
break;
|
||||
}
|
||||
*eof = '\0';
|
||||
ParseHeaderLine(buffer, &hdr, &val);
|
||||
buffer = eof + 1;
|
||||
if (*buffer == '\n') {
|
||||
buffer++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpHeaderArray::Flatten(nsACString &buf, bool pruneProxyHeaders)
|
||||
{
|
||||
|
|
|
@ -54,6 +54,8 @@ public:
|
|||
|
||||
void Flatten(nsACString &, bool pruneProxyHeaders=false);
|
||||
|
||||
void ParseHeaderSet(char *buffer);
|
||||
|
||||
uint32_t Count() const { return mHeaders.Length(); }
|
||||
|
||||
const char *PeekHeaderAt(uint32_t i, nsHttpAtom &header) const;
|
||||
|
|
|
@ -93,6 +93,7 @@ public:
|
|||
bool IsHead() const { return EqualsMethod(kMethod_Head); }
|
||||
bool IsPut() const { return EqualsMethod(kMethod_Put); }
|
||||
bool IsTrace() const { return EqualsMethod(kMethod_Trace); }
|
||||
void ParseHeaderSet(char *buffer) { mHeaders.ParseHeaderSet(buffer); }
|
||||
|
||||
private:
|
||||
// All members must be copy-constructable and assignable
|
||||
|
|
|
@ -96,6 +96,7 @@ nsHttpTransaction::nsHttpTransaction()
|
|||
, mContentLength(-1)
|
||||
, mContentRead(0)
|
||||
, mInvalidResponseBytesRead(0)
|
||||
, mPushedStream(nullptr)
|
||||
, mChunkedDecoder(nullptr)
|
||||
, mStatus(NS_OK)
|
||||
, mPriority(0)
|
||||
|
@ -143,6 +144,11 @@ nsHttpTransaction::~nsHttpTransaction()
|
|||
{
|
||||
LOG(("Destroying nsHttpTransaction @%p\n", this));
|
||||
|
||||
if (mPushedStream) {
|
||||
mPushedStream->OnPushFailed();
|
||||
mPushedStream = nullptr;
|
||||
}
|
||||
|
||||
if (mTokenBucketCancel) {
|
||||
mTokenBucketCancel->Cancel(NS_ERROR_ABORT);
|
||||
mTokenBucketCancel = nullptr;
|
||||
|
@ -225,7 +231,6 @@ nsHttpTransaction::Init(uint32_t caps,
|
|||
if (NS_SUCCEEDED(rv) && activityDistributorActive) {
|
||||
// there are some observers registered at activity distributor, gather
|
||||
// nsISupports for the channel that called Init()
|
||||
mChannel = do_QueryInterface(eventsink);
|
||||
LOG(("nsHttpTransaction::Init() " \
|
||||
"mActivityDistributor is active " \
|
||||
"this=%p", this));
|
||||
|
@ -234,7 +239,7 @@ nsHttpTransaction::Init(uint32_t caps,
|
|||
activityDistributorActive = false;
|
||||
mActivityDistributor = nullptr;
|
||||
}
|
||||
|
||||
mChannel = do_QueryInterface(eventsink);
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(eventsink);
|
||||
if (channel) {
|
||||
bool isInBrowser;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "nsILoadGroup.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "TimingStruct.h"
|
||||
#include "Http2Push.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "nsINetworkManager.h"
|
||||
|
@ -89,6 +90,7 @@ public:
|
|||
nsISupports *SecurityInfo() { return mSecurityInfo; }
|
||||
|
||||
nsIEventTarget *ConsumerTarget() { return mConsumerTarget; }
|
||||
nsISupports *HttpChannel() { return mChannel; }
|
||||
|
||||
void SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks);
|
||||
|
||||
|
@ -137,6 +139,15 @@ public:
|
|||
|
||||
nsHttpTransaction *QueryHttpTransaction() MOZ_OVERRIDE { return this; }
|
||||
|
||||
Http2PushedStream *GetPushedStream() { return mPushedStream; }
|
||||
Http2PushedStream *TakePushedStream()
|
||||
{
|
||||
Http2PushedStream *r = mPushedStream;
|
||||
mPushedStream = nullptr;
|
||||
return r;
|
||||
}
|
||||
void SetPushedStream(Http2PushedStream *push) { mPushedStream = push; }
|
||||
|
||||
private:
|
||||
friend class DeleteHttpTransaction;
|
||||
virtual ~nsHttpTransaction();
|
||||
|
@ -222,7 +233,9 @@ private:
|
|||
// so far been skipped.
|
||||
uint32_t mInvalidResponseBytesRead;
|
||||
|
||||
nsHttpChunkedDecoder *mChunkedDecoder;
|
||||
Http2PushedStream *mPushedStream;
|
||||
|
||||
nsHttpChunkedDecoder *mChunkedDecoder;
|
||||
|
||||
TimingStruct mTimings;
|
||||
|
||||
|
|
|
@ -393,6 +393,86 @@ function test_http2_altsvc() {
|
|||
chan.asyncOpen(altsvcClientListener, null);
|
||||
}
|
||||
|
||||
var Http2PushApiListener = function() {};
|
||||
|
||||
Http2PushApiListener.prototype = {
|
||||
checksPending: 9, // 4 onDataAvailable and 5 onStop
|
||||
|
||||
getInterface: function(aIID) {
|
||||
return this.QueryInterface(aIID);
|
||||
},
|
||||
|
||||
QueryInterface: function(aIID) {
|
||||
if (aIID.equals(Ci.nsIHttpPushListener) ||
|
||||
aIID.equals(Ci.nsIStreamListener))
|
||||
return this;
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
||||
// nsIHttpPushListener
|
||||
onPush: function onPush(associatedChannel, pushChannel) {
|
||||
do_check_eq(associatedChannel.originalURI.spec, "https://localhost:6944/pushapi1");
|
||||
do_check_eq (pushChannel.getRequestHeader("x-pushed-request"), "true");
|
||||
|
||||
pushChannel.asyncOpen(this, pushChannel);
|
||||
if (pushChannel.originalURI.spec == "https://localhost:6944/pushapi1/2") {
|
||||
pushChannel.cancel(Components.results.NS_ERROR_ABORT);
|
||||
}
|
||||
},
|
||||
|
||||
// normal Channel listeners
|
||||
onStartRequest: function pushAPIOnStart(request, ctx) {
|
||||
},
|
||||
|
||||
onDataAvailable: function pushAPIOnDataAvailable(request, ctx, stream, offset, cnt) {
|
||||
do_check_neq(ctx.originalURI.spec, "https://localhost:6944/pushapi1/2");
|
||||
|
||||
var data = read_stream(stream, cnt);
|
||||
|
||||
if (ctx.originalURI.spec == "https://localhost:6944/pushapi1") {
|
||||
do_check_eq(data[0], '0');
|
||||
--this.checksPending;
|
||||
} else if (ctx.originalURI.spec == "https://localhost:6944/pushapi1/1") {
|
||||
do_check_eq(data[0], '1');
|
||||
--this.checksPending; // twice
|
||||
} else if (ctx.originalURI.spec == "https://localhost:6944/pushapi1/3") {
|
||||
do_check_eq(data[0], '3');
|
||||
--this.checksPending;
|
||||
} else {
|
||||
do_check_eq(true, false);
|
||||
}
|
||||
},
|
||||
|
||||
onStopRequest: function test_onStopR(request, ctx, status) {
|
||||
if (ctx.originalURI.spec == "https://localhost:6944/pushapi1/2") {
|
||||
do_check_eq(request.status, Components.results.NS_ERROR_ABORT);
|
||||
} else {
|
||||
do_check_eq(request.status, Components.results.NS_OK);
|
||||
}
|
||||
|
||||
--this.checksPending; // 5 times - one for each push plus the pull
|
||||
if (!this.checksPending) {
|
||||
run_next_test();
|
||||
do_test_finished();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// pushAPI testcase 1 expects
|
||||
// 1 to pull /pushapi1 with 0
|
||||
// 2 to see /pushapi1/1 with 1
|
||||
// 3 to see /pushapi1/1 with 1 (again)
|
||||
// 4 to see /pushapi1/2 that it will cancel
|
||||
// 5 to see /pushapi1/3 with 3
|
||||
|
||||
function test_http2_pushapi_1() {
|
||||
var chan = makeChan("https://localhost:6944/pushapi1");
|
||||
chan.loadGroup = loadGroup;
|
||||
var listener = new Http2PushApiListener();
|
||||
chan.notificationCallbacks = listener;
|
||||
chan.asyncOpen(listener, chan);
|
||||
}
|
||||
|
||||
// hack - the header test resets the multiplex object on the server,
|
||||
// so make sure header is always run before the multiplex test.
|
||||
//
|
||||
|
@ -413,6 +493,7 @@ var tests = [ test_http2_post_big
|
|||
, test_http2_multiplex
|
||||
, test_http2_big
|
||||
, test_http2_post
|
||||
, test_http2_pushapi_1
|
||||
];
|
||||
var current_test = 0;
|
||||
|
||||
|
@ -483,6 +564,8 @@ var spdy3pref;
|
|||
var spdypush;
|
||||
var http2pref;
|
||||
var tlspref;
|
||||
var altsvcpref1;
|
||||
var altsvcpref2;
|
||||
|
||||
var loadGroup;
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ var m = {
|
|||
function handleRequest(req, res) {
|
||||
var u = url.parse(req.url);
|
||||
var content = getHttpContent(u.pathname);
|
||||
var push;
|
||||
var push, push1, push1a, push2, push3;
|
||||
|
||||
if (req.httpVersionMajor === 2) {
|
||||
res.setHeader('X-Connection-Http2', 'yes');
|
||||
|
@ -151,6 +151,54 @@ function handleRequest(req, res) {
|
|||
content = '<head> <script src="push2.js"/></head>body text';
|
||||
}
|
||||
|
||||
else if (u.pathname === "/pushapi1") {
|
||||
push1 = res.push(
|
||||
{ hostname: 'localhost:6944', port: 6944, path : '/pushapi1/1', method : 'GET',
|
||||
headers: {'x-pushed-request': 'true', 'x-foo' : 'bar'}});
|
||||
push1.writeHead(200, {
|
||||
'pushed' : 'yes',
|
||||
'content-length' : 1,
|
||||
'subresource' : '1',
|
||||
'X-Connection-Http2': 'yes'
|
||||
});
|
||||
push1.end('1');
|
||||
|
||||
push1a = res.push(
|
||||
{ hostname: 'localhost:6944', port: 6944, path : '/pushapi1/1', method : 'GET',
|
||||
headers: {'x-foo' : 'bar', 'x-pushed-request': 'true'}});
|
||||
push1a.writeHead(200, {
|
||||
'pushed' : 'yes',
|
||||
'content-length' : 1,
|
||||
'subresource' : '1a',
|
||||
'X-Connection-Http2': 'yes'
|
||||
});
|
||||
push1a.end('1');
|
||||
|
||||
push2 = res.push(
|
||||
{ hostname: 'localhost:6944', port: 6944, path : '/pushapi1/2', method : 'GET',
|
||||
headers: {'x-pushed-request': 'true'}});
|
||||
push2.writeHead(200, {
|
||||
'pushed' : 'yes',
|
||||
'subresource' : '2',
|
||||
'content-length' : 1,
|
||||
'X-Connection-Http2': 'yes'
|
||||
});
|
||||
push2.end('2');
|
||||
|
||||
push3 = res.push(
|
||||
{ hostname: 'localhost:6944', port: 6944, path : '/pushapi1/3', method : 'GET',
|
||||
headers: {'x-pushed-request': 'true'}});
|
||||
push3.writeHead(200, {
|
||||
'pushed' : 'yes',
|
||||
'content-length' : 1,
|
||||
'subresource' : '3',
|
||||
'X-Connection-Http2': 'yes'
|
||||
});
|
||||
push3.end('3');
|
||||
|
||||
content = '0';
|
||||
}
|
||||
|
||||
else if (u.pathname === "/big") {
|
||||
content = generateContent(128 * 1024);
|
||||
var hash = crypto.createHash('md5');
|
||||
|
|
Загрузка…
Ссылка в новой задаче