static void
ngx_ssl_ocsp_write_handler(ngx_event_t *wev)
{
    ssize_t              n, size;
    ngx_connection_t    *c;
    ngx_ssl_ocsp_ctx_t  *ctx;

    c = wev->data;
    ctx = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0,
                   "ssl ocsp write handler");

    if (wev->timedout) {
        ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
                      "OCSP responder timed out");
        ngx_ssl_ocsp_error(ctx);
        return;
    }

    size = ctx->request->last - ctx->request->pos;

    n = ngx_send(c, ctx->request->pos, size);

    if (n == NGX_ERROR) {
        ngx_ssl_ocsp_error(ctx);
        return;
    }

    if (n > 0) {
        ctx->request->pos += n;

        if (n == size) {
            wev->handler = ngx_ssl_ocsp_dummy_handler;

            if (wev->timer_set) {
                ngx_del_timer(wev);
            }

            if (ngx_handle_write_event(wev, 0) != NGX_OK) {
                ngx_ssl_ocsp_error(ctx);
            }

            return;
        }
    }

    if (!wev->timer_set) {
        ngx_add_timer(wev, ctx->timeout);
    }
}
static void
ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx)
{
    ngx_resolver_ctx_t  *resolve, temp;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
                   "ssl ocsp request");

    if (ngx_ssl_ocsp_create_request(ctx) != NGX_OK) {
        ngx_ssl_ocsp_error(ctx);
        return;
    }

    if (ctx->resolver) {
        /* resolve OCSP responder hostname */

        temp.name = ctx->host;

        resolve = ngx_resolve_start(ctx->resolver, &temp);
        if (resolve == NULL) {
            ngx_ssl_ocsp_error(ctx);
            return;
        }

        if (resolve == NGX_NO_RESOLVER) {
            ngx_log_error(NGX_LOG_WARN, ctx->log, 0,
                          "no resolver defined to resolve %V", &ctx->host);
            goto connect;
        }

        resolve->name = ctx->host;
        resolve->type = NGX_RESOLVE_A;
        resolve->handler = ngx_ssl_ocsp_resolve_handler;
        resolve->data = ctx;
        resolve->timeout = ctx->resolver_timeout;

        if (ngx_resolve_name(resolve) != NGX_OK) {
            ngx_ssl_ocsp_error(ctx);
            return;
        }

        return;
    }

connect:

    ngx_ssl_ocsp_connect(ctx);
}
static void
ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)
{
    ngx_int_t    rc;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
                   "ssl ocsp connect");

    /* TODO: use all ip addresses */

    ctx->peer.sockaddr = ctx->addrs[0].sockaddr;
    ctx->peer.socklen = ctx->addrs[0].socklen;
    ctx->peer.name = &ctx->addrs[0].name;
    ctx->peer.get = ngx_event_get_peer;
    ctx->peer.log = ctx->log;
    ctx->peer.log_error = NGX_ERROR_ERR;

    rc = ngx_event_connect_peer(&ctx->peer);

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
                   "ssl ocsp connect peer done");

    if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
        ngx_ssl_ocsp_error(ctx);
        return;
    }

    ctx->peer.connection->data = ctx;
    ctx->peer.connection->pool = ctx->pool;

    ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler;
    ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler;

    ctx->process = ngx_ssl_ocsp_process_status_line;

    ngx_add_timer(ctx->peer.connection->read, ctx->timeout);
    ngx_add_timer(ctx->peer.connection->write, ctx->timeout);

    if (rc == NGX_OK) {
        ngx_ssl_ocsp_write_handler(ctx->peer.connection->write);
        return;
    }
}
static void
ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve)
{
    ngx_ssl_ocsp_ctx_t *ctx = resolve->data;

    u_char           *p;
    size_t            len;
    in_port_t         port;
    socklen_t         socklen;
    ngx_uint_t        i;
    struct sockaddr  *sockaddr;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
                   "ssl ocsp resolve handler");

    if (resolve->state) {
        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
                      "%V could not be resolved (%i: %s)",
                      &resolve->name, resolve->state,
                      ngx_resolver_strerror(resolve->state));
        goto failed;
    }

#if (NGX_DEBUG)
    {
    u_char     text[NGX_SOCKADDR_STRLEN];
    ngx_str_t  addr;

    addr.data = text;

    for (i = 0; i < resolve->naddrs; i++) {
        addr.len = ngx_sock_ntop(resolve->addrs[i].sockaddr,
                                 resolve->addrs[i].socklen,
                                 text, NGX_SOCKADDR_STRLEN, 0);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
                       "name was resolved to %V", &addr);

    }
    }
#endif

    ctx->naddrs = resolve->naddrs;
    ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t));

    if (ctx->addrs == NULL) {
        goto failed;
    }

    port = htons(ctx->port);

    for (i = 0; i < resolve->naddrs; i++) {

        socklen = resolve->addrs[i].socklen;

        sockaddr = ngx_palloc(ctx->pool, socklen);
        if (sockaddr == NULL) {
            goto failed;
        }

        ngx_memcpy(sockaddr, resolve->addrs[i].sockaddr, socklen);

        switch (sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
        case AF_INET6:
            ((struct sockaddr_in6 *) sockaddr)->sin6_port = port;
            break;
#endif
        default: /* AF_INET */
            ((struct sockaddr_in *) sockaddr)->sin_port = port;
        }

        ctx->addrs[i].sockaddr = sockaddr;
        ctx->addrs[i].socklen = socklen;

        p = ngx_pnalloc(ctx->pool, NGX_SOCKADDR_STRLEN);
        if (p == NULL) {
            goto failed;
        }

        len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);

        ctx->addrs[i].name.len = len;
        ctx->addrs[i].name.data = p;
    }

    ngx_resolve_name_done(resolve);

    ngx_ssl_ocsp_connect(ctx);
    return;

failed:

    ngx_resolve_name_done(resolve);
    ngx_ssl_ocsp_error(ctx);
}
static void
ngx_ssl_ocsp_read_handler(ngx_event_t *rev)
{
    ssize_t            n, size;
    ngx_int_t          rc;
    ngx_ssl_ocsp_ctx_t    *ctx;
    ngx_connection_t  *c;

    c = rev->data;
    ctx = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0,
                   "ssl ocsp read handler");

    if (rev->timedout) {
        ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
                      "OCSP responder timed out");
        ngx_ssl_ocsp_error(ctx);
        return;
    }

    if (ctx->response == NULL) {
        ctx->response = ngx_create_temp_buf(ctx->pool, 16384);
        if (ctx->response == NULL) {
            ngx_ssl_ocsp_error(ctx);
            return;
        }
    }

    for ( ;; ) {

        size = ctx->response->end - ctx->response->last;

        n = ngx_recv(c, ctx->response->last, size);

        if (n > 0) {
            ctx->response->last += n;

            rc = ctx->process(ctx);

            if (rc == NGX_ERROR) {
                ngx_ssl_ocsp_error(ctx);
                return;
            }

            continue;
        }

        if (n == NGX_AGAIN) {

            if (ngx_handle_read_event(rev, 0) != NGX_OK) {
                ngx_ssl_ocsp_error(ctx);
            }

            return;
        }

        break;
    }

    ctx->done = 1;

    rc = ctx->process(ctx);

    if (rc == NGX_DONE) {
        /* ctx->handler() was called */
        return;
    }

    ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
                  "OCSP responder prematurely closed connection");

    ngx_ssl_ocsp_error(ctx);
}
static void
ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve)
{
    ngx_ssl_ocsp_ctx_t *ctx = resolve->data;

    u_char              *p;
    size_t               len;
    in_port_t            port;
    ngx_uint_t           i;
    struct sockaddr_in  *sin;

    ngx_log_debug0(NGX_LOG_ALERT, ctx->log, 0,
                   "ssl ocsp resolve handler");

    if (resolve->state) {
        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
                      "%V could not be resolved (%i: %s)",
                      &resolve->name, resolve->state,
                      ngx_resolver_strerror(resolve->state));
        goto failed;
    }

#if (NGX_DEBUG)
    {
    in_addr_t   addr;

    for (i = 0; i < resolve->naddrs; i++) {
        addr = ntohl(resolve->addrs[i]);

        ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
                       "name was resolved to %ud.%ud.%ud.%ud",
                       (addr >> 24) & 0xff, (addr >> 16) & 0xff,
                       (addr >> 8) & 0xff, addr & 0xff);
    }
    }
#endif

    ctx->naddrs = resolve->naddrs;
    ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t));

    if (ctx->addrs == NULL) {
        goto failed;
    }

    port = htons(ctx->port);

    for (i = 0; i < resolve->naddrs; i++) {

        sin = ngx_pcalloc(ctx->pool, sizeof(struct sockaddr_in));
        if (sin == NULL) {
            goto failed;
        }

        sin->sin_family = AF_INET;
        sin->sin_port = port;
        sin->sin_addr.s_addr = resolve->addrs[i];

        ctx->addrs[i].sockaddr = (struct sockaddr *) sin;
        ctx->addrs[i].socklen = sizeof(struct sockaddr_in);

        len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;

        p = ngx_pnalloc(ctx->pool, len);
        if (p == NULL) {
            goto failed;
        }

        len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1);

        ctx->addrs[i].name.len = len;
        ctx->addrs[i].name.data = p;
    }

    ngx_resolve_name_done(resolve);

    ngx_ssl_ocsp_connect(ctx);
    return;

failed:

    ngx_resolve_name_done(resolve);
    ngx_ssl_ocsp_error(ctx);
}