/* -*- 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_Http2Stream_h #define mozilla_net_Http2Stream_h // HTTP/2 - RFC7540 // https://www.rfc-editor.org/rfc/rfc7540.txt #include "mozilla/Attributes.h" #include "mozilla/UniquePtr.h" #include "nsAHttpTransaction.h" #include "nsISupportsPriority.h" #include "SimpleBuffer.h" class nsIInputStream; class nsIOutputStream; namespace mozilla { namespace net { class nsStandardURL; class Http2Session; class Http2Decompressor; class Http2Stream : public nsAHttpSegmentReader , public nsAHttpSegmentWriter , public SupportsWeakPtr { public: MOZ_DECLARE_WEAKREFERENCE_TYPENAME(Http2Stream) NS_DECL_NSAHTTPSEGMENTREADER NS_DECL_NSAHTTPSEGMENTWRITER 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; Http2Stream(nsAHttpTransaction *, Http2Session *, int32_t); uint32_t StreamID() { return mStreamID; } Http2PushedStream *PushSource() { return mPushSource; } void ClearPushSource(); stateType HTTPState() { return mState; } void SetHTTPState(stateType val) { mState = val; } virtual nsresult ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *); virtual nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *); virtual bool DeferCleanup(nsresult status); // 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() { return static_cast(mRequestBlockedOnRead); } bool HasRegisteredID() { return mStreamID != 0; } nsAHttpTransaction *Transaction() { return mTransaction; } virtual nsIRequestContext *RequestContext() { return mTransaction ? mTransaction->RequestContext() : nullptr; } void Close(nsresult reason); 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 nsresult ConvertResponseHeaders(Http2Decompressor *, nsACString &, nsACString &, int32_t &); nsresult ConvertPushHeaders(Http2Decompressor *, nsACString &, 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; } void SetPriority(uint32_t); void SetPriorityDependency(uint32_t, uint8_t, bool); void UpdatePriorityDependency(); // 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; } virtual ~Http2Stream(); Http2Session *Session() { return mSession; } static nsresult MakeOriginURL(const nsACString &origin, RefPtr &url); static nsresult MakeOriginURL(const nsACString &scheme, const nsACString &origin, RefPtr &url); // Mirrors nsAHttpTransaction bool Do0RTT(); nsresult Finish0RTT(bool aRestart, bool aAlpnIgnored); protected: static void CreatePushHashKey(const nsCString &scheme, const nsCString &hostHeader, uint64_t serial, const nsCSubstring &pathInfo, nsCString &outOrigin, nsCString &outKey); // These internal states track request generation enum upstreamStateType { GENERATING_HEADERS, GENERATING_BODY, SENDING_BODY, SENDING_FIN_STREAM, UPSTREAM_COMPLETE }; uint32_t mStreamID; // The session that this stream is a subset of Http2Session *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. nsAHttpSegmentReader *mSegmentReader; nsAHttpSegmentWriter *mSegmentWriter; 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; // The HTTP/2 state for the stream from section 5.1 enum stateType mState; // 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(); nsresult TransmitFrame(const char *, uint32_t *, bool forceCommitment); private: friend class nsAutoPtr; nsresult ParseHttpRequestHeaders(const char *, uint32_t, uint32_t *); nsresult GenerateOpen(); void AdjustPushedPriority(); void GenerateDataFrameHeader(uint32_t, bool); nsresult BufferInput(uint32_t , uint32_t *); // The underlying HTTP transaction. This pointer is used as the key // in the Http2Session mStreamTransactionHash so it is important to // keep a reference to it as long as this stream is a member of that hash. // (i.e. don't change it or release it after it is set in the ctor). RefPtr mTransaction; // The underlying socket transport object is needed to propogate some events nsISocketTransport *mSocketTransport; // 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; // The InlineFrame and associated data is used for composing control // frames and data frame headers. UniquePtr mTxInlineFrame; uint32_t mTxInlineFrameSize; uint32_t mTxInlineFrameUsed; // 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; // 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; uint32_t mPriority; // geckoish weight uint32_t mPriorityDependency; // h2 stream id 3 - 0xb uint8_t mPriorityWeight; // h2 weight // 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; // True when sending is suspended becuase the server receive window is // <= 0 bool mBlockedOnRwin; // For Progress Events uint64_t mTotalSent; uint64_t mTotalRead; // For Http2Push Http2PushedStream *mPushSource; // Used to store stream data when the transaction channel cannot keep up // and flow control has not yet kicked in. SimpleBuffer mSimpleBuffer; bool mAttempting0RTT; /// connect tunnels public: bool IsTunnel() { return mIsTunnel; } private: void ClearTransactionsBlockedOnTunnel(); void MapStreamToPlainText(); void MapStreamToHttpConnection(); bool mIsTunnel; bool mPlainTextTunnel; }; } // namespace net } // namespace mozilla #endif // mozilla_net_Http2Stream_h