Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this
3 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 : */
5 :
6 : #include "StreamFunctions.h"
7 : #include "nsDeflateConverter.h"
8 : #include "nsStringStream.h"
9 : #include "nsIInputStreamPump.h"
10 : #include "nsComponentManagerUtils.h"
11 : #include "nsMemory.h"
12 : #include "plstr.h"
13 : #include "mozilla/UniquePtr.h"
14 :
15 : #define ZLIB_TYPE "deflate"
16 : #define GZIP_TYPE "gzip"
17 : #define X_GZIP_TYPE "x-gzip"
18 :
19 : using namespace mozilla;
20 :
21 : /**
22 : * nsDeflateConverter is a stream converter applies the deflate compression
23 : * method to the data.
24 : */
25 0 : NS_IMPL_ISUPPORTS(nsDeflateConverter, nsIStreamConverter,
26 : nsIStreamListener,
27 : nsIRequestObserver)
28 :
29 0 : nsresult nsDeflateConverter::Init()
30 : {
31 : int zerr;
32 :
33 0 : mOffset = 0;
34 :
35 0 : mZstream.zalloc = Z_NULL;
36 0 : mZstream.zfree = Z_NULL;
37 0 : mZstream.opaque = Z_NULL;
38 :
39 0 : int32_t window = MAX_WBITS;
40 0 : switch (mWrapMode) {
41 : case WRAP_NONE:
42 0 : window = -window;
43 0 : break;
44 : case WRAP_GZIP:
45 0 : window += 16;
46 0 : break;
47 : default:
48 0 : break;
49 : }
50 :
51 0 : zerr = deflateInit2(&mZstream, mLevel, Z_DEFLATED, window, 8,
52 0 : Z_DEFAULT_STRATEGY);
53 0 : if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
54 :
55 0 : mZstream.next_out = mWriteBuffer;
56 0 : mZstream.avail_out = sizeof(mWriteBuffer);
57 :
58 : // mark the input buffer as empty.
59 0 : mZstream.avail_in = 0;
60 0 : mZstream.next_in = Z_NULL;
61 :
62 0 : return NS_OK;
63 : }
64 :
65 0 : NS_IMETHODIMP nsDeflateConverter::Convert(nsIInputStream *aFromStream,
66 : const char *aFromType,
67 : const char *aToType,
68 : nsISupports *aCtxt,
69 : nsIInputStream **_retval)
70 : {
71 0 : return NS_ERROR_NOT_IMPLEMENTED;
72 : }
73 :
74 0 : NS_IMETHODIMP nsDeflateConverter::AsyncConvertData(const char *aFromType,
75 : const char *aToType,
76 : nsIStreamListener *aListener,
77 : nsISupports *aCtxt)
78 : {
79 0 : if (mListener)
80 0 : return NS_ERROR_ALREADY_INITIALIZED;
81 :
82 0 : NS_ENSURE_ARG_POINTER(aListener);
83 :
84 0 : if (!PL_strncasecmp(aToType, ZLIB_TYPE, sizeof(ZLIB_TYPE)-1))
85 0 : mWrapMode = WRAP_ZLIB;
86 0 : else if (!PL_strcasecmp(aToType, GZIP_TYPE) ||
87 0 : !PL_strcasecmp(aToType, X_GZIP_TYPE))
88 0 : mWrapMode = WRAP_GZIP;
89 : else
90 0 : mWrapMode = WRAP_NONE;
91 :
92 0 : nsresult rv = Init();
93 0 : NS_ENSURE_SUCCESS(rv, rv);
94 :
95 0 : mListener = aListener;
96 0 : mContext = aCtxt;
97 0 : return rv;
98 : }
99 :
100 0 : NS_IMETHODIMP nsDeflateConverter::OnDataAvailable(nsIRequest *aRequest,
101 : nsISupports *aContext,
102 : nsIInputStream *aInputStream,
103 : uint64_t aOffset,
104 : uint32_t aCount)
105 : {
106 0 : if (!mListener)
107 0 : return NS_ERROR_NOT_INITIALIZED;
108 :
109 0 : auto buffer = MakeUnique<char[]>(aCount);
110 0 : NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
111 :
112 0 : nsresult rv = ZW_ReadData(aInputStream, buffer.get(), aCount);
113 0 : NS_ENSURE_SUCCESS(rv, rv);
114 :
115 : // make sure we aren't reading too much
116 0 : mZstream.avail_in = aCount;
117 0 : mZstream.next_in = (unsigned char*)buffer.get();
118 :
119 0 : int zerr = Z_OK;
120 : // deflate loop
121 0 : while (mZstream.avail_in > 0 && zerr == Z_OK) {
122 0 : zerr = deflate(&mZstream, Z_NO_FLUSH);
123 :
124 0 : while (mZstream.avail_out == 0) {
125 : // buffer is full, push the data out to the listener
126 0 : rv = PushAvailableData(aRequest, aContext);
127 0 : NS_ENSURE_SUCCESS(rv, rv);
128 0 : zerr = deflate(&mZstream, Z_NO_FLUSH);
129 : }
130 : }
131 :
132 0 : return NS_OK;
133 : }
134 :
135 0 : NS_IMETHODIMP nsDeflateConverter::OnStartRequest(nsIRequest *aRequest,
136 : nsISupports *aContext)
137 : {
138 0 : if (!mListener)
139 0 : return NS_ERROR_NOT_INITIALIZED;
140 :
141 0 : return mListener->OnStartRequest(aRequest, mContext);
142 : }
143 :
144 0 : NS_IMETHODIMP nsDeflateConverter::OnStopRequest(nsIRequest *aRequest,
145 : nsISupports *aContext,
146 : nsresult aStatusCode)
147 : {
148 0 : if (!mListener)
149 0 : return NS_ERROR_NOT_INITIALIZED;
150 :
151 : nsresult rv;
152 :
153 : int zerr;
154 0 : do {
155 0 : zerr = deflate(&mZstream, Z_FINISH);
156 0 : rv = PushAvailableData(aRequest, aContext);
157 0 : NS_ENSURE_SUCCESS(rv, rv);
158 0 : } while (zerr == Z_OK);
159 :
160 0 : deflateEnd(&mZstream);
161 :
162 0 : return mListener->OnStopRequest(aRequest, mContext, aStatusCode);
163 : }
164 :
165 0 : nsresult nsDeflateConverter::PushAvailableData(nsIRequest *aRequest,
166 : nsISupports *aContext)
167 : {
168 0 : uint32_t bytesToWrite = sizeof(mWriteBuffer) - mZstream.avail_out;
169 : // We don't need to do anything if there isn't any data
170 0 : if (bytesToWrite == 0)
171 0 : return NS_OK;
172 :
173 0 : MOZ_ASSERT(bytesToWrite <= INT32_MAX);
174 0 : nsCOMPtr<nsIInputStream> stream;
175 0 : nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
176 0 : (char*)mWriteBuffer, bytesToWrite);
177 0 : NS_ENSURE_SUCCESS(rv, rv);
178 :
179 0 : rv = mListener->OnDataAvailable(aRequest, mContext, stream, mOffset,
180 0 : bytesToWrite);
181 :
182 : // now set the state for 'deflate'
183 0 : mZstream.next_out = mWriteBuffer;
184 0 : mZstream.avail_out = sizeof(mWriteBuffer);
185 :
186 0 : mOffset += bytesToWrite;
187 0 : return rv;
188 : }
|