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