Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

361 lines (287 sloc) 8.968 kb
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* Cherokee
*
* Authors:
* Alvaro Lopez Ortega <alvaro@alobbs.com>
*
* Copyright (C) 2001-2010 Alvaro Lopez Ortega
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "common-internal.h"
#include "crc32.h"
#include "encoder_gzip.h"
#include "plugin_loader.h"
/* Plug-in initialization
*/
PLUGIN_INFO_ENCODER_EASIEST_INIT (gzip);
/* Specs:
* GZIP file format specification version 4.3:
* RFC-1952: http://www.ietf.org/rfc/rfc1952.txt
*/
/* Links:
* http://leknor.com/code/php/class.gzip_encode.php.txt
*/
/* Some operating systems
*/
enum {
OS_FAT = 0,
OS_UNIX = 3,
OS_OS2 = 6,
OS_MACINTOSH = 7,
OS_NTFS = 11,
OS_UNKNOWN = 255
};
#define gzip_header_len 10
static unsigned char gzip_header[gzip_header_len] = {0x1F, 0x8B, /* 16 bits: IDentification */
Z_DEFLATED, /* b bits: Compression Method */
0, /* 8 bits: FLags */
0, 0, 0, 0, /* 32 bits: Modification TIME */
0, /* 8 bits: Extra Flags */
OS_UNIX}; /* 8 bits: Operating System */
/* GZIP
* ====
* gzip_header (10 bytes) + [gzip_encoder_content] + crc32 (4 bytes) + length (4 bytes)
*
*/
static ret_t
props_free (cherokee_encoder_gzip_t *props)
{
return cherokee_encoder_props_free_base (ENCODER_PROPS(props));
}
ret_t
cherokee_encoder_gzip_configure (cherokee_config_node_t *config,
cherokee_server_t *srv,
cherokee_module_props_t **_props)
{
cherokee_list_t *i;
cherokee_encoder_gzip_props_t *props;
UNUSED(srv);
if (*_props == NULL) {
CHEROKEE_NEW_STRUCT (n, encoder_gzip_props);
cherokee_encoder_props_init_base (ENCODER_PROPS(n),
MODULE_PROPS_FREE(props_free));
n->compression_level = Z_DEFAULT_COMPRESSION;
*_props = MODULE_PROPS(n);
}
props = PROP_GZIP(*_props);
cherokee_config_node_foreach (i, config) {
cherokee_config_node_t *subconf = CONFIG_NODE(i);
if (equal_buf_str (&subconf->key, "compression_level")) {
props->compression_level = atoi (subconf->val.buf);
}
}
return cherokee_encoder_configure (config, srv, _props);
}
ret_t
cherokee_encoder_gzip_new (cherokee_encoder_gzip_t **encoder,
cherokee_encoder_props_t *props)
{
cuint_t workspacesize;
CHEROKEE_NEW_STRUCT (n, encoder_gzip);
/* Init
*/
cherokee_encoder_init_base (ENCODER(n), PLUGIN_INFO_PTR(gzip), props);
MODULE(n)->init = (encoder_func_init_t) cherokee_encoder_gzip_init;
MODULE(n)->free = (module_func_free_t) cherokee_encoder_gzip_free;
ENCODER(n)->add_headers = (encoder_func_add_headers_t) cherokee_encoder_gzip_add_headers;
ENCODER(n)->encode = (encoder_func_encode_t) cherokee_encoder_gzip_encode;
ENCODER(n)->flush = (encoder_func_flush_t) cherokee_encoder_gzip_flush;
/* More stuff
*/
n->size = 0;
n->crc32 = 0;
n->add_header = true;
workspacesize = zlib_deflate_workspacesize();
n->workspace = malloc (workspacesize);
if (unlikely (n->workspace == NULL))
return ret_nomem;
memset (n->workspace, 0, workspacesize);
memset (&n->stream, 0, sizeof(z_stream));
/* Return the object
*/
*encoder = n;
return ret_ok;
}
ret_t
cherokee_encoder_gzip_free (cherokee_encoder_gzip_t *encoder)
{
if (encoder->workspace != NULL) {
free (encoder->workspace);
}
free (encoder);
return ret_ok;
}
ret_t
cherokee_encoder_gzip_add_headers (cherokee_encoder_gzip_t *encoder,
cherokee_buffer_t *buf)
{
UNUSED(encoder);
cherokee_buffer_ensure_addlen (buf, 50);
cherokee_buffer_add_str (buf, "Content-Encoding: gzip"CRLF);
cherokee_buffer_add_str (buf, "Vary: Accept-Encoding"CRLF);
return ret_ok;
}
static const char *
get_gzip_error_string (int err)
{
switch (err) {
case Z_NEED_DICT: return "Need dict.";
case Z_ERRNO: return "Errno";
case Z_STREAM_ERROR: return "Stream error";
case Z_DATA_ERROR: return "Data error";
case Z_MEM_ERROR: return "Memory error";
case Z_BUF_ERROR: return "Buffer error";
case Z_VERSION_ERROR: return "Version error";
default:
SHOULDNT_HAPPEN;
}
return "unknown";
}
ret_t
cherokee_encoder_gzip_init (cherokee_encoder_gzip_t *encoder)
{
int err;
z_stream *z = &encoder->stream;
/* Set the workspace
*/
z->workspace = encoder->workspace;
/* Comment from the PHP source code:
* windowBits is passed < 0 to suppress zlib header & trailer
*/
err = zlib_deflateInit2 (z,
ENC_GZIP_PROP(encoder)->compression_level,
Z_DEFLATED,
-MAX_WBITS,
MAX_MEM_LEVEL,
Z_DEFAULT_STRATEGY);
if (err != Z_OK) {
LOG_ERROR (CHEROKEE_ERROR_ENCODER_DEFLATEINIT2,
get_gzip_error_string(err));
return ret_error;
}
return ret_ok;
}
/* "A gzip file consists of a series of "members" (compressed data
* sets). The format of each member is specified in the following
* section. The members simply appear one after another in the file,
* with no additional information before, between, or after them."
*
* The step() method build a "member":
*/
/* From OpenSSH:
* Compresses the contents of input_buffer into output_buffer. All packets
* compressed using this function will form a single compressed data stream;
* however, data will be flushed at the end of every call so that each
* output_buffer can be decompressed independently (but in the appropriate
* order since they together form a single compression stream) by the
* receiver. This appends the compressed data to the output buffer.
*/
static ret_t
do_encode (cherokee_encoder_gzip_t *encoder,
cherokee_buffer_t *in,
cherokee_buffer_t *out,
int flush)
{
int err;
cchar_t buf[DEFAULT_READ_SIZE];
z_stream *z = &encoder->stream;
/* Update crc32 and size if needed
*/
if (in->len <= 0) {
if (flush != Z_FINISH)
return ret_ok;
z->avail_in = 0;
z->next_in = NULL;
} else {
z->avail_in = in->len;
z->next_in = (void *)in->buf;
encoder->size += in->len;
encoder->crc32 = crc32_partial_sz (encoder->crc32, in->buf, in->len);
}
/* Add the GZip header:
* +---+---+---+---+---+---+---+---+---+---+
* |ID1|ID2|CM |FLG| MTIME |XFL|OS |
* +---+---+---+---+---+---+---+---+---+---+
*/
if (encoder->add_header) {
cherokee_buffer_add (out, (const char *)gzip_header, gzip_header_len);
encoder->add_header = false;
}
/* Compress it
*/
do {
/* Set up fixed-size output buffer.
*/
z->next_out = (Byte *)buf;
z->avail_out = sizeof(buf);
err = zlib_deflate (z, flush);
switch (err) {
case Z_OK:
cherokee_buffer_add (out, buf, sizeof(buf) - z->avail_out);
break;
case Z_STREAM_END:
err = zlib_deflateEnd (z);
if (err != Z_OK) {
LOG_ERROR (CHEROKEE_ERROR_ENCODER_DEFLATEEND,
get_gzip_error_string(err));
return ret_error;
}
cherokee_buffer_add (out, buf, sizeof(buf) - z->avail_out);
break;
default:
LOG_ERROR (CHEROKEE_ERROR_ENCODER_DEFLATE,
get_gzip_error_string(err), z->avail_in);
zlib_deflateEnd (z);
return ret_error;
}
} while (z->avail_out == 0);
return ret_ok;
}
ret_t
cherokee_encoder_gzip_encode (cherokee_encoder_gzip_t *encoder,
cherokee_buffer_t *in,
cherokee_buffer_t *out)
{
return do_encode (encoder, in, out, Z_PARTIAL_FLUSH);
}
ret_t
cherokee_encoder_gzip_flush (cherokee_encoder_gzip_t *encoder, cherokee_buffer_t *in, cherokee_buffer_t *out)
{
ret_t ret;
char footer[8];
ret = do_encode (encoder, in, out, Z_FINISH);
if (unlikely (ret != ret_ok)) {
return ret;
}
/* Add the ending:
* +---+---+---+---+---+---+---+---+
* | CRC32 | ISIZE |
* +---+---+---+---+---+---+---+---+
*/
footer[0] = (char) encoder->crc32 & 0xFF;
footer[1] = (char) (encoder->crc32 >> 8) & 0xFF;
footer[2] = (char) (encoder->crc32 >> 16) & 0xFF;
footer[3] = (char) (encoder->crc32 >> 24) & 0xFF;
footer[4] = (char) encoder->size & 0xFF;
footer[5] = (char) (encoder->size >> 8) & 0xFF;
footer[6] = (char) (encoder->size >> 16) & 0xFF;
footer[7] = (char) (encoder->size >> 24) & 0xFF;
cherokee_buffer_add (out, footer, 8);
#if 0
cherokee_buffer_print_debug (out, -1);
#endif
return ret_ok;
}
Jump to Line
Something went wrong with that request. Please try again.