/* Returns the amount written. */
static int bio_bucket_write(BIO *bio, const char *in, int inl)
{
    serf_ssl_context_t *ctx = bio->ptr;
    serf_bucket_t *tmp;

#ifdef SSL_VERBOSE
    printf("bio_bucket_write called for %d bytes\n", inl);
#endif
    if (ctx->encrypt.status == SERF_ERROR_WAIT_CONN
        && !BIO_should_read(ctx->bio)) {
#ifdef SSL_VERBOSE
        printf("bio_bucket_write waiting: (%d %d %d)\n",
           BIO_should_retry(ctx->bio), BIO_should_read(ctx->bio),
           BIO_get_retry_flags(ctx->bio));
#endif
        /* Falling back... */
        ctx->encrypt.exhausted_reset = 1;
        BIO_clear_retry_flags(bio);
    }

    tmp = serf_bucket_simple_copy_create(in, inl,
                                         ctx->encrypt.pending->allocator);

    serf_bucket_aggregate_append(ctx->encrypt.pending, tmp);

    return inl;
}
Beispiel #2
0
void serf_bucket_aggregate_append_iovec(
    serf_bucket_t *aggregate_bucket,
    struct iovec *vecs,
    int vecs_count)
{
    serf_bucket_t *new_bucket;

    new_bucket = serf_bucket_iovec_create(vecs, vecs_count,
                                          aggregate_bucket->allocator);

    serf_bucket_aggregate_append(aggregate_bucket, new_bucket);
}
serf_bucket_t *serf_bucket_ssl_encrypt_create(
    serf_bucket_t *stream,
    serf_ssl_context_t *ssl_ctx,
    serf_bucket_alloc_t *allocator)
{
    serf_bucket_t *bkt;
    ssl_context_t *ctx;

    bkt = serf_bucket_ssl_create(ssl_ctx, allocator,
                                 &serf_bucket_type_ssl_encrypt);

    ctx = bkt->data;

    ctx->databuf = &ctx->ssl_ctx->encrypt.databuf;
    ctx->our_stream = &ctx->ssl_ctx->encrypt.stream;
    if (ctx->ssl_ctx->encrypt.stream == NULL) {
        serf_bucket_t *tmp = serf_bucket_aggregate_create(stream->allocator);
        serf_bucket_aggregate_append(tmp, stream);
        ctx->ssl_ctx->encrypt.stream = tmp;
    }
    else {
        bucket_list_t *new_list;

        new_list = serf_bucket_mem_alloc(ctx->ssl_ctx->allocator,
                                         sizeof(*new_list));
        new_list->bucket = stream;
        new_list->next = NULL;
        if (ctx->ssl_ctx->encrypt.stream_next == NULL) {
            ctx->ssl_ctx->encrypt.stream_next = new_list;
        }
        else {
            bucket_list_t *scan = ctx->ssl_ctx->encrypt.stream_next;

            while (scan->next != NULL)
                scan = scan->next;
            scan->next = new_list;
        }
    }

    return bkt;
}
Beispiel #4
0
/* Implements svn_ra_serf__request_body_delegate_t */
static svn_error_t *
create_propfind_body(serf_bucket_t **bkt,
                     void *setup_baton,
                     serf_bucket_alloc_t *alloc,
                     apr_pool_t *pool /* request pool */,
                     apr_pool_t *scratch_pool)
{
  propfind_context_t *ctx = setup_baton;

  serf_bucket_t *body_bkt, *tmp;
  const svn_ra_serf__dav_props_t *prop;
  svn_boolean_t requested_allprop = FALSE;

  body_bkt = serf_bucket_aggregate_create(alloc);

  prop = ctx->find_props;
  while (prop && prop->xmlns)
    {
      /* special case the allprop case. */
      if (strcmp(prop->name, "allprop") == 0)
        {
          requested_allprop = TRUE;
        }

      /* <*propname* xmlns="*propns*" /> */
      tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, alloc);
      serf_bucket_aggregate_append(body_bkt, tmp);

      tmp = SERF_BUCKET_SIMPLE_STRING(prop->name, alloc);
      serf_bucket_aggregate_append(body_bkt, tmp);

      tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" xmlns=\"",
                                          sizeof(" xmlns=\"")-1,
                                          alloc);
      serf_bucket_aggregate_append(body_bkt, tmp);

      tmp = SERF_BUCKET_SIMPLE_STRING(prop->xmlns, alloc);
      serf_bucket_aggregate_append(body_bkt, tmp);

      tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"/>", sizeof("\"/>")-1,
                                          alloc);
      serf_bucket_aggregate_append(body_bkt, tmp);

      prop++;
    }

  /* If we're not doing an allprop, add <prop> tags. */
  if (!requested_allprop)
    {
      tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<prop>",
                                          sizeof("<prop>")-1,
                                          alloc);
      serf_bucket_aggregate_prepend(body_bkt, tmp);
    }

  tmp = SERF_BUCKET_SIMPLE_STRING_LEN(PROPFIND_HEADER,
                                      sizeof(PROPFIND_HEADER)-1,
                                      alloc);

  serf_bucket_aggregate_prepend(body_bkt, tmp);

  if (!requested_allprop)
    {
      tmp = SERF_BUCKET_SIMPLE_STRING_LEN("</prop>",
                                          sizeof("</prop>")-1,
                                          alloc);
      serf_bucket_aggregate_append(body_bkt, tmp);
    }

  tmp = SERF_BUCKET_SIMPLE_STRING_LEN(PROPFIND_TRAILER,
                                      sizeof(PROPFIND_TRAILER)-1,
                                      alloc);
  serf_bucket_aggregate_append(body_bkt, tmp);

  *bkt = body_bkt;
  return SVN_NO_ERROR;
}
static apr_status_t serf_deflate_read(serf_bucket_t *bucket,
                                      apr_size_t requested,
                                      const char **data, apr_size_t *len)
{
    deflate_context_t *ctx = bucket->data;
    apr_status_t status;
    const char *private_data;
    apr_size_t private_len;
    int zRC;

    while (1) {
        switch (ctx->state) {
        case STATE_READING_HEADER:
        case STATE_READING_VERIFY:
            status = serf_bucket_read(ctx->stream, ctx->stream_left,
                                      &private_data, &private_len);

            if (SERF_BUCKET_READ_ERROR(status)) {
                return status;
            }

            memcpy(ctx->hdr_buffer + (ctx->stream_size - ctx->stream_left),
                   private_data, private_len);

            ctx->stream_left -= private_len;

            if (ctx->stream_left == 0) {
                ctx->state++;
                if (APR_STATUS_IS_EAGAIN(status)) {
                    *len = 0;
                    return status;
                }
            }
            else if (status) {
                *len = 0;
                return status;
            }
            break;
        case STATE_HEADER:
            if (ctx->hdr_buffer[0] != deflate_magic[0] ||
                ctx->hdr_buffer[1] != deflate_magic[1]) {
                return SERF_ERROR_DECOMPRESSION_FAILED;
            }
            if (ctx->hdr_buffer[3] != 0) {
                return SERF_ERROR_DECOMPRESSION_FAILED;
            }
            ctx->state++;
            break;
        case STATE_VERIFY:
        {
            unsigned long compCRC, compLen, actualLen;

            /* Do the checksum computation. */
            compCRC = getLong((unsigned char*)ctx->hdr_buffer);
            if (ctx->crc != compCRC) {
                return SERF_ERROR_DECOMPRESSION_FAILED;
            }
            compLen = getLong((unsigned char*)ctx->hdr_buffer + 4);
            /* The length in the trailer is module 2^32, so do the same for
               the actual length. */
            actualLen = ctx->zstream.total_out;
            actualLen &= 0xFFFFFFFF;
            if (actualLen != compLen) {
                return SERF_ERROR_DECOMPRESSION_FAILED;
            }
            ctx->state++;
            break;
        }
        case STATE_INIT:
            zRC = inflateInit2(&ctx->zstream, ctx->windowSize);
            if (zRC != Z_OK) {
                return SERF_ERROR_DECOMPRESSION_FAILED;
            }
            ctx->zstream.next_out = ctx->buffer;
            ctx->zstream.avail_out = ctx->bufferSize;
            ctx->state++;
            break;
        case STATE_FINISH:
            inflateEnd(&ctx->zstream);
            serf_bucket_aggregate_prepend(ctx->stream, ctx->inflate_stream);
            ctx->inflate_stream = 0;
            ctx->state++;
            break;
        case STATE_INFLATE:
            /* Do we have anything already uncompressed to read? */
            status = serf_bucket_read(ctx->inflate_stream, requested, data,
                                      len);
            if (SERF_BUCKET_READ_ERROR(status)) {
                return status;
            }
            /* Hide EOF. */
            if (APR_STATUS_IS_EOF(status)) {
                status = ctx->stream_status;
                if (APR_STATUS_IS_EOF(status)) {
                    /* We've read all of the data from our stream, but we
                     * need to continue to iterate until we flush
                     * out the zlib buffer.
                     */
                    status = APR_SUCCESS;
                }
            }
            if (*len != 0) {
                return status;
            }

            /* We tried; but we have nothing buffered. Fetch more. */

            /* It is possible that we maxed out avail_out before
             * exhausting avail_in; therefore, continue using the
             * previous buffer.  Otherwise, fetch more data from
             * our stream bucket.
             */
            if (ctx->zstream.avail_in == 0) {
                /* When we empty our inflated stream, we'll return this
                 * status - this allow us to eventually pass up EAGAINs.
                 */
                ctx->stream_status = serf_bucket_read(ctx->stream,
                                                      ctx->bufferSize,
                                                      &private_data,
                                                      &private_len);

                if (SERF_BUCKET_READ_ERROR(ctx->stream_status)) {
                    return ctx->stream_status;
                }

                if (!private_len && APR_STATUS_IS_EAGAIN(ctx->stream_status)) {
                    *len = 0;
                    status = ctx->stream_status;
                    ctx->stream_status = APR_SUCCESS;
                    return status;
                }

                ctx->zstream.next_in = (unsigned char*)private_data;
                ctx->zstream.avail_in = private_len;
            }

            while (1) {

                zRC = inflate(&ctx->zstream, Z_NO_FLUSH);

                /* We're full or zlib requires more space. Either case, clear
                   out our buffer, reset, and return. */
                if (zRC == Z_BUF_ERROR || ctx->zstream.avail_out == 0) {
                    serf_bucket_t *tmp;
                    ctx->zstream.next_out = ctx->buffer;
                    private_len = ctx->bufferSize - ctx->zstream.avail_out;

                    ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer,
                                     private_len);

                    /* FIXME: There probably needs to be a free func. */
                    tmp = SERF_BUCKET_SIMPLE_STRING_LEN((char *)ctx->buffer,
                                                        private_len,
                                                        bucket->allocator);
                    serf_bucket_aggregate_append(ctx->inflate_stream, tmp);
                    ctx->zstream.avail_out = ctx->bufferSize;
                    break;
                }

                if (zRC == Z_STREAM_END) {
                    serf_bucket_t *tmp;

                    private_len = ctx->bufferSize - ctx->zstream.avail_out;
                    ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer,
                                     private_len);
                    /* FIXME: There probably needs to be a free func. */
                    tmp = SERF_BUCKET_SIMPLE_STRING_LEN((char *)ctx->buffer,
                                                        private_len,
                                                        bucket->allocator);
                    serf_bucket_aggregate_append(ctx->inflate_stream, tmp);

                    ctx->zstream.avail_out = ctx->bufferSize;

                    /* Push back the remaining data to be read. */
                    tmp = serf_bucket_aggregate_create(bucket->allocator);
                    serf_bucket_aggregate_prepend(tmp, ctx->stream);
                    ctx->stream = tmp;

                    /* We now need to take the remaining avail_in and
                     * throw it in ctx->stream so our next read picks it up.
                     */
                    tmp = SERF_BUCKET_SIMPLE_STRING_LEN(
                                        (const char*)ctx->zstream.next_in,
                                                     ctx->zstream.avail_in,
                                                     bucket->allocator);
                    serf_bucket_aggregate_prepend(ctx->stream, tmp);

                    switch (ctx->format) {
                    case SERF_DEFLATE_GZIP:
                        ctx->stream_left = ctx->stream_size =
                            DEFLATE_VERIFY_SIZE;
                        ctx->state++;
                        break;
                    case SERF_DEFLATE_DEFLATE:
                        /* Deflate does not have a verify footer. */
                        ctx->state = STATE_FINISH;
                        break;
                    default:
                        /* Not reachable */
                        return APR_EGENERAL;
                    }

                    break;
                }

                /* Any other error? */
                if (zRC != Z_OK) {
                    return SERF_ERROR_DECOMPRESSION_FAILED;
                }

                /* As long as zRC == Z_OK, just keep looping. */
            }
            /* Okay, we've inflated.  Try to read. */
            status = serf_bucket_read(ctx->inflate_stream, requested, data,
                                      len);
            /* Hide EOF. */
            if (APR_STATUS_IS_EOF(status)) {
                status = ctx->stream_status;

                /* If the inflation wasn't finished, return APR_SUCCESS. */
                if (zRC != Z_STREAM_END)
                    return APR_SUCCESS;

                /* If our stream is finished too and all data was inflated,
                 * return SUCCESS so we'll iterate one more time.
                 */
                if (APR_STATUS_IS_EOF(status)) {
                    /* No more data to read from the stream, and everything
                       inflated. If all data was received correctly, state
                       should have been advanced to STATE_READING_VERIFY or
                       STATE_FINISH. If not, then the data was incomplete
                       and we have an error. */
                    if (ctx->state != STATE_INFLATE)
                        return APR_SUCCESS;
                    else
                        return SERF_ERROR_DECOMPRESSION_FAILED;
                }
            }
            return status;
        case STATE_DONE:
            /* We're done inflating.  Use our finished buffer. */
            return serf_bucket_read(ctx->stream, requested, data, len);
        default:
            /* Not reachable */
            return APR_EGENERAL;
        }
    }

    /* NOTREACHED */
}
Beispiel #6
0
/* Exchange data between proxy and server */
static apr_status_t proxy_replay(serv_ctx_t *servctx,
                                 apr_int16_t rtnevents,
                                 apr_pool_t *pool)
{
    apr_status_t status;

    if (rtnevents & APR_POLLIN) {
        apr_size_t len;
        char buf[BUFSIZE];
        serf_bucket_t *tmp;

        serf__log(TEST_VERBOSE, __FILE__, "proxy_replay: POLLIN\n");
        /* Read all incoming data from the server to forward it to the
           client later. */
        do
        {
            len = BUFSIZE;

            status = apr_socket_recv(servctx->proxy_client_sock, buf, &len);
            if (SERF_BUCKET_READ_ERROR(status))
                return status;

            serf__log(TEST_VERBOSE, __FILE__,
                      "proxy: reading %d bytes %.*s from server.\n",
                      len, len, buf);
            tmp = serf_bucket_simple_copy_create(buf, len,
                                                 servctx->allocator);
            serf_bucket_aggregate_append(servctx->clientstream, tmp);
        } while (!status);
    }

    if (rtnevents & APR_POLLOUT) {
        apr_size_t len;
        char *buf;

        serf__log(TEST_VERBOSE, __FILE__, "proxy_replay: POLLOUT\n");
        /* Send all data received from the client to the server. */
        do
        {
            apr_size_t readlen;

            readlen = BUFSIZE;

            if (!servctx->servstream)
                servctx->servstream = serf__bucket_stream_create(
                                          servctx->allocator,
                                          detect_eof,servctx);
            status = serf_bucket_read(servctx->servstream, BUFSIZE,
                                      &buf, &readlen);
            if (SERF_BUCKET_READ_ERROR(status))
                return status;
            if (!readlen)
                break;

            len = readlen;

            serf__log(TEST_VERBOSE, __FILE__,
                      "proxy: sending %d bytes %.*s to server.\n",
                      len, len, buf);
            status = apr_socket_send(servctx->proxy_client_sock, buf, &len);
            if (status != APR_SUCCESS) {
                return status;
            }

            if (len != readlen) /* abort for now */
                return APR_EGENERAL;
        } while (!status);
    }
    else if (rtnevents & APR_POLLIN) {
        /* ignore */
    }
    else {
        printf("Unknown rtnevents: %d\n", rtnevents);
        abort();
    }

    return status;
}
Beispiel #7
0
/* Verify received requests and take the necessary actions
   (return a response, kill the connection ...) */
static apr_status_t replay(serv_ctx_t *servctx,
                           apr_int16_t rtnevents,
                           apr_pool_t *pool)
{
    apr_status_t status = APR_SUCCESS;
    test_server_action_t *action;

    if (rtnevents & APR_POLLIN) {
        if (servctx->message_list == NULL) {
            /* we're not expecting any requests to reach this server! */
            serf__log(TEST_VERBOSE, __FILE__,
                      "Received request where none was expected.\n");

            return SERF_ERROR_ISSUE_IN_TESTSUITE;
        }

        if (servctx->cur_action >= servctx->action_count) {
            char buf[128];
            apr_size_t len = sizeof(buf);

            status = servctx->read(servctx, buf, &len);
            if (! APR_STATUS_IS_EAGAIN(status)) {
                /* we're out of actions! */
                serf__log(TEST_VERBOSE, __FILE__,
                          "Received more requests than expected.\n");

                return SERF_ERROR_ISSUE_IN_TESTSUITE;
            }
            return status;
        }

        action = &servctx->action_list[servctx->cur_action];

        serf__log(TEST_VERBOSE, __FILE__,
                  "POLLIN while replaying action %d, kind: %d.\n",
                  servctx->cur_action, action->kind);

        /* Read the remaining data from the client and kill the socket. */
        if (action->kind == SERVER_IGNORE_AND_KILL_CONNECTION) {
            char buf[128];
            apr_size_t len = sizeof(buf);

            status = servctx->read(servctx, buf, &len);

            if (status == APR_EOF) {
                serf__log(TEST_VERBOSE, __FILE__,
                          "Killing this connection.\n");
                apr_socket_close(servctx->client_sock);
                servctx->client_sock = NULL;
                next_action(servctx);
                return APR_SUCCESS;
            }

            return status;
        }
        else if (action->kind == SERVER_RECV ||
                 (action->kind == SERVER_RESPOND &&
                  servctx->outstanding_responses == 0)) {
            apr_size_t msg_len, len;
            char buf[128];
            test_server_message_t *message;

            message = &servctx->message_list[servctx->cur_message];
            msg_len = strlen(message->text);

            do
            {
                len = msg_len - servctx->message_buf_pos;
                if (len > sizeof(buf))
                    len = sizeof(buf);

                status = servctx->read(servctx, buf, &len);
                if (SERF_BUCKET_READ_ERROR(status))
                    return status;

                if (status == APR_EOF) {
                    serf__log(TEST_VERBOSE, __FILE__,
                              "Server: Client hung up the connection.\n");
                    break;
                }
                if (servctx->options & TEST_SERVER_DUMP)
                    fwrite(buf, len, 1, stdout);

                if (strncmp(buf,
                            message->text + servctx->message_buf_pos,
                            len) != 0) {
                    /* ## TODO: Better diagnostics. */
                    printf("Expected: (\n");
                    fwrite(message->text + servctx->message_buf_pos, len, 1,
                           stdout);
                    printf(")\n");
                    printf("Actual: (\n");
                    fwrite(buf, len, 1, stdout);
                    printf(")\n");

                    return SERF_ERROR_ISSUE_IN_TESTSUITE;
                }

                servctx->message_buf_pos += len;

                if (servctx->message_buf_pos >= msg_len) {
                    next_message(servctx);
                    servctx->message_buf_pos -= msg_len;
                    if (action->kind == SERVER_RESPOND)
                        servctx->outstanding_responses++;
                    if (action->kind == SERVER_RECV)
                        next_action(servctx);
                    break;
                }
            } while (!status);
        }
        else if (action->kind == PROXY_FORWARD) {
            apr_size_t len;
            char buf[BUFSIZE];
            serf_bucket_t *tmp;

            /* Read all incoming data from the client to forward it to the
               server later. */
            do
            {
                len = BUFSIZE;

                status = servctx->read(servctx, buf, &len);
                if (SERF_BUCKET_READ_ERROR(status))
                    return status;

                serf__log(TEST_VERBOSE, __FILE__,
                          "proxy: reading %d bytes %.*s from client with "
                          "status %d.\n",
                          len, len, buf, status);

                if (status == APR_EOF) {
                    serf__log(TEST_VERBOSE, __FILE__,
                              "Proxy: client hung up the connection. Reset the "
                              "connection to the server.\n");
                    /* We have to stop forwarding, if a new connection opens
                       the CONNECT request should not be forwarded to the
                       server. */
                    next_action(servctx);
                }
                if (!servctx->servstream)
                    servctx->servstream = serf__bucket_stream_create(
                                              servctx->allocator,
                                              detect_eof,servctx);
                if (len) {
                    tmp = serf_bucket_simple_copy_create(buf, len,
                                                         servctx->allocator);
                    serf_bucket_aggregate_append(servctx->servstream, tmp);
                }
            } while (!status);
        }
    }
    if (rtnevents & APR_POLLOUT) {
        action = &servctx->action_list[servctx->cur_action];

        serf__log(TEST_VERBOSE, __FILE__,
                  "POLLOUT when replaying action %d, kind: %d.\n", servctx->cur_action,
                  action->kind);

        if (action->kind == SERVER_RESPOND && servctx->outstanding_responses) {
            apr_size_t msg_len;
            apr_size_t len;

            msg_len = strlen(action->text);
            len = msg_len - servctx->action_buf_pos;

            status = servctx->send(servctx,
                                   action->text + servctx->action_buf_pos,
                                   &len);
            if (status != APR_SUCCESS)
                return status;

            if (servctx->options & TEST_SERVER_DUMP)
                fwrite(action->text + servctx->action_buf_pos, len, 1, stdout);

            servctx->action_buf_pos += len;

            if (servctx->action_buf_pos >= msg_len) {
                next_action(servctx);
                servctx->outstanding_responses--;
            }
        }
        else if (action->kind == SERVER_KILL_CONNECTION ||
                 action->kind == SERVER_IGNORE_AND_KILL_CONNECTION) {
            serf__log(TEST_VERBOSE, __FILE__,
                      "Killing this connection.\n");
            apr_socket_close(servctx->client_sock);
            servctx->client_sock = NULL;
            next_action(servctx);
        }
        else if (action->kind == PROXY_FORWARD) {
            apr_size_t len;
            char *buf;

            if (!servctx->proxy_client_sock) {
                serf__log(TEST_VERBOSE, __FILE__, "Proxy: setting up connection "
                          "to server.\n");
                status = create_client_socket(&servctx->proxy_client_sock,
                                              servctx, action->text);
                if (!servctx->clientstream)
                    servctx->clientstream = serf__bucket_stream_create(
                                                servctx->allocator,
                                                detect_eof,servctx);
            }

            /* Send all data received from the server to the client. */
            do
            {
                apr_size_t readlen;

                readlen = BUFSIZE;

                status = serf_bucket_read(servctx->clientstream, readlen,
                                          &buf, &readlen);
                if (SERF_BUCKET_READ_ERROR(status))
                    return status;
                if (!readlen)
                    break;

                len = readlen;

                serf__log(TEST_VERBOSE, __FILE__,
                          "proxy: sending %d bytes to client.\n", len);
                status = servctx->send(servctx, buf, &len);
                if (status != APR_SUCCESS) {
                    return status;
                }
                
                if (len != readlen) /* abort for now, return buf to aggregate
                                       if not everything could be sent. */
                    return APR_EGENERAL;
            } while (!status);
        }
    }
    else if (rtnevents & APR_POLLIN) {
        /* ignore */
    }
    else {
        printf("Unknown rtnevents: %d\n", rtnevents);
        abort();
    }

    return status;
}
/* This function reads an encrypted stream and returns the decrypted stream. */
static apr_status_t ssl_decrypt(void *baton, apr_size_t bufsize,
                                char *buf, apr_size_t *len)
{
    serf_ssl_context_t *ctx = baton;
    apr_size_t priv_len;
    apr_status_t status;
    const char *data;
    int ssl_len;

#ifdef SSL_VERBOSE
    printf("ssl_decrypt: begin %d\n", bufsize);
#endif

    /* Is there some data waiting to be read? */
    ssl_len = SSL_read(ctx->ssl, buf, bufsize);
    if (ssl_len > 0) {
#ifdef SSL_VERBOSE
        printf("ssl_decrypt: %d bytes (%d); status: %d; flags: %d\n",
               ssl_len, bufsize, ctx->decrypt.status,
               BIO_get_retry_flags(ctx->bio));
#endif
        *len = ssl_len;
        return APR_SUCCESS;
    }

    status = serf_bucket_read(ctx->decrypt.stream, bufsize, &data, &priv_len);

    if (!SERF_BUCKET_READ_ERROR(status) && priv_len) {
        serf_bucket_t *tmp;

#ifdef SSL_VERBOSE
        printf("ssl_decrypt: read %d bytes (%d); status: %d\n", priv_len,
               bufsize, status);
#endif

        tmp = serf_bucket_simple_copy_create(data, priv_len,
                                             ctx->decrypt.pending->allocator);

        serf_bucket_aggregate_append(ctx->decrypt.pending, tmp);

        ssl_len = SSL_read(ctx->ssl, buf, bufsize);
        if (ssl_len < 0) {
            int ssl_err;

            ssl_err = SSL_get_error(ctx->ssl, ssl_len);
            switch (ssl_err) {
            case SSL_ERROR_SYSCALL:
                *len = 0;
                status = ctx->decrypt.status;
                break;
            case SSL_ERROR_WANT_READ:
                *len = 0;
                status = APR_EAGAIN;
                break;
            case SSL_ERROR_SSL:
                *len = 0;
                status = ctx->pending_err ? ctx->pending_err : APR_EGENERAL;
                ctx->pending_err = 0;
                break;
            default:
                *len = 0;
                status = APR_EGENERAL;
                break;
            }
        }
        else {
            *len = ssl_len;
#ifdef SSL_VERBOSE
            printf("---\n%s\n-(%d)-\n", buf, *len);
#endif
        }
    }
    else {
        *len = 0;
    }
#ifdef SSL_VERBOSE
    printf("ssl_decrypt: %d %d %d\n", status, *len,
           BIO_get_retry_flags(ctx->bio));
#endif
    return status;
}
static apr_status_t create_chunk(serf_bucket_t *bucket)
{
    chunk_context_t *ctx = bucket->data;
    serf_bucket_t *simple_bkt;
    apr_size_t chunk_len;
    apr_size_t stream_len;
    struct iovec vecs[66]; /* 64 + chunk trailer + EOF trailer = 66 */
    int vecs_read;
    int i;

    if (ctx->state != STATE_FETCH) {
        return APR_SUCCESS;
    }

    ctx->last_status =
        serf_bucket_read_iovec(ctx->stream, SERF_READ_ALL_AVAIL,
                               64, vecs, &vecs_read);

    if (SERF_BUCKET_READ_ERROR(ctx->last_status)) {
        /* Uh-oh. */
        return ctx->last_status;
    }

    /* Count the length of the data we read. */
    stream_len = 0;
    for (i = 0; i < vecs_read; i++) {
        stream_len += vecs[i].iov_len;
    }

    /* assert: stream_len in hex < sizeof(ctx->chunk_hdr) */

    /* Inserting a 0 byte chunk indicates a terminator, which already happens
     * during the EOF handler below.  Adding another one here will cause the
     * EOF chunk to be interpreted by the server as a new request.  So,
     * we'll only do this if we have something to write.
     */
    if (stream_len) {
        /* Build the chunk header. */
        chunk_len = apr_snprintf(ctx->chunk_hdr, sizeof(ctx->chunk_hdr),
                                 "%" APR_UINT64_T_HEX_FMT CRLF,
                                 (apr_uint64_t)stream_len);

        /* Create a copy of the chunk header so we can have multiple chunks
         * in the pipeline at the same time.
         */
        simple_bkt = serf_bucket_simple_copy_create(ctx->chunk_hdr, chunk_len,
                                                    bucket->allocator);
        serf_bucket_aggregate_append(ctx->chunk, simple_bkt);

        /* Insert the chunk footer. */
        vecs[vecs_read].iov_base = CRLF;
        vecs[vecs_read++].iov_len = sizeof(CRLF) - 1;
    }

    /* We've reached the end of the line for the stream. */
    if (APR_STATUS_IS_EOF(ctx->last_status)) {
        /* Insert the chunk footer. */
        vecs[vecs_read].iov_base = "0" CRLF CRLF;
        vecs[vecs_read++].iov_len = sizeof("0" CRLF CRLF) - 1;

        ctx->state = STATE_EOF;
    }
    else {
        /* Okay, we can return data.  */
        ctx->state = STATE_CHUNK;
    }

    serf_bucket_aggregate_append_iovec(ctx->chunk, vecs, vecs_read);

    return APR_SUCCESS;
}