diff options
Diffstat (limited to 'netwerk/protocol/http/Http2StreamBase.h')
-rw-r--r-- | netwerk/protocol/http/Http2StreamBase.h | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/netwerk/protocol/http/Http2StreamBase.h b/netwerk/protocol/http/Http2StreamBase.h new file mode 100644 index 0000000000..158b6559a5 --- /dev/null +++ b/netwerk/protocol/http/Http2StreamBase.h @@ -0,0 +1,377 @@ +/* -*- 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_Http2StreamBase_h +#define mozilla_net_Http2StreamBase_h + +// HTTP/2 - RFC7540 +// https://www.rfc-editor.org/rfc/rfc7540.txt + +#include "mozilla/Attributes.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/WeakPtr.h" +#include "nsAHttpTransaction.h" +#include "nsISupportsPriority.h" +#include "SimpleBuffer.h" +#include "nsISupportsImpl.h" +#include "nsIURI.h" + +class nsISocketTransport; +class nsIInputStream; +class nsIOutputStream; + +namespace mozilla { +class OriginAttributes; +} + +namespace mozilla::net { + +class nsStandardURL; +class Http2Session; +class Http2Stream; +class Http2PushedStream; +class Http2Decompressor; + +class Http2StreamBase : public nsAHttpSegmentReader, + public nsAHttpSegmentWriter, + public SupportsWeakPtr { + public: + NS_DECL_NSAHTTPSEGMENTREADER + + enum stateType { + IDLE, + RESERVED_BY_REMOTE, + OPEN, + CLOSED_BY_LOCAL, + CLOSED_BY_REMOTE, + CLOSED + }; + + const static int32_t kNormalPriority = 0x1000; + const static int32_t kWorstPriority = + kNormalPriority + nsISupportsPriority::PRIORITY_LOWEST; + const static int32_t kBestPriority = + kNormalPriority + nsISupportsPriority::PRIORITY_HIGHEST; + + Http2StreamBase(uint64_t, Http2Session*, int32_t, uint64_t); + + uint32_t StreamID() { return mStreamID; } + + stateType HTTPState() { return mState; } + void SetHTTPState(stateType val) { mState = val; } + + [[nodiscard]] virtual nsresult ReadSegments(nsAHttpSegmentReader*, uint32_t, + uint32_t*); + [[nodiscard]] virtual nsresult WriteSegments(nsAHttpSegmentWriter*, uint32_t, + uint32_t*); + virtual bool DeferCleanup(nsresult status); + + const nsCString& Origin() const { return mOrigin; } + const nsCString& Host() const { return mHeaderHost; } + const nsCString& Path() const { return mHeaderPath; } + + bool RequestBlockedOnRead() { + return static_cast<bool>(mRequestBlockedOnRead); + } + + bool HasRegisteredID() { return mStreamID != 0; } + + virtual nsAHttpTransaction* Transaction() { return nullptr; } + nsHttpTransaction* HttpTransaction(); + virtual nsIRequestContext* RequestContext() { return nullptr; } + + virtual void CloseStream(nsresult reason) = 0; + void SetResponseIsComplete(); + + void SetRecvdFin(bool aStatus); + bool RecvdFin() { return mRecvdFin; } + + void SetRecvdData(bool aStatus) { mReceivedData = aStatus ? 1 : 0; } + bool RecvdData() { return mReceivedData; } + + void SetSentFin(bool aStatus); + bool SentFin() { return mSentFin; } + + void SetRecvdReset(bool aStatus); + bool RecvdReset() { return mRecvdReset; } + + void SetSentReset(bool aStatus); + bool SentReset() { return mSentReset; } + + 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 SetAllHeadersReceived(); + void UnsetAllHeadersReceived() { mAllHeadersReceived = 0; } + bool AllHeadersReceived() { return mAllHeadersReceived; } + + void UpdateTransportSendEvents(uint32_t count); + void UpdateTransportReadEvents(uint32_t count); + + // NS_ERROR_ABORT terminates stream, other failure terminates session + [[nodiscard]] nsresult ConvertResponseHeaders(Http2Decompressor*, nsACString&, + nsACString&, int32_t&); + [[nodiscard]] nsresult ConvertResponseTrailers(Http2Decompressor*, + nsACString&); + + bool AllowFlowControlledWrite(); + void UpdateServerReceiveWindow(int32_t delta); + int64_t ServerReceiveWindow() { return mServerReceiveWindow; } + + void DecrementClientReceiveWindow(uint32_t delta) { + mClientReceiveWindow -= delta; + mLocalUnacked += delta; + } + + void IncrementClientReceiveWindow(uint32_t delta) { + mClientReceiveWindow += delta; + mLocalUnacked -= delta; + } + + uint64_t LocalUnAcked(); + int64_t ClientReceiveWindow() { return mClientReceiveWindow; } + + bool BlockedOnRwin() { return mBlockedOnRwin; } + + uint32_t Priority() { return mPriority; } + uint32_t PriorityDependency() { return mPriorityDependency; } + uint8_t PriorityWeight() { return mPriorityWeight; } + void SetPriority(uint32_t); + void SetPriorityDependency(uint32_t, uint32_t); + void UpdatePriorityDependency(); + + uint64_t TransactionTabId() { return mTransactionTabId; } + + // 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; } + + already_AddRefed<Http2Session> Session(); + + // Mirrors nsAHttpTransaction + bool Do0RTT(); + nsresult Finish0RTT(bool aRestart, bool aAlpnChanged); + + nsresult GetOriginAttributes(mozilla::OriginAttributes* oa); + + virtual void TopBrowsingContextIdChanged(uint64_t id); + void TopBrowsingContextIdChangedInternal( + uint64_t id); // For use by pushed streams only + + virtual bool IsTunnel() { return false; } + + virtual uint32_t GetWireStreamId() { return mStreamID; } + virtual Http2Stream* GetHttp2Stream() { return nullptr; } + virtual Http2PushedStream* GetHttp2PushedStream() { return nullptr; } + + [[nodiscard]] virtual nsresult OnWriteSegment(char*, uint32_t, + uint32_t*) override; + + virtual nsHttpConnectionInfo* ConnectionInfo(); + + bool DataBuffered() { return mSimpleBuffer.Available(); } + + virtual nsresult Condition() { return NS_OK; } + + virtual void DisableSpdy() { + if (Transaction()) { + Transaction()->DisableSpdy(); + } + } + virtual void ReuseConnectionOnRestartOK(bool aReuse) { + if (Transaction()) { + Transaction()->ReuseConnectionOnRestartOK(aReuse); + } + } + virtual void MakeNonSticky() { + if (Transaction()) { + Transaction()->MakeNonSticky(); + } + } + + protected: + virtual ~Http2StreamBase(); + + virtual void HandleResponseHeaders(nsACString& aHeadersOut, + int32_t httpResponseCode) {} + virtual nsresult CallToWriteData(uint32_t count, uint32_t* countRead) = 0; + virtual nsresult CallToReadData(uint32_t count, uint32_t* countWritten) = 0; + virtual bool CloseSendStreamWhenDone() { return true; } + + // These internal states track request generation + enum upstreamStateType { + GENERATING_HEADERS, + GENERATING_BODY, + SENDING_BODY, + SENDING_FIN_STREAM, + UPSTREAM_COMPLETE + }; + + uint32_t mStreamID{0}; + + // The session that this stream is a subset of + nsWeakPtr mSession; + + // 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. + RefPtr<nsAHttpSegmentReader> mSegmentReader; + nsAHttpSegmentWriter* mSegmentWriter{nullptr}; + + nsCString mOrigin; + nsCString mHeaderHost; + nsCString mHeaderScheme; + nsCString mHeaderPath; + + // Each stream goes from generating_headers to upstream_complete, perhaps + // looping on multiple instances of generating_body and + // sending_body for each frame in the upload. + enum upstreamStateType mUpstreamState { GENERATING_HEADERS }; + + // The HTTP/2 state for the stream from section 5.1 + enum stateType mState { IDLE }; + + // Flag is set when all http request headers have been read ID is not stable + uint32_t mRequestHeadersDone : 1; + + // Flag is set when ID is stable and concurrency limits are met + uint32_t mOpenGenerated : 1; + + // Flag is set when all http response headers have been read + uint32_t mAllHeadersReceived : 1; + + // Flag is set when stream is queued inside the session due to + // concurrency limits being exceeded + uint32_t mQueued : 1; + + void ChangeState(enum upstreamStateType); + + virtual void AdjustInitialWindow(); + [[nodiscard]] nsresult TransmitFrame(const char*, uint32_t*, + bool forceCommitment); + + // The underlying socket transport object is needed to propogate some events + nsCOMPtr<nsISocketTransport> mSocketTransport; + + uint8_t mPriorityWeight = 0; // h2 weight + uint32_t mPriorityDependency = 0; // h2 stream id this one depends on + uint64_t mCurrentTopBrowsingContextId; + uint64_t mTransactionTabId{0}; + + // The InlineFrame and associated data is used for composing control + // frames and data frame headers. + UniquePtr<uint8_t[]> mTxInlineFrame; + uint32_t mTxInlineFrameSize{0}; + uint32_t mTxInlineFrameUsed{0}; + + uint32_t mPriority = 0; // geckoish weight + + // Buffer for request header compression. + nsCString mFlatHttpRequestHeaders; + + // 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{0}; + + private: + friend class mozilla::DefaultDelete<Http2StreamBase>; + + [[nodiscard]] nsresult ParseHttpRequestHeaders(const char*, uint32_t, + uint32_t*); + [[nodiscard]] nsresult GenerateOpen(); + + virtual nsresult GenerateHeaders(nsCString& aCompressedData, + uint8_t& firstFrameFlags) = 0; + + void GenerateDataFrameHeader(uint32_t, bool); + + [[nodiscard]] nsresult BufferInput(uint32_t, uint32_t*); + + // 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 1st DATA frame has been passed to stream + uint32_t mReceivedData : 1; + + // Flag is set after RST_STREAM has been received for this stream + uint32_t mRecvdReset : 1; + + // Flag is set after RST_STREAM has been generated for this stream + uint32_t mSentReset : 1; + + // Flag is set when stream is counted towards MAX_CONCURRENT streams in + // session + uint32_t mCountAsActive : 1; + + // Flag is set when a FIN has been placed on a data or header frame + // (i.e after the client has closed) + uint32_t mSentFin : 1; + + // Flag is set after the WAITING_FOR Transport event has been generated + uint32_t mSentWaitingFor : 1; + + // Flag is set after TCP send autotuning has been disabled + uint32_t mSetTCPSocketBuffer : 1; + + // Flag is set when OnWriteSegment is being called directly from stream + // instead of transaction + uint32_t mBypassInputBuffer : 1; + + // 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{0}; + + // mClientReceiveWindow, mServerReceiveWindow, and mLocalUnacked are for flow + // control. *window are signed because the race conditions in asynchronous + // SETTINGS messages can force them temporarily negative. + + // mClientReceiveWindow is how much data the server will send without getting + // a + // window update + int64_t mClientReceiveWindow; + + // mServerReceiveWindow is how much data the client is allowed to send without + // getting a window update + int64_t mServerReceiveWindow; + + // LocalUnacked is the number of bytes received by the client but not + // yet reflected in a window update. Sending that update will increment + // ClientReceiveWindow + uint64_t mLocalUnacked{0}; + + // True when sending is suspended becuase the server receive window is + // <= 0 + bool mBlockedOnRwin{false}; + + // For Progress Events + uint64_t mTotalSent{0}; + uint64_t mTotalRead{0}; + + // Used to store stream data when the transaction channel cannot keep up + // and flow control has not yet kicked in. + SimpleBuffer mSimpleBuffer; + + bool mAttempting0RTT{false}; +}; + +} // namespace mozilla::net + +#endif // mozilla_net_Http2StreamBase_h |