http-transfer-chunked.c revision fb025942616dfec7770455a7092d01f2e516314d
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch * Chunked input stream
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch/* Chunk parser */
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic inline const char *_chr_sanitize(unsigned char c)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch(struct http_transfer_chunked_istream *tcstream)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* chunk-size = 1*HEXDIG */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (*tcstream->cur >= '0' && *tcstream->cur <= '9')
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch else if (*tcstream->cur >= 'A' && *tcstream->cur <= 'F')
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch else if (*tcstream->cur >= 'a' && *tcstream->cur <= 'f')
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Expected chunk size digit, but found %s",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->error = "Chunk size exceeds integer limit";
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch(struct http_transfer_chunked_istream *tcstream)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* token = 1*tchar */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch while (tcstream->cur < tcstream->end && http_char_is_token(*tcstream->cur))
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->parsed_chars += (tcstream->cur-first);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch(struct http_transfer_chunked_istream *tcstream)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* qdtext-nf = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch while (tcstream->cur < tcstream->end && http_char_is_qdtext(*tcstream->cur))
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_transfer_chunked_parse(struct http_transfer_chunked_istream *tcstream)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunked-body = *chunk
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk = chunk-size [ chunk-ext ] CRLF
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk-data CRLF
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk-size = 1*HEXDIG
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch last-chunk = 1*("0") [ chunk-ext ] CRLF
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk-ext-name = token
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk-ext-val = token / quoted-str-nf
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk-data = 1*OCTET ; a sequence of chunk-size octets
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch trailer-part = *( header-field CRLF )
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch quoted-str-nf = DQUOTE *( qdtext-nf / quoted-pair ) DQUOTE
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch ; like quoted-string, but disallowing line folding
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch qdtext-nf = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_SIZE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((ret=http_transfer_chunked_parse_size(tcstream)) <= 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_CR;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* chunk-ext */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_NAME;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* chunk-ext-name = token */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((ret=http_transfer_chunked_skip_token(tcstream)) <= 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->error = "Invalid chunked extension name";
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_EQ;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* chunk-ext-val = token / quoted-str-nf */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_TOKEN;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch } else if ((ret=http_transfer_chunked_skip_qdtext(tcstream)) <= 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->error = "Invalid chunked extension value";
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_ESCAPE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Invalid character %s in chunked extension value string",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_ESCAPE:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* ( HTAB / SP / VCHAR / obs-text ) */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Escaped invalid character %s in chunked extension value string",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_TOKEN:
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((ret=http_transfer_chunked_skip_token(tcstream)) <= 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->error = "Invalid chunked extension value";
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_LF;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Expected new line after chunk size, but found %s",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_TRAILER;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_LF;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* fall through */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Expected new line after chunk data, but found %s",
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_INIT;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_transfer_chunked_istream *tcstream)
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch struct istream_private *stream = &tcstream->istream;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct istream *input = tcstream->istream.parent;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch if ((ret=http_transfer_chunked_parse(tcstream)) < 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_stream_skip(input, tcstream->cur - tcstream->begin);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (tcstream->state == HTTP_CHUNKED_PARSE_STATE_DATA)
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch if ( stream->parent->eof && stream->parent->stream_errno == 0 ) {
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch /* unexpected EOF */
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch tcstream->error = "Unexpected end of payload";
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch /* parent stream error */
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch stream->istream.stream_errno = stream->parent->stream_errno;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch/* Input stream */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_transfer_chunked_istream *tcstream)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct istream_private *stream = &tcstream->istream;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const unsigned char *data;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (tcstream->chunk_pos >= tcstream->chunk_size) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_READY;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch // FIXME: is this even necessary?
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_stream_seek(stream->parent, tcstream->chunk_v_offset + tcstream->chunk_pos);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* read from parent if necessary */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch data = i_stream_get_data(stream->parent, &size);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (ret <= 0 && (ret != -2 || stream->skip == 0)) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ( stream->parent->eof && stream->parent->stream_errno == 0 ) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* unexpected EOF */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->error = "Unexpected end of payload";
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* parent stream error */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch stream->istream.stream_errno = stream->parent->stream_errno;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch data = i_stream_get_data(stream->parent, &size);
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch size = size > (tcstream->chunk_size - tcstream->chunk_pos) ?
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch (tcstream->chunk_size - tcstream->chunk_pos) : size;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* Allocate buffer space */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (!i_stream_try_alloc(stream, size, &avail))
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* Copy payload */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch memcpy(&stream->w_buffer[stream->pos], data, size);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (tcstream->chunk_pos >= tcstream->chunk_size)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_READY;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ( ret < 0 ) {
7384b4e78eaab44693c985192276e31322155e32Stephan Boschstatic int http_transfer_chunked_parse_trailer(
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_transfer_chunked_istream *tcstream)
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch struct istream_private *stream = &tcstream->istream;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch const unsigned char *field_data;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->header_parser = http_header_parser_init(tcstream->istream.parent);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch while ((ret=http_header_parse_next_field(tcstream->header_parser,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch &field_name, &field_data, &field_size, &error)) > 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch ("Failed to parse chunked trailer: %s", error);
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_transfer_chunked_istream_read(struct istream_private *stream)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_transfer_chunked_istream *tcstream =
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch (struct http_transfer_chunked_istream *)stream;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((ret=http_transfer_chunked_istream_read_data(tcstream)) != 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if (tcstream->state != HTTP_CHUNKED_PARSE_STATE_DATA_READY)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((ret=http_transfer_chunked_parse_trailer(tcstream)) <= 0)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->state = HTTP_CHUNKED_PARSE_STATE_FINISHED;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ((ret=http_transfer_chunked_parse_next(tcstream)) <= 0)
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainenhttp_transfer_chunked_istream_destroy(struct iostream_private *stream)
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen struct http_transfer_chunked_istream *tcstream =
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen (struct http_transfer_chunked_istream *)stream;
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen http_header_parser_deinit(&tcstream->header_parser);
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen // FIXME: copied from istream.c; there's got to be a better way.
7384b4e78eaab44693c985192276e31322155e32Stephan Boschhttp_transfer_chunked_istream_create(struct istream *input)
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_transfer_chunked_istream *tcstream;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream = i_new(struct http_transfer_chunked_istream, 1);
65c0e43da8cfc730eeb4634f8aa384081bbfa4e7Timo Sirainen tcstream->istream.iostream.destroy = http_transfer_chunked_istream_destroy;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->istream.read = http_transfer_chunked_istream_read;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->istream.istream.readable_fd = FALSE;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch tcstream->istream.istream.blocking = input->blocking;
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch return i_stream_create(&tcstream->istream, input, i_stream_get_fd(input));
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch * Chunked output stream
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch// FIXME: provide support for corking the stream. This means that we'll have
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch// to buffer sent data here rather than in the parent steam; we need to know
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch// the size of the chunks before we can send them.
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch while (in > 0) {
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch /* Make sure we have room for both chunk data and overhead
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk = chunk-size CRLF
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk-data CRLF
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk-size = 1*HEXDIG
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Boschhttp_transfer_chunked_ostream_close(struct iostream_private *stream)
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch struct http_transfer_chunked_ostream *tcstream =
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch (struct http_transfer_chunked_ostream *)stream;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch (void)o_stream_send(tcstream->ostream.parent, "0\r\n\r\n", 5);
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch (void)o_stream_flush(&tcstream->ostream.ostream);
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Boschhttp_transfer_chunked_ostream_sendv(struct ostream_private *stream,
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch const struct const_iovec *iov, unsigned int iov_count)
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch struct http_transfer_chunked_ostream *tcstream =
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch (struct http_transfer_chunked_ostream *)stream;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch unsigned int iov_count_new, i;
fb025942616dfec7770455a7092d01f2e516314dTimo Sirainen i_assert(stream->parent->real_stream->max_buffer_size >= MIN_CHUNK_SIZE_WITH_EXTRA);
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch if ((ret=o_stream_flush(stream->parent)) <= 0) {
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch /* error / we still couldn't flush existing data to
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch parent stream. */
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch /* check how many bytes we want to send */
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch for (i = 0; i < iov_count; i++) {
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch /* check if we have room to send at least one byte */
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch max_bytes = o_stream_get_buffer_avail_size(stream->parent);
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch tcstream->chunk_size = bytes > max_bytes ? max_bytes : bytes;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch /* determine what to send */
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch for (i = 0; i < iov_count && bytes > 0; i++) {
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch /* create new iovec */
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch prefix = t_strdup_printf("%llx\r\n", (unsigned long long)tcstream->chunk_size);
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch iov_new = t_malloc(sizeof(struct const_iovec) * iov_count);
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch memcpy(&iov_new[1], iov, sizeof(struct const_iovec) * iov_count_new);
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch if ((ret=o_stream_sendv(stream->parent, iov_new, iov_count)) <= 0) {
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch /* all must be sent */
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch i_assert((size_t)ret == (tcstream->chunk_size +
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch iov_new[0].iov_len + iov_new[iov_count-1].iov_len));
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch stream->ostream.offset += tcstream->chunk_size;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Boschhttp_transfer_chunked_ostream_create(struct ostream *output)
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch struct http_transfer_chunked_ostream *tcstream;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch tcstream = i_new(struct http_transfer_chunked_ostream, 1);
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch tcstream->ostream.sendv = http_transfer_chunked_ostream_sendv;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch tcstream->ostream.iostream.close = http_transfer_chunked_ostream_close;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch max_size = output->real_stream->max_buffer_size;
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch tcstream->ostream.max_buffer_size = _max_chunk_size(max_size);