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__ */