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