diff -r 000000000000 -r 6474c204b198 netwerk/protocol/http/Http2Push.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netwerk/protocol/http/Http2Push.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,344 @@ +/* -*- 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 + +#include "Http2Push.h" + +#include "nsDependentString.h" + +namespace mozilla { +namespace net { + +////////////////////////////////////////// +// Http2PushedStream +////////////////////////////////////////// + +Http2PushedStream::Http2PushedStream(Http2PushTransactionBuffer *aTransaction, + Http2Session *aSession, + Http2Stream *aAssociatedStream, + uint32_t aID) + :Http2Stream(aTransaction, aSession, 0) + , mConsumerStream(nullptr) + , mBufferedPush(aTransaction) + , mStatus(NS_OK) + , mPushCompleted(false) + , mDeferCleanupOnSuccess(true) +{ + LOG3(("Http2PushedStream ctor this=%p 0x%X\n", this, aID)); + mStreamID = aID; + MOZ_ASSERT(!(aID & 1)); // must be even to be a pushed stream + mBufferedPush->SetPushStream(this); + mLoadGroupCI = aAssociatedStream->LoadGroupConnectionInfo(); + mLastRead = TimeStamp::Now(); + SetPriority(aAssociatedStream->Priority() + 1); +} + +bool +Http2PushedStream::GetPushComplete() +{ + return mPushCompleted; +} + +nsresult +Http2PushedStream::WriteSegments(nsAHttpSegmentWriter *writer, + uint32_t count, uint32_t *countWritten) +{ + nsresult rv = Http2Stream::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 +Http2PushedStream::ReadSegments(nsAHttpSegmentReader *, + uint32_t, uint32_t *count) +{ + // The request headers for this has been processed, so we need to verify + // that :authority, :scheme, and :path MUST be present. :method MUST NOT be + // present + CreatePushHashKey(mHeaderScheme, mHeaderHost, + mSession->Serial(), mHeaderPath, + mOrigin, mHashKey); + + LOG3(("Http2PushStream 0x%X hash key %s\n", mStreamID, mHashKey.get())); + + // the write side of a pushed transaction just involves manipulating a little state + SetSentFin(true); + Http2Stream::mAllHeadersSent = 1; + Http2Stream::ChangeState(UPSTREAM_COMPLETE); + *count = 0; + return NS_OK; +} + +bool +Http2PushedStream::GetHashKey(nsCString &key) +{ + if (mHashKey.IsEmpty()) + return false; + + key = mHashKey; + return true; +} + +void +Http2PushedStream::ConnectPushedStream(Http2Stream *stream) +{ + mSession->ConnectPushedStream(stream); +} + +bool +Http2PushedStream::IsOrphaned(TimeStamp now) +{ + MOZ_ASSERT(!now.IsNull()); + + // 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) + return false; + + bool rv = ((now - mLastRead).ToSeconds() > 30.0); + if (rv) { + LOG3(("Http2PushedStream:IsOrphaned 0x%X IsOrphaned %3.2f\n", + mStreamID, (now - mLastRead).ToSeconds())); + } + return rv; +} + +nsresult +Http2PushedStream::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; +} + +////////////////////////////////////////// +// Http2PushTransactionBuffer +// This is the nsAHttpTransction owned by the stream when the pushed +// stream has not yet been matched with a pull request +////////////////////////////////////////// + +NS_IMPL_ISUPPORTS0(Http2PushTransactionBuffer) + +Http2PushTransactionBuffer::Http2PushTransactionBuffer() + : mStatus(NS_OK) + , mRequestHead(nullptr) + , mPushStream(nullptr) + , mIsDone(false) + , mBufferedHTTP1Size(kDefaultBufferSize) + , mBufferedHTTP1Used(0) + , mBufferedHTTP1Consumed(0) +{ + mBufferedHTTP1 = new char[mBufferedHTTP1Size]; +} + +Http2PushTransactionBuffer::~Http2PushTransactionBuffer() +{ + delete mRequestHead; +} + +void +Http2PushTransactionBuffer::SetConnection(nsAHttpConnection *conn) +{ +} + +nsAHttpConnection * +Http2PushTransactionBuffer::Connection() +{ + return nullptr; +} + +void +Http2PushTransactionBuffer::GetSecurityCallbacks(nsIInterfaceRequestor **outCB) +{ + *outCB = nullptr; +} + +void +Http2PushTransactionBuffer::OnTransportStatus(nsITransport* transport, + nsresult status, uint64_t progress) +{ +} + +bool +Http2PushTransactionBuffer::IsDone() +{ + return mIsDone; +} + +nsresult +Http2PushTransactionBuffer::Status() +{ + return mStatus; +} + +uint32_t +Http2PushTransactionBuffer::Caps() +{ + return 0; +} + +void +Http2PushTransactionBuffer::SetDNSWasRefreshed() +{ +} + +uint64_t +Http2PushTransactionBuffer::Available() +{ + return mBufferedHTTP1Used - mBufferedHTTP1Consumed; +} + +nsresult +Http2PushTransactionBuffer::ReadSegments(nsAHttpSegmentReader *reader, + uint32_t count, uint32_t *countRead) +{ + *countRead = 0; + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +Http2PushTransactionBuffer::WriteSegments(nsAHttpSegmentWriter *writer, + uint32_t count, uint32_t *countWritten) +{ + if ((mBufferedHTTP1Size - mBufferedHTTP1Used) < 20480) { + Http2Session::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()) { + Http2Stream *consumer = mPushStream->GetConsumerStream(); + + if (consumer) { + LOG3(("Http2PushTransactionBuffer::WriteSegments notifying connection " + "consumer data available 0x%X [%u]\n", + mPushStream->StreamID(), Available())); + mPushStream->ConnectPushedStream(consumer); + } + } + + return rv; +} + +uint32_t +Http2PushTransactionBuffer::Http1xTransactionCount() +{ + return 0; +} + +nsHttpRequestHead * +Http2PushTransactionBuffer::RequestHead() +{ + if (!mRequestHead) + mRequestHead = new nsHttpRequestHead(); + return mRequestHead; +} + +nsresult +Http2PushTransactionBuffer::TakeSubTransactions( + nsTArray > &outTransactions) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +void +Http2PushTransactionBuffer::SetProxyConnectFailed() +{ +} + +void +Http2PushTransactionBuffer::Close(nsresult reason) +{ + mStatus = reason; + mIsDone = true; +} + +nsresult +Http2PushTransactionBuffer::AddTransaction(nsAHttpTransaction *trans) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +uint32_t +Http2PushTransactionBuffer::PipelineDepth() +{ + return 0; +} + +nsresult +Http2PushTransactionBuffer::SetPipelinePosition(int32_t position) +{ + return NS_OK; +} + +int32_t +Http2PushTransactionBuffer::PipelinePosition() +{ + return 1; +} + +nsresult +Http2PushTransactionBuffer::GetBufferedData(char *buf, + uint32_t count, + uint32_t *countWritten) +{ + *countWritten = std::min(count, static_cast(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 mozilla::net +} // namespace mozilla