http-transfer-chunked.c revision ac3d5a11ff86b23eb2e416a7fe9406cef271930d
/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "istream-private.h"
#include "ostream-private.h"
#include "http-parser.h"
#include "http-header-parser.h"
#include "http-transfer.h"
#define MIN_CHUNK_SIZE_WITH_EXTRA 6
/*
* Chunked input stream
*/
};
struct http_transfer_chunked_istream {
struct istream_private istream;
unsigned int parsed_chars;
struct http_header_parser *header_parser;
unsigned int finished:1;
};
/* Chunk parser */
static inline const char *_chr_sanitize(unsigned char c)
{
if (c >= 0x20 && c < 0x7F)
return t_strdup_printf("'%c'", c);
return t_strdup_printf("0x%02x", c);
}
static int http_transfer_chunked_parse_size
(struct http_transfer_chunked_istream *tcstream)
{
/* chunk-size = 1*HEXDIG */
else {
if (tcstream->parsed_chars == 0) {
"Expected chunk size digit, but found %s",
return -1;
}
tcstream->parsed_chars = 0;
return 1;
}
"Chunk size exceeds integer limit");
return -1;
}
tcstream->parsed_chars++;
}
return 0;
}
static int http_transfer_chunked_skip_token
(struct http_transfer_chunked_istream *tcstream)
{
/* token = 1*tchar */
return 0;
if (tcstream->parsed_chars == 0)
return -1;
return 1;
}
static int http_transfer_chunked_skip_qdtext
(struct http_transfer_chunked_istream *tcstream)
{
/* qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text */
return 0;
return 1;
}
static int
{
int ret;
/* RFC 7230, Section 4.1: Chunked Transfer Encoding
chunked-body = *chunk
last-chunk
trailer-part
CRLF
chunk = chunk-size [ chunk-ext ] CRLF
chunk-data CRLF
chunk-size = 1*HEXDIG
last-chunk = 1*("0") [ chunk-ext ] CRLF
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
chunk-ext-name = token
chunk-ext-val = token / quoted-string
chunk-data = 1*OCTET ; a sequence of chunk-size octets
trailer-part = *( header-field CRLF )
*/
for (;;) {
tcstream->chunk_size = 0;
tcstream->parsed_chars = 0;
/* fall through */
return ret;
/* fall through */
break;
}
/* chunk-ext */
return 0;
/* fall through */
/* chunk-ext-name = token */
if (ret < 0) {
"Invalid chunked extension name");
}
return ret;
}
/* fall through */
break;
}
return 0;
/* fall through */
/* chunk-ext-val = token / quoted-string */
break;
}
return 0;
/* fall through */
return 0;
if (ret < 0) {
"Invalid chunked extension value");
}
return ret;
return 0;
} else {
"Invalid character %s in chunked extension value string",
return -1;
}
break;
/* ( HTAB / SP / VCHAR / obs-text ) */
"Escaped invalid character %s in chunked extension value string",
return -1;
}
return 0;
break;
if (ret < 0) {
"Invalid chunked extension value");
}
return ret;
}
break;
return 0;
}
/* fall through */
"Expected new line after chunk size, but found %s",
return -1;
}
if (tcstream->chunk_size > 0)
else
return 1;
/* fall through */
return 0;
}
/* fall through */
"Expected new line after chunk data, but found %s",
return -1;
}
break;
default:
i_unreached();
}
}
i_unreached();
return -1;
}
static int http_transfer_chunked_parse_next(
struct http_transfer_chunked_istream *tcstream)
{
int ret;
while ((ret=i_stream_read_data
return -1;
}
if (ret > 0) {
"Total chunked payload size exceeds maximum");
return -1;
}
}
return ret;
}
}
if (ret < 0) {
/* unexpected EOF */
"Unexpected end of payload");
} else {
/* parent stream error */
}
}
return ret;
}
/* Input stream */
static ssize_t
struct http_transfer_chunked_istream *tcstream)
{
const unsigned char *data;
return 0;
}
// FIXME: is this even necessary?
/* read from parent if necessary */
if (size == 0) {
/* unexpected EOF */
"Unexpected end of payload");
} else {
/* parent stream error */
}
return ret;
}
}
/* Allocate buffer space */
return -2;
/* Copy payload */
if ( ret < 0 ) {
return ret;
}
return ret;
}
static int http_transfer_chunked_parse_trailer(
struct http_transfer_chunked_istream *tcstream)
{
const char *field_name, *error;
const unsigned char *field_data;
int ret;
/* NOTE: trailer is currently ignored */
/* FIXME: limit trailer size */
}
if (field_name == NULL) break;
}
if (ret <= 0) {
if (ret < 0) {
"Failed to parse chunked trailer: %s", error);
}
return ret;
}
return 1;
}
static ssize_t
{
struct http_transfer_chunked_istream *tcstream =
(struct http_transfer_chunked_istream *)stream;
for (;;) {
return -1;
return ret;
return 0;
break;
return ret;
return -1;
default:
return ret;
}
}
return -1;
}
static void
{
struct http_transfer_chunked_istream *tcstream =
(struct http_transfer_chunked_istream *)stream;
// FIXME: copied from istream.c; there's got to be a better way.
}
struct istream *
{
struct http_transfer_chunked_istream *tcstream;
}
/*
* Chunked output stream
*/
// FIXME: provide support for corking the stream. This means that we'll have
// to buffer sent data here rather than in the parent steam; we need to know
// the size of the chunks before we can send them.
struct http_transfer_chunked_ostream {
struct ostream_private ostream;
unsigned int chunk_active:1;
};
{
while (in > 0) {
in >>= 4;
res++;
}
return res;
}
{
/* Make sure we have room for both chunk data and overhead
chunk = chunk-size [ chunk-ext ] CRLF
chunk-data CRLF
chunk-size = 1*HEXDIG
*/
return avail < chunk_extra ? 0 :
avail - chunk_extra;
}
static void
bool close_parent)
{
struct http_transfer_chunked_ostream *tcstream =
(struct http_transfer_chunked_ostream *)stream;
if (close_parent)
}
static ssize_t
{
struct http_transfer_chunked_ostream *tcstream =
(struct http_transfer_chunked_ostream *)stream;
struct const_iovec *iov_new;
unsigned int iov_count_new, i;
const char *prefix;
/* error / we still couldn't flush existing data to
parent stream. */
return ret;
}
/* check how many bytes we want to send */
bytes = 0;
for (i = 0; i < iov_count; i++) {
}
/* check if we have room to send at least one byte */
return 0;
/* determine what to send */
iov_count_new = 1;
break;
}
/* create new iovec */
/* send */
return -1;
}
/* all must be sent */
return tcstream->chunk_size;
}
struct ostream *
{
struct http_transfer_chunked_ostream *tcstream;
else
}