Exemplo n.º 1
0
ngx_int_t
ngx_mysql_query(ngx_mysql_t *m)
{
    ssize_t                   n;
    ngx_mysql_command_pkt_t  *pkt;

    pkt = (ngx_mysql_command_pkt_t *) m->query.data;

    ngx_htom24(pkt->pktlen, m->query.len - 4);
    pkt->pktn = (u_char) m->pktn++;
    pkt->command = NGX_MYSQL_CMD_QUERY;

    n = ngx_send(m->peer.connection, m->query.data, m->query.len);

    if (n < (ssize_t) m->query.len) {
        ngx_log_error(NGX_LOG_ERR, m->peer.log, 0,
                      "the incomplete packet was sent to mysql server %V",
                      m->peer.name);

        ngx_mysql_close(m, NGX_ERROR);
        return NGX_OK;
    }

    m->peer.connection->read->handler = ngx_mysql_read_query_result;

    ngx_add_timer(m->peer.connection->read, /* STUB */ 5000);

    /* STUB handle event */

    return NGX_OK;
}
Exemplo n.º 2
0
static ngx_int_t
ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn)
{
    ssize_t                n;
    ngx_udp_connection_t  *uc;

    uc = r->udp_connection;

    if (uc->connection == NULL) {
        if (ngx_udp_connect(uc) != NGX_OK) {
            return NGX_ERROR;
        }

        uc->connection->data = r;
        uc->connection->read->handler = ngx_resolver_read_response;
        uc->connection->read->resolver = 1;
    }

    n = ngx_send(uc->connection, rn->query, rn->qlen);

    if (n == -1) {
        return NGX_ERROR;
    }

    if ((size_t) n != (size_t) rn->qlen) {
        ngx_log_error(NGX_LOG_CRIT, &uc->log, 0, "send() incomplete");
        return NGX_ERROR;
    }

    return NGX_OK;
}
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);
    }
}
Exemplo n.º 4
0
static ngx_int_t
ngx_http_statsd_udp_send(ngx_udp_endpoint_t *l, u_char *buf, size_t len)
{
    ssize_t                n;
    ngx_udp_connection_t  *uc;

    uc = l->udp_connection;
    
    if (!uc) {
        return NGX_ERROR;
    }
    if (uc->connection == NULL) {

        uc->log = *l->log;
        uc->log.handler = NULL;
        uc->log.data = NULL;
        uc->log.action = "logging";

        if(ngx_udp_connect(uc) != NGX_OK) {
            if(uc->connection != NULL) {
                ngx_free_connection(uc->connection);
                uc->connection = NULL;
            }

            return NGX_ERROR;
        }

        uc->connection->data = l;
        uc->connection->read->handler = ngx_http_statsd_udp_dummy_handler;
        uc->connection->read->resolver = 0;
    }

    n = ngx_send(uc->connection, buf, len);

    if (n == -1) {
        return NGX_ERROR;
    }

    if ((size_t) n != (size_t) len) {
#if defined nginx_version && nginx_version >= 8032
        ngx_log_error(NGX_LOG_CRIT, &uc->log, 0, "send() incomplete");
#else
        ngx_log_error(NGX_LOG_CRIT, uc->log, 0, "send() incomplete");
#endif
        return NGX_ERROR;
    }

    return NGX_OK;
}
int ngx_http_clojure_socket_upstream_write(ngx_http_clojure_socket_upstream_t *u, void *buf, size_t size) {
	ngx_connection_t  *c = u->peer.connection;
	ngx_int_t rc = ngx_send(c, buf, size);
	if (rc == 0 || rc == NGX_AGAIN) {
		/*Because if connected immediately successfully or we have deleted this event in
		 * ngx_http_clojure_socket_upstream_handler the write event was not registered
		 * so we need register it here.*/
		if (!c->write->active) {
			(void)ngx_handle_write_event(c->write, 0);
		}
		if (u->write_timeout > 0) {
			ngx_add_timer(c->write, u->write_timeout);
		}
		rc = NGX_HTTP_CLOJURE_SOCKET_ERR_AGAIN;
	}else if (rc < 0) {
		rc = NGX_HTTP_CLOJURE_SOCKET_ERR_WRITE;
	}
	return rc;
}
Exemplo n.º 6
0
static ngx_int_t
ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn)
{
    ssize_t                n;
    ngx_udp_connection_t  *uc;

    uc = r->udp_connections.elts;

    uc = &uc[r->last_connection++];
    if (r->last_connection == r->udp_connections.nelts) {
        r->last_connection = 0;
    }

    if (uc->connection == NULL) {

        uc->log = *r->log;
        uc->log.handler = ngx_resolver_log_error;
        uc->log.data = uc;
        uc->log.action = "resolving";

        if (ngx_udp_connect(uc) != NGX_OK) {
            return NGX_ERROR;
        }

        uc->connection->data = r;
        uc->connection->read->handler = ngx_resolver_read_response;
        uc->connection->read->resolver = 1;
    }

    n = ngx_send(uc->connection, rn->query, rn->qlen);

    if (n == -1) {
        return NGX_ERROR;
    }

    if ((size_t) n != (size_t) rn->qlen) {
        ngx_log_error(NGX_LOG_CRIT, &uc->log, 0, "send() incomplete");
        return NGX_ERROR;
    }

    return NGX_OK;
}
static void
ngx_http_cloudrouter_peer_preconnect_write(ngx_event_t *wev) {
    ngx_connection_t *c;
    ngx_http_cloudrouter_peer_preconnect_data_t *pcd;
    ngx_http_upstream_t *u;
    ngx_http_request_t *r;
    ngx_http_cloudrouter_peer_t *peer;

    c = wev->data;
    pcd = c->data;

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

    if(wev->timedout) {
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                      "preconnect: write: timedout");
        return ngx_http_cloudrouter_peer_preconnect_close(c, pcd, NGX_ERROR);
    }

    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;
    }

    peer = (ngx_http_cloudrouter_peer_t*)u->peer.data;

    if(pcd->sendbufpos < peer->sendbuf.len) {
        int n = ngx_send(c, peer->sendbuf.data + pcd->sendbufpos,
                         peer->sendbuf.len - pcd->sendbufpos);
        pcd->sendbufpos += n;
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "preconnect: write: %d of %d", pcd->sendbufpos,
                       peer->sendbuf.len);
    }
}
static void
ngx_mail_auth_http_write_handler(ngx_event_t *wev)
{
    ssize_t                     n, size;
    ngx_connection_t           *c;
    ngx_mail_session_t         *s;
    ngx_mail_auth_http_ctx_t   *ctx;
    ngx_mail_auth_http_conf_t  *ahcf;

    c = wev->data;
    s = c->data;

    ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0,
                   "mail auth http write handler");

    if (wev->timedout) {
        ngx_log_error(NGX_LOG_ERR, wev->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;
    }

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

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

    if (n == NGX_ERROR) {
        ngx_close_connection(c);
        ngx_destroy_pool(ctx->pool);
        ngx_mail_session_internal_server_error(s);
        return;
    }

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

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

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

            if (ngx_handle_write_event(wev, 0) == NGX_ERROR) {
                ngx_close_connection(c);
                ngx_destroy_pool(ctx->pool);
                ngx_mail_session_internal_server_error(s);
            }

            return;
        }
    }

    if (!wev->timer_set) {
        ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
        ngx_add_timer(wev, ahcf->timeout);
    }
}
static int
ngx_http_lua_socket_udp_send(lua_State *L)
{
    ssize_t                              n;
    ngx_http_request_t                  *r;
    u_char                              *p;
    size_t                               len;
    ngx_http_lua_socket_udp_upstream_t  *u;
    int                                  type;
    const char                          *msg;
    ngx_str_t                            query;
    ngx_http_lua_loc_conf_t             *llcf;

    if (lua_gettop(L) != 2) {
        return luaL_error(L, "expecting 2 arguments (including the object), "
                          "but got %d", lua_gettop(L));
    }

    lua_pushlightuserdata(L, &ngx_http_lua_request_key);
    lua_rawget(L, LUA_GLOBALSINDEX);
    r = lua_touserdata(L, -1);
    lua_pop(L, 1);

    if (r == NULL) {
        return luaL_error(L, "request object not found");
    }

    luaL_checktype(L, 1, LUA_TTABLE);

    lua_rawgeti(L, 1, SOCKET_CTX_INDEX);
    u = lua_touserdata(L, -1);
    lua_pop(L, 1);

    if (u == NULL || u->udp_connection.connection == NULL) {
        llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);

        if (llcf->log_socket_errors) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "attempt to send data on a closed socket: u:%p, c:%p",
                          u, u ? u->udp_connection.connection : NULL);
        }

        lua_pushnil(L);
        lua_pushliteral(L, "closed");
        return 2;
    }

    if (u->ft_type) {
        u->ft_type = 0;
    }

    if (u->waiting) {
        lua_pushnil(L);
        lua_pushliteral(L, "socket busy");
        return 2;
    }

    type = lua_type(L, 2);
    switch (type) {
        case LUA_TNUMBER:
        case LUA_TSTRING:
            lua_tolstring(L, 2, &len);
            break;

        case LUA_TTABLE:
            len = ngx_http_lua_calc_strlen_in_table(L, 2, 2, 1 /* strict */);
            break;

        default:
            msg = lua_pushfstring(L, "string, number, boolean, nil, "
                                  "or array table expected, got %s",
                                  lua_typename(L, type));

            return luaL_argerror(L, 2, msg);
    }

    query.data = lua_newuserdata(L, len);
    query.len = len;

    switch (type) {
        case LUA_TNUMBER:
        case LUA_TSTRING:
            p = (u_char *) lua_tolstring(L, 2, &len);
            ngx_memcpy(query.data, (u_char *) p, len);
            break;

        case LUA_TTABLE:
            (void) ngx_http_lua_copy_str_in_table(L, 2, query.data);
            break;

        default:
            return luaL_error(L, "impossible to reach here");
    }

    u->ft_type = 0;

    /* mimic ngx_http_upstream_init_request here */

#if 1
    u->waiting = 0;
#endif

    dd("sending query %.*s", (int) query.len, query.data);

    n = ngx_send(u->udp_connection.connection, query.data, query.len);

    dd("ngx_send returns %d (query len %d)", (int) n, (int) query.len);

    if (n == NGX_ERROR || n == NGX_AGAIN) {
        u->socket_errno = ngx_socket_errno;

        return ngx_http_lua_socket_error_retval_handler(r, u, L);
    }

    if (n != (ssize_t) query.len) {
        dd("not the while query was sent");

        u->ft_type |= NGX_HTTP_LUA_SOCKET_FT_PARTIALWRITE;
        return ngx_http_lua_socket_error_retval_handler(r, u, L);
    }

    dd("n == len");

    lua_pushinteger(L, 1);
    return 1;
}
Exemplo n.º 10
0
static void
ngx_lua_smtp_write_handler(ngx_event_t *wev)
{
    ssize_t              n, size;
    ngx_buf_t           *b;
    ngx_connection_t    *c;
    ngx_lua_smtp_ctx_t  *ctx;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, wev->log, 0, "lua smtp write handler");

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

    if (wev->timedout) {
        ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
                      "lua smtp write %V timed out", ctx->peer.name);
        ngx_lua_smtp_finalize(ctx, "ngx_lua_smtp_write_handler() failed");
        return;
    }

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

    b = ctx->request;

    while (1) {

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

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

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

            if (n < size) {
                continue;
            }

            /* n == size */

            c->read->handler = ngx_lua_smtp_read_handler;
            wev->handler = ngx_lua_smtp_dummy_handler;

            ctx->response->last = ctx->response->pos;

            ngx_lua_smtp_read_handler(c->read);
            return;
        }

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

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

        ngx_lua_smtp_finalize(ctx, "ngx_send() failed");
        return;
    }
}
Exemplo n.º 11
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);
}
Exemplo n.º 12
0
static void ngx_imap_proxy_auth_handler(ngx_event_t *rev)
{
    u_char              *p;
    ngx_int_t            rc;
    ngx_str_t            line;
    ngx_connection_t    *c;
    ngx_imap_session_t  *s;

    ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy auth handler");

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

    if (rev->timedout) {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        ngx_imap_proxy_close_session(s);
        return;
    }

    if (s->proxy->buffer == NULL) {
        s->proxy->buffer = ngx_create_temp_buf(c->pool, /* STUB */ 4096);
        if (s->proxy->buffer == NULL) {
            ngx_imap_proxy_close_session(s);
            return;
        }
    }

    rc = ngx_imap_proxy_read_response(s);

    if (rc == NGX_AGAIN) {
        return;
    }

    if (rc == NGX_ERROR) {
        /* TODO: ngx_imap_proxy_finalize_session(s, NGX_IMAP_INTERNAL_ERROR) */
        ngx_imap_proxy_close_session(s);
        return;
    }

    if (s->imap_state == ngx_pop3_start) {

        ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send user");

        line.len = sizeof("USER ") + s->login.len - 1 + 2;
        if (!(line.data = ngx_palloc(c->pool, line.len))) {
            ngx_imap_proxy_close_session(s);
            return;
        }

        p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1);
        p = ngx_cpymem(p, s->login.data, s->login.len);
        *p++ = CR; *p++ = LF;

        if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) {
            /*
             * we treat the incomplete sending as NGX_ERROR
             * because it is very strange here
             */
            ngx_imap_close_connection(c);
            return;
        }

        s->imap_state = ngx_pop3_user;

        s->proxy->buffer->pos = s->proxy->buffer->start;
        s->proxy->buffer->last = s->proxy->buffer->start;

        return;
    }

    ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send pass");

    line.len = sizeof("PASS ") + s->passwd.len - 1 + 2;
    if (!(line.data = ngx_palloc(c->pool, line.len))) {
        ngx_imap_proxy_close_session(s);
        return;
    }

    p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1);
    p = ngx_cpymem(p, s->passwd.data, s->passwd.len);
    *p++ = CR; *p++ = LF;

    if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) {
        /*
         * we treat the incomplete sending as NGX_ERROR
         * because it is very strange here
         */
        ngx_imap_close_connection(c);
        return;
    }

    s->proxy->buffer->pos = s->proxy->buffer->start;
    s->proxy->buffer->last = s->proxy->buffer->start;

    s->connection->read->event_handler = ngx_imap_proxy_handler;
    s->connection->write->event_handler = ngx_imap_proxy_handler;
    rev->event_handler = ngx_imap_proxy_handler;
    c->write->event_handler = ngx_imap_proxy_handler;
}
Exemplo n.º 13
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);
}