1 // Written by James Turner
3 // Copyright (C) 2013 James Turner <zakalawe@mac.com>
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Library General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "HTTPContentDecode.hxx"
23 #include <cstdlib> // rand()
25 #include <cstring> // for memset, memcpy
27 #include <simgear/debug/logstream.hxx>
28 #include <simgear/structure/exception.hxx>
29 #include <simgear/io/lowlevel.hxx> // for sgEndian stuff
37 const int ZLIB_DECOMPRESS_BUFFER_SIZE = 32 * 1024;
38 const int ZLIB_INFLATE_WINDOW_BITS = -MAX_WBITS;
40 // see http://www.ietf.org/rfc/rfc1952.txt for these values and
41 // detailed description of the logic
42 const int GZIP_HEADER_ID1 = 31;
43 const int GZIP_HEADER_ID2 = 139;
44 const int GZIP_HEADER_METHOD_DEFLATE = 8;
45 const unsigned int GZIP_HEADER_SIZE = 10;
46 const int GZIP_HEADER_FEXTRA = 1 << 2;
47 const int GZIP_HEADER_FNAME = 1 << 3;
48 const int GZIP_HEADER_COMMENT = 1 << 4;
49 const int GZIP_HEADER_CRC = 1 << 1;
51 ContentDecoder::ContentDecoder() :
60 ContentDecoder::~ContentDecoder()
67 void ContentDecoder::setEncoding(const std::string& encoding)
69 std::cout << "setEncoding:" << encoding << std::endl;
70 if (encoding == "gzip") {
71 _contentDeflate = true;
72 _needGZipHeader = true;
73 } else if (encoding == "deflate") {
74 _contentDeflate = true;
75 _needGZipHeader = false;
76 } else if (encoding != "identity") {
77 SG_LOG(SG_IO, SG_WARN, "unsupported content encoding:" << encoding);
81 void ContentDecoder::reset()
84 _contentDeflate = false;
85 _needGZipHeader = false;
89 void ContentDecoder::initWithRequest(Request_ptr req)
92 if (!_contentDeflate) {
97 _zlib = (z_stream*) malloc(sizeof(z_stream));
100 memset(_zlib, 0, sizeof(z_stream));
102 _output = (unsigned char*) malloc(ZLIB_DECOMPRESS_BUFFER_SIZE);
106 // NULLs means we'll get default alloc+free methods
107 // which is absolutely fine
108 _zlib->avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
109 _zlib->next_out = _output;
110 if (inflateInit2(_zlib, ZLIB_INFLATE_WINDOW_BITS) != Z_OK) {
111 SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
115 void ContentDecoder::finish()
117 if (_contentDeflate) {
123 void ContentDecoder::receivedBytes(const char* n, size_t s)
125 if (!_contentDeflate) {
126 _request->processBodyBytes(n, s);
130 // allocate more space if needed (this will only happen rarely once the
131 // buffer has hit something proportionate to the server's compression
133 size_t requiredSize = _inputSize + s;
134 if (requiredSize > _inputAllocated) {
135 reallocateInputBuffer(requiredSize);
138 // copy newly recieved bytes into the buffer
139 memcpy(_input + _inputSize, n, s);
142 if (_needGZipHeader && !consumeGZipHeader()) {
143 std::cout << "waiting on GZIP header" << std::endl;
144 // still waiting on the full GZIP header, so done
151 void ContentDecoder::consumeBytes(size_t consumed)
153 assert(_inputSize >= consumed);
154 // move existing (consumed) bytes down
156 size_t newSize = _inputSize - consumed;
157 memmove(_input, _input + consumed, newSize);
158 _inputSize = newSize;
162 void ContentDecoder::reallocateInputBuffer(size_t newSize)
164 std::cout << "reallocate:" << newSize << std::endl;
167 _input = (unsigned char*) realloc(_input, newSize);
168 _inputAllocated = newSize;
171 void ContentDecoder::runDecoder()
173 _zlib->next_in = (unsigned char*) _input;
174 _zlib->avail_in = _inputSize;
177 // loop, running zlib() inflate and sending output bytes to
178 // our request body handler. Keep calling inflate until no bytes are
179 // written, and ZLIB has consumed all available input
181 _zlib->next_out = _output;
182 _zlib->avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
183 int result = inflate(_zlib, Z_NO_FLUSH);
184 if (result == Z_OK || result == Z_STREAM_END) {
186 } else if (result == Z_BUF_ERROR) {
187 // transient error, fall through
193 writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - _zlib->avail_out;
194 if (writtenSize > 0) {
195 _request->processBodyBytes((char*) _output, writtenSize);
198 if (result == Z_STREAM_END) {
201 } while ((_zlib->avail_in > 0) || (writtenSize > 0));
203 // update input buffers based on what we consumed
204 consumeBytes(_inputSize - _zlib->avail_in);
207 bool ContentDecoder::consumeGZipHeader()
209 size_t avail = _inputSize;
210 if (avail < GZIP_HEADER_SIZE) {
211 return false; // need more header bytes
214 if ((_input[0] != GZIP_HEADER_ID1) ||
215 (_input[1] != GZIP_HEADER_ID2) ||
216 (_input[2] != GZIP_HEADER_METHOD_DEFLATE))
218 return false; // invalid GZip header
221 char flags = _input[3];
222 unsigned int gzipHeaderSize = GZIP_HEADER_SIZE;
223 if (flags & GZIP_HEADER_FEXTRA) {
225 if (avail < gzipHeaderSize) {
226 return false; // need more header bytes
229 unsigned short extraHeaderBytes = *(reinterpret_cast<unsigned short*>(_input + GZIP_HEADER_FEXTRA));
230 if ( sgIsBigEndian() ) {
231 sgEndianSwap( &extraHeaderBytes );
234 gzipHeaderSize += extraHeaderBytes;
235 if (avail < gzipHeaderSize) {
236 return false; // need more header bytes
241 if (flags & GZIP_HEADER_FNAME) {
243 while (gzipHeaderSize <= avail) {
244 if (_input[gzipHeaderSize-1] == 0) {
245 break; // found terminating NULL character
250 if (flags & GZIP_HEADER_COMMENT) {
252 while (gzipHeaderSize <= avail) {
253 if (_input[gzipHeaderSize-1] == 0) {
254 break; // found terminating NULL character
260 if (flags & GZIP_HEADER_CRC) {
264 if (avail < gzipHeaderSize) {
265 return false; // need more header bytes
268 consumeBytes(gzipHeaderSize);
269 _needGZipHeader = false;
273 } // of namespace HTTP
275 } // of namespace simgear