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 "SpdyPush3.h" michael@0: #include "PSpdyPush.h" michael@0: #include "SpdySession3.h" michael@0: #include "nsHttpRequestHead.h" michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: ////////////////////////////////////////// michael@0: // SpdyPushedStream3 michael@0: ////////////////////////////////////////// michael@0: michael@0: SpdyPushedStream3::SpdyPushedStream3(SpdyPush3TransactionBuffer *aTransaction, michael@0: SpdySession3 *aSession, michael@0: SpdyStream3 *aAssociatedStream, michael@0: uint32_t aID) michael@0: :SpdyStream3(aTransaction, aSession, michael@0: 0 /* priority is only for sending, so ignore it on push */) 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(("SpdyPushedStream3 ctor this=%p 0x%X\n", this, aID)); michael@0: mStreamID = aID; michael@0: mBufferedPush->SetPushStream(this); michael@0: mLoadGroupCI = aAssociatedStream->LoadGroupConnectionInfo(); michael@0: mLastRead = TimeStamp::Now(); michael@0: } michael@0: michael@0: bool michael@0: SpdyPushedStream3::GetPushComplete() michael@0: { michael@0: return mPushCompleted; michael@0: } michael@0: michael@0: nsresult michael@0: SpdyPushedStream3::WriteSegments(nsAHttpSegmentWriter *writer, michael@0: uint32_t count, michael@0: uint32_t *countWritten) michael@0: { michael@0: nsresult rv = SpdyStream3::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: SpdyPushedStream3::ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *count) michael@0: { michael@0: // The SYN_STREAM for this has been processed, so we need to verify michael@0: // that :host, :scheme, and :path MUST be present michael@0: nsDependentCSubstring host, scheme, path; michael@0: nsresult rv; michael@0: michael@0: rv = SpdyStream3::FindHeader(NS_LITERAL_CSTRING(":host"), host); michael@0: if (NS_FAILED(rv)) { michael@0: LOG3(("SpdyPushedStream3::ReadSegments session=%p ID 0x%X " michael@0: "push without required :host\n", mSession, mStreamID)); michael@0: return rv; michael@0: } michael@0: michael@0: rv = SpdyStream3::FindHeader(NS_LITERAL_CSTRING(":scheme"), scheme); michael@0: if (NS_FAILED(rv)) { michael@0: LOG3(("SpdyPushedStream3::ReadSegments session=%p ID 0x%X " michael@0: "push without required :scheme\n", mSession, mStreamID)); michael@0: return rv; michael@0: } michael@0: michael@0: rv = SpdyStream3::FindHeader(NS_LITERAL_CSTRING(":path"), path); michael@0: if (NS_FAILED(rv)) { michael@0: LOG3(("SpdyPushedStream3::ReadSegments session=%p ID 0x%X " michael@0: "push without required :host\n", mSession, mStreamID)); michael@0: return rv; michael@0: } michael@0: michael@0: CreatePushHashKey(nsCString(scheme), nsCString(host), michael@0: mSession->Serial(), path, michael@0: mOrigin, mHashKey); michael@0: michael@0: LOG3(("SpdyPushStream3 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: SpdyStream3::mSentFinOnData = 1; michael@0: SpdyStream3::mSynFrameComplete = 1; michael@0: SpdyStream3::ChangeState(UPSTREAM_COMPLETE); michael@0: *count = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: SpdyPushedStream3::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: SpdyPushedStream3::ConnectPushedStream(SpdyStream3 *stream) michael@0: { michael@0: mSession->ConnectPushedStream(stream); michael@0: } michael@0: michael@0: bool michael@0: SpdyPushedStream3::IsOrphaned(TimeStamp now) michael@0: { michael@0: MOZ_ASSERT(!now.IsNull()); michael@0: michael@0: // if spdy 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(("SpdyPushCache::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: SpdyPushedStream3::GetBufferedData(char *buf, michael@0: uint32_t count, michael@0: 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: // SpdyPush3TransactionBuffer 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(SpdyPush3TransactionBuffer) michael@0: michael@0: SpdyPush3TransactionBuffer::SpdyPush3TransactionBuffer() 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: SpdyPush3TransactionBuffer::~SpdyPush3TransactionBuffer() michael@0: { michael@0: delete mRequestHead; michael@0: } michael@0: michael@0: void michael@0: SpdyPush3TransactionBuffer::SetConnection(nsAHttpConnection *conn) michael@0: { michael@0: } michael@0: michael@0: nsAHttpConnection * michael@0: SpdyPush3TransactionBuffer::Connection() michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: SpdyPush3TransactionBuffer::GetSecurityCallbacks(nsIInterfaceRequestor **outCB) michael@0: { michael@0: *outCB = nullptr; michael@0: } michael@0: michael@0: void michael@0: SpdyPush3TransactionBuffer::OnTransportStatus(nsITransport* transport, michael@0: nsresult status, uint64_t progress) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: SpdyPush3TransactionBuffer::IsDone() michael@0: { michael@0: return mIsDone; michael@0: } michael@0: michael@0: nsresult michael@0: SpdyPush3TransactionBuffer::Status() michael@0: { michael@0: return mStatus; michael@0: } michael@0: michael@0: uint32_t michael@0: SpdyPush3TransactionBuffer::Caps() michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: void michael@0: SpdyPush3TransactionBuffer::SetDNSWasRefreshed() michael@0: { michael@0: } michael@0: michael@0: uint64_t michael@0: SpdyPush3TransactionBuffer::Available() michael@0: { michael@0: return mBufferedHTTP1Used - mBufferedHTTP1Consumed; michael@0: } michael@0: michael@0: nsresult michael@0: SpdyPush3TransactionBuffer::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: SpdyPush3TransactionBuffer::WriteSegments(nsAHttpSegmentWriter *writer, michael@0: uint32_t count, uint32_t *countWritten) michael@0: { michael@0: if ((mBufferedHTTP1Size - mBufferedHTTP1Used) < 20480) { michael@0: SpdySession3::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: SpdyStream3 *consumer = mPushStream->GetConsumerStream(); michael@0: michael@0: if (consumer) { michael@0: LOG3(("SpdyPush3TransactionBuffer::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: SpdyPush3TransactionBuffer::Http1xTransactionCount() michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: nsHttpRequestHead * michael@0: SpdyPush3TransactionBuffer::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: SpdyPush3TransactionBuffer::TakeSubTransactions( michael@0: nsTArray > &outTransactions) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: void michael@0: SpdyPush3TransactionBuffer::SetProxyConnectFailed() michael@0: { michael@0: } michael@0: michael@0: void michael@0: SpdyPush3TransactionBuffer::Close(nsresult reason) michael@0: { michael@0: mStatus = reason; michael@0: mIsDone = true; michael@0: } michael@0: michael@0: nsresult michael@0: SpdyPush3TransactionBuffer::AddTransaction(nsAHttpTransaction *trans) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: uint32_t michael@0: SpdyPush3TransactionBuffer::PipelineDepth() michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: nsresult michael@0: SpdyPush3TransactionBuffer::SetPipelinePosition(int32_t position) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t michael@0: SpdyPush3TransactionBuffer::PipelinePosition() michael@0: { michael@0: return 1; michael@0: } michael@0: michael@0: nsresult michael@0: SpdyPush3TransactionBuffer::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