1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set ts=4 sw=4 sts=4 cindent et: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * David Dick <ddick@cpan.org>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsHTTPCompressConv.h"
44 #include "nsIChannel.h"
46 #include "nsReadableUtils.h"
47 #include "nsNetError.h"
48 #include "nsStreamUtils.h"
49 #include "nsStringStream.h"
50 #include "nsComponentManagerUtils.h"
52 // nsISupports implementation
53 NS_IMPL_ISUPPORTS3(nsHTTPCompressConv
,
58 // nsFTPDirListingConv methods
59 nsHTTPCompressConv::nsHTTPCompressConv()
61 , mMode(HTTP_COMPRESS_IDENTITY
)
66 , mCheckHeaderDone(PR_FALSE
)
67 , mStreamEnded(PR_FALSE
)
68 , mStreamInitialized(PR_FALSE
)
76 nsHTTPCompressConv::~nsHTTPCompressConv()
78 NS_IF_RELEASE(mListener
);
81 nsMemory::Free(mInpBuffer
);
84 nsMemory::Free(mOutBuffer
);
86 // For some reason we are not getting Z_STREAM_END. But this was also seen
87 // for mozilla bug 198133. Need to handle this case.
88 if ((mStreamInitialized
== PR_TRUE
) && (mStreamEnded
== PR_FALSE
))
89 inflateEnd (&d_stream
);
93 nsHTTPCompressConv::AsyncConvertData(const char *aFromType
,
95 nsIStreamListener
*aListener
,
98 if (!PL_strncasecmp(aFromType
, HTTP_COMPRESS_TYPE
, sizeof(HTTP_COMPRESS_TYPE
)-1) ||
99 !PL_strncasecmp(aFromType
, HTTP_X_COMPRESS_TYPE
, sizeof(HTTP_X_COMPRESS_TYPE
)-1))
100 mMode
= HTTP_COMPRESS_COMPRESS
;
102 else if (!PL_strncasecmp(aFromType
, HTTP_GZIP_TYPE
, sizeof(HTTP_GZIP_TYPE
)-1) ||
103 !PL_strncasecmp(aFromType
, HTTP_X_GZIP_TYPE
, sizeof(HTTP_X_GZIP_TYPE
)-1))
104 mMode
= HTTP_COMPRESS_GZIP
;
106 else if (!PL_strncasecmp(aFromType
, HTTP_DEFLATE_TYPE
, sizeof(HTTP_DEFLATE_TYPE
)-1))
107 mMode
= HTTP_COMPRESS_DEFLATE
;
109 // hook ourself up with the receiving listener.
110 mListener
= aListener
;
111 NS_ADDREF(mListener
);
113 mAsyncConvContext
= aCtxt
;
118 nsHTTPCompressConv::OnStartRequest(nsIRequest
* request
, nsISupports
*aContext
)
120 return mListener
->OnStartRequest(request
, aContext
);
124 nsHTTPCompressConv::OnStopRequest(nsIRequest
* request
, nsISupports
*aContext
,
127 return mListener
->OnStopRequest(request
, aContext
, aStatus
);
131 nsHTTPCompressConv::OnDataAvailable(nsIRequest
* request
,
132 nsISupports
*aContext
,
133 nsIInputStream
*iStr
,
134 PRUint32 aSourceOffset
,
137 nsresult rv
= NS_ERROR_INVALID_CONTENT_ENCODING
;
138 PRUint32 streamLen
= aCount
;
142 NS_ERROR("count of zero passed to OnDataAvailable");
143 return NS_ERROR_UNEXPECTED
;
148 // Hmm... this may just indicate that the data stream is done and that
149 // what's left is either metadata or padding of some sort.... throwing
150 // it out is probably the safe thing to do.
152 return iStr
->ReadSegments(NS_DiscardSegment
, nsnull
, streamLen
, &n
);
157 case HTTP_COMPRESS_GZIP
:
158 streamLen
= check_header(iStr
, streamLen
, &rv
);
166 case HTTP_COMPRESS_DEFLATE
:
168 if (mInpBuffer
!= NULL
&& streamLen
> mInpBufferLen
)
170 mInpBuffer
= (unsigned char *) nsMemory::Realloc(mInpBuffer
, mInpBufferLen
= streamLen
);
172 if (mOutBufferLen
< streamLen
* 2)
173 mOutBuffer
= (unsigned char *) nsMemory::Realloc(mOutBuffer
, mOutBufferLen
= streamLen
* 3);
175 if (mInpBuffer
== NULL
|| mOutBuffer
== NULL
)
176 return NS_ERROR_OUT_OF_MEMORY
;
179 if (mInpBuffer
== NULL
)
180 mInpBuffer
= (unsigned char *) nsMemory::Alloc(mInpBufferLen
= streamLen
);
182 if (mOutBuffer
== NULL
)
183 mOutBuffer
= (unsigned char *) nsMemory::Alloc(mOutBufferLen
= streamLen
* 3);
185 if (mInpBuffer
== NULL
|| mOutBuffer
== NULL
)
186 return NS_ERROR_OUT_OF_MEMORY
;
188 iStr
->Read((char *)mInpBuffer
, streamLen
, &rv
);
193 if (mMode
== HTTP_COMPRESS_DEFLATE
)
195 if (!mStreamInitialized
)
197 memset(&d_stream
, 0, sizeof (d_stream
));
199 if (inflateInit(&d_stream
) != Z_OK
)
200 return NS_ERROR_FAILURE
;
202 mStreamInitialized
= PR_TRUE
;
204 d_stream
.next_in
= mInpBuffer
;
205 d_stream
.avail_in
= (uInt
)streamLen
;
207 mDummyStreamInitialised
= PR_FALSE
;
210 d_stream
.next_out
= mOutBuffer
;
211 d_stream
.avail_out
= (uInt
)mOutBufferLen
;
213 int code
= inflate(&d_stream
, Z_NO_FLUSH
);
214 unsigned bytesWritten
= (uInt
)mOutBufferLen
- d_stream
.avail_out
;
216 if (code
== Z_STREAM_END
)
220 rv
= do_OnDataAvailable(request
, aContext
, aSourceOffset
, (char *)mOutBuffer
, bytesWritten
);
225 inflateEnd(&d_stream
);
226 mStreamEnded
= PR_TRUE
;
229 else if (code
== Z_OK
)
233 rv
= do_OnDataAvailable(request
, aContext
, aSourceOffset
, (char *)mOutBuffer
, bytesWritten
);
238 else if (code
== Z_BUF_ERROR
)
242 rv
= do_OnDataAvailable(request
, aContext
, aSourceOffset
, (char *)mOutBuffer
, bytesWritten
);
248 else if (code
== Z_DATA_ERROR
)
250 // some servers (notably Apache with mod_deflate) don't generate zlib headers
251 // insert a dummy header and try again
252 static char dummy_head
[2] =
255 (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
257 inflateReset(&d_stream
);
258 d_stream
.next_in
= (Bytef
*) dummy_head
;
259 d_stream
.avail_in
= sizeof(dummy_head
);
261 code
= inflate(&d_stream
, Z_NO_FLUSH
);
263 return NS_ERROR_FAILURE
;
265 // stop an endless loop caused by non-deflate data being labelled as deflate
266 if (mDummyStreamInitialised
) {
267 NS_ERROR("endless loop detected");
268 return NS_ERROR_INVALID_CONTENT_ENCODING
;
270 mDummyStreamInitialised
= PR_TRUE
;
271 // reset stream pointers to our original data
272 d_stream
.next_in
= mInpBuffer
;
273 d_stream
.avail_in
= (uInt
)streamLen
;
276 return NS_ERROR_INVALID_CONTENT_ENCODING
;
281 if (!mStreamInitialized
)
283 memset(&d_stream
, 0, sizeof (d_stream
));
285 if (inflateInit2(&d_stream
, -MAX_WBITS
) != Z_OK
)
286 return NS_ERROR_FAILURE
;
288 mStreamInitialized
= PR_TRUE
;
291 d_stream
.next_in
= mInpBuffer
;
292 d_stream
.avail_in
= (uInt
)streamLen
;
296 d_stream
.next_out
= mOutBuffer
;
297 d_stream
.avail_out
= (uInt
)mOutBufferLen
;
299 int code
= inflate (&d_stream
, Z_NO_FLUSH
);
300 unsigned bytesWritten
= (uInt
)mOutBufferLen
- d_stream
.avail_out
;
302 if (code
== Z_STREAM_END
)
306 rv
= do_OnDataAvailable(request
, aContext
, aSourceOffset
, (char *)mOutBuffer
, bytesWritten
);
311 inflateEnd(&d_stream
);
312 mStreamEnded
= PR_TRUE
;
315 else if (code
== Z_OK
)
319 rv
= do_OnDataAvailable(request
, aContext
, aSourceOffset
, (char *)mOutBuffer
, bytesWritten
);
324 else if (code
== Z_BUF_ERROR
)
328 rv
= do_OnDataAvailable(request
, aContext
, aSourceOffset
, (char *)mOutBuffer
, bytesWritten
);
335 return NS_ERROR_INVALID_CONTENT_ENCODING
;
341 rv
= mListener
->OnDataAvailable(request
, aContext
, iStr
, aSourceOffset
, aCount
);
347 } /* OnDataAvailable */
350 // XXX/ruslan: need to implement this too
353 nsHTTPCompressConv::Convert(nsIInputStream
*aFromStream
,
354 const char *aFromType
,
357 nsIInputStream
**_retval
)
359 return NS_ERROR_NOT_IMPLEMENTED
;
363 nsHTTPCompressConv::do_OnDataAvailable(nsIRequest
* request
,
364 nsISupports
*context
, PRUint32 offset
,
365 const char *buffer
, PRUint32 count
)
368 mStream
= do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID
);
369 NS_ENSURE_STATE(mStream
);
372 mStream
->ShareData(buffer
, count
);
374 nsresult rv
= mListener
->OnDataAvailable(request
, context
, mStream
,
377 // Make sure the stream no longer references |buffer| in case our listener
378 // is crazy enough to try to read from |mStream| after ODA.
379 mStream
->ShareData("", 0);
384 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
385 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
386 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
387 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
388 #define COMMENT 0x10 /* bit 4 set: file comment present */
389 #define RESERVED 0xE0 /* bits 5..7: reserved */
391 static unsigned gz_magic
[2] = {0x1f, 0x8b}; /* gzip magic header */
394 nsHTTPCompressConv::check_header(nsIInputStream
*iStr
, PRUint32 streamLen
, nsresult
*rs
)
397 enum { GZIP_INIT
= 0, GZIP_OS
, GZIP_EXTRA0
, GZIP_EXTRA1
, GZIP_EXTRA2
, GZIP_ORIG
, GZIP_COMMENT
, GZIP_CRC
};
402 if (mCheckHeaderDone
)
410 iStr
->Read (&c
, 1, &rv
);
413 if (mSkipCount
== 0 && ((unsigned)c
& 0377) != gz_magic
[0])
415 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
419 if (mSkipCount
== 1 && ((unsigned)c
& 0377) != gz_magic
[1])
421 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
425 if (mSkipCount
== 2 && ((unsigned)c
& 0377) != Z_DEFLATED
)
427 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
434 mFlags
= (unsigned) c
& 0377;
435 if (mFlags
& RESERVED
)
437 *rs
= NS_ERROR_INVALID_CONTENT_ENCODING
;
446 iStr
->Read(&c
, 1, &rv
);
455 if (mFlags
& EXTRA_FIELD
)
457 iStr
->Read(&c
, 1, &rv
);
459 mLen
= (uInt
) c
& 0377;
467 iStr
->Read(&c
, 1, &rv
);
469 mLen
= ((uInt
) c
& 0377) << 8;
475 if (mSkipCount
== mLen
)
479 iStr
->Read(&c
, 1, &rv
);
486 if (mFlags
& ORIG_NAME
)
488 iStr
->Read(&c
, 1, &rv
);
491 hMode
= GZIP_COMMENT
;
494 hMode
= GZIP_COMMENT
;
498 if (mFlags
& COMMENT
)
500 iStr
->Read(&c
, 1, &rv
);
516 if (mFlags
& HEAD_CRC
)
518 iStr
->Read(&c
, 1, &rv
);
523 mCheckHeaderDone
= PR_TRUE
;
529 mCheckHeaderDone
= PR_TRUE
;
539 NS_NewHTTPCompressConv(nsHTTPCompressConv
**aHTTPCompressConv
)
541 NS_PRECONDITION(aHTTPCompressConv
!= nsnull
, "null ptr");
543 if (!aHTTPCompressConv
)
544 return NS_ERROR_NULL_POINTER
;
546 *aHTTPCompressConv
= new nsHTTPCompressConv();
548 if (!*aHTTPCompressConv
)
549 return NS_ERROR_OUT_OF_MEMORY
;
551 NS_ADDREF(*aHTTPCompressConv
);