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