1/* Copyright 2002-2004 Justin Erenkrantz and Greg Stein
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <apr_strings.h>
17
18#include <zlib.h>
19
20/* This conditional isn't defined anywhere yet. */
21#ifdef HAVE_ZUTIL_H
22#include <zutil.h>
23#endif
24
25#include "serf.h"
26#include "serf_bucket_util.h"
27
28/* magic header */
29static char deflate_magic[2] = { '\037', '\213' };
30#define DEFLATE_MAGIC_SIZE 10
31#define DEFLATE_VERIFY_SIZE 8
32#define DEFLATE_BUFFER_SIZE 8096
33
34static const int DEFLATE_WINDOW_SIZE = -15;
35static const int DEFLATE_MEMLEVEL = 9;
36
37typedef struct {
38    serf_bucket_t *stream;
39    serf_bucket_t *inflate_stream;
40
41    int format;                 /* Are we 'deflate' or 'gzip'? */
42
43    enum {
44        STATE_READING_HEADER,   /* reading the gzip header */
45        STATE_HEADER,           /* read the gzip header */
46        STATE_INIT,             /* init'ing zlib functions */
47        STATE_INFLATE,          /* inflating the content now */
48        STATE_READING_VERIFY,   /* reading the final gzip CRC */
49        STATE_VERIFY,           /* verifying the final gzip CRC */
50        STATE_FINISH,           /* clean up after reading body */
51        STATE_DONE,             /* body is done; we'll return EOF here */
52    } state;
53
54    z_stream zstream;
55    char hdr_buffer[DEFLATE_MAGIC_SIZE];
56    unsigned char buffer[DEFLATE_BUFFER_SIZE];
57    unsigned long crc;
58    int windowSize;
59    int memLevel;
60    int bufferSize;
61
62    /* How much of the chunk, or the terminator, do we have left to read? */
63    apr_size_t stream_left;
64
65    /* How much are we supposed to read? */
66    apr_size_t stream_size;
67
68    int stream_status; /* What was the last status we read? */
69
70} deflate_context_t;
71
72/* Inputs a string and returns a long.  */
73static unsigned long getLong(unsigned char *string)
74{
75    return ((unsigned long)string[0])
76          | (((unsigned long)string[1]) << 8)
77          | (((unsigned long)string[2]) << 16)
78          | (((unsigned long)string[3]) << 24);
79}
80
81serf_bucket_t *serf_bucket_deflate_create(
82    serf_bucket_t *stream,
83    serf_bucket_alloc_t *allocator,
84    int format)
85{
86    deflate_context_t *ctx;
87
88    ctx = serf_bucket_mem_alloc(allocator, sizeof(*ctx));
89    ctx->stream = stream;
90    ctx->stream_status = APR_SUCCESS;
91    ctx->inflate_stream = serf_bucket_aggregate_create(allocator);
92    ctx->format = format;
93    ctx->crc = 0;
94    /* zstream must be NULL'd out. */
95    memset(&ctx->zstream, 0, sizeof(ctx->zstream));
96
97    switch (ctx->format) {
98        case SERF_DEFLATE_GZIP:
99            ctx->state = STATE_READING_HEADER;
100            break;
101        case SERF_DEFLATE_DEFLATE:
102            /* deflate doesn't have a header. */
103            ctx->state = STATE_INIT;
104            break;
105        default:
106            /* Not reachable */
107            return NULL;
108    }
109
110    /* Initial size of gzip header. */
111    ctx->stream_left = ctx->stream_size = DEFLATE_MAGIC_SIZE;
112
113    ctx->windowSize = DEFLATE_WINDOW_SIZE;
114    ctx->memLevel = DEFLATE_MEMLEVEL;
115    ctx->bufferSize = DEFLATE_BUFFER_SIZE;
116
117    return serf_bucket_create(&serf_bucket_type_deflate, allocator, ctx);
118}
119
120static void serf_deflate_destroy_and_data(serf_bucket_t *bucket)
121{
122    deflate_context_t *ctx = bucket->data;
123
124    if (ctx->state > STATE_INIT &&
125        ctx->state <= STATE_FINISH)
126        inflateEnd(&ctx->zstream);
127
128    /* We may have appended inflate_stream into the stream bucket.
129     * If so, avoid free'ing it twice.
130     */
131    if (ctx->inflate_stream) {
132        serf_bucket_destroy(ctx->inflate_stream);
133    }
134    serf_bucket_destroy(ctx->stream);
135
136    serf_default_destroy_and_data(bucket);
137}
138
139static apr_status_t serf_deflate_read(serf_bucket_t *bucket,
140                                      apr_size_t requested,
141                                      const char **data, apr_size_t *len)
142{
143    deflate_context_t *ctx = bucket->data;
144    unsigned long compCRC, compLen;
145    apr_status_t status;
146    const char *private_data;
147    apr_size_t private_len;
148    int zRC;
149
150    while (1) {
151        switch (ctx->state) {
152        case STATE_READING_HEADER:
153        case STATE_READING_VERIFY:
154            status = serf_bucket_read(ctx->stream, ctx->stream_left,
155                                      &private_data, &private_len);
156
157            if (SERF_BUCKET_READ_ERROR(status)) {
158                return status;
159            }
160
161            memcpy(ctx->hdr_buffer + (ctx->stream_size - ctx->stream_left),
162                   private_data, private_len);
163
164            ctx->stream_left -= private_len;
165
166            if (ctx->stream_left == 0) {
167                ctx->state++;
168                if (APR_STATUS_IS_EAGAIN(status)) {
169                    *len = 0;
170                    return status;
171                }
172            }
173            else if (status) {
174                *len = 0;
175                return status;
176            }
177            break;
178        case STATE_HEADER:
179            if (ctx->hdr_buffer[0] != deflate_magic[0] ||
180                ctx->hdr_buffer[1] != deflate_magic[1]) {
181                return SERF_ERROR_DECOMPRESSION_FAILED;
182            }
183            if (ctx->hdr_buffer[3] != 0) {
184                return SERF_ERROR_DECOMPRESSION_FAILED;
185            }
186            ctx->state++;
187            break;
188        case STATE_VERIFY:
189            /* Do the checksum computation. */
190            compCRC = getLong((unsigned char*)ctx->hdr_buffer);
191            if (ctx->crc != compCRC) {
192                return SERF_ERROR_DECOMPRESSION_FAILED;
193            }
194            compLen = getLong((unsigned char*)ctx->hdr_buffer + 4);
195            if (ctx->zstream.total_out != compLen) {
196                return SERF_ERROR_DECOMPRESSION_FAILED;
197            }
198            ctx->state++;
199            break;
200        case STATE_INIT:
201            zRC = inflateInit2(&ctx->zstream, ctx->windowSize);
202            if (zRC != Z_OK) {
203                return SERF_ERROR_DECOMPRESSION_FAILED;
204            }
205            ctx->zstream.next_out = ctx->buffer;
206            ctx->zstream.avail_out = ctx->bufferSize;
207            ctx->state++;
208            break;
209        case STATE_FINISH:
210            inflateEnd(&ctx->zstream);
211            serf_bucket_aggregate_prepend(ctx->stream, ctx->inflate_stream);
212            ctx->inflate_stream = 0;
213            ctx->state++;
214            break;
215        case STATE_INFLATE:
216            /* Do we have anything already uncompressed to read? */
217            status = serf_bucket_read(ctx->inflate_stream, requested, data,
218                                      len);
219            if (SERF_BUCKET_READ_ERROR(status)) {
220                return status;
221            }
222            /* Hide EOF. */
223            if (APR_STATUS_IS_EOF(status)) {
224                status = ctx->stream_status;
225                if (APR_STATUS_IS_EOF(status)) {
226                    /* We've read all of the data from our stream, but we
227                     * need to continue to iterate until we flush
228                     * out the zlib buffer.
229                     */
230                    status = APR_SUCCESS;
231                }
232            }
233            if (*len != 0) {
234                return status;
235            }
236
237            /* We tried; but we have nothing buffered. Fetch more. */
238
239            /* It is possible that we maxed out avail_out before
240             * exhausting avail_in; therefore, continue using the
241             * previous buffer.  Otherwise, fetch more data from
242             * our stream bucket.
243             */
244            if (ctx->zstream.avail_in == 0) {
245                /* When we empty our inflated stream, we'll return this
246                 * status - this allow us to eventually pass up EAGAINs.
247                 */
248                ctx->stream_status = serf_bucket_read(ctx->stream,
249                                                      ctx->bufferSize,
250                                                      &private_data,
251                                                      &private_len);
252
253                if (SERF_BUCKET_READ_ERROR(ctx->stream_status)) {
254                    return ctx->stream_status;
255                }
256
257                if (!private_len && APR_STATUS_IS_EAGAIN(ctx->stream_status)) {
258                    *len = 0;
259                    status = ctx->stream_status;
260                    ctx->stream_status = APR_SUCCESS;
261                    return status;
262                }
263
264                ctx->zstream.next_in = (unsigned char*)private_data;
265                ctx->zstream.avail_in = private_len;
266            }
267            zRC = Z_OK;
268            while (ctx->zstream.avail_in != 0) {
269                /* We're full, clear out our buffer, reset, and return. */
270                if (ctx->zstream.avail_out == 0) {
271                    serf_bucket_t *tmp;
272                    ctx->zstream.next_out = ctx->buffer;
273                    private_len = ctx->bufferSize - ctx->zstream.avail_out;
274
275                    ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer,
276                                     private_len);
277
278                    /* FIXME: There probably needs to be a free func. */
279                    tmp = SERF_BUCKET_SIMPLE_STRING_LEN((char *)ctx->buffer,
280                                                        private_len,
281                                                        bucket->allocator);
282                    serf_bucket_aggregate_append(ctx->inflate_stream, tmp);
283                    ctx->zstream.avail_out = ctx->bufferSize;
284                    break;
285                }
286                zRC = inflate(&ctx->zstream, Z_NO_FLUSH);
287
288                if (zRC == Z_STREAM_END) {
289                    serf_bucket_t *tmp;
290
291                    private_len = ctx->bufferSize - ctx->zstream.avail_out;
292                    ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer,
293                                     private_len);
294                    /* FIXME: There probably needs to be a free func. */
295                    tmp = SERF_BUCKET_SIMPLE_STRING_LEN((char *)ctx->buffer,
296                                                        private_len,
297                                                        bucket->allocator);
298                    serf_bucket_aggregate_append(ctx->inflate_stream, tmp);
299
300                    ctx->zstream.avail_out = ctx->bufferSize;
301
302                    /* Push back the remaining data to be read. */
303                    tmp = serf_bucket_aggregate_create(bucket->allocator);
304                    serf_bucket_aggregate_prepend(tmp, ctx->stream);
305                    ctx->stream = tmp;
306
307                    /* We now need to take the remaining avail_in and
308                     * throw it in ctx->stream so our next read picks it up.
309                     */
310                    tmp = SERF_BUCKET_SIMPLE_STRING_LEN(
311                                        (const char*)ctx->zstream.next_in,
312                                                     ctx->zstream.avail_in,
313                                                     bucket->allocator);
314                    serf_bucket_aggregate_prepend(ctx->stream, tmp);
315
316                    switch (ctx->format) {
317                    case SERF_DEFLATE_GZIP:
318                        ctx->stream_left = ctx->stream_size =
319                            DEFLATE_VERIFY_SIZE;
320                        ctx->state++;
321                        break;
322                    case SERF_DEFLATE_DEFLATE:
323                        /* Deflate does not have a verify footer. */
324                        ctx->state = STATE_FINISH;
325                        break;
326                    default:
327                        /* Not reachable */
328                        return APR_EGENERAL;
329                    }
330
331                    break;
332                }
333                if (zRC != Z_OK) {
334                    return SERF_ERROR_DECOMPRESSION_FAILED;
335                }
336            }
337            /* Okay, we've inflated.  Try to read. */
338            status = serf_bucket_read(ctx->inflate_stream, requested, data,
339                                      len);
340            /* Hide EOF. */
341            if (APR_STATUS_IS_EOF(status)) {
342                status = ctx->stream_status;
343                /* If our stream is finished too, return SUCCESS so
344                 * we'll iterate one more time.
345                 */
346                if (APR_STATUS_IS_EOF(status)) {
347                    /* No more data to read from the stream, and everything
348                       inflated. If all data was received correctly, state
349                       should have been advanced to STATE_READING_VERIFY or
350                       STATE_FINISH. If not, then the data was incomplete
351                       and we have an error. */
352                    if (ctx->state != STATE_INFLATE)
353                        return APR_SUCCESS;
354                    else
355                        return SERF_ERROR_DECOMPRESSION_FAILED;
356                }
357            }
358            return status;
359        case STATE_DONE:
360            /* We're done inflating.  Use our finished buffer. */
361            return serf_bucket_read(ctx->stream, requested, data, len);
362        default:
363            /* Not reachable */
364            return APR_EGENERAL;
365        }
366    }
367
368    /* NOTREACHED */
369}
370
371/* ### need to implement */
372#define serf_deflate_readline NULL
373#define serf_deflate_peek NULL
374
375const serf_bucket_type_t serf_bucket_type_deflate = {
376    "DEFLATE",
377    serf_deflate_read,
378    serf_deflate_readline,
379    serf_default_read_iovec,
380    serf_default_read_for_sendfile,
381    serf_default_read_bucket,
382    serf_deflate_peek,
383    serf_deflate_destroy_and_data,
384};
385