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