1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/streamconv/converters/nsHTTPCompressConv.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,518 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim:set ts=4 sw=4 sts=4 cindent et: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsHTTPCompressConv.h" 1.11 +#include "nsMemory.h" 1.12 +#include "plstr.h" 1.13 +#include "nsCOMPtr.h" 1.14 +#include "nsError.h" 1.15 +#include "nsStreamUtils.h" 1.16 +#include "nsStringStream.h" 1.17 +#include "nsComponentManagerUtils.h" 1.18 + 1.19 +// nsISupports implementation 1.20 +NS_IMPL_ISUPPORTS(nsHTTPCompressConv, 1.21 + nsIStreamConverter, 1.22 + nsIStreamListener, 1.23 + nsIRequestObserver) 1.24 + 1.25 +// nsFTPDirListingConv methods 1.26 +nsHTTPCompressConv::nsHTTPCompressConv() 1.27 + : mListener(nullptr) 1.28 + , mMode(HTTP_COMPRESS_IDENTITY) 1.29 + , mOutBuffer(nullptr) 1.30 + , mInpBuffer(nullptr) 1.31 + , mOutBufferLen(0) 1.32 + , mInpBufferLen(0) 1.33 + , mCheckHeaderDone(false) 1.34 + , mStreamEnded(false) 1.35 + , mStreamInitialized(false) 1.36 + , mLen(0) 1.37 + , hMode(0) 1.38 + , mSkipCount(0) 1.39 + , mFlags(0) 1.40 +{ 1.41 +} 1.42 + 1.43 +nsHTTPCompressConv::~nsHTTPCompressConv() 1.44 +{ 1.45 + NS_IF_RELEASE(mListener); 1.46 + 1.47 + if (mInpBuffer) 1.48 + nsMemory::Free(mInpBuffer); 1.49 + 1.50 + if (mOutBuffer) 1.51 + nsMemory::Free(mOutBuffer); 1.52 + 1.53 + // For some reason we are not getting Z_STREAM_END. But this was also seen 1.54 + // for mozilla bug 198133. Need to handle this case. 1.55 + if (mStreamInitialized && !mStreamEnded) 1.56 + inflateEnd (&d_stream); 1.57 +} 1.58 + 1.59 +NS_IMETHODIMP 1.60 +nsHTTPCompressConv::AsyncConvertData(const char *aFromType, 1.61 + const char *aToType, 1.62 + nsIStreamListener *aListener, 1.63 + nsISupports *aCtxt) 1.64 +{ 1.65 + if (!PL_strncasecmp(aFromType, HTTP_COMPRESS_TYPE, sizeof(HTTP_COMPRESS_TYPE)-1) || 1.66 + !PL_strncasecmp(aFromType, HTTP_X_COMPRESS_TYPE, sizeof(HTTP_X_COMPRESS_TYPE)-1)) 1.67 + mMode = HTTP_COMPRESS_COMPRESS; 1.68 + 1.69 + else if (!PL_strncasecmp(aFromType, HTTP_GZIP_TYPE, sizeof(HTTP_GZIP_TYPE)-1) || 1.70 + !PL_strncasecmp(aFromType, HTTP_X_GZIP_TYPE, sizeof(HTTP_X_GZIP_TYPE)-1)) 1.71 + mMode = HTTP_COMPRESS_GZIP; 1.72 + 1.73 + else if (!PL_strncasecmp(aFromType, HTTP_DEFLATE_TYPE, sizeof(HTTP_DEFLATE_TYPE)-1)) 1.74 + mMode = HTTP_COMPRESS_DEFLATE; 1.75 + 1.76 + // hook ourself up with the receiving listener. 1.77 + mListener = aListener; 1.78 + NS_ADDREF(mListener); 1.79 + 1.80 + mAsyncConvContext = aCtxt; 1.81 + return NS_OK; 1.82 +} 1.83 + 1.84 +NS_IMETHODIMP 1.85 +nsHTTPCompressConv::OnStartRequest(nsIRequest* request, nsISupports *aContext) 1.86 +{ 1.87 + return mListener->OnStartRequest(request, aContext); 1.88 +} 1.89 + 1.90 +NS_IMETHODIMP 1.91 +nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext, 1.92 + nsresult aStatus) 1.93 +{ 1.94 + return mListener->OnStopRequest(request, aContext, aStatus); 1.95 +} 1.96 + 1.97 +NS_IMETHODIMP 1.98 +nsHTTPCompressConv::OnDataAvailable(nsIRequest* request, 1.99 + nsISupports *aContext, 1.100 + nsIInputStream *iStr, 1.101 + uint64_t aSourceOffset, 1.102 + uint32_t aCount) 1.103 +{ 1.104 + nsresult rv = NS_ERROR_INVALID_CONTENT_ENCODING; 1.105 + uint32_t streamLen = aCount; 1.106 + 1.107 + if (streamLen == 0) 1.108 + { 1.109 + NS_ERROR("count of zero passed to OnDataAvailable"); 1.110 + return NS_ERROR_UNEXPECTED; 1.111 + } 1.112 + 1.113 + if (mStreamEnded) 1.114 + { 1.115 + // Hmm... this may just indicate that the data stream is done and that 1.116 + // what's left is either metadata or padding of some sort.... throwing 1.117 + // it out is probably the safe thing to do. 1.118 + uint32_t n; 1.119 + return iStr->ReadSegments(NS_DiscardSegment, nullptr, streamLen, &n); 1.120 + } 1.121 + 1.122 + switch (mMode) 1.123 + { 1.124 + case HTTP_COMPRESS_GZIP: 1.125 + streamLen = check_header(iStr, streamLen, &rv); 1.126 + 1.127 + if (rv != NS_OK) 1.128 + return rv; 1.129 + 1.130 + if (streamLen == 0) 1.131 + return NS_OK; 1.132 + 1.133 + // FALLTHROUGH 1.134 + 1.135 + case HTTP_COMPRESS_DEFLATE: 1.136 + 1.137 + if (mInpBuffer != nullptr && streamLen > mInpBufferLen) 1.138 + { 1.139 + mInpBuffer = (unsigned char *) moz_realloc(mInpBuffer, mInpBufferLen = streamLen); 1.140 + 1.141 + if (mOutBufferLen < streamLen * 2) 1.142 + mOutBuffer = (unsigned char *) moz_realloc(mOutBuffer, mOutBufferLen = streamLen * 3); 1.143 + 1.144 + if (mInpBuffer == nullptr || mOutBuffer == nullptr) 1.145 + return NS_ERROR_OUT_OF_MEMORY; 1.146 + } 1.147 + 1.148 + if (mInpBuffer == nullptr) 1.149 + mInpBuffer = (unsigned char *) moz_malloc(mInpBufferLen = streamLen); 1.150 + 1.151 + if (mOutBuffer == nullptr) 1.152 + mOutBuffer = (unsigned char *) moz_malloc(mOutBufferLen = streamLen * 3); 1.153 + 1.154 + if (mInpBuffer == nullptr || mOutBuffer == nullptr) 1.155 + return NS_ERROR_OUT_OF_MEMORY; 1.156 + 1.157 + uint32_t unused; 1.158 + iStr->Read((char *)mInpBuffer, streamLen, &unused); 1.159 + 1.160 + if (mMode == HTTP_COMPRESS_DEFLATE) 1.161 + { 1.162 + if (!mStreamInitialized) 1.163 + { 1.164 + memset(&d_stream, 0, sizeof (d_stream)); 1.165 + 1.166 + if (inflateInit(&d_stream) != Z_OK) 1.167 + return NS_ERROR_FAILURE; 1.168 + 1.169 + mStreamInitialized = true; 1.170 + } 1.171 + d_stream.next_in = mInpBuffer; 1.172 + d_stream.avail_in = (uInt)streamLen; 1.173 + 1.174 + mDummyStreamInitialised = false; 1.175 + for (;;) 1.176 + { 1.177 + d_stream.next_out = mOutBuffer; 1.178 + d_stream.avail_out = (uInt)mOutBufferLen; 1.179 + 1.180 + int code = inflate(&d_stream, Z_NO_FLUSH); 1.181 + unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out; 1.182 + 1.183 + if (code == Z_STREAM_END) 1.184 + { 1.185 + if (bytesWritten) 1.186 + { 1.187 + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); 1.188 + if (NS_FAILED (rv)) 1.189 + return rv; 1.190 + } 1.191 + 1.192 + inflateEnd(&d_stream); 1.193 + mStreamEnded = true; 1.194 + break; 1.195 + } 1.196 + else if (code == Z_OK) 1.197 + { 1.198 + if (bytesWritten) 1.199 + { 1.200 + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); 1.201 + if (NS_FAILED (rv)) 1.202 + return rv; 1.203 + } 1.204 + } 1.205 + else if (code == Z_BUF_ERROR) 1.206 + { 1.207 + if (bytesWritten) 1.208 + { 1.209 + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); 1.210 + if (NS_FAILED (rv)) 1.211 + return rv; 1.212 + } 1.213 + break; 1.214 + } 1.215 + else if (code == Z_DATA_ERROR) 1.216 + { 1.217 + // some servers (notably Apache with mod_deflate) don't generate zlib headers 1.218 + // insert a dummy header and try again 1.219 + static char dummy_head[2] = 1.220 + { 1.221 + 0x8 + 0x7 * 0x10, 1.222 + (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, 1.223 + }; 1.224 + inflateReset(&d_stream); 1.225 + d_stream.next_in = (Bytef*) dummy_head; 1.226 + d_stream.avail_in = sizeof(dummy_head); 1.227 + 1.228 + code = inflate(&d_stream, Z_NO_FLUSH); 1.229 + if (code != Z_OK) 1.230 + return NS_ERROR_FAILURE; 1.231 + 1.232 + // stop an endless loop caused by non-deflate data being labelled as deflate 1.233 + if (mDummyStreamInitialised) { 1.234 + NS_WARNING("endless loop detected" 1.235 + " - invalid deflate"); 1.236 + return NS_ERROR_INVALID_CONTENT_ENCODING; 1.237 + } 1.238 + mDummyStreamInitialised = true; 1.239 + // reset stream pointers to our original data 1.240 + d_stream.next_in = mInpBuffer; 1.241 + d_stream.avail_in = (uInt)streamLen; 1.242 + } 1.243 + else 1.244 + return NS_ERROR_INVALID_CONTENT_ENCODING; 1.245 + } /* for */ 1.246 + } 1.247 + else 1.248 + { 1.249 + if (!mStreamInitialized) 1.250 + { 1.251 + memset(&d_stream, 0, sizeof (d_stream)); 1.252 + 1.253 + if (inflateInit2(&d_stream, -MAX_WBITS) != Z_OK) 1.254 + return NS_ERROR_FAILURE; 1.255 + 1.256 + mStreamInitialized = true; 1.257 + } 1.258 + 1.259 + d_stream.next_in = mInpBuffer; 1.260 + d_stream.avail_in = (uInt)streamLen; 1.261 + 1.262 + for (;;) 1.263 + { 1.264 + d_stream.next_out = mOutBuffer; 1.265 + d_stream.avail_out = (uInt)mOutBufferLen; 1.266 + 1.267 + int code = inflate (&d_stream, Z_NO_FLUSH); 1.268 + unsigned bytesWritten = (uInt)mOutBufferLen - d_stream.avail_out; 1.269 + 1.270 + if (code == Z_STREAM_END) 1.271 + { 1.272 + if (bytesWritten) 1.273 + { 1.274 + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); 1.275 + if (NS_FAILED (rv)) 1.276 + return rv; 1.277 + } 1.278 + 1.279 + inflateEnd(&d_stream); 1.280 + mStreamEnded = true; 1.281 + break; 1.282 + } 1.283 + else if (code == Z_OK) 1.284 + { 1.285 + if (bytesWritten) 1.286 + { 1.287 + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); 1.288 + if (NS_FAILED (rv)) 1.289 + return rv; 1.290 + } 1.291 + } 1.292 + else if (code == Z_BUF_ERROR) 1.293 + { 1.294 + if (bytesWritten) 1.295 + { 1.296 + rv = do_OnDataAvailable(request, aContext, aSourceOffset, (char *)mOutBuffer, bytesWritten); 1.297 + if (NS_FAILED (rv)) 1.298 + return rv; 1.299 + } 1.300 + break; 1.301 + } 1.302 + else 1.303 + return NS_ERROR_INVALID_CONTENT_ENCODING; 1.304 + } /* for */ 1.305 + } /* gzip */ 1.306 + break; 1.307 + 1.308 + default: 1.309 + rv = mListener->OnDataAvailable(request, aContext, iStr, aSourceOffset, aCount); 1.310 + if (NS_FAILED (rv)) 1.311 + return rv; 1.312 + } /* switch */ 1.313 + 1.314 + return NS_OK; 1.315 +} /* OnDataAvailable */ 1.316 + 1.317 + 1.318 +// XXX/ruslan: need to implement this too 1.319 + 1.320 +NS_IMETHODIMP 1.321 +nsHTTPCompressConv::Convert(nsIInputStream *aFromStream, 1.322 + const char *aFromType, 1.323 + const char *aToType, 1.324 + nsISupports *aCtxt, 1.325 + nsIInputStream **_retval) 1.326 +{ 1.327 + return NS_ERROR_NOT_IMPLEMENTED; 1.328 +} 1.329 + 1.330 +nsresult 1.331 +nsHTTPCompressConv::do_OnDataAvailable(nsIRequest* request, 1.332 + nsISupports *context, uint64_t offset, 1.333 + const char *buffer, uint32_t count) 1.334 +{ 1.335 + if (!mStream) { 1.336 + mStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID); 1.337 + NS_ENSURE_STATE(mStream); 1.338 + } 1.339 + 1.340 + mStream->ShareData(buffer, count); 1.341 + 1.342 + nsresult rv = mListener->OnDataAvailable(request, context, mStream, 1.343 + offset, count); 1.344 + 1.345 + // Make sure the stream no longer references |buffer| in case our listener 1.346 + // is crazy enough to try to read from |mStream| after ODA. 1.347 + mStream->ShareData("", 0); 1.348 + 1.349 + return rv; 1.350 +} 1.351 + 1.352 +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ 1.353 +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ 1.354 +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ 1.355 +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ 1.356 +#define COMMENT 0x10 /* bit 4 set: file comment present */ 1.357 +#define RESERVED 0xE0 /* bits 5..7: reserved */ 1.358 + 1.359 +static unsigned gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ 1.360 + 1.361 +uint32_t 1.362 +nsHTTPCompressConv::check_header(nsIInputStream *iStr, uint32_t streamLen, nsresult *rs) 1.363 +{ 1.364 + enum { GZIP_INIT = 0, GZIP_OS, GZIP_EXTRA0, GZIP_EXTRA1, GZIP_EXTRA2, GZIP_ORIG, GZIP_COMMENT, GZIP_CRC }; 1.365 + char c; 1.366 + 1.367 + *rs = NS_OK; 1.368 + 1.369 + if (mCheckHeaderDone) 1.370 + return streamLen; 1.371 + 1.372 + while (streamLen) 1.373 + { 1.374 + switch (hMode) 1.375 + { 1.376 + case GZIP_INIT: 1.377 + uint32_t unused; 1.378 + iStr->Read(&c, 1, &unused); 1.379 + streamLen--; 1.380 + 1.381 + if (mSkipCount == 0 && ((unsigned)c & 0377) != gz_magic[0]) 1.382 + { 1.383 + *rs = NS_ERROR_INVALID_CONTENT_ENCODING; 1.384 + return 0; 1.385 + } 1.386 + 1.387 + if (mSkipCount == 1 && ((unsigned)c & 0377) != gz_magic[1]) 1.388 + { 1.389 + *rs = NS_ERROR_INVALID_CONTENT_ENCODING; 1.390 + return 0; 1.391 + } 1.392 + 1.393 + if (mSkipCount == 2 && ((unsigned)c & 0377) != Z_DEFLATED) 1.394 + { 1.395 + *rs = NS_ERROR_INVALID_CONTENT_ENCODING; 1.396 + return 0; 1.397 + } 1.398 + 1.399 + mSkipCount++; 1.400 + if (mSkipCount == 4) 1.401 + { 1.402 + mFlags = (unsigned) c & 0377; 1.403 + if (mFlags & RESERVED) 1.404 + { 1.405 + *rs = NS_ERROR_INVALID_CONTENT_ENCODING; 1.406 + return 0; 1.407 + } 1.408 + hMode = GZIP_OS; 1.409 + mSkipCount = 0; 1.410 + } 1.411 + break; 1.412 + 1.413 + case GZIP_OS: 1.414 + iStr->Read(&c, 1, &unused); 1.415 + streamLen--; 1.416 + mSkipCount++; 1.417 + 1.418 + if (mSkipCount == 6) 1.419 + hMode = GZIP_EXTRA0; 1.420 + break; 1.421 + 1.422 + case GZIP_EXTRA0: 1.423 + if (mFlags & EXTRA_FIELD) 1.424 + { 1.425 + iStr->Read(&c, 1, &unused); 1.426 + streamLen--; 1.427 + mLen = (uInt) c & 0377; 1.428 + hMode = GZIP_EXTRA1; 1.429 + } 1.430 + else 1.431 + hMode = GZIP_ORIG; 1.432 + break; 1.433 + 1.434 + case GZIP_EXTRA1: 1.435 + iStr->Read(&c, 1, &unused); 1.436 + streamLen--; 1.437 + mLen = ((uInt) c & 0377) << 8; 1.438 + mSkipCount = 0; 1.439 + hMode = GZIP_EXTRA2; 1.440 + break; 1.441 + 1.442 + case GZIP_EXTRA2: 1.443 + if (mSkipCount == mLen) 1.444 + hMode = GZIP_ORIG; 1.445 + else 1.446 + { 1.447 + iStr->Read(&c, 1, &unused); 1.448 + streamLen--; 1.449 + mSkipCount++; 1.450 + } 1.451 + break; 1.452 + 1.453 + case GZIP_ORIG: 1.454 + if (mFlags & ORIG_NAME) 1.455 + { 1.456 + iStr->Read(&c, 1, &unused); 1.457 + streamLen--; 1.458 + if (c == 0) 1.459 + hMode = GZIP_COMMENT; 1.460 + } 1.461 + else 1.462 + hMode = GZIP_COMMENT; 1.463 + break; 1.464 + 1.465 + case GZIP_COMMENT: 1.466 + if (mFlags & COMMENT) 1.467 + { 1.468 + iStr->Read(&c, 1, &unused); 1.469 + streamLen--; 1.470 + if (c == 0) 1.471 + { 1.472 + hMode = GZIP_CRC; 1.473 + mSkipCount = 0; 1.474 + } 1.475 + } 1.476 + else 1.477 + { 1.478 + hMode = GZIP_CRC; 1.479 + mSkipCount = 0; 1.480 + } 1.481 + break; 1.482 + 1.483 + case GZIP_CRC: 1.484 + if (mFlags & HEAD_CRC) 1.485 + { 1.486 + iStr->Read(&c, 1, &unused); 1.487 + streamLen--; 1.488 + mSkipCount++; 1.489 + if (mSkipCount == 2) 1.490 + { 1.491 + mCheckHeaderDone = true; 1.492 + return streamLen; 1.493 + } 1.494 + } 1.495 + else 1.496 + { 1.497 + mCheckHeaderDone = true; 1.498 + return streamLen; 1.499 + } 1.500 + break; 1.501 + } 1.502 + } 1.503 + return streamLen; 1.504 +} 1.505 + 1.506 +nsresult 1.507 +NS_NewHTTPCompressConv(nsHTTPCompressConv **aHTTPCompressConv) 1.508 +{ 1.509 + NS_PRECONDITION(aHTTPCompressConv != nullptr, "null ptr"); 1.510 + 1.511 + if (!aHTTPCompressConv) 1.512 + return NS_ERROR_NULL_POINTER; 1.513 + 1.514 + *aHTTPCompressConv = new nsHTTPCompressConv(); 1.515 + 1.516 + if (!*aHTTPCompressConv) 1.517 + return NS_ERROR_OUT_OF_MEMORY; 1.518 + 1.519 + NS_ADDREF(*aHTTPCompressConv); 1.520 + return NS_OK; 1.521 +}