tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsMultiMixedConv.h (9313B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 #ifndef __nsmultimixedconv__h__
      6 #define __nsmultimixedconv__h__
      7 
      8 #include "nsIStreamConverter.h"
      9 #include "nsIChannel.h"
     10 #include "nsString.h"
     11 #include "nsCOMPtr.h"
     12 #include "nsIByteRangeRequest.h"
     13 #include "nsIMultiPartChannel.h"
     14 #include "mozilla/IncrementalTokenizer.h"
     15 #include "nsHttpResponseHead.h"
     16 #include "mozilla/UniquePtr.h"
     17 
     18 #define NS_MULTIMIXEDCONVERTER_CID            \
     19  {/* 7584CE90-5B25-11d3-A175-0050041CAF44 */ \
     20   0x7584ce90,                                \
     21   0x5b25,                                    \
     22   0x11d3,                                    \
     23   {0xa1, 0x75, 0x0, 0x50, 0x4, 0x1c, 0xaf, 0x44}}
     24 
     25 //
     26 // nsPartChannel is a "dummy" channel which represents an individual part of
     27 // a multipart/mixed stream...
     28 //
     29 // Instances on this channel are passed out to the consumer through the
     30 // nsIStreamListener interface.
     31 //
     32 class nsPartChannel final : public nsIChannel,
     33                            public nsIByteRangeRequest,
     34                            public nsIMultiPartChannel {
     35 public:
     36  nsPartChannel(nsIChannel* aMultipartChannel, uint32_t aPartID,
     37                bool aIsFirstPart, nsIStreamListener* aListener);
     38 
     39  void InitializeByteRange(int64_t aStart, int64_t aEnd);
     40  void SetIsLastPart() { mIsLastPart = true; }
     41  nsresult SendOnStartRequest(nsISupports* aContext);
     42  nsresult SendOnDataAvailable(nsISupports* aContext, nsIInputStream* aStream,
     43                               uint64_t aOffset, uint32_t aLen);
     44  nsresult SendOnStopRequest(nsISupports* aContext, nsresult aStatus);
     45  /* SetContentDisposition expects the full value of the Content-Disposition
     46   * header */
     47  void SetContentDisposition(const nsACString& aContentDispositionHeader);
     48  // TODO(ER): This appears to be dead code
     49  void SetResponseHead(mozilla::net::nsHttpResponseHead* head) {
     50    mResponseHead.reset(head);
     51  }
     52 
     53  NS_DECL_ISUPPORTS
     54  NS_DECL_NSIREQUEST
     55  NS_DECL_NSICHANNEL
     56  NS_DECL_NSIBYTERANGEREQUEST
     57  NS_DECL_NSIMULTIPARTCHANNEL
     58 
     59 protected:
     60  ~nsPartChannel() = default;
     61 
     62 protected:
     63  nsCOMPtr<nsIChannel> mMultipartChannel;
     64  nsCOMPtr<nsIStreamListener> mListener;
     65  mozilla::UniquePtr<mozilla::net::nsHttpResponseHead> mResponseHead;
     66 
     67  nsresult mStatus{NS_OK};
     68  nsLoadFlags mLoadFlags{0};
     69 
     70  nsCOMPtr<nsILoadGroup> mLoadGroup;
     71 
     72  nsCString mContentType;
     73  nsCString mContentCharset;
     74  uint32_t mContentDisposition{0};
     75  nsString mContentDispositionFilename;
     76  nsCString mContentDispositionHeader;
     77  uint64_t mContentLength{UINT64_MAX};
     78 
     79  bool mIsByteRangeRequest{false};
     80  int64_t mByteRangeStart{0};
     81  int64_t mByteRangeEnd{0};
     82 
     83  uint32_t mPartID;  // unique ID that can be used to identify
     84                     // this part of the multipart document
     85  bool mIsFirstPart;
     86  bool mIsLastPart{false};
     87 };
     88 
     89 // The nsMultiMixedConv stream converter converts a stream of type
     90 // "multipart/x-mixed-replace" to it's subparts. There was some debate as to
     91 // whether or not the functionality desired when HTTP confronted this type
     92 // required a stream converter. After all, this type really prompts various
     93 // viewer related actions rather than stream conversion. There simply needs to
     94 // be a piece in place that can strip out the multiple parts of a stream of this
     95 // type, and "display" them accordingly.
     96 //
     97 // With that said, this "stream converter" spends more time packaging up the sub
     98 // parts of the main stream and sending them off the destination stream
     99 // listener, than doing any real stream parsing/converting.
    100 //
    101 // WARNING: This converter requires that it's destination stream listener be
    102 //   able to handle multiple OnStartRequest(), OnDataAvailable(), and
    103 //   OnStopRequest() call combinations.  Each series represents the beginning,
    104 //   data production, and ending phase of each sub- part of the original
    105 //   stream.
    106 //
    107 // NOTE: this MIME-type is used by HTTP, *not* SMTP, or IMAP.
    108 //
    109 // NOTE: For reference, a general description of how this MIME type should be
    110 //   handled via HTTP, see
    111 //   http://home.netscape.com/assist/net_sites/pushpull.html . Note that real
    112 //   world server content deviates considerably from this overview.
    113 //
    114 // Implementation assumptions:
    115 //  Assumed structue:
    116 //  --BoundaryToken[\r]\n
    117 //  content-type: foo/bar[\r]\n
    118 //  ... (other headers if any)
    119 //  [\r]\n (second line feed to delimit end of headers)
    120 //  data
    121 //  --BoundaryToken-- (end delimited by final "--")
    122 //
    123 // linebreaks can be either CRLF or LFLF. linebreaks preceding
    124 // boundary tokens are NOT considered part of the data. BoundaryToken
    125 // is any opaque string.
    126 //
    127 //
    128 
    129 class nsMultiMixedConv : public nsIStreamConverter {
    130 public:
    131  NS_DECL_ISUPPORTS
    132  NS_DECL_NSISTREAMCONVERTER
    133  NS_DECL_NSISTREAMLISTENER
    134  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
    135  NS_DECL_NSIREQUESTOBSERVER
    136 
    137  explicit nsMultiMixedConv();
    138 
    139 protected:
    140  using Token = mozilla::IncrementalTokenizer::Token;
    141 
    142  virtual ~nsMultiMixedConv() = default;
    143 
    144  nsresult SendStart();
    145  void AccumulateData(Token const& aToken);
    146  nsresult SendData();
    147  nsresult SendStop(nsresult aStatus);
    148 
    149  // member data
    150  nsCOMPtr<nsIStreamListener> mFinalListener;  // this guy gets the converted
    151                                               // data via his OnDataAvailable()
    152 
    153  // The channel as we get it in OnStartRequest call
    154  nsCOMPtr<nsIChannel> mChannel;
    155  // the channel for the given part we're
    156  // processing. one channel per part.
    157  RefPtr<nsPartChannel> mPartChannel;
    158  nsCOMPtr<nsISupports> mContext;
    159  nsCString mContentType;
    160  nsCString mContentDisposition;
    161  nsCString mContentSecurityPolicy;
    162  nsCString mRootContentSecurityPolicy;
    163  nsCString mRootContentDisposition;
    164  uint64_t mContentLength{UINT64_MAX};
    165  uint64_t mTotalSent{0};
    166 
    167  // The following members are for tracking the byte ranges in
    168  // multipart/mixed content which specified the 'Content-Range:'
    169  // header...
    170  int64_t mByteRangeStart{0};
    171  int64_t mByteRangeEnd{0};
    172  bool mIsByteRangeRequest{false};
    173  // This flag is set first time we create a part channel.
    174  // We use it to prevent duplicated OnStopRequest call on the listener
    175  // when we fail from some reason to ever create a part channel that
    176  // ensures correct notifications.
    177  bool mRequestListenerNotified{false};
    178 
    179  uint32_t mCurrentPartID{0};
    180 
    181  // Flag preventing reenter of OnDataAvailable in case the target listener
    182  // ends up spinning the event loop.
    183  bool mInOnDataAvailable{false};
    184 
    185  // Current state of the incremental parser
    186  enum EParserState {
    187    PREAMBLE,
    188    BOUNDARY_CRLF,
    189    HEADER_NAME,
    190    HEADER_SEP,
    191    HEADER_VALUE,
    192    BODY_INIT,
    193    BODY,
    194    TRAIL_DASH1,
    195    TRAIL_DASH2,
    196    EPILOGUE,
    197 
    198    INIT = PREAMBLE
    199  } mParserState{INIT};
    200 
    201  // Response part header value, valid when we find a header name
    202  // we recognize.
    203  enum EHeader : uint32_t {
    204    HEADER_FIRST,
    205    HEADER_CONTENT_TYPE = HEADER_FIRST,
    206    HEADER_CONTENT_LENGTH,
    207    HEADER_CONTENT_DISPOSITION,
    208    HEADER_SET_COOKIE,
    209    HEADER_CONTENT_RANGE,
    210    HEADER_RANGE,
    211    HEADER_CONTENT_SECURITY_POLICY,
    212    HEADER_UNKNOWN
    213  } mResponseHeader{HEADER_UNKNOWN};
    214  // Cumulated value of a response header.
    215  nsCString mResponseHeaderValue;
    216 
    217  nsCString mBoundary;
    218  mozilla::IncrementalTokenizer mTokenizer;
    219 
    220  // When in the "body parsing" mode, see below, we cumulate raw data
    221  // incrementally to mainly avoid any unnecessary granularity.
    222  // mRawData points to the first byte in the tokenizer buffer where part
    223  // body data begins or continues.  mRawDataLength is a cumulated length
    224  // of that data during a single tokenizer input feed.  This is always
    225  // flushed right after we fed the tokenizer.
    226  nsACString::const_char_iterator mRawData{nullptr};
    227  nsACString::size_type mRawDataLength{0};
    228 
    229  // At the start we don't know if the server will be sending boundary with
    230  // or without the leading dashes.
    231  Token mBoundaryToken;
    232  Token mBoundaryTokenWithDashes;
    233  // We need these custom tokens to allow finding CRLF when in the binary mode.
    234  // CRLF before boundary is considered part of the boundary and not part of
    235  // the data.
    236  Token mLFToken;
    237  Token mCRLFToken;
    238  // Custom tokens for each of the response headers we recognize.
    239  Token mHeaderTokens[HEADER_UNKNOWN];
    240 
    241  // Resets values driven by part headers, like content type, to their defaults,
    242  // called at the start of every part processing.
    243  void HeadersToDefault();
    244  // Processes captured value of mResponseHeader header.
    245  nsresult ProcessHeader();
    246  // Switches the parser and tokenizer state to "binary mode" which only
    247  // searches for the 'CRLF boundary' delimiter.
    248  void SwitchToBodyParsing();
    249  // Switches to the default mode, we are in this mode when parsing headers and
    250  // control data around the boundary delimiters.
    251  void SwitchToControlParsing();
    252  // Turns on or off recognition of the headers we recognize in part heads.
    253  void SetHeaderTokensEnabled(bool aEnable);
    254 
    255  // The main parser callback called by the IncrementalTokenizer
    256  // instance from OnDataAvailable or OnStopRequest.
    257  nsresult ConsumeToken(Token const& token);
    258 };
    259 
    260 #endif /* __nsmultimixedconv__h__ */