static void
ngx_mail_auth_http_read_handler(ngx_event_t *rev)
{
    ssize_t                     n, size;
    ngx_connection_t          *c;
    ngx_mail_session_t        *s;
    ngx_mail_auth_http_ctx_t  *ctx;

    c = rev->data;
    s = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
                   "mail auth http read handler");

    ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);

    if (rev->timedout) {
        ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
                      "auth http server %V timed out", ctx->peer.name);
        ngx_close_connection(c);
        ngx_destroy_pool(ctx->pool);
        ngx_mail_session_internal_server_error(s);
        return;
    }

    if (ctx->response == NULL) {
        ctx->response = ngx_create_temp_buf(ctx->pool, 1024);
        if (ctx->response == NULL) {
            ngx_close_connection(c);
            ngx_destroy_pool(ctx->pool);
            ngx_mail_session_internal_server_error(s);
            return;
        }
    }

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

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

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

        ctx->handler(s, ctx);
        return;
    }

    if (n == NGX_AGAIN) {
        return;
    }

    ngx_close_connection(c);
    ngx_destroy_pool(ctx->pool);
    ngx_mail_session_internal_server_error(s);
}
Example #2
0
static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s)
{
    u_char     *p;
    ssize_t     n;
    ngx_buf_t  *b;

    b = s->proxy->buffer;

    n = ngx_recv(s->proxy->upstream.connection, b->last, b->end - b->last);

    if (n == NGX_ERROR || n == 0) {
        return NGX_ERROR;
    }

    if (n == NGX_AGAIN) {
        return NGX_AGAIN;
    }

    b->last += n;

    if (b->last - b->pos < 5) {
        return NGX_AGAIN;
    }

    if (*(b->last - 2) != CR || *(b->last - 1) != LF) {
        if (b->last == b->end) {
            *(b->last - 1) = '\0';
            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                          "upstream sent too long response line: \"%s\"",
                          b->pos);
            return NGX_IMAP_PROXY_INVALID;
        }

        return NGX_AGAIN;
    }

    p = b->pos;

    if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
        return NGX_OK;
    }

    if (p[0] == '-' && p[1] == 'E' && p[2] == 'R' && p[3] == 'R') {
        return NGX_IMAP_PROXY_ERROR;
    }

    *(b->last - 2) = '\0';
    ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                  "upstream sent invalid greeting line: \"%s\"", p);

    return NGX_IMAP_PROXY_INVALID;
}
int ngx_http_clojure_socket_upstream_read(ngx_http_clojure_socket_upstream_t *u, void *buf, size_t size) {
	ngx_connection_t  *c = u->peer.connection;
	ngx_int_t rc = ngx_recv(c, buf, size);
	if (rc == NGX_AGAIN) {
		if (u->read_timeout > 0) {
			ngx_add_timer(c->read, u->read_timeout);
		}
		rc = NGX_HTTP_CLOJURE_SOCKET_ERR_AGAIN;
	}else if (rc < 0) {
		rc = NGX_HTTP_CLOJURE_SOCKET_ERR_READ;
	}
	return rc;
}
Example #4
0
static void
ngx_mysql_read_query_result(ngx_event_t *rev)
{
    ssize_t                    n, len;
    ngx_str_t                  msg;
    ngx_mysql_t               *m;
    ngx_connection_t          *c;
    ngx_mysql_error_pkt_t     *epkt;
    ngx_mysql_response_pkt_t  *pkt;
    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "mysql read query result");
    c = rev->data;
    m = c->data;
    m->peer.log->action = "reading mysql read query result";
    n = ngx_recv(m->peer.connection, m->buf->pos, /* STUB */ 1024);
    if (n == NGX_AGAIN)
    {
        return;
    }
    if (n < 5)
    {
        ngx_mysql_close(m, NGX_ERROR);
        return;
    }
    pkt = (ngx_mysql_response_pkt_t *) m->buf->pos;
    len = ngx_m24toh(pkt->pktlen);
    if (len > n - 4)
    {
        ngx_log_error(NGX_LOG_ERR, rev->log, 0,
                      "mysql server %V sent incomplete response packet",
                      m->peer.name);
        ngx_mysql_close(m, NGX_ERROR);
        return;
    }
    if (pkt->fields != 0xff)
    {
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "mysql query OK");
        m->state = NGX_OK;
        m->pktn = pkt->pktn;
        m->handler(m);
        return;
    }
    epkt = (ngx_mysql_error_pkt_t *) pkt;
    msg.len = (u_char *) epkt + 4 + len - epkt->message;
    msg.data = epkt->message;
    ngx_log_error(NGX_LOG_ERR, rev->log, 0,
                  "mysql server %V sent error (%ud): \"%V\"",
                  m->peer.name, ngx_m16toh(epkt->code), &msg);
    ngx_mysql_close(m, NGX_ERROR);
}
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_lua_smtp_read_handler(ngx_event_t *rev)
{
    ssize_t              n;
    ngx_int_t            rc;
    ngx_buf_t           *b;
    ngx_connection_t    *c;
    ngx_lua_smtp_ctx_t  *ctx;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, rev->log, 0, "lua smtp read handler");

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

    if (rev->timedout) {
        ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
                      "lua smtp read %V timed out", ctx->peer.name);
        ngx_lua_smtp_finalize(ctx, "ngx_lua_smtp_read_handler() timed out");
        return;
    }

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

    b = ctx->response;

    while (1) {

        n = ngx_recv(c, b->last, b->end - b->last);

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

            rc = ngx_lua_smtp_handle_response(ctx);

            if (rc == NGX_OK) {
                return;
            }

            if (rc == NGX_AGAIN) {
                continue;
            }

            if (rc == NGX_DONE) {
                ngx_lua_smtp_finalize(ctx, NULL);
                return;
            }

            /* rc == NGX_ERROR */

            ngx_lua_smtp_finalize(ctx, "ngx_lua_smtp_handle_response() failed");
            return;
        }

        if (n == NGX_AGAIN) {
            ngx_add_timer(rev, ctx->read_timeout);
            ctx->rc = NGX_AGAIN;
            return;
        }

        /* n == NGX_ERROR || n == 0 */

        ngx_lua_smtp_finalize(ctx, "ngx_recv() failed");
        return;
    }
}
static void
ngx_http_cloudrouter_peer_preconnect_read(ngx_event_t *rev) {
    ngx_connection_t    *c;
    ngx_http_cloudrouter_peer_preconnect_data_t *pcd;
    ngx_http_upstream_t *u;
    ngx_http_request_t  *r;
    int                 i;
    hs_route_t         *route;

    c   = rev->data;
    pcd = c->data;

    r   = pcd->r;
    u   = pcd->u;

    route = &pcd->route;

    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "preconnect: read");

    if (pcd->done>0) {
        return;
    }

    if (r->main==NULL||r->request_complete||r->pool==NULL||r!=r->main) {
        ngx_close_connection(c); c->destroyed = 1;
        return;
    }

    if (rev->timedout) {
        ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
                      "cloudrouter preconnect server timed out");
        return ngx_http_cloudrouter_peer_preconnect_close(c, pcd, NGX_ERROR);
    }

    if (pcd->buf==NULL) {
        int size = sizeof(char)*1000;
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "creating buf");
        pcd->buf = ngx_pcalloc(r->pool, size);
        pcd->bufend = pcd->buf+size;
        pcd->bufpos = pcd->buf;

        if (pcd->buf==NULL) {
            ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ENOMEM,
                          "preconnect: read: could not allocate buf");
            return ngx_http_cloudrouter_peer_preconnect_close(c, pcd, NGX_ERROR);
        }
    }

    /*
     * Protocol format:
     * IP1\nPORT1\n
     * (optional) IPz\nPORTz\n
     * --
     */

    int bufsize = pcd->bufend - pcd->bufpos;
    ngx_int_t received;
    if (bufsize > 0) {
        received = ngx_recv(c, pcd->bufpos, bufsize);
    } else {
        received = 0;
    }

    if (received==NGX_AGAIN) {
        return;
    } else if (received>=0) {
        pcd->bufpos += received;

        for (i=0;i<(pcd->bufpos-pcd->buf);i++) {
            if (*(pcd->buf + i )=='-') {
                pcd->end_marker_count++;
            }
        }

        if (pcd->end_marker_count>=2) {
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "CloudRouter preconnect: message complete");

            ngx_http_cloudrouter_peer_t *peer = (ngx_http_cloudrouter_peer_t *)u->peer.data;
            unsigned char* next = pcd->buf;
            int new_node = 0;

            ngx_shmtx_lock(&ngx_http_cloudrouter_shpool->mutex);

            ngx_http_cloudrouter_node_t *e = ngx_http_cloudrouter_get_locked(route);
            if (e == NULL) {
                /* likely() */

                e = ngx_slab_alloc_locked(ngx_http_cloudrouter_shpool, sizeof *e);
                new_node = 1;

                e->node.key = ngx_http_cloudrouter_hash_route(route);

                (void)ngx_copy(e->di_name, route->di_name, sizeof(route->di_name));
                e->di_nlen = route->di_nlen;
            } else {
                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "CloudRouter preconnect: reusing existing node");
            }

            e->timestamp = time(NULL);
            ngx_http_cloudrouter_clear_remotes_locked(e, rev->log);

            while (next < pcd->bufpos) {
                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "CloudRouter preconnect: parsing message");

                unsigned char *ip = NULL;
                unsigned char *port = NULL;
                ip = next;
                while (++next < pcd->bufpos) {
                    if (*(next-1) == '\n') {
                        if (ip && port)
                            break;
                        port = next;
                    }
                }

                if (ip && port) {
                    ngx_http_cloudrouter_remote_t *remote = ngx_http_cloudrouter_add_remote_locked(e);

                    int iplen = port-ip-1;
                    iplen = iplen > 16 ? 16 : iplen;

                    remote->inet_addr = ngx_inet_addr(ip, iplen);
                    if (remote->inet_addr == INADDR_NONE) {
                        ngx_log_error(NGX_LOG_ERR, rev->log, NGX_EINVAL,
                                      "CloudRouter preconnect: IP address from Agent invalid for route %s",
                                      e->di_name);
                        goto failure;
                    }

                    int portlen = next-port-1;
                    remote->port_n = htons(ngx_atoi(port,portlen));

                    ngx_log_debug7(NGX_LOG_DEBUG_HTTP, rev->log, 0,
                                   "CloudRouter preconnect: cached values SET: e=%p rem=%p ts=%d %s[%d] %uxD:%d",
                                   e, remote, e->timestamp,
                                   e->di_name, e->di_nlen,
                                   remote->inet_addr, remote->port_n);
                }
            }

            if (!e->remote) {
                ngx_log_debug(NGX_LOG_DEBUG_HTTP, rev->log, 0,
                              "CloudRouter preconnect: Agent sent no remotes");
                goto failure;
            }

            ngx_http_cloudrouter_set_hostandport(r, peer, e);
            if (new_node) {
                ngx_rbtree_insert(ngx_http_cloudrouter_rbtree, &e->node);
            }
            ngx_shmtx_unlock(&ngx_http_cloudrouter_shpool->mutex);
            return ngx_http_cloudrouter_peer_preconnect_close(c, pcd, NGX_OK);

failure:
            if (!new_node) {
                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pcd->r->connection->log, 0, "peer_preconnect_read: calling rbtree_delete");
                ngx_rbtree_delete(ngx_http_cloudrouter_rbtree, &e->node);
            }
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pcd->r->connection->log, 0, "peer_preconnect_read: calling free_node_locked");
            ngx_http_cloudrouter_free_node_locked(e, rev->log);
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pcd->r->connection->log, 0, "peer_preconnect_read: calling shmtx_unlock");
            ngx_shmtx_unlock(&ngx_http_cloudrouter_shpool->mutex);
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pcd->r->connection->log, 0, "peer_preconnect_read: calling peer_preconnect_close");
            return ngx_http_cloudrouter_peer_preconnect_close(c, pcd, NGX_ERROR);
        }
        return;
    }

    /* unknown error condition from ngx_recv */
    return;
}
Example #8
0
static void
ngx_mysql_read_server_greeting(ngx_event_t *rev)
{
    size_t                      len;
    u_char                     *p;
    ssize_t                     n;
    ngx_uint_t                  i, capacity;
    ngx_mysql_t                *m;
    ngx_connection_t           *c;
    ngx_mysql_greeting1_pkt_t  *gr1;
    ngx_mysql_greeting2_pkt_t  *gr2;
    ngx_mysql_auth_pkt_t       *auth;
    ngx_sha1_t                  sha;
    u_char                      hash1[20], hash2[20];
    c = rev->data;
    m = c->data;
    if (rev->timedout)
    {
        ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
                      "mysql server %V timed out", m->peer.name);
        ngx_mysql_close(m, NGX_ERROR);
        return;
    }
    if (m->buf == NULL)
    {
        m->peer.log->action = "reading mysql server greeting";
        m->buf = ngx_create_temp_buf(m->pool, /* STUB */ 1024);
        if (m->buf == NULL)
        {
            ngx_mysql_close(m, NGX_ERROR);
            return;
        }
    }
    n = ngx_recv(m->peer.connection, m->buf->pos, /* STUB */ 1024);
    if (n == NGX_AGAIN)
    {
        return;
    }
    if (n < 5)
    {
        ngx_mysql_close(m, NGX_ERROR);
        return;
    }
    gr1 = (ngx_mysql_greeting1_pkt_t *) m->buf->pos;
    if (ngx_m24toh(gr1->pktlen) > n - 4)
    {
        ngx_log_error(NGX_LOG_ERR, rev->log, 0,
                      "mysql server %V sent incomplete greeting packet",
                      m->peer.name);
        ngx_mysql_close(m, NGX_ERROR);
        return;
    }
    if (gr1->protocol < 10)
    {
        ngx_log_error(NGX_LOG_ERR, rev->log, 0,
                      "mysql server %V sent unsupported protocol version %ud",
                      m->peer.name, gr1->protocol);
        ngx_mysql_close(m, NGX_ERROR);
        return;
    }
    gr2 = (ngx_mysql_greeting2_pkt_t *)
          (gr1->version + ngx_strlen(gr1->version) + 1);
    capacity = ngx_m16toh(gr2->capacity);
    ngx_log_debug8(NGX_LOG_DEBUG_MYSQL, rev->log, 0,
                   "mysql version: %ud, \"%s\", thread: %ud, salt: \"%s\", "
                   "capacity: %Xd, charset: %ud, status: %ud, salt rest \"%s\"",
                   gr1->protocol, gr1->version, ngx_m32toh(gr2->thread),
                   gr2->salt1, capacity, gr2->charset,
                   ngx_m16toh(gr2->status), &gr2->salt2);
    capacity = NGX_MYSQL_LONG_PASSWORD
               | NGX_MYSQL_CONNECT_WITH_DB
               | NGX_MYSQL_PROTOCOL_41
               | NGX_MYSQL_SECURE_CONNECTION;
    len = 4 + 4 + 4 + 1 + 23 + m->login->len + 1 + 1 + m->database->len + 1;
    if (m->passwd->len)
    {
        len += 20;
    }
    auth = ngx_pnalloc(m->pool, len);
    if (auth == NULL)
    {
        ngx_mysql_close(m, NGX_ERROR);
        return;
    }
    ngx_htom24(auth->pktlen, len - 4);
    auth->pktn = (u_char)(gr1->pktn + 1);
    ngx_htom32(auth->capacity, capacity);
    ngx_htom32(auth->max_packet, 0x01000000);  /* max packet size 2^24 */
    ngx_memzero(auth->zero, 24);
    auth->charset = gr2->charset;
    p = ngx_copy(auth->login, m->login->data, m->login->len);
    *p++ = '\0';
    if (m->passwd->len)
    {
        *p++ = (u_char) 20;
        ngx_sha1_init(&sha);
        ngx_sha1_update(&sha, m->passwd->data, m->passwd->len);
        ngx_sha1_final(hash1, &sha);
        ngx_sha1_init(&sha);
        ngx_sha1_update(&sha, hash1, 20);
        ngx_sha1_final(hash2, &sha);
        ngx_sha1_init(&sha);
        ngx_sha1_update(&sha, gr2->salt1, 8);
        ngx_sha1_update(&sha, gr2->salt2, 12);
        ngx_sha1_update(&sha, hash2, 20);
        ngx_sha1_final(hash2, &sha);
        for (i = 0; i < 20; i++)
        {
            *p++ = (u_char)(hash1[i] ^ hash2[i]);
        }
    }
    else
    {
        *p++ = '\0';
    }
    p = ngx_copy(p, m->database->data, m->database->len);
    *p = '\0';
    n = ngx_send(m->peer.connection, (void *) auth, len);
    if (n < (ssize_t) len)
    {
        ngx_log_error(NGX_LOG_ERR, rev->log, 0,
                      "the incomplete packet was sent to mysql server %V",
                      m->peer.name);
        ngx_mysql_close(m, NGX_ERROR);
        return;
    }
    m->peer.connection->read->handler = ngx_mysql_read_auth_result;
    ngx_add_timer(m->peer.connection->read, /* STUB */ 5000);
}
static void
ngx_squ_tcp_request_read_handler(ngx_event_t *rev)
{
    char               *errstr;
    ssize_t             n;
    ngx_int_t           rc;
    ngx_buf_t          *b;
    ngx_connection_t   *c;
    ngx_squ_thread_t   *thr;
    ngx_squ_tcp_ctx_t  *ctx;
    ngx_tcp_session_t  *s;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, rev->log, 0,
                   "squ tcp request read handler");

    c = rev->data;
    s = c->data;

    thr = ngx_tcp_get_module_ctx(s, ngx_squ_tcp_module);

    ctx = thr->module_ctx;
    b = s->buffer;
    errstr = NULL;

    if (rev->timedout) {
        ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
                      "squ tcp request read %V timed out", &c->addr_text);
        errstr = "ngx_squ_tcp_request_read_handler() timed out";
        n = NGX_ERROR;
        goto done;
    }

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

    while (1) {

        n = ngx_recv(c, b->last, b->end - b->last);

        if (n > 0) {
            b->last += n;
            break;
        }

        if (n == NGX_AGAIN) {
            /* TODO */
            ngx_add_timer(rev, 60000);

            if (ngx_handle_read_event(rev, 0) != NGX_OK) {
                errstr = "ngx_handle_read_event() failed";
                n = NGX_ERROR;
                goto done;
            }

            ctx->rc = NGX_AGAIN;
            return;
        }

        /* n == NGX_ERROR || n == 0 */

        break;
    }

done:

    rev->handler = ngx_squ_tcp_request_dummy_handler;

    ctx->rc = 1;

    if (n > 0) {
        sq_pushstring(thr->v, (SQChar *) b->pos, n);

    } else {
        sq_pushbool(thr->v, SQFalse);

#if 0
        if (errstr != NULL) {
            sq_pushstring(thr->v, errstr, -1);

            ctx->rc++;
        }
#endif
    }

    if (ctx->not_event) {
        return;
    }

    rc = ngx_squ_thread_run(thr, ctx->rc);
    if (rc == NGX_AGAIN) {
        return;
    }

    ngx_squ_finalize(thr, rc);
}
Example #10
0
static void ngx_imap_proxy_handler(ngx_event_t *ev)
{
    size_t               size;
    ssize_t              n;
    ngx_buf_t           *b;
    ngx_uint_t           again, do_write;
    ngx_connection_t    *c, *src, *dst;
    ngx_imap_session_t  *s;

    c = ev->data;
    s = c->data;

    if (ev->timedout) {
        if (c == s->connection) {
            ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
                          "client timed out");
        } else {
            ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
                          "upstream timed out");
        }

        ngx_imap_proxy_close_session(s);
        return;
    }

    if (c == s->connection) {
        if (ev->write) {
            src = s->proxy->upstream.connection;
            dst = c;
            b = s->proxy->buffer;

        } else {
            src = c;
            dst = s->proxy->upstream.connection;
            b = s->buffer;
        }

    } else {
        if (ev->write) {
            src = s->connection;
            dst = c;
            b = s->buffer;

        } else {
            src = c;
            dst = s->connection;
            b = s->proxy->buffer;
        }
    }

    do_write = ev->write ? 1 : 0;

    ngx_log_debug3(NGX_LOG_DEBUG_IMAP, ev->log, 0,
                   "imap proxy handler: %d, #%d > #%d",
                   do_write, src->fd, dst->fd);

    do {
        again = 0;

        if (do_write == 1) {

            size = b->last - b->pos;

            if (size && dst->write->ready) {
                n = ngx_send(dst, b->pos, size);

                if (n == NGX_ERROR) {
                    ngx_imap_proxy_close_session(s);
                    return;
                }

                if (n > 0) {
                    again = 1;
                    b->pos += n;

                    if (b->pos == b->last) {
                        b->pos = b->start;
                        b->last = b->start;
                    }
                }

                if (n == NGX_AGAIN || n < (ssize_t) size) {
                    dst->write->available = 0;
                    if (ngx_handle_write_event(dst->write, NGX_LOWAT_EVENT)
                                                                  == NGX_ERROR)
                    {
                        ngx_imap_proxy_close_session(s);
                        return;
                    }
                }
            }
        }

        size = b->end - b->last;

        if (size && src->read->ready) {
            n = ngx_recv(src, b->last, size);

            if (n == NGX_ERROR || n == 0) {
                ngx_imap_proxy_close_session(s);
                return;
            }

            if (n > 0) {
                again = 1;
                do_write = 1;
                b->last += n;
            }

            if (n == NGX_AGAIN || n < (ssize_t) size) {
                if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) {
                    ngx_imap_proxy_close_session(s);
                    return;
                }
            }
        }

    } while (again);
}