nsHTTPCompressConv.cpp 21 KB

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