/src/mozilla-central/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set sw=2 ts=8 et tw=80 : */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsHTTPCompressConv.h" |
8 | | #include "nsMemory.h" |
9 | | #include "plstr.h" |
10 | | #include "nsCOMPtr.h" |
11 | | #include "nsError.h" |
12 | | #include "nsStreamUtils.h" |
13 | | #include "nsStringStream.h" |
14 | | #include "nsComponentManagerUtils.h" |
15 | | #include "nsThreadUtils.h" |
16 | | #include "mozilla/Preferences.h" |
17 | | #include "mozilla/Logging.h" |
18 | | #include "nsIForcePendingChannel.h" |
19 | | #include "nsIRequest.h" |
20 | | |
21 | | // brotli headers |
22 | | #include "state.h" |
23 | | #include "brotli/decode.h" |
24 | | |
25 | | namespace mozilla { |
26 | | namespace net { |
27 | | |
28 | | extern LazyLogModule gHttpLog; |
29 | 0 | #define LOG(args) MOZ_LOG(mozilla::net::gHttpLog, mozilla::LogLevel::Debug, args) |
30 | | |
31 | | // nsISupports implementation |
32 | | NS_IMPL_ISUPPORTS(nsHTTPCompressConv, |
33 | | nsIStreamConverter, |
34 | | nsIStreamListener, |
35 | | nsIRequestObserver, |
36 | | nsICompressConvStats, |
37 | | nsIThreadRetargetableStreamListener) |
38 | | |
39 | | // nsFTPDirListingConv methods |
40 | | nsHTTPCompressConv::nsHTTPCompressConv() |
41 | | : mMode(HTTP_COMPRESS_IDENTITY) |
42 | | , mOutBuffer(nullptr) |
43 | | , mInpBuffer(nullptr) |
44 | | , mOutBufferLen(0) |
45 | | , mInpBufferLen(0) |
46 | | , mCheckHeaderDone(false) |
47 | | , mStreamEnded(false) |
48 | | , mStreamInitialized(false) |
49 | | , mDummyStreamInitialised(false) |
50 | | , d_stream{} |
51 | | , mLen(0) |
52 | | , hMode(0) |
53 | | , mSkipCount(0) |
54 | | , mFlags(0) |
55 | | , mDecodedDataLength(0) |
56 | | , mMutex("nsHTTPCompressConv") |
57 | 0 | { |
58 | 0 | LOG(("nsHttpCompresssConv %p ctor\n", this)); |
59 | 0 | if (NS_IsMainThread()) { |
60 | 0 | mFailUncleanStops = |
61 | 0 | Preferences::GetBool("network.http.enforce-framing.http", false); |
62 | 0 | } else { |
63 | 0 | mFailUncleanStops = false; |
64 | 0 | } |
65 | 0 | } |
66 | | |
67 | | nsHTTPCompressConv::~nsHTTPCompressConv() |
68 | 0 | { |
69 | 0 | LOG(("nsHttpCompresssConv %p dtor\n", this)); |
70 | 0 | if (mInpBuffer) { |
71 | 0 | free(mInpBuffer); |
72 | 0 | } |
73 | 0 |
|
74 | 0 | if (mOutBuffer) { |
75 | 0 | free(mOutBuffer); |
76 | 0 | } |
77 | 0 |
|
78 | 0 | // For some reason we are not getting Z_STREAM_END. But this was also seen |
79 | 0 | // for mozilla bug 198133. Need to handle this case. |
80 | 0 | if (mStreamInitialized && !mStreamEnded) { |
81 | 0 | inflateEnd (&d_stream); |
82 | 0 | } |
83 | 0 | } |
84 | | |
85 | | NS_IMETHODIMP |
86 | | nsHTTPCompressConv::GetDecodedDataLength(uint64_t *aDecodedDataLength) |
87 | 0 | { |
88 | 0 | *aDecodedDataLength = mDecodedDataLength; |
89 | 0 | return NS_OK; |
90 | 0 | } |
91 | | |
92 | | NS_IMETHODIMP |
93 | | nsHTTPCompressConv::AsyncConvertData(const char *aFromType, |
94 | | const char *aToType, |
95 | | nsIStreamListener *aListener, |
96 | | nsISupports *aCtxt) |
97 | 0 | { |
98 | 0 | if (!PL_strncasecmp(aFromType, HTTP_COMPRESS_TYPE, sizeof(HTTP_COMPRESS_TYPE)-1) || |
99 | 0 | !PL_strncasecmp(aFromType, HTTP_X_COMPRESS_TYPE, sizeof(HTTP_X_COMPRESS_TYPE)-1)) { |
100 | 0 | mMode = HTTP_COMPRESS_COMPRESS; |
101 | 0 | } else if (!PL_strncasecmp(aFromType, HTTP_GZIP_TYPE, sizeof(HTTP_GZIP_TYPE)-1) || |
102 | 0 | !PL_strncasecmp(aFromType, HTTP_X_GZIP_TYPE, sizeof(HTTP_X_GZIP_TYPE)-1)) { |
103 | 0 | mMode = HTTP_COMPRESS_GZIP; |
104 | 0 | } else if (!PL_strncasecmp(aFromType, HTTP_DEFLATE_TYPE, sizeof(HTTP_DEFLATE_TYPE)-1)) { |
105 | 0 | mMode = HTTP_COMPRESS_DEFLATE; |
106 | 0 | } else if (!PL_strncasecmp(aFromType, HTTP_BROTLI_TYPE, sizeof(HTTP_BROTLI_TYPE)-1)) { |
107 | 0 | mMode = HTTP_COMPRESS_BROTLI; |
108 | 0 | } |
109 | 0 | LOG(("nsHttpCompresssConv %p AsyncConvertData %s %s mode %d\n", |
110 | 0 | this, aFromType, aToType, (CompressMode)mMode)); |
111 | 0 |
|
112 | 0 | MutexAutoLock lock(mMutex); |
113 | 0 | // hook ourself up with the receiving listener. |
114 | 0 | mListener = aListener; |
115 | 0 |
|
116 | 0 | return NS_OK; |
117 | 0 | } |
118 | | |
119 | | NS_IMETHODIMP |
120 | | nsHTTPCompressConv::OnStartRequest(nsIRequest* request, nsISupports *aContext) |
121 | 0 | { |
122 | 0 | LOG(("nsHttpCompresssConv %p onstart\n", this)); |
123 | 0 | nsCOMPtr<nsIStreamListener> listener; |
124 | 0 | { |
125 | 0 | MutexAutoLock lock(mMutex); |
126 | 0 | listener = mListener; |
127 | 0 | } |
128 | 0 | return listener->OnStartRequest(request, aContext); |
129 | 0 | } |
130 | | |
131 | | NS_IMETHODIMP |
132 | | nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext, |
133 | | nsresult aStatus) |
134 | 0 | { |
135 | 0 | nsresult status = aStatus; |
136 | 0 | LOG(("nsHttpCompresssConv %p onstop %" PRIx32 "\n", this, static_cast<uint32_t>(aStatus))); |
137 | 0 |
|
138 | 0 | // Framing integrity is enforced for content-encoding: gzip, but not for |
139 | 0 | // content-encoding: deflate. Note that gzip vs deflate is NOT determined |
140 | 0 | // by content sniffing but only via header. |
141 | 0 | if (!mStreamEnded && NS_SUCCEEDED(status) && |
142 | 0 | (mFailUncleanStops && (mMode == HTTP_COMPRESS_GZIP)) ) { |
143 | 0 | // This is not a clean end of gzip stream: the transfer is incomplete. |
144 | 0 | status = NS_ERROR_NET_PARTIAL_TRANSFER; |
145 | 0 | LOG(("nsHttpCompresssConv %p onstop partial gzip\n", this)); |
146 | 0 | } |
147 | 0 | if (NS_SUCCEEDED(status) && mMode == HTTP_COMPRESS_BROTLI) { |
148 | 0 | nsCOMPtr<nsIForcePendingChannel> fpChannel = do_QueryInterface(request); |
149 | 0 | bool isPending = false; |
150 | 0 | if (request) { |
151 | 0 | request->IsPending(&isPending); |
152 | 0 | } |
153 | 0 | if (fpChannel && !isPending) { |
154 | 0 | fpChannel->ForcePending(true); |
155 | 0 | } |
156 | 0 | if (mBrotli && (mBrotli->mTotalOut == 0) && !mBrotli->mBrotliStateIsStreamEnd) { |
157 | 0 | status = NS_ERROR_INVALID_CONTENT_ENCODING; |
158 | 0 | } |
159 | 0 | LOG(("nsHttpCompresssConv %p onstop brotlihandler rv %" PRIx32 "\n", |
160 | 0 | this, static_cast<uint32_t>(status))); |
161 | 0 | if (fpChannel && !isPending) { |
162 | 0 | fpChannel->ForcePending(false); |
163 | 0 | } |
164 | 0 | } |
165 | 0 |
|
166 | 0 | nsCOMPtr<nsIStreamListener> listener; |
167 | 0 | { |
168 | 0 | MutexAutoLock lock(mMutex); |
169 | 0 | listener = mListener; |
170 | 0 | } |
171 | 0 | return listener->OnStopRequest(request, aContext, status); |
172 | 0 | } |
173 | | |
174 | | |
175 | | /* static */ nsresult |
176 | | nsHTTPCompressConv::BrotliHandler(nsIInputStream *stream, void *closure, const char *dataIn, |
177 | | uint32_t, uint32_t aAvail, uint32_t *countRead) |
178 | 0 | { |
179 | 0 | MOZ_ASSERT(stream); |
180 | 0 | nsHTTPCompressConv *self = static_cast<nsHTTPCompressConv *>(closure); |
181 | 0 | *countRead = 0; |
182 | 0 |
|
183 | 0 | const size_t kOutSize = 128 * 1024; // just a chunk size, we call in a loop |
184 | 0 | uint8_t *outPtr; |
185 | 0 | size_t outSize; |
186 | 0 | size_t avail = aAvail; |
187 | 0 | BrotliDecoderResult res; |
188 | 0 |
|
189 | 0 | if (!self->mBrotli) { |
190 | 0 | *countRead = aAvail; |
191 | 0 | return NS_OK; |
192 | 0 | } |
193 | 0 | |
194 | 0 | auto outBuffer = MakeUniqueFallible<uint8_t[]>(kOutSize); |
195 | 0 | if (outBuffer == nullptr) { |
196 | 0 | self->mBrotli->mStatus = NS_ERROR_OUT_OF_MEMORY; |
197 | 0 | return self->mBrotli->mStatus; |
198 | 0 | } |
199 | 0 | |
200 | 0 | do { |
201 | 0 | outSize = kOutSize; |
202 | 0 | outPtr = outBuffer.get(); |
203 | 0 |
|
204 | 0 | // brotli api is documented in brotli/dec/decode.h and brotli/dec/decode.c |
205 | 0 | LOG(("nsHttpCompresssConv %p brotlihandler decompress %zu\n", self, avail)); |
206 | 0 | size_t totalOut = self->mBrotli->mTotalOut; |
207 | 0 | res = ::BrotliDecoderDecompressStream( |
208 | 0 | &self->mBrotli->mState, |
209 | 0 | &avail, reinterpret_cast<const unsigned char **>(&dataIn), |
210 | 0 | &outSize, &outPtr, &totalOut); |
211 | 0 | outSize = kOutSize - outSize; |
212 | 0 | self->mBrotli->mTotalOut = totalOut; |
213 | 0 | self->mBrotli->mBrotliStateIsStreamEnd = BrotliDecoderIsFinished(&self->mBrotli->mState); |
214 | 0 | LOG(("nsHttpCompresssConv %p brotlihandler decompress rv=%" PRIx32 " out=%zu\n", |
215 | 0 | self, static_cast<uint32_t>(res), outSize)); |
216 | 0 |
|
217 | 0 | if (res == BROTLI_DECODER_RESULT_ERROR) { |
218 | 0 | LOG(("nsHttpCompressConv %p marking invalid encoding", self)); |
219 | 0 | self->mBrotli->mStatus = NS_ERROR_INVALID_CONTENT_ENCODING; |
220 | 0 | return self->mBrotli->mStatus; |
221 | 0 | } |
222 | 0 |
|
223 | 0 | // in 'the current implementation' brotli must consume everything before |
224 | 0 | // asking for more input |
225 | 0 | if (res == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { |
226 | 0 | MOZ_ASSERT(!avail); |
227 | 0 | if (avail) { |
228 | 0 | LOG(("nsHttpCompressConv %p did not consume all input", self)); |
229 | 0 | self->mBrotli->mStatus = NS_ERROR_UNEXPECTED; |
230 | 0 | return self->mBrotli->mStatus; |
231 | 0 | } |
232 | 0 | } |
233 | 0 | if (outSize > 0) { |
234 | 0 | nsresult rv = self->do_OnDataAvailable(self->mBrotli->mRequest, |
235 | 0 | self->mBrotli->mContext, |
236 | 0 | self->mBrotli->mSourceOffset, |
237 | 0 | reinterpret_cast<const char *>(outBuffer.get()), |
238 | 0 | outSize); |
239 | 0 | LOG(("nsHttpCompressConv %p BrotliHandler ODA rv=%" PRIx32, self, static_cast<uint32_t>(rv))); |
240 | 0 | if (NS_FAILED(rv)) { |
241 | 0 | self->mBrotli->mStatus = rv; |
242 | 0 | return self->mBrotli->mStatus; |
243 | 0 | } |
244 | 0 | } |
245 | 0 | |
246 | 0 | if (res == BROTLI_DECODER_RESULT_SUCCESS || |
247 | 0 | res == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { |
248 | 0 | *countRead = aAvail; |
249 | 0 | return NS_OK; |
250 | 0 | } |
251 | 0 | MOZ_ASSERT (res == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT); |
252 | 0 | } while (res == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT); |
253 | 0 |
|
254 | 0 | self->mBrotli->mStatus = NS_ERROR_UNEXPECTED; |
255 | 0 | return self->mBrotli->mStatus; |
256 | 0 | } |
257 | | |
258 | | NS_IMETHODIMP |
259 | | nsHTTPCompressConv::OnDataAvailable(nsIRequest* request, |
260 | | nsISupports *aContext, |
261 | | nsIInputStream *iStr, |
262 | | uint64_t aSourceOffset, |
263 | | uint32_t aCount) |
264 | 0 | { |
265 | 0 | nsresult rv = NS_ERROR_INVALID_CONTENT_ENCODING; |
266 | 0 | uint32_t streamLen = aCount; |
267 | 0 | LOG(("nsHttpCompressConv %p OnDataAvailable %d", this, aCount)); |
268 | 0 |
|
269 | 0 | if (streamLen == 0) { |
270 | 0 | NS_ERROR("count of zero passed to OnDataAvailable"); |
271 | 0 | return NS_ERROR_UNEXPECTED; |
272 | 0 | } |
273 | 0 |
|
274 | 0 | if (mStreamEnded) { |
275 | 0 | // Hmm... this may just indicate that the data stream is done and that |
276 | 0 | // what's left is either metadata or padding of some sort.... throwing |
277 | 0 | // it out is probably the safe thing to do. |
278 | 0 | uint32_t n; |
279 | 0 | return iStr->ReadSegments(NS_DiscardSegment, nullptr, streamLen, &n); |
280 | 0 | } |
281 | 0 | |
282 | 0 | switch (mMode) { |
283 | 0 | case HTTP_COMPRESS_GZIP: |
284 | 0 | streamLen = check_header(iStr, streamLen, &rv); |
285 | 0 |
|
286 | 0 | if (rv != NS_OK) { |
287 | 0 | return rv; |
288 | 0 | } |
289 | 0 | |
290 | 0 | if (streamLen == 0) { |
291 | 0 | return NS_OK; |
292 | 0 | } |
293 | 0 | |
294 | 0 | MOZ_FALLTHROUGH; |
295 | 0 |
|
296 | 0 | case HTTP_COMPRESS_DEFLATE: |
297 | 0 |
|
298 | 0 | if (mInpBuffer != nullptr && streamLen > mInpBufferLen) { |
299 | 0 | unsigned char* originalInpBuffer = mInpBuffer; |
300 | 0 | if (!(mInpBuffer = (unsigned char *) realloc(originalInpBuffer, mInpBufferLen = streamLen))) { |
301 | 0 | free(originalInpBuffer); |
302 | 0 | } |
303 | 0 |
|
304 | 0 | if (mOutBufferLen < streamLen * 2) { |
305 | 0 | unsigned char* originalOutBuffer = mOutBuffer; |
306 | 0 | if (!(mOutBuffer = (unsigned char *) realloc(mOutBuffer, mOutBufferLen = streamLen * 3))) { |
307 | 0 | free(originalOutBuffer); |
308 | 0 | } |
309 | 0 | } |
310 | 0 |
|
311 | 0 | if (mInpBuffer == nullptr || mOutBuffer == nullptr) { |
312 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
313 | 0 | } |
314 | 0 | } |
315 | 0 | |
316 | 0 | if (mInpBuffer == nullptr) { |
317 | 0 | mInpBuffer = (unsigned char *) malloc(mInpBufferLen = streamLen); |
318 | 0 | } |
319 | 0 |
|
320 | 0 | if (mOutBuffer == nullptr) { |
321 | 0 | mOutBuffer = (unsigned char *) malloc(mOutBufferLen = streamLen * 3); |
322 | 0 | } |
323 | 0 |
|
324 | 0 | if (mInpBuffer == nullptr || mOutBuffer == nullptr) { |
325 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
326 | 0 | } |
327 | 0 | |
328 | 0 | uint32_t unused; |
329 | 0 | iStr->Read((char *)mInpBuffer, streamLen, &unused); |
330 | 0 |
|
331 | 0 | if (mMode == HTTP_COMPRESS_DEFLATE) { |
332 | 0 | if (!mStreamInitialized) { |
333 | 0 | memset(&d_stream, 0, sizeof (d_stream)); |
334 | 0 |
|
335 | 0 | if (inflateInit(&d_stream) != Z_OK) { |
336 | 0 | return NS_ERROR_FAILURE; |
337 | 0 | } |
338 | 0 | |
339 | 0 | mStreamInitialized = true; |
340 | 0 | } |
341 | 0 | d_stream.next_in = mInpBuffer; |
342 | 0 | d_stream.avail_in = (uInt)streamLen; |
343 | 0 |
|
344 | 0 | mDummyStreamInitialised = false; |
345 | 0 | for (;;) { |
346 | 0 | d_stream.next_out = mOutBuffer; |
347 | 0 | d_stream.avail_out = (uInt)mOutBufferLen; |
348 | 0 |
|
349 | 0 | int code = inflate(&d_stream, Z_NO_FLUSH); |
350 | 0 | unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out; |
351 | 0 |
|
352 | 0 | if (code == Z_STREAM_END) { |
353 | 0 | if (bytesWritten) { |
354 | 0 | rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); |
355 | 0 | if (NS_FAILED (rv)) { |
356 | 0 | return rv; |
357 | 0 | } |
358 | 0 | } |
359 | 0 | |
360 | 0 | inflateEnd(&d_stream); |
361 | 0 | mStreamEnded = true; |
362 | 0 | break; |
363 | 0 | } else if (code == Z_OK) { |
364 | 0 | if (bytesWritten) { |
365 | 0 | rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); |
366 | 0 | if (NS_FAILED (rv)) { |
367 | 0 | return rv; |
368 | 0 | } |
369 | 0 | } |
370 | 0 | } else if (code == Z_BUF_ERROR) { |
371 | 0 | if (bytesWritten) { |
372 | 0 | rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); |
373 | 0 | if (NS_FAILED (rv)) { |
374 | 0 | return rv; |
375 | 0 | } |
376 | 0 | } |
377 | 0 | break; |
378 | 0 | } else if (code == Z_DATA_ERROR) { |
379 | 0 | // some servers (notably Apache with mod_deflate) don't generate zlib headers |
380 | 0 | // insert a dummy header and try again |
381 | 0 | static char dummy_head[2] = |
382 | 0 | { |
383 | 0 | 0x8 + 0x7 * 0x10, |
384 | 0 | (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, |
385 | 0 | }; |
386 | 0 | inflateReset(&d_stream); |
387 | 0 | d_stream.next_in = (Bytef*) dummy_head; |
388 | 0 | d_stream.avail_in = sizeof(dummy_head); |
389 | 0 |
|
390 | 0 | code = inflate(&d_stream, Z_NO_FLUSH); |
391 | 0 | if (code != Z_OK) { |
392 | 0 | return NS_ERROR_FAILURE; |
393 | 0 | } |
394 | 0 | |
395 | 0 | // stop an endless loop caused by non-deflate data being labelled as deflate |
396 | 0 | if (mDummyStreamInitialised) { |
397 | 0 | NS_WARNING("endless loop detected" |
398 | 0 | " - invalid deflate"); |
399 | 0 | return NS_ERROR_INVALID_CONTENT_ENCODING; |
400 | 0 | } |
401 | 0 | mDummyStreamInitialised = true; |
402 | 0 | // reset stream pointers to our original data |
403 | 0 | d_stream.next_in = mInpBuffer; |
404 | 0 | d_stream.avail_in = (uInt)streamLen; |
405 | 0 | } else { |
406 | 0 | return NS_ERROR_INVALID_CONTENT_ENCODING; |
407 | 0 | } |
408 | 0 | } /* for */ |
409 | 0 | } else { |
410 | 0 | if (!mStreamInitialized) { |
411 | 0 | memset(&d_stream, 0, sizeof (d_stream)); |
412 | 0 |
|
413 | 0 | if (inflateInit2(&d_stream, -MAX_WBITS) != Z_OK) { |
414 | 0 | return NS_ERROR_FAILURE; |
415 | 0 | } |
416 | 0 | |
417 | 0 | mStreamInitialized = true; |
418 | 0 | } |
419 | 0 |
|
420 | 0 | d_stream.next_in = mInpBuffer; |
421 | 0 | d_stream.avail_in = (uInt)streamLen; |
422 | 0 |
|
423 | 0 | for (;;) { |
424 | 0 | d_stream.next_out = mOutBuffer; |
425 | 0 | d_stream.avail_out = (uInt)mOutBufferLen; |
426 | 0 |
|
427 | 0 | int code = inflate (&d_stream, Z_NO_FLUSH); |
428 | 0 | unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out; |
429 | 0 |
|
430 | 0 | if (code == Z_STREAM_END) { |
431 | 0 | if (bytesWritten) { |
432 | 0 | rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); |
433 | 0 | if (NS_FAILED (rv)) { |
434 | 0 | return rv; |
435 | 0 | } |
436 | 0 | } |
437 | 0 | |
438 | 0 | inflateEnd(&d_stream); |
439 | 0 | mStreamEnded = true; |
440 | 0 | break; |
441 | 0 | } else if (code == Z_OK) { |
442 | 0 | if (bytesWritten) { |
443 | 0 | rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); |
444 | 0 | if (NS_FAILED (rv)) { |
445 | 0 | return rv; |
446 | 0 | } |
447 | 0 | } |
448 | 0 | } else if (code == Z_BUF_ERROR) { |
449 | 0 | if (bytesWritten) { |
450 | 0 | rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); |
451 | 0 | if (NS_FAILED (rv)) { |
452 | 0 | return rv; |
453 | 0 | } |
454 | 0 | } |
455 | 0 | break; |
456 | 0 | } else { |
457 | 0 | return NS_ERROR_INVALID_CONTENT_ENCODING; |
458 | 0 | } |
459 | 0 | } /* for */ |
460 | 0 | } /* gzip */ |
461 | 0 | break; |
462 | 0 |
|
463 | 0 | case HTTP_COMPRESS_BROTLI: |
464 | 0 | { |
465 | 0 | if (!mBrotli) { |
466 | 0 | mBrotli = new BrotliWrapper(); |
467 | 0 | } |
468 | 0 |
|
469 | 0 | mBrotli->mRequest = request; |
470 | 0 | mBrotli->mContext = aContext; |
471 | 0 | mBrotli->mSourceOffset = aSourceOffset; |
472 | 0 |
|
473 | 0 | uint32_t countRead; |
474 | 0 | rv = iStr->ReadSegments(BrotliHandler, this, streamLen, &countRead); |
475 | 0 | if (NS_SUCCEEDED(rv)) { |
476 | 0 | rv = mBrotli->mStatus; |
477 | 0 | } |
478 | 0 | if (NS_FAILED(rv)) { |
479 | 0 | return rv; |
480 | 0 | } |
481 | 0 | } |
482 | 0 | break; |
483 | 0 |
|
484 | 0 | default: |
485 | 0 | nsCOMPtr<nsIStreamListener> listener; |
486 | 0 | { |
487 | 0 | MutexAutoLock lock(mMutex); |
488 | 0 | listener = mListener; |
489 | 0 | } |
490 | 0 | rv = listener->OnDataAvailable(request, aContext, iStr, aSourceOffset, aCount); |
491 | 0 | if (NS_FAILED (rv)) { |
492 | 0 | return rv; |
493 | 0 | } |
494 | 0 | } /* switch */ |
495 | 0 | |
496 | 0 | return NS_OK; |
497 | 0 | } /* OnDataAvailable */ |
498 | | |
499 | | // XXX/ruslan: need to implement this too |
500 | | |
501 | | NS_IMETHODIMP |
502 | | nsHTTPCompressConv::Convert(nsIInputStream *aFromStream, |
503 | | const char *aFromType, |
504 | | const char *aToType, |
505 | | nsISupports *aCtxt, |
506 | | nsIInputStream **_retval) |
507 | 0 | { |
508 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
509 | 0 | } |
510 | | |
511 | | nsresult |
512 | | nsHTTPCompressConv::do_OnDataAvailable(nsIRequest* request, |
513 | | nsISupports *context, uint64_t offset, |
514 | | const char *buffer, uint32_t count) |
515 | 0 | { |
516 | 0 | if (!mStream) { |
517 | 0 | mStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID); |
518 | 0 | NS_ENSURE_STATE(mStream); |
519 | 0 | } |
520 | 0 |
|
521 | 0 | mStream->ShareData(buffer, count); |
522 | 0 |
|
523 | 0 | nsCOMPtr<nsIStreamListener> listener; |
524 | 0 | { |
525 | 0 | MutexAutoLock lock(mMutex); |
526 | 0 | listener = mListener; |
527 | 0 | } |
528 | 0 | nsresult rv = listener->OnDataAvailable(request, context, mStream, |
529 | 0 | offset, count); |
530 | 0 |
|
531 | 0 | // Make sure the stream no longer references |buffer| in case our listener |
532 | 0 | // is crazy enough to try to read from |mStream| after ODA. |
533 | 0 | mStream->ShareData("", 0); |
534 | 0 | mDecodedDataLength += count; |
535 | 0 |
|
536 | 0 | return rv; |
537 | 0 | } |
538 | | |
539 | | #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ |
540 | 0 | #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ |
541 | 0 | #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ |
542 | 0 | #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ |
543 | 0 | #define COMMENT 0x10 /* bit 4 set: file comment present */ |
544 | 0 | #define RESERVED 0xE0 /* bits 5..7: reserved */ |
545 | | |
546 | | static unsigned gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ |
547 | | |
548 | | uint32_t |
549 | | nsHTTPCompressConv::check_header(nsIInputStream *iStr, uint32_t streamLen, nsresult *rs) |
550 | 0 | { |
551 | 0 | enum { GZIP_INIT = 0, GZIP_OS, GZIP_EXTRA0, GZIP_EXTRA1, GZIP_EXTRA2, GZIP_ORIG, GZIP_COMMENT, GZIP_CRC }; |
552 | 0 | char c; |
553 | 0 |
|
554 | 0 | *rs = NS_OK; |
555 | 0 |
|
556 | 0 | if (mCheckHeaderDone) { |
557 | 0 | return streamLen; |
558 | 0 | } |
559 | 0 | |
560 | 0 | while (streamLen) { |
561 | 0 | switch (hMode) { |
562 | 0 | case GZIP_INIT: |
563 | 0 | uint32_t unused; |
564 | 0 | iStr->Read(&c, 1, &unused); |
565 | 0 | streamLen--; |
566 | 0 |
|
567 | 0 | if (mSkipCount == 0 && ((unsigned)c & 0377) != gz_magic[0]) { |
568 | 0 | *rs = NS_ERROR_INVALID_CONTENT_ENCODING; |
569 | 0 | return 0; |
570 | 0 | } |
571 | 0 | |
572 | 0 | if (mSkipCount == 1 && ((unsigned)c & 0377) != gz_magic[1]) { |
573 | 0 | *rs = NS_ERROR_INVALID_CONTENT_ENCODING; |
574 | 0 | return 0; |
575 | 0 | } |
576 | 0 | |
577 | 0 | if (mSkipCount == 2 && ((unsigned)c & 0377) != Z_DEFLATED) { |
578 | 0 | *rs = NS_ERROR_INVALID_CONTENT_ENCODING; |
579 | 0 | return 0; |
580 | 0 | } |
581 | 0 | |
582 | 0 | mSkipCount++; |
583 | 0 | if (mSkipCount == 4) { |
584 | 0 | mFlags = (unsigned) c & 0377; |
585 | 0 | if (mFlags & RESERVED) { |
586 | 0 | *rs = NS_ERROR_INVALID_CONTENT_ENCODING; |
587 | 0 | return 0; |
588 | 0 | } |
589 | 0 | hMode = GZIP_OS; |
590 | 0 | mSkipCount = 0; |
591 | 0 | } |
592 | 0 | break; |
593 | 0 |
|
594 | 0 | case GZIP_OS: |
595 | 0 | iStr->Read(&c, 1, &unused); |
596 | 0 | streamLen--; |
597 | 0 | mSkipCount++; |
598 | 0 |
|
599 | 0 | if (mSkipCount == 6) { |
600 | 0 | hMode = GZIP_EXTRA0; |
601 | 0 | } |
602 | 0 | break; |
603 | 0 |
|
604 | 0 | case GZIP_EXTRA0: |
605 | 0 | if (mFlags & EXTRA_FIELD) { |
606 | 0 | iStr->Read(&c, 1, &unused); |
607 | 0 | streamLen--; |
608 | 0 | mLen = (uInt) c & 0377; |
609 | 0 | hMode = GZIP_EXTRA1; |
610 | 0 | } else { |
611 | 0 | hMode = GZIP_ORIG; |
612 | 0 | } |
613 | 0 | break; |
614 | 0 |
|
615 | 0 | case GZIP_EXTRA1: |
616 | 0 | iStr->Read(&c, 1, &unused); |
617 | 0 | streamLen--; |
618 | 0 | mLen |= ((uInt) c & 0377) << 8; |
619 | 0 | mSkipCount = 0; |
620 | 0 | hMode = GZIP_EXTRA2; |
621 | 0 | break; |
622 | 0 |
|
623 | 0 | case GZIP_EXTRA2: |
624 | 0 | if (mSkipCount == mLen) { |
625 | 0 | hMode = GZIP_ORIG; |
626 | 0 | } else { |
627 | 0 | iStr->Read(&c, 1, &unused); |
628 | 0 | streamLen--; |
629 | 0 | mSkipCount++; |
630 | 0 | } |
631 | 0 | break; |
632 | 0 |
|
633 | 0 | case GZIP_ORIG: |
634 | 0 | if (mFlags & ORIG_NAME) { |
635 | 0 | iStr->Read(&c, 1, &unused); |
636 | 0 | streamLen--; |
637 | 0 | if (c == 0) |
638 | 0 | hMode = GZIP_COMMENT; |
639 | 0 | } else { |
640 | 0 | hMode = GZIP_COMMENT; |
641 | 0 | } |
642 | 0 | break; |
643 | 0 |
|
644 | 0 | case GZIP_COMMENT: |
645 | 0 | if (mFlags & COMMENT) { |
646 | 0 | iStr->Read(&c, 1, &unused); |
647 | 0 | streamLen--; |
648 | 0 | if (c == 0) { |
649 | 0 | hMode = GZIP_CRC; |
650 | 0 | mSkipCount = 0; |
651 | 0 | } |
652 | 0 | } else { |
653 | 0 | hMode = GZIP_CRC; |
654 | 0 | mSkipCount = 0; |
655 | 0 | } |
656 | 0 | break; |
657 | 0 |
|
658 | 0 | case GZIP_CRC: |
659 | 0 | if (mFlags & HEAD_CRC) { |
660 | 0 | iStr->Read(&c, 1, &unused); |
661 | 0 | streamLen--; |
662 | 0 | mSkipCount++; |
663 | 0 | if (mSkipCount == 2) { |
664 | 0 | mCheckHeaderDone = true; |
665 | 0 | return streamLen; |
666 | 0 | } |
667 | 0 | } else { |
668 | 0 | mCheckHeaderDone = true; |
669 | 0 | return streamLen; |
670 | 0 | } |
671 | 0 | break; |
672 | 0 | } |
673 | 0 | } |
674 | 0 | return streamLen; |
675 | 0 | } |
676 | | |
677 | | NS_IMETHODIMP |
678 | | nsHTTPCompressConv::CheckListenerChain() |
679 | 0 | { |
680 | 0 | nsCOMPtr<nsIThreadRetargetableStreamListener> listener; |
681 | 0 | { |
682 | 0 | MutexAutoLock lock(mMutex); |
683 | 0 | listener = do_QueryInterface(mListener); |
684 | 0 | } |
685 | 0 |
|
686 | 0 | if (!listener) { |
687 | 0 | return NS_ERROR_NO_INTERFACE; |
688 | 0 | } |
689 | 0 | |
690 | 0 | return listener->CheckListenerChain(); |
691 | 0 | } |
692 | | |
693 | | } // namespace net |
694 | | } // namespace mozilla |
695 | | |
696 | | nsresult |
697 | | NS_NewHTTPCompressConv(mozilla::net::nsHTTPCompressConv **aHTTPCompressConv) |
698 | 0 | { |
699 | 0 | MOZ_ASSERT(aHTTPCompressConv != nullptr, "null ptr"); |
700 | 0 | if (!aHTTPCompressConv) { |
701 | 0 | return NS_ERROR_NULL_POINTER; |
702 | 0 | } |
703 | 0 | |
704 | 0 | RefPtr<mozilla::net::nsHTTPCompressConv> outVal = |
705 | 0 | new mozilla::net::nsHTTPCompressConv(); |
706 | 0 | if (!outVal) { |
707 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
708 | 0 | } |
709 | 0 | outVal.forget(aHTTPCompressConv); |
710 | 0 | return NS_OK; |
711 | 0 | } |