netwerk/streamconv/converters/nsHTTPCompressConv.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim:set ts=4 sw=4 sts=4 cindent et: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "nsHTTPCompressConv.h"
michael@0 8 #include "nsMemory.h"
michael@0 9 #include "plstr.h"
michael@0 10 #include "nsCOMPtr.h"
michael@0 11 #include "nsError.h"
michael@0 12 #include "nsStreamUtils.h"
michael@0 13 #include "nsStringStream.h"
michael@0 14 #include "nsComponentManagerUtils.h"
michael@0 15
michael@0 16 // nsISupports implementation
michael@0 17 NS_IMPL_ISUPPORTS(nsHTTPCompressConv,
michael@0 18 nsIStreamConverter,
michael@0 19 nsIStreamListener,
michael@0 20 nsIRequestObserver)
michael@0 21
michael@0 22 // nsFTPDirListingConv methods
michael@0 23 nsHTTPCompressConv::nsHTTPCompressConv()
michael@0 24 : mListener(nullptr)
michael@0 25 , mMode(HTTP_COMPRESS_IDENTITY)
michael@0 26 , mOutBuffer(nullptr)
michael@0 27 , mInpBuffer(nullptr)
michael@0 28 , mOutBufferLen(0)
michael@0 29 , mInpBufferLen(0)
michael@0 30 , mCheckHeaderDone(false)
michael@0 31 , mStreamEnded(false)
michael@0 32 , mStreamInitialized(false)
michael@0 33 , mLen(0)
michael@0 34 , hMode(0)
michael@0 35 , mSkipCount(0)
michael@0 36 , mFlags(0)
michael@0 37 {
michael@0 38 }
michael@0 39
michael@0 40 nsHTTPCompressConv::~nsHTTPCompressConv()
michael@0 41 {
michael@0 42 NS_IF_RELEASE(mListener);
michael@0 43
michael@0 44 if (mInpBuffer)
michael@0 45 nsMemory::Free(mInpBuffer);
michael@0 46
michael@0 47 if (mOutBuffer)
michael@0 48 nsMemory::Free(mOutBuffer);
michael@0 49
michael@0 50 // For some reason we are not getting Z_STREAM_END. But this was also seen
michael@0 51 // for mozilla bug 198133. Need to handle this case.
michael@0 52 if (mStreamInitialized && !mStreamEnded)
michael@0 53 inflateEnd (&d_stream);
michael@0 54 }
michael@0 55
michael@0 56 NS_IMETHODIMP
michael@0 57 nsHTTPCompressConv::AsyncConvertData(const char *aFromType,
michael@0 58 const char *aToType,
michael@0 59 nsIStreamListener *aListener,
michael@0 60 nsISupports *aCtxt)
michael@0 61 {
michael@0 62 if (!PL_strncasecmp(aFromType, HTTP_COMPRESS_TYPE, sizeof(HTTP_COMPRESS_TYPE)-1) ||
michael@0 63 !PL_strncasecmp(aFromType, HTTP_X_COMPRESS_TYPE, sizeof(HTTP_X_COMPRESS_TYPE)-1))
michael@0 64 mMode = HTTP_COMPRESS_COMPRESS;
michael@0 65
michael@0 66 else if (!PL_strncasecmp(aFromType, HTTP_GZIP_TYPE, sizeof(HTTP_GZIP_TYPE)-1) ||
michael@0 67 !PL_strncasecmp(aFromType, HTTP_X_GZIP_TYPE, sizeof(HTTP_X_GZIP_TYPE)-1))
michael@0 68 mMode = HTTP_COMPRESS_GZIP;
michael@0 69
michael@0 70 else if (!PL_strncasecmp(aFromType, HTTP_DEFLATE_TYPE, sizeof(HTTP_DEFLATE_TYPE)-1))
michael@0 71 mMode = HTTP_COMPRESS_DEFLATE;
michael@0 72
michael@0 73 // hook ourself up with the receiving listener.
michael@0 74 mListener = aListener;
michael@0 75 NS_ADDREF(mListener);
michael@0 76
michael@0 77 mAsyncConvContext = aCtxt;
michael@0 78 return NS_OK;
michael@0 79 }
michael@0 80
michael@0 81 NS_IMETHODIMP
michael@0 82 nsHTTPCompressConv::OnStartRequest(nsIRequest* request, nsISupports *aContext)
michael@0 83 {
michael@0 84 return mListener->OnStartRequest(request, aContext);
michael@0 85 }
michael@0 86
michael@0 87 NS_IMETHODIMP
michael@0 88 nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext,
michael@0 89 nsresult aStatus)
michael@0 90 {
michael@0 91 return mListener->OnStopRequest(request, aContext, aStatus);
michael@0 92 }
michael@0 93
michael@0 94 NS_IMETHODIMP
michael@0 95 nsHTTPCompressConv::OnDataAvailable(nsIRequest* request,
michael@0 96 nsISupports *aContext,
michael@0 97 nsIInputStream *iStr,
michael@0 98 uint64_t aSourceOffset,
michael@0 99 uint32_t aCount)
michael@0 100 {
michael@0 101 nsresult rv = NS_ERROR_INVALID_CONTENT_ENCODING;
michael@0 102 uint32_t streamLen = aCount;
michael@0 103
michael@0 104 if (streamLen == 0)
michael@0 105 {
michael@0 106 NS_ERROR("count of zero passed to OnDataAvailable");
michael@0 107 return NS_ERROR_UNEXPECTED;
michael@0 108 }
michael@0 109
michael@0 110 if (mStreamEnded)
michael@0 111 {
michael@0 112 // Hmm... this may just indicate that the data stream is done and that
michael@0 113 // what's left is either metadata or padding of some sort.... throwing
michael@0 114 // it out is probably the safe thing to do.
michael@0 115 uint32_t n;
michael@0 116 return iStr->ReadSegments(NS_DiscardSegment, nullptr, streamLen, &n);
michael@0 117 }
michael@0 118
michael@0 119 switch (mMode)
michael@0 120 {
michael@0 121 case HTTP_COMPRESS_GZIP:
michael@0 122 streamLen = check_header(iStr, streamLen, &rv);
michael@0 123
michael@0 124 if (rv != NS_OK)
michael@0 125 return rv;
michael@0 126
michael@0 127 if (streamLen == 0)
michael@0 128 return NS_OK;
michael@0 129
michael@0 130 // FALLTHROUGH
michael@0 131
michael@0 132 case HTTP_COMPRESS_DEFLATE:
michael@0 133
michael@0 134 if (mInpBuffer != nullptr && streamLen > mInpBufferLen)
michael@0 135 {
michael@0 136 mInpBuffer = (unsigned char *) moz_realloc(mInpBuffer, mInpBufferLen = streamLen);
michael@0 137
michael@0 138 if (mOutBufferLen < streamLen * 2)
michael@0 139 mOutBuffer = (unsigned char *) moz_realloc(mOutBuffer, mOutBufferLen = streamLen * 3);
michael@0 140
michael@0 141 if (mInpBuffer == nullptr || mOutBuffer == nullptr)
michael@0 142 return NS_ERROR_OUT_OF_MEMORY;
michael@0 143 }
michael@0 144
michael@0 145 if (mInpBuffer == nullptr)
michael@0 146 mInpBuffer = (unsigned char *) moz_malloc(mInpBufferLen = streamLen);
michael@0 147
michael@0 148 if (mOutBuffer == nullptr)
michael@0 149 mOutBuffer = (unsigned char *) moz_malloc(mOutBufferLen = streamLen * 3);
michael@0 150
michael@0 151 if (mInpBuffer == nullptr || mOutBuffer == nullptr)
michael@0 152 return NS_ERROR_OUT_OF_MEMORY;
michael@0 153
michael@0 154 uint32_t unused;
michael@0 155 iStr->Read((char *)mInpBuffer, streamLen, &unused);
michael@0 156
michael@0 157 if (mMode == HTTP_COMPRESS_DEFLATE)
michael@0 158 {
michael@0 159 if (!mStreamInitialized)
michael@0 160 {
michael@0 161 memset(&d_stream, 0, sizeof (d_stream));
michael@0 162
michael@0 163 if (inflateInit(&d_stream) != Z_OK)
michael@0 164 return NS_ERROR_FAILURE;
michael@0 165
michael@0 166 mStreamInitialized = true;
michael@0 167 }
michael@0 168 d_stream.next_in = mInpBuffer;
michael@0 169 d_stream.avail_in = (uInt)streamLen;
michael@0 170
michael@0 171 mDummyStreamInitialised = false;
michael@0 172 for (;;)
michael@0 173 {
michael@0 174 d_stream.next_out = mOutBuffer;
michael@0 175 d_stream.avail_out = (uInt)mOutBufferLen;
michael@0 176
michael@0 177 int code = inflate(&d_stream, Z_NO_FLUSH);
michael@0 178 unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out;
michael@0 179
michael@0 180 if (code == Z_STREAM_END)
michael@0 181 {
michael@0 182 if (bytesWritten)
michael@0 183 {
michael@0 184 rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
michael@0 185 if (NS_FAILED (rv))
michael@0 186 return rv;
michael@0 187 }
michael@0 188
michael@0 189 inflateEnd(&d_stream);
michael@0 190 mStreamEnded = true;
michael@0 191 break;
michael@0 192 }
michael@0 193 else if (code == Z_OK)
michael@0 194 {
michael@0 195 if (bytesWritten)
michael@0 196 {
michael@0 197 rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
michael@0 198 if (NS_FAILED (rv))
michael@0 199 return rv;
michael@0 200 }
michael@0 201 }
michael@0 202 else if (code == Z_BUF_ERROR)
michael@0 203 {
michael@0 204 if (bytesWritten)
michael@0 205 {
michael@0 206 rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
michael@0 207 if (NS_FAILED (rv))
michael@0 208 return rv;
michael@0 209 }
michael@0 210 break;
michael@0 211 }
michael@0 212 else if (code == Z_DATA_ERROR)
michael@0 213 {
michael@0 214 // some servers (notably Apache with mod_deflate) don't generate zlib headers
michael@0 215 // insert a dummy header and try again
michael@0 216 static char dummy_head[2] =
michael@0 217 {
michael@0 218 0x8 + 0x7 * 0x10,
michael@0 219 (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
michael@0 220 };
michael@0 221 inflateReset(&d_stream);
michael@0 222 d_stream.next_in = (Bytef*) dummy_head;
michael@0 223 d_stream.avail_in = sizeof(dummy_head);
michael@0 224
michael@0 225 code = inflate(&d_stream, Z_NO_FLUSH);
michael@0 226 if (code != Z_OK)
michael@0 227 return NS_ERROR_FAILURE;
michael@0 228
michael@0 229 // stop an endless loop caused by non-deflate data being labelled as deflate
michael@0 230 if (mDummyStreamInitialised) {
michael@0 231 NS_WARNING("endless loop detected"
michael@0 232 " - invalid deflate");
michael@0 233 return NS_ERROR_INVALID_CONTENT_ENCODING;
michael@0 234 }
michael@0 235 mDummyStreamInitialised = true;
michael@0 236 // reset stream pointers to our original data
michael@0 237 d_stream.next_in = mInpBuffer;
michael@0 238 d_stream.avail_in = (uInt)streamLen;
michael@0 239 }
michael@0 240 else
michael@0 241 return NS_ERROR_INVALID_CONTENT_ENCODING;
michael@0 242 } /* for */
michael@0 243 }
michael@0 244 else
michael@0 245 {
michael@0 246 if (!mStreamInitialized)
michael@0 247 {
michael@0 248 memset(&d_stream, 0, sizeof (d_stream));
michael@0 249
michael@0 250 if (inflateInit2(&d_stream, -MAX_WBITS) != Z_OK)
michael@0 251 return NS_ERROR_FAILURE;
michael@0 252
michael@0 253 mStreamInitialized = true;
michael@0 254 }
michael@0 255
michael@0 256 d_stream.next_in = mInpBuffer;
michael@0 257 d_stream.avail_in = (uInt)streamLen;
michael@0 258
michael@0 259 for (;;)
michael@0 260 {
michael@0 261 d_stream.next_out = mOutBuffer;
michael@0 262 d_stream.avail_out = (uInt)mOutBufferLen;
michael@0 263
michael@0 264 int code = inflate (&d_stream, Z_NO_FLUSH);
michael@0 265 unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out;
michael@0 266
michael@0 267 if (code == Z_STREAM_END)
michael@0 268 {
michael@0 269 if (bytesWritten)
michael@0 270 {
michael@0 271 rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
michael@0 272 if (NS_FAILED (rv))
michael@0 273 return rv;
michael@0 274 }
michael@0 275
michael@0 276 inflateEnd(&d_stream);
michael@0 277 mStreamEnded = true;
michael@0 278 break;
michael@0 279 }
michael@0 280 else if (code == Z_OK)
michael@0 281 {
michael@0 282 if (bytesWritten)
michael@0 283 {
michael@0 284 rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
michael@0 285 if (NS_FAILED (rv))
michael@0 286 return rv;
michael@0 287 }
michael@0 288 }
michael@0 289 else if (code == Z_BUF_ERROR)
michael@0 290 {
michael@0 291 if (bytesWritten)
michael@0 292 {
michael@0 293 rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten);
michael@0 294 if (NS_FAILED (rv))
michael@0 295 return rv;
michael@0 296 }
michael@0 297 break;
michael@0 298 }
michael@0 299 else
michael@0 300 return NS_ERROR_INVALID_CONTENT_ENCODING;
michael@0 301 } /* for */
michael@0 302 } /* gzip */
michael@0 303 break;
michael@0 304
michael@0 305 default:
michael@0 306 rv = mListener->OnDataAvailable(request, aContext, iStr, aSourceOffset, aCount);
michael@0 307 if (NS_FAILED (rv))
michael@0 308 return rv;
michael@0 309 } /* switch */
michael@0 310
michael@0 311 return NS_OK;
michael@0 312 } /* OnDataAvailable */
michael@0 313
michael@0 314
michael@0 315 // XXX/ruslan: need to implement this too
michael@0 316
michael@0 317 NS_IMETHODIMP
michael@0 318 nsHTTPCompressConv::Convert(nsIInputStream *aFromStream,
michael@0 319 const char *aFromType,
michael@0 320 const char *aToType,
michael@0 321 nsISupports *aCtxt,
michael@0 322 nsIInputStream **_retval)
michael@0 323 {
michael@0 324 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 325 }
michael@0 326
michael@0 327 nsresult
michael@0 328 nsHTTPCompressConv::do_OnDataAvailable(nsIRequest* request,
michael@0 329 nsISupports *context, uint64_t offset,
michael@0 330 const char *buffer, uint32_t count)
michael@0 331 {
michael@0 332 if (!mStream) {
michael@0 333 mStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID);
michael@0 334 NS_ENSURE_STATE(mStream);
michael@0 335 }
michael@0 336
michael@0 337 mStream->ShareData(buffer, count);
michael@0 338
michael@0 339 nsresult rv = mListener->OnDataAvailable(request, context, mStream,
michael@0 340 offset, count);
michael@0 341
michael@0 342 // Make sure the stream no longer references |buffer| in case our listener
michael@0 343 // is crazy enough to try to read from |mStream| after ODA.
michael@0 344 mStream->ShareData("", 0);
michael@0 345
michael@0 346 return rv;
michael@0 347 }
michael@0 348
michael@0 349 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
michael@0 350 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
michael@0 351 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
michael@0 352 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
michael@0 353 #define COMMENT 0x10 /* bit 4 set: file comment present */
michael@0 354 #define RESERVED 0xE0 /* bits 5..7: reserved */
michael@0 355
michael@0 356 static unsigned gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
michael@0 357
michael@0 358 uint32_t
michael@0 359 nsHTTPCompressConv::check_header(nsIInputStream *iStr, uint32_t streamLen, nsresult *rs)
michael@0 360 {
michael@0 361 enum { GZIP_INIT = 0, GZIP_OS, GZIP_EXTRA0, GZIP_EXTRA1, GZIP_EXTRA2, GZIP_ORIG, GZIP_COMMENT, GZIP_CRC };
michael@0 362 char c;
michael@0 363
michael@0 364 *rs = NS_OK;
michael@0 365
michael@0 366 if (mCheckHeaderDone)
michael@0 367 return streamLen;
michael@0 368
michael@0 369 while (streamLen)
michael@0 370 {
michael@0 371 switch (hMode)
michael@0 372 {
michael@0 373 case GZIP_INIT:
michael@0 374 uint32_t unused;
michael@0 375 iStr->Read(&c, 1, &unused);
michael@0 376 streamLen--;
michael@0 377
michael@0 378 if (mSkipCount == 0 && ((unsigned)c & 0377) != gz_magic[0])
michael@0 379 {
michael@0 380 *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
michael@0 381 return 0;
michael@0 382 }
michael@0 383
michael@0 384 if (mSkipCount == 1 && ((unsigned)c & 0377) != gz_magic[1])
michael@0 385 {
michael@0 386 *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
michael@0 387 return 0;
michael@0 388 }
michael@0 389
michael@0 390 if (mSkipCount == 2 && ((unsigned)c & 0377) != Z_DEFLATED)
michael@0 391 {
michael@0 392 *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
michael@0 393 return 0;
michael@0 394 }
michael@0 395
michael@0 396 mSkipCount++;
michael@0 397 if (mSkipCount == 4)
michael@0 398 {
michael@0 399 mFlags = (unsigned) c & 0377;
michael@0 400 if (mFlags & RESERVED)
michael@0 401 {
michael@0 402 *rs = NS_ERROR_INVALID_CONTENT_ENCODING;
michael@0 403 return 0;
michael@0 404 }
michael@0 405 hMode = GZIP_OS;
michael@0 406 mSkipCount = 0;
michael@0 407 }
michael@0 408 break;
michael@0 409
michael@0 410 case GZIP_OS:
michael@0 411 iStr->Read(&c, 1, &unused);
michael@0 412 streamLen--;
michael@0 413 mSkipCount++;
michael@0 414
michael@0 415 if (mSkipCount == 6)
michael@0 416 hMode = GZIP_EXTRA0;
michael@0 417 break;
michael@0 418
michael@0 419 case GZIP_EXTRA0:
michael@0 420 if (mFlags & EXTRA_FIELD)
michael@0 421 {
michael@0 422 iStr->Read(&c, 1, &unused);
michael@0 423 streamLen--;
michael@0 424 mLen = (uInt) c & 0377;
michael@0 425 hMode = GZIP_EXTRA1;
michael@0 426 }
michael@0 427 else
michael@0 428 hMode = GZIP_ORIG;
michael@0 429 break;
michael@0 430
michael@0 431 case GZIP_EXTRA1:
michael@0 432 iStr->Read(&c, 1, &unused);
michael@0 433 streamLen--;
michael@0 434 mLen = ((uInt) c & 0377) << 8;
michael@0 435 mSkipCount = 0;
michael@0 436 hMode = GZIP_EXTRA2;
michael@0 437 break;
michael@0 438
michael@0 439 case GZIP_EXTRA2:
michael@0 440 if (mSkipCount == mLen)
michael@0 441 hMode = GZIP_ORIG;
michael@0 442 else
michael@0 443 {
michael@0 444 iStr->Read(&c, 1, &unused);
michael@0 445 streamLen--;
michael@0 446 mSkipCount++;
michael@0 447 }
michael@0 448 break;
michael@0 449
michael@0 450 case GZIP_ORIG:
michael@0 451 if (mFlags & ORIG_NAME)
michael@0 452 {
michael@0 453 iStr->Read(&c, 1, &unused);
michael@0 454 streamLen--;
michael@0 455 if (c == 0)
michael@0 456 hMode = GZIP_COMMENT;
michael@0 457 }
michael@0 458 else
michael@0 459 hMode = GZIP_COMMENT;
michael@0 460 break;
michael@0 461
michael@0 462 case GZIP_COMMENT:
michael@0 463 if (mFlags & COMMENT)
michael@0 464 {
michael@0 465 iStr->Read(&c, 1, &unused);
michael@0 466 streamLen--;
michael@0 467 if (c == 0)
michael@0 468 {
michael@0 469 hMode = GZIP_CRC;
michael@0 470 mSkipCount = 0;
michael@0 471 }
michael@0 472 }
michael@0 473 else
michael@0 474 {
michael@0 475 hMode = GZIP_CRC;
michael@0 476 mSkipCount = 0;
michael@0 477 }
michael@0 478 break;
michael@0 479
michael@0 480 case GZIP_CRC:
michael@0 481 if (mFlags & HEAD_CRC)
michael@0 482 {
michael@0 483 iStr->Read(&c, 1, &unused);
michael@0 484 streamLen--;
michael@0 485 mSkipCount++;
michael@0 486 if (mSkipCount == 2)
michael@0 487 {
michael@0 488 mCheckHeaderDone = true;
michael@0 489 return streamLen;
michael@0 490 }
michael@0 491 }
michael@0 492 else
michael@0 493 {
michael@0 494 mCheckHeaderDone = true;
michael@0 495 return streamLen;
michael@0 496 }
michael@0 497 break;
michael@0 498 }
michael@0 499 }
michael@0 500 return streamLen;
michael@0 501 }
michael@0 502
michael@0 503 nsresult
michael@0 504 NS_NewHTTPCompressConv(nsHTTPCompressConv **aHTTPCompressConv)
michael@0 505 {
michael@0 506 NS_PRECONDITION(aHTTPCompressConv != nullptr, "null ptr");
michael@0 507
michael@0 508 if (!aHTTPCompressConv)
michael@0 509 return NS_ERROR_NULL_POINTER;
michael@0 510
michael@0 511 *aHTTPCompressConv = new nsHTTPCompressConv();
michael@0 512
michael@0 513 if (!*aHTTPCompressConv)
michael@0 514 return NS_ERROR_OUT_OF_MEMORY;
michael@0 515
michael@0 516 NS_ADDREF(*aHTTPCompressConv);
michael@0 517 return NS_OK;
michael@0 518 }

mercurial