Commit b1271234 authored by Kevin Bracey's avatar Kevin Bracey
Browse files

Added support for Content-Encoding: gzip and deflate.

Appears to work on the one server I've found to test against - more
servers need to be found. Particularly, we need to test unusual gzip
headers and interaction with chunking.
parent 7c0341e7
| Copyright 1998 Acorn Computers Ltd
|
| Licensed under the Apache License, Version 2.0 (the "License");
| you may not use this file except in compliance with the License.
| You may obtain a copy of the License at
|
| http://www.apache.org/licenses/LICENSE-2.0
|
| Unless required by applicable law or agreed to in writing, software
| distributed under the License is distributed on an "AS IS" BASIS,
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
| See the License for the specific language governing permissions and
| limitations under the License.
|
Dir <Obey$Dir>
amu_machine ramtrace
This diff is collapsed.
......@@ -54,9 +54,9 @@ CPFLAGS = ~cfr~v
WFLAGS = ~c~v
CFLAGS = -c -depend !Depend ${INCLUDES} -zM -Wp -ffah -zps1 ${DFLAGS}
DFLAGS = -D${SYSTEM} -DCOMPAT_INET4 -DCOOKIE -UTML
DFLAGS = -D${SYSTEM} -DCOMPAT_INET4 -DCOOKIE -DCOMPRESSION -UTML
ROMFLAGS = -DROM
INCLUDES = -ITCPIPLibs:,C:
INCLUDES = -ITCPIPLibs:,C:,C:zlib
DEBFLAGS = -DTRACE -DUSE_SYSLOG
#
......@@ -64,6 +64,7 @@ DEBFLAGS = -DTRACE -DUSE_SYSLOG
#
ANSILIB = CLib:o.ansilib
CLIB = CLIB:o.stubs
ZLIB = C:zlib.o.zlibzm
DEBLIBS = <syslog$dir>.c-veneer.o.syslog
RLIB = RISCOSLIB:o.risc_oslib
ROMCSTUBS = RISCOSLIB:o.romcstubs
......@@ -76,6 +77,7 @@ OBJS =\
o.connpool\
o.cookie\
o.dates\
o.decompress\
o.dns\
o.generic\
o.header\
......@@ -99,6 +101,7 @@ RAM_OBJS =\
o.connpool\
o.cookie\
o.dates\
o.decompress\
o.dns\
o.generic\
o.header\
......@@ -123,6 +126,7 @@ DBG_OBJS =\
od.connpool\
od.cookie\
od.dates\
od.decompress\
od.dns\
od.generic\
od.header\
......@@ -203,14 +207,14 @@ rom_link:
${MSGSF}: @.Resources.${LOCALE}.Messages
resgen messages_file ${MSGSF} Resources.${LOCALE}.Messages ${RESFSDIR}.Messages
${RAM_MODULE}: ${RAM_OBJS} ${MSGSF} ${TCPIPLIBS} ${CLIB}
${RAM_MODULE}: ${RAM_OBJS} ${MSGSF} ${TCPIPLIBS} ${ZLIB} ${CLIB}
${mkdir} rm
${LD} -o $@ -module ${RAM_OBJS} ${MSGSF} ${TCPIPLIBS} ${CLIB}
${LD} -o $@ -module ${RAM_OBJS} ${MSGSF} ${TCPIPLIBS} ${ZLIB} ${CLIB}
Access $@ RW/R
${DBG_MODULE}: ${DBG_OBJS} ${MSGSF} ${TCPIPLIBS} ${CLIB} ${DEBLIBS}
${DBG_MODULE}: ${DBG_OBJS} ${MSGSF} ${TCPIPLIBS} ${ZLIB} ${CLIB} ${DEBLIBS}
${mkdir} rm
${LD} -o $@ -module ${DBG_OBJS} ${MSGSF} ${TCPIPLIBS} ${CLIB} ${DEBLIBS}
${LD} -o $@ -module ${DBG_OBJS} ${MSGSF} ${TCPIPLIBS} ${ZLIB} ${CLIB} ${DEBLIBS}
Access $@ RW/R
o.moduleRAM: module.c
......
/* Copyright 1998 Acorn Computers Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* HTTP (c.decompress)
*
* Acorn Computers Ltd. 1998
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "kernel.h"
#include "swis.h"
#include "sys/errno.h"
#include "module.h"
#include "socklib.h"
#include "cookie.h"
#include "protocol.h"
#include "writedata.h"
#include "header.h"
#include "generic.h"
#ifdef COMPRESSION
int decompress(Session *ses, char *buffer, int bufsize, int toread, int *rawread)
{
int dataread;
int orig_toread = toread;
ses->zstream.next_out = (Bytef *) buffer;
ses->zstream.avail_out = bufsize;
for (;;) {
switch (ses->compstate) {
case compress_INFLATE:
#ifdef TRACE
protocol_debug("decompress: INFLATE\n");
#endif
if (ses->zstream.avail_in) {
/* Something left over from last time */
int ret = inflate(&ses->zstream, Z_NO_FLUSH);
if (ret == Z_STREAM_END) {
inflateEnd(&ses->zstream);
ses->compression = compression_NONE;
ses->compstate = compress_FINISHED;
}
else if (ret != Z_OK) {
#ifdef TRACE
protocol_debug("inflate error: %s\n", ses->zstream.msg);
#endif
*rawread = orig_toread - toread;
return 0;
}
}
while (ses->zstream.avail_in == 0 && ses->zstream.avail_out > 0 && toread > 0 && ses->compstate == compress_INFLATE) {
int ret;
dataread = ses->op->s_recv(ses->sd, ses->compbuf, toread > ses->compbufsize ? ses->compbufsize : toread, 0);
if (dataread > 0) {
toread -= dataread;
ses->zstream.next_in = ses->compbuf;
ses->zstream.avail_in = dataread;
ret = inflate(&ses->zstream, Z_NO_FLUSH);
if (ret == Z_STREAM_END) {
inflateEnd(&ses->zstream);
ses->compression = compression_NONE;
ses->compstate = compress_FINISHED;
}
else if (ret != Z_OK) {
#ifdef TRACE
protocol_debug("inflate error: %s\n", ses->zstream.msg);
#endif
*rawread = orig_toread - toread;
return 0;
}
}
else if (dataread == 0) {
inflateEnd(&ses->zstream);
ses->compression = compression_NONE;
ses->compstate = compress_FINISHED;
*rawread = orig_toread - toread;
return dataread;
}
else {
*rawread = orig_toread - toread;
return dataread;
}
}
*rawread = orig_toread - toread;
return (char *) ses->zstream.next_out - buffer;
case compress_READ_GZIP_HEADER:
#ifdef TRACE
protocol_debug("decompress: READ_GZIP_HEADER\n");
#endif
if (ses->zstream.next_in == Z_NULL) {
ses->zstream.next_in = ses->compbuf;
ses->zstream.avail_in = 0;
}
dataread = ses->op->s_recv(ses->sd, ses->zstream.next_in, toread > 10 - ses->zstream.avail_in ? 10 - ses->zstream.avail_in : toread, 0);
if (dataread <= 0) {
*rawread = orig_toread - toread;
return dataread;
}
toread -= dataread;
ses->zstream.avail_in += dataread;
if (ses->zstream.avail_in < 10) {
*rawread = orig_toread - toread;
errno = EWOULDBLOCK;
return -1;
}
if (ses->compbuf[0] != 0x1F || ses->compbuf[1] != 0x8B ||
ses->compbuf[2] != 8) {
*rawread = orig_toread - toread;
return 0;
}
ses->compflags = ses->compbuf[3];
ses->compstate = compress_READ_GZIP_EXTRA;
ses->zstream.next_in = ses->compbuf;
ses->zstream.avail_in = 0;
/* Fall through */
case compress_READ_GZIP_EXTRA:
#ifdef TRACE
protocol_debug("decompress: READ_GZIP_EXTRA\n");
#endif
if (ses->compflags & 0x04) {
int s;
if (ses->zstream.avail_in < 2) {
dataread = ses->op->s_recv(ses->sd, ses->zstream.next_in, toread > 2 - ses->zstream.avail_in ? 2 - ses->zstream.avail_in : toread, 0);
if (dataread <= 0) {
*rawread = orig_toread - toread;
return dataread;
}
toread -= dataread;
ses->zstream.avail_in += dataread;
if (ses->zstream.avail_in < 2) {
*rawread = orig_toread - toread;
errno = EWOULDBLOCK;
return -1;
}
ses->zstream.avail_out = ses->compbuf[0] + 256 * ses->compbuf[1];
}
suck_more:
s = ses->compbufsize > ses->zstream.avail_out ? ses->compbufsize : ses->zstream.avail_out;
s = toread > s ? s : toread;
dataread = ses->op->s_recv(ses->sd, ses->compbuf, s, 0);
toread -= dataread;
if (dataread <= 0) {
*rawread = orig_toread - toread;
return dataread;
}
ses->zstream.avail_out -= dataread;
if (ses->zstream.avail_out > 0)
goto suck_more;
}
ses->compstate = compress_READ_GZIP_NAME;
ses->zstream.next_in = ses->compbuf;
ses->zstream.avail_in = 0;
/* Fall through */
case compress_READ_GZIP_NAME:
#ifdef TRACE
protocol_debug("decompress: READ_GZIP_NAME\n");
#endif
if (ses->compflags & 0x08) {
suck_more_fname:
dataread = ses->op->s_recv(ses->sd, ses->compbuf, toread > ses->compbufsize ? ses->compbufsize : toread, 0);
if (dataread <= 0) {
*rawread = orig_toread - toread;
return dataread;
}
toread -= dataread;
for (ses->zstream.next_in=ses->compbuf,
ses->zstream.avail_in=dataread;
ses->zstream.avail_in;
ses->zstream.next_in++,
ses->zstream.avail_in--) {
if (*ses->zstream.next_in == 0) goto finished_fname;
}
goto suck_more_fname;
}
finished_fname:
ses->compstate = compress_READ_GZIP_COMMENT;
/* Fall through */
case compress_READ_GZIP_COMMENT:
#ifdef TRACE
protocol_debug("decompress: READ_GZIP_COMMENT\n");
#endif
if (ses->compflags & 0x10) {
suck_more_comment:
for ( ; ses->zstream.avail_in;
ses->zstream.next_in++,
ses->zstream.avail_in--) {
if (*ses->zstream.next_in == 0) goto finished_comment;
}
dataread = ses->op->s_recv(ses->sd, ses->compbuf, toread > ses->compbufsize - 2 ? ses->compbufsize - 2 : toread, 0); /* Leave room for CRC... */
if (dataread <= 0) {
*rawread = orig_toread - toread;
return dataread;
}
toread -= dataread;
ses->zstream.next_in = ses->compbuf;
ses->zstream.avail_in = dataread;
goto suck_more_comment;
}
finished_comment:
ses->compstate = compress_READ_GZIP_HCRC;
/* Fall through */
case compress_READ_GZIP_HCRC:
#ifdef TRACE
protocol_debug("decompress: READ_GZIP_HCRC\n");
#endif
if (ses->compflags & 0x02) {
if (ses->zstream.avail_in < 2) {
dataread = ses->op->s_recv(ses->sd, ses->zstream.next_in, toread > 2 - ses->zstream.avail_in ? 2 - ses->zstream.avail_in : toread, 0);
if (dataread <= 0) {
*rawread = orig_toread - toread;
return dataread;
}
toread -= dataread;
ses->zstream.next_in += dataread;
ses->zstream.avail_in += dataread;
if (ses->zstream.avail_in < 2) {
*rawread = orig_toread - toread;
errno = EWOULDBLOCK;
return -1;
}
}
ses->zstream.avail_in -= 2;
}
ses->compstate = compress_INFLATE;
break;
case compress_FINISHED:
dataread=0;
}
}
*rawread = orig_toread - toread;
return dataread;
}
#endif
......@@ -372,6 +372,9 @@ static int http_recognise_header(const char *hdr)
{
if (Strcmp_ci(hdr, "content-length") == 0) return header_CONTENT_LENGTH;
if (Strcmp_ci(hdr, "transfer-encoding") == 0) return header_TRANSFER_ENCODING;
#ifdef COMPRESSION
if (Strcmp_ci(hdr, "content-encoding") == 0) return header_CONTENT_ENCODING;
#endif
#ifdef COOKIE
if (Strcmp_ci(hdr, "set-cookie") == 0) return header_SET_COOKIE;
if (Strcmp_ci(hdr, "set-cookie2") == 0) return header_SET_COOKIE2;
......@@ -568,6 +571,48 @@ int parse_http_header(char *const buffer, int buflen, Session *ses, _kernel_swi_
ses->chunk_state = chunkstate_reading_headers;
}
break;
#ifdef COMPRESSION
case header_CONTENT_ENCODING:
#ifdef TRACE
protocol_debug("Server sent us a Content-Encoding header: %s\n", value);
#endif
if (Strcmp_ci(value, "gzip") == 0 ||
Strcmp_ci(value, "x-gzip") == 0) {
ses->compression = compression_GZIP;
ses->compstate = compress_READ_GZIP_HEADER;
goto prepare_compression;
}
else if (Strcmp_ci(value, "deflate") == 0) {
int ret;
ses->compression = compression_DEFLATE;
ses->compstate = compress_INFLATE;
prepare_compression:
suppress = 1;
ses->zstream.next_in=Z_NULL;
ses->zstream.avail_in=0;
ses->zstream.zalloc=Z_NULL;
ses->zstream.zfree=Z_NULL;
if (ses->compression == compression_DEFLATE)
ret = inflateInit(&ses->zstream);
else
ret = inflateInit2(&ses->zstream, -MAX_WBITS);
if (ret != Z_OK) {
ses->compression = compression_NONE;
#ifdef TRACE
protocol_debug("inflateInit error: %s\n", ses->zstream.msg);
#endif
} else
{
ses->compbuf = malloc(1024);
ses->compbufsize = 1024;
if (ses->compbuf == NULL) {
inflateEnd(&ses->zstream);
ses->compression = compression_NONE;
}
}
}
break;
#endif
case header_CONNECTION:
#ifdef TRACE
protocol_debug("Server sent us a connection header");
......@@ -621,7 +666,11 @@ int parse_http_header(char *const buffer, int buflen, Session *ses, _kernel_swi_
#ifdef TRACE
protocol_debug("** End of headers detected\n");
#endif
#ifdef COMPRESSION
if (ses->chunking || ses->compression) {
#else
if (ses->chunking) {
#endif
/* Ignore Content-Length if we are going for a chunked transfer */
ses->size = -1;
}
......
......@@ -110,6 +110,9 @@ static int http_read_more_header(_kernel_swi_regs *r, Session *ses)
ses->buffer = new_buffer;
}
else {
#ifdef TRACE
protocol_debug("module_realloc failed!\n");
#endif
ses->done = HTTP_ERROR;
http_set_return(r, status_ABORTED, r->r[4], 0, ses);
return 0;
......@@ -223,9 +226,9 @@ static int http_process_chunks(_kernel_swi_regs *r, Session *ses, int *maxbytes)
*/
static void http_reading_response(_kernel_swi_regs *r, Session *ses)
{
int dataread = 0;
int dataread = 0, rawread;
char *buffer;
int bufsize;
int bufsize, toread;
if (ses->donehead == FALSE) {
if (http_read_more_header(r, ses) == 0) {
......@@ -239,7 +242,7 @@ static void http_reading_response(_kernel_swi_regs *r, Session *ses)
errno = 0;
buffer = (char *) r->r[2];
bufsize = r->r[3];
bufsize = toread = r->r[3];
if (bufsize <= 0) {
http_set_return(r, status_READING_REPLY, r->r[4], -1, ses);
......@@ -260,7 +263,7 @@ static void http_reading_response(_kernel_swi_regs *r, Session *ses)
* the limit of the SVC stack).
*
*/
int newsize = bufsize;
int newsize = toread;
/* First rule is that we don't know the entity header size */
r->r[5] = -1;
......@@ -276,7 +279,7 @@ static void http_reading_response(_kernel_swi_regs *r, Session *ses)
return;
}*/
bufsize = newsize;
toread = newsize;
/* To get here, the chunker has determined that it is safe to read data directly
* into the client buffer. The amount of data to read is stored in bufsize, which
......@@ -285,7 +288,12 @@ static void http_reading_response(_kernel_swi_regs *r, Session *ses)
*/
}
dataread = ses->op->s_recv(ses->sd, buffer, bufsize, 0);
#ifdef COMPRESSION
if (ses->compression) {
dataread = decompress(ses, buffer, bufsize, toread, &rawread);
} else
#endif
dataread = rawread = ses->op->s_recv(ses->sd, buffer, toread, 0);
#ifdef TRACE
protocol_debug("receiving_body state: recv returns %d (errno now %d)\n", dataread, errno);
......@@ -298,7 +306,7 @@ static void http_reading_response(_kernel_swi_regs *r, Session *ses)
ses->sent += dataread;
http_write_data_to_client(r, buffer, dataread);
if (ses->chunking) {
ses->chunk_bytes -= dataread;
ses->chunk_bytes -= rawread;
if (ses->chunk_bytes == 0 && r->r[3] > 0) {
/* See comment in block containing call to http_process_chunks about
* this tail-recursive call.
......
......@@ -107,6 +107,12 @@ static Session *kill_session_data(Session *s)
if (s->headers != NULL) {
http_free_headers(&s->headers);
}
#ifdef COMPRESSION
if (s->compression) {
inflateEnd(&s->zstream);
}
free(s->compbuf);
#endif
memset(s, '\0', sizeof(*s)); /* Let's be careful */
free(s);
......
......@@ -446,6 +446,16 @@ static int http_validate_user_supplied_data(Session *ses)
#endif
http_delete_header(&ses->headers, hdr);
}
#ifdef COMPRESSION
for (;;) {
http_header *hdr = http_find_header(ses->headers, "content-encoding");
if (!hdr) break;
#ifdef TRACE
protocol_debug("Grrr. Removing content-encoding from idempotent request\n");
#endif
http_delete_header(&ses->headers, hdr);
}
#endif
ses->data_len = 0; /* Forcibly ignore the extra data */
}
......@@ -544,6 +554,16 @@ static int http_generate_request(Session *ses, _kernel_swi_regs *r)
http_add_header(&ses->headers, "Accept", "image/png; q=1.0, image/jpeg; q=0.9, image/*; q=0.8, */*");
#endif
#ifdef COMPRESSION
{
http_header *hdr;
if ((hdr = http_find_header(ses->headers, "accept-encoding")) != NULL) {
http_delete_header(&ses->headers, hdr);
}
http_add_header(&ses->headers, "Accept-Encoding", "deflate, gzip");
}
#endif
/* Construct the host header as required */
if (endport == ((ses->flags & flags_USING_HTTPS) ? CONNECT_DEFAULT_PORT_NUMBER2:CONNECT_DEFAULT_PORT_NUMBER) || endhost == 0) {
http_add_header(&ses->headers, "Host", endhost);
......@@ -716,6 +736,7 @@ static Session *http_new_session(_kernel_swi_regs *r)
ses->read_status = 0;
ses->bufsize = 0;
ses->donehead = 0;
ses->compression = compression_NONE;
ses->chunking = 0;
ses->chunk_bytes = 0;
ses->chunk_state = 0;
......
......@@ -22,6 +22,7 @@ typedef enum {
header_CONTENT_LENGTH,
header_SET_COOKIE,
header_SET_COOKIE2,
header_CONTENT_ENCODING,
header_TRANSFER_ENCODING,
header_CONNECTION,
header_END_OF_HEADERS
......
......@@ -20,6 +20,8 @@
#define TRUE 1
#define FALSE 0
#include "zlib.h"
typedef enum {
flags_USER_AGENT_IN_R6 = 1,
flags_DATA_LENGTH_IN_R5 = 2,
......@@ -41,6 +43,22 @@ struct http_header {
char text[1]; /* Entire header but ':' and "\r\n" represented by '\0' terminators */
};
typedef enum {
compression_NONE,
compression_DEFLATE,
compression_GZIP
} compress_type;
typedef enum {
compress_INFLATE,
compress_READ_GZIP_HEADER,
compress_READ_GZIP_EXTRA,
compress_READ_GZIP_NAME,
compress_READ_GZIP_COMMENT,
compress_READ_GZIP_HCRC,
compress_FINISHED
} compress_state;
typedef struct {
int (*s_ioctl)(int /*s*/, unsigned long /*command*/, ...);
......@@ -88,6 +106,12 @@ typedef struct session {
int chunking; /* Flag indicating we are decoding a chunked transfer */
int chunk_bytes; /* Bytes left in current chunk */
int chunk_state; /* Used to keep track of our buffer state in the de-chunker */
compress_type compression; /* What type of Content-Encoding compression is in use */
compress_state compstate; /* Where is the decompression up to? */
unsigned int compflags; /* Various flags... */
z_stream zstream; /* zlib stream structure */
Bytef *compbuf; /* Temporary buffer to read data into */
int compbufsize; /* How big is that buffer? */
int wait_interim; /* Set to non-zero when waiting for a multi-stage response (eg. HTTP/1.1 POST) */
int reused_socket; /* Set to non-zero when a socket is being re-used on a persistent connection */
http_header *headers; /* List of headers */
......@@ -174,6 +198,13 @@ _kernel_oserror *enumerate_cookies(_kernel_swi_regs *r);
_kernel_oserror *consume_cookie(_kernel_swi_regs *r);
_kernel_oserror *add_cookie(_kernel_swi_regs *r);
/*************************************************************/
/* Found in ^.c.decompress */
/*************************************************************/
/* Routines to handle Content-Encoding */
/*************************************************************/
int decompress(Session *ses, char *buffer, int bufsize, int toread, int *rawread);
/*************************************************************/
/* This is the description of the URL module error base and */
/* where we are within it. We have 32 error messages that */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment