bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 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')
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch "Expected chunk size digit, but found %s",
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen "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)
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* qdtext = 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)
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* RFC 7230, Section 4.1: Chunked Transfer Encoding
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
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch chunk-ext-val = token / quoted-string
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk-data = 1*OCTET ; a sequence of chunk-size octets
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch trailer-part = *( header-field CRLF )
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) {
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen "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 */
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch /* chunk-ext-val = token / quoted-string */
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:
59bc0bc0c0383d522cd1289c5847d6eac673121aTimo Sirainen tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT;
59bc0bc0c0383d522cd1289c5847d6eac673121aTimo Sirainen } else if ((ret=http_transfer_chunked_skip_qdtext(tcstream)) <= 0) {
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen "Invalid chunked extension value");
59bc0bc0c0383d522cd1289c5847d6eac673121aTimo Sirainen tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_ESCAPE;
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
59bc0bc0c0383d522cd1289c5847d6eac673121aTimo Sirainen "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 ) */
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
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) {
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen "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 */
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
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 */
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
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;
9184983183ae28fb543695c54c85bc5396c07e42Phil Carmody while ((ret=i_stream_read_more(input, &tcstream->begin, &size)) > 0) {
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch if ((ret=http_transfer_chunked_parse(tcstream)) < 0) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch i_stream_skip(input, tcstream->cur - tcstream->begin);
1175415b88ff168e367c77df23901eada13225b9Stephan Bosch if (tcstream->state == HTTP_CHUNKED_PARSE_STATE_DATA) {
1175415b88ff168e367c77df23901eada13225b9Stephan Bosch if (tcstream->max_size > 0 && tcstream->size > tcstream->max_size) {
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen "Total chunked payload size exceeds maximum");
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch if ( stream->parent->eof && stream->parent->stream_errno == 0 ) {
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch /* unexpected EOF */
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen "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 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);
22ee6e1e6193299034ab99f77a650290de2fd6caTimo Sirainen i_assert(ret != -2); /* 0 sized buffer can't be full */
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch if ( stream->parent->eof && stream->parent->stream_errno == 0 ) {
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch /* unexpected EOF */
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen io_stream_set_error(&tcstream->istream.iostream,
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen "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 Boschstatic int http_transfer_chunked_parse_trailer(
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch struct http_transfer_chunked_istream *tcstream)
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch struct istream_private *stream = &tcstream->istream;
e0cf44fb802a5e7aa4cbeee5e80ef8f3f6aecdbeStephan Bosch /* NOTE: trailer is currently ignored */
feba5e502b2131c9a1c766b7ef9ff041dbf71d1dStephan Bosch /* FIXME: limit trailer size */
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch http_header_parser_init(tcstream->istream.parent, NULL, 0);
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch while ((ret=http_header_parse_next_field(tcstream->header_parser,
7384b4e78eaab44693c985192276e31322155e32Stephan Bosch &field_name, &field_data, &field_size, &error)) > 0) {
ac3d5a11ff86b23eb2e416a7fe9406cef271930dTimo Sirainen "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.
1175415b88ff168e367c77df23901eada13225b9Stephan Boschhttp_transfer_chunked_istream_create(struct istream *input, uoff_t max_size)
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;
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen return i_stream_create(&tcstream->istream, input, i_stream_get_fd(input), 0);
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
6ee9ce5ed955a1283dc22ad28980bf9cc23d4c4eStephan Bosch chunk = chunk-size [ chunk-ext ] CRLF
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk-data CRLF
50a6d26bd9041f44b4cad0c0357c0c604c132cc8Stephan Bosch chunk-size = 1*HEXDIG
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenhttp_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;
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);
657d704a513d74c8bd0b2d355c88d8723a4ec69bMartti Rannanjärvi iov_new = t_new(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);