changeset 419:47709bff4468

nginx-0.0.10-2004-09-09-19:40:48 import
author Igor Sysoev <igor@sysoev.ru>
date Thu, 09 Sep 2004 15:40:48 +0000
parents cf072d26d6d6
children 33a8253115b4
files auto/sources src/core/ngx_connection.h src/event/ngx_event_accept.c src/event/ngx_event_connect.h src/http/modules/proxy/ngx_http_proxy_handler.c src/http/ngx_http.c src/http/ngx_http_parse.c src/http/ngx_http_request.c src/http/ngx_http_request.h src/imap/ngx_imap.c src/imap/ngx_imap.h src/imap/ngx_imap_handler.c src/imap/ngx_imap_parse.c src/imap/ngx_imap_proxy.c src/os/unix/ngx_send.c src/os/win32/ngx_win32_config.h
diffstat 16 files changed, 499 insertions(+), 114 deletions(-) [+]
line wrap: on
line diff
--- a/auto/sources
+++ b/auto/sources
@@ -299,4 +299,5 @@ IMAP_DEPS="src/imap/ngx_imap.h"
 IMAP_MODULE=ngx_imap_module
 IMAP_SRCS="src/imap/ngx_imap.c \
 	   src/imap/ngx_imap_handler.c \
+	   src/imap/ngx_imap_parse.c \
 	   src/imap/ngx_imap_proxy.c"
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -49,6 +49,8 @@ typedef struct {
 #if (HAVE_DEFERRED_ACCEPT)
     unsigned          deferred_accept:1;
 #endif
+
+    unsigned          addr_ntop:1;
 } ngx_listening_t;
 
 
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -11,6 +11,7 @@ typedef struct {
 } ngx_accept_log_ctx_t;
 
 
+static void ngx_close_accepted_socket(ngx_socket_t s, ngx_log_t *log);
 static size_t ngx_accept_log_error(void *data, char *buf, size_t len);
 
 
@@ -138,11 +139,7 @@ void ngx_event_accept(ngx_event_t *ev)
                           "closing the connection",
                           ls->listening->addr_text.data, s, ecf->connections);
 
-            if (ngx_close_socket(s) == -1) {
-                ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
-                              ngx_close_socket_n "failed");
-            }
-
+            ngx_close_accepted_socket(s, log);
             ngx_destroy_pool(pool);
             return;
         }
@@ -155,11 +152,7 @@ void ngx_event_accept(ngx_event_t *ev)
                     ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
                                   ngx_blocking_n " failed");
 
-                    if (ngx_close_socket(s) == -1) {
-                        ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
-                                      ngx_close_socket_n " failed");
-                    }
-
+                    ngx_close_accepted_socket(s, log);
                     ngx_destroy_pool(pool);
                     return;
                 }
@@ -171,11 +164,7 @@ void ngx_event_accept(ngx_event_t *ev)
                     ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
                                   ngx_nonblocking_n " failed");
 
-                    if (ngx_close_socket(s) == -1) {
-                        ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
-                                      ngx_close_socket_n " failed");
-                    }
-
+                    ngx_close_accepted_socket(s, log);
                     ngx_destroy_pool(pool);
                     return;
                 }
@@ -286,6 +275,25 @@ void ngx_event_accept(ngx_event_t *ev)
         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                        "accept: fd:%d c:%d", s, c->number);
 
+        if (c->listening->addr_ntop) {
+            c->addr_text.data = ngx_palloc(c->pool,
+                                           c->listening->addr_text_max_len);
+            if (c->addr_text.data == NULL) {
+                ngx_close_accepted_socket(s, log);
+                ngx_destroy_pool(pool);
+                return;
+            }
+    
+            c->addr_text.len = ngx_sock_ntop(c->listening->family, c->sockaddr,
+                                             c->addr_text.data,
+                                             c->listening->addr_text_max_len);
+            if (c->addr_text.len == 0) {
+                ngx_close_accepted_socket(s, log);
+                ngx_destroy_pool(pool);
+                return;
+            }
+        }
+
 #if (NGX_DEBUG)
         {
 
@@ -307,11 +315,7 @@ void ngx_event_accept(ngx_event_t *ev)
 
         if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {
             if (ngx_add_conn(c) == NGX_ERROR) {
-                if (ngx_close_socket(s) == -1) {
-                    ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
-                                  ngx_close_socket_n " failed");
-                }
-
+                ngx_close_accepted_socket(s, log);
                 ngx_destroy_pool(pool);
                 return;
             }
@@ -440,6 +444,15 @@ ngx_int_t ngx_disable_accept_events(ngx_
 }
 
 
+static void ngx_close_accepted_socket(ngx_socket_t s, ngx_log_t *log)
+{
+    if (ngx_close_socket(s) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
+                      ngx_close_socket_n " failed");
+    }
+}
+
+
 static size_t ngx_accept_log_error(void *data, char *buf, size_t len)
 {
     ngx_accept_log_ctx_t  *ctx = data;
--- a/src/event/ngx_event_connect.h
+++ b/src/event/ngx_event_connect.h
@@ -13,20 +13,20 @@
 typedef struct {
     in_addr_t          addr;
     ngx_str_t          host;
-    int                port;
+    in_port_t          port;
     ngx_str_t          addr_port_text;
 
-    int                fails;
+    ngx_int_t          fails;
     time_t             accessed;
 } ngx_peer_t;
 
 
 typedef struct {
-    int                 current;
-    int                 number;
-    int                 max_fails;
-    int                 fail_timeout;
-    int                 last_cached;
+    ngx_int_t           current;
+    ngx_int_t           number;
+    ngx_int_t           max_fails;
+    ngx_int_t           fail_timeout;
+    ngx_int_t           last_cached;
 
  /* ngx_mutex_t        *mutex; */
     ngx_connection_t  **cached;
@@ -37,8 +37,8 @@ typedef struct {
 
 typedef struct {
     ngx_peers_t       *peers;
-    int                cur_peer;
-    int                tries;
+    ngx_int_t          cur_peer;
+    ngx_int_t          tries;
 
     ngx_connection_t  *connection;
 #if (NGX_THREADS)
--- a/src/http/modules/proxy/ngx_http_proxy_handler.c
+++ b/src/http/modules/proxy/ngx_http_proxy_handler.c
@@ -1227,7 +1227,8 @@ static char *ngx_http_proxy_parse_upstre
             u->port_text.len = &url->data[i] - u->port_text.data;
 
             if (u->port_text.len > 0) {
-                u->port = ngx_atoi(u->port_text.data, u->port_text.len);
+                u->port = (in_port_t) ngx_atoi(u->port_text.data,
+                                               u->port_text.len);
                 if (u->port > 0) {
 
                     if (u->port == 80) {
@@ -1263,7 +1264,7 @@ static char *ngx_http_proxy_parse_upstre
     u->port_text.len = &url->data[i] - u->port_text.data;
 
     if (u->port_text.len > 0) {
-        u->port = ngx_atoi(u->port_text.data, u->port_text.len);
+        u->port = (in_port_t) ngx_atoi(u->port_text.data, u->port_text.len);
         if (u->port > 0) {
             u->port = htons(u->port);
             return NULL;
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -54,10 +54,9 @@ ngx_module_t  ngx_http_module = {
 
 static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
+    char                        *rv;
     ngx_uint_t                   mi, m, s, l, p, a, n;
     ngx_uint_t                   port_found, addr_found, virtual_names;
-    char                        *rv;
-    struct sockaddr_in          *addr_in;
     ngx_conf_t                   pcf;
     ngx_array_t                  in_ports;
     ngx_listening_t             *ls;
@@ -514,6 +513,7 @@ static char *ngx_http_block(ngx_conf_t *
             ls->nonblocking = 0;
 #endif
 #endif
+            ls->addr_ntop = 1;
 
             ls->handler = ngx_http_init_connection;
 
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -65,19 +65,19 @@ ngx_int_t ngx_http_parse_request_line(ng
 
                 if (r->method_end - m == 3) {
 
-                    if (*m == 'G' && *(m + 1) == 'E' && *(m + 2) == 'T') {
+                    if (m[0] == 'G' && m[1] == 'E' && m[2] == 'T') {
                         r->method = NGX_HTTP_GET;
                     }
 
                 } else if (r->method_end - m == 4) {
 
-                    if (*m == 'P' && *(m + 1) == 'O'
-                        && *(m + 2) == 'T' && *(m + 3) == 'T')
+                    if (m[0] == 'P' && m[1] == 'O'
+                        && m[2] == 'T' && m[3] == 'T')
                     {
                         r->method = NGX_HTTP_POST;
 
-                    } else if (*m == 'H' && *(m + 1) == 'E'
-                               && *(m + 2) == 'A' && *(m + 3) == 'D')
+                    } else if (m[0] == 'H' && m[1] == 'E'
+                               && m[2] == 'A' && m[3] == 'D')
                     {
                         r->method = NGX_HTTP_HEAD;
                     }
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -97,21 +97,6 @@ void ngx_http_init_connection(ngx_connec
     ngx_event_t         *rev;
     ngx_http_log_ctx_t  *ctx;
 
-    c->addr_text.data = ngx_palloc(c->pool, c->listening->addr_text_max_len);
-    if (c->addr_text.data == NULL) {
-        ngx_http_close_connection(c);
-        return;
-    }
-
-    c->addr_text.len = ngx_sock_ntop(c->listening->family, c->sockaddr,
-                                     c->addr_text.data,
-                                     c->listening->addr_text_max_len);
-
-    if (c->addr_text.len == 0) {
-        ngx_http_close_connection(c);
-        return;
-    }
-
     if (!(ctx = ngx_pcalloc(c->pool, sizeof(ngx_http_log_ctx_t)))) {
         ngx_http_close_connection(c);
         return;
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -311,7 +311,7 @@ struct ngx_http_request_s {
     ngx_uint_t           headers_n;
 
     /* used to parse HTTP headers */
-    ngx_int_t            state;
+    ngx_uint_t           state;
     u_char              *uri_start;
     u_char              *uri_end;
     u_char              *uri_ext;
--- a/src/imap/ngx_imap.c
+++ b/src/imap/ngx_imap.c
@@ -50,6 +50,7 @@ static char *ngx_imap_block(ngx_conf_t *
     }
 
     ls->backlog = -1;
+    ls->addr_ntop = 1;
     ls->handler = ngx_imap_init_connection;
     ls->pool_size = 16384;
     /* ls->post_accept_timeout = 0; */
--- a/src/imap/ngx_imap.h
+++ b/src/imap/ngx_imap.h
@@ -4,40 +4,63 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
 
 
 typedef struct {
-    ngx_connection_t      *connection;
+    ngx_peer_connection_t   upstream;
 
-    ngx_buf_t             *downstream_buffer;
-    ngx_buf_t             *upstream_buffer;
+    ngx_buf_t              *buffer;
 } ngx_imap_proxy_ctx_t;
 
 
 typedef struct {
-    uint32_t               signature;         /* "IMAP" */
+    uint32_t                signature;         /* "IMAP" */
+
+    ngx_connection_t       *connection;
+    ngx_buf_t              *buffer;
+
+    ngx_imap_proxy_ctx_t   *proxy;
 
-    ngx_connection_t      *connection;
-    ngx_imap_proxy_ctx_t  *proxy;
+    ngx_uint_t              command;
+    ngx_array_t             args;
+
+    /* used to parse IMAP/POP3 command */
+
+    ngx_uint_t              state;
+    u_char                 *arg_start;
+    u_char                 *arg_end;
 } ngx_imap_session_t;
 
 
-#define NGX_POP3_USER      1
-#define NGX_POP3_PASS      2
-#define NGX_POP3_APOP      3
-#define NGX_POP3_STAT      4
-#define NGX_POP3_LIST      5
-#define NGX_POP3_RETR      6
-#define NGX_POP3_DELE      7
-#define NGX_POP3_NOOP      8
-#define NGX_POP3_RSET      9
-#define NGX_POP3_TOP       10
-#define NGX_POP3_UIDL      11
-#define NGX_POP3_QUIT      12
+#define NGX_POP3_USER       1
+#define NGX_POP3_PASS       2
+#define NGX_POP3_APOP       3
+#define NGX_POP3_STAT       4
+#define NGX_POP3_LIST       5
+#define NGX_POP3_RETR       6
+#define NGX_POP3_DELE       7
+#define NGX_POP3_NOOP       8
+#define NGX_POP3_RSET       9
+#define NGX_POP3_TOP        10
+#define NGX_POP3_UIDL       11
+#define NGX_POP3_QUIT       12
+
+
+#define NGX_IMAP_PARSE_INVALID_COMMAND  10
+
+
+#define NGX_IMAP_PROXY_INVALID  10
+#define NGX_IMAP_PROXY_ERROR    11
 
 
 void ngx_imap_init_connection(ngx_connection_t *c);
 void ngx_imap_close_connection(ngx_connection_t *c);
 
+void ngx_imap_proxy_init(ngx_imap_session_t *s);
+
+ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s);
+
 
 #endif /* _NGX_IMAP_H_INCLUDED_ */
--- a/src/imap/ngx_imap_handler.c
+++ b/src/imap/ngx_imap_handler.c
@@ -6,7 +6,7 @@
 #include <nginx.h>
 
 
-static void ngx_imap_auth_state(ngx_event_t *rev);
+static void ngx_imap_init_session(ngx_event_t *rev);
 
 
 static char pop3_greeting[] = "+OK " NGINX_VER " ready" CRLF;
@@ -15,36 +15,66 @@ static char imap_greeting[] = "* OK " NG
 
 void ngx_imap_init_connection(ngx_connection_t *c)
 {
-    ngx_int_t  n;
+    char       *greeting;
+    ssize_t     size;
+    ngx_int_t   n;
 
     ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0,
                    "imap init connection");
 
     c->log_error = NGX_ERROR_INFO;
 
-    n = ngx_send(c, pop3_greeting, sizeof(pop3_greeting) - 1);
+    greeting = pop3_greeting;
+    size = sizeof(pop3_greeting) - 1;
+
+    n = ngx_send(c, greeting, size);
 
-    if (n == NGX_ERROR) {
+    if (n < size) {
+        /*
+         * we treat the incomplete sending as NGX_ERROR
+         * because it is very strange here
+         */
         ngx_imap_close_connection(c);
         return;
     }
 
-    c->read->event_handler = ngx_imap_auth_state;
+    c->read->event_handler = ngx_imap_init_session;
+
+    ngx_add_timer(c->read, /* STUB */ 60000);
 
     if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
         ngx_imap_close_connection(c);
-        return;
     }
 }
 
 
-static void ngx_imap_auth_state(ngx_event_t *rev)
+static void ngx_imap_init_session(ngx_event_t *rev)
 {
-    ngx_connection_t  *c;
+    ngx_connection_t    *c;
+    ngx_imap_session_t  *s;
 
     c = rev->data;
 
-    ngx_imap_close_connection(c);
+    if (!(s = ngx_pcalloc(c->pool, sizeof(ngx_imap_session_t)))) {
+        ngx_imap_close_connection(c);
+        return;
+    }
+
+    c->data = s;
+    s->connection = c;
+
+    if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) {
+        ngx_imap_close_connection(s->connection);
+        return;
+    }
+
+    s->buffer = ngx_create_temp_buf(s->connection->pool, /* STUB */ 4096);
+    if (s->buffer == NULL) {
+        ngx_imap_close_connection(s->connection);
+        return;
+    }
+
+    ngx_imap_proxy_init(s);
 }
 
 
--- a/src/imap/ngx_imap_parse.c
+++ b/src/imap/ngx_imap_parse.c
@@ -5,49 +5,75 @@
 #include <ngx_imap.h>
 
 
-ngx_int_t ngx_pop3_parse_command(ngx_imap_request_t *r)
+ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s)
 {
-    u_char  ch, *p, *c;
+    u_char      ch, *p, *c;
+    ngx_str_t  *arg;
     enum {
         sw_start = 0,
+        sw_spaces_before_argument,
+        sw_argument,
+        sw_almost_done,
         sw_done
     } state;
 
-    while (p < r->buf->last && state < sw_done) {
+    state = s->state;
+    p = s->buffer->pos;
+
+    while (p < s->buffer->last && state < sw_done) {
         ch = *p++;
 
         switch (state) {
 
-        /* POP3 commands */
+        /* POP3 command */
+
         case sw_start:
-            if (ch == ' ') {
-                c = r->buf->start;
+            if (ch == ' ' || ch == CR || ch == LF) {
+                c = s->buffer->start;
+
+                if (p - 1 - c == 4) {
 
-                if (p - 1 - m == 4) {
+                    if (c[0] == 'U' && c[1] == 'S'
+                        && c[2] == 'E' && c[3] == 'R')
+                    {
+                        s->command = NGX_POP3_USER;
 
-                    if (*c == 'U' && *(c + 1) == 'S'
-                        && *(c + 2) == 'E' && *(c + 3) == 'R')
+                    } else if (c[0] == 'P' && c[1] == 'A'
+                               && c[2] == 'A' && c[3] == 'S')
                     {
-                        r->command = NGX_POP3_USER;
+                        s->command = NGX_POP3_PASS;
 
-                    } else if (*c == 'P' && *(c + 1) == 'A'
-                               && *(c + 2) == 'A' && *(c + 3) == 'S')
+                    } else if (c[0] == 'Q' && c[1] == 'U'
+                               && c[2] == 'I' && c[3] == 'T')
                     {
-                        r->method = NGX_POP3_PASS;
+                        s->command = NGX_POP3_QUIT;
 
-                    } else if (*c == 'Q' && *(c + 1) == 'U'
-                               && *(c + 2) == 'I' && *(c + 3) == 'T')
+#if 0
+                    } else if (c[0] == 'N' && c[1] == 'O'
+                               && c[2] == 'O' && c[3] == 'P')
                     {
-                        r->method = NGX_POP3_QUIT;
+                        s->command = NGX_POP3_NOOP;
+#endif
 
-                    } else if (*c == 'N' && *(c + 1) == 'O'
-                               && *(c + 2) == 'O' && *(c + 3) == 'P')
-                    {
-                        r->method = NGX_POP3_NOOP;
+                    } else {
+                        return NGX_IMAP_PARSE_INVALID_COMMAND;
                     }
+
+                } else {
+                    return NGX_IMAP_PARSE_INVALID_COMMAND;
                 }
 
-                state = sw_spaces_before_arg;
+                switch (ch) {
+                case ' ':
+                    state = sw_spaces_before_argument;
+                    break;
+                case CR:
+                    state = sw_almost_done;
+                    break;
+                case LF:
+                    state = sw_done;
+                    break;
+                }
                 break;
             }
 
@@ -56,7 +82,72 @@ ngx_int_t ngx_pop3_parse_command(ngx_ima
             }
 
             break;
-        }
+
+        /* the spaces before the argument */
+        case sw_spaces_before_argument:
+            switch (ch) {
+            case ' ':
+                break;
+            case CR:
+                state = sw_almost_done;
+                s->arg_end = p - 1;
+                break;
+            case LF:
+                state = sw_done;
+                s->arg_end = p - 1;
+                break;
+            default:
+                if (s->args.nelts > 2) {
+                    return NGX_IMAP_PARSE_INVALID_COMMAND;
+                }
+
+                state = sw_argument;
+                s->arg_start = p - 1;
+                break;
+            }
+            break;
+
+        /* the argument */
+        case sw_argument:
+            switch (ch) {
+            case ' ':
+            case CR:
+            case LF:
+                if (!(arg = ngx_array_push(&s->args))) {
+                    return NGX_ERROR;
+                }
+                arg->len = p - s->arg_start;
+                arg->data = s->arg_start;
+                s->arg_start = NULL;
+
+                switch (ch) {
+                case ' ':
+                    state = sw_spaces_before_argument;
+                    break;
+                case CR:
+                    state = sw_almost_done;
+                    break;
+                case LF:
+                    state = sw_done;
+                    break;
+                }
+                break;
+
+            default:
+                break;
+            }
+            break;
+
+        /* end of request line */
+        case sw_almost_done:
+            switch (ch) {
+            case LF:
+                state = sw_done;
+                break;
+            default:
+                return NGX_IMAP_PARSE_INVALID_COMMAND;
+            }
+            break;
 
         /* suppress warning */
         case sw_done:
@@ -64,5 +155,22 @@ ngx_int_t ngx_pop3_parse_command(ngx_ima
         }
     }
 
-    return NGX_OK;
+    s->buffer->pos = p;
+
+    if (state == sw_done) {
+        if (s->arg_start) {
+            if (!(arg = ngx_array_push(&s->args))) {
+                return NGX_ERROR;
+            }
+            arg->len = s->arg_end - s->arg_start;
+            arg->data = s->arg_start;
+            s->arg_start = NULL;
+        }
+
+        return NGX_OK;
+
+    } else {
+        s->state = state;
+        return NGX_AGAIN;
+    }
 }
--- a/src/imap/ngx_imap_proxy.c
+++ b/src/imap/ngx_imap_proxy.c
@@ -2,14 +2,200 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_event.h>
+#include <ngx_event_connect.h>
 #include <ngx_imap.h>
 
 
+static void ngx_imap_proxy_block_read(ngx_event_t *rev);
+static void ngx_imap_proxy_greeting_handler(ngx_event_t *rev);
+static void ngx_imap_proxy_init_handler(ngx_event_t *wev);
+static void ngx_imap_proxy_dummy_handler(ngx_event_t *ev);
+static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s);
+static void ngx_imap_proxy_handler(ngx_event_t *ev);
 static void ngx_imap_proxy_close_session(ngx_imap_session_t *s);
 
 
-void ngx_imap_proxy_handler(ngx_event_t *ev)
+void ngx_imap_proxy_init(ngx_imap_session_t *s)
+{
+    ngx_int_t              rc;
+    ngx_peers_t           *peers;
+    ngx_imap_proxy_ctx_t  *p;
+
+    if (!(p = ngx_pcalloc(s->connection->pool, sizeof(ngx_imap_proxy_ctx_t)))) {
+        ngx_imap_close_connection(s->connection);
+        return;
+    }
+
+    s->proxy = p;
+
+    /**/
+
+    if (!(peers = ngx_pcalloc(s->connection->pool, sizeof(ngx_peers_t)))) {
+        ngx_imap_close_connection(s->connection);
+        return;
+    }
+
+    p->upstream.peers = peers;
+    p->upstream.log = s->connection->log;
+    p->upstream.log_error = NGX_ERROR_ERR;
+
+    peers->number = 1;
+    peers->max_fails = 1;
+    peers->peers[0].addr = inet_addr("81.19.69.70");
+    peers->peers[0].addr_port_text.len = sizeof("81.19.69.70:110") - 1;
+    peers->peers[0].addr_port_text.data = "81.19.69.70:110";
+    peers->peers[0].port = htons(110);
+
+    rc = ngx_event_connect_peer(&p->upstream);
+
+    if (rc == NGX_ERROR) {
+        ngx_imap_proxy_close_session(s);
+        return;
+    }
+
+    p->upstream.connection->data = s;
+    p->upstream.connection->pool = s->connection->pool;
+
+    s->connection->read->event_handler = ngx_imap_proxy_block_read;
+    p->upstream.connection->read->event_handler =
+                                               ngx_imap_proxy_greeting_handler;
+    p->upstream.connection->write->event_handler = ngx_imap_proxy_dummy_handler;
+}
+
+
+static void ngx_imap_proxy_block_read(ngx_event_t *rev)
+{
+    ngx_connection_t    *c;
+    ngx_imap_session_t  *s;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy block read");
+
+    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        c = rev->data;
+        s = c->data;
+
+        ngx_imap_proxy_close_session(s);
+    }
+}
+
+
+static void ngx_imap_proxy_greeting_handler(ngx_event_t *rev)
 {
+    ngx_int_t            rc;
+    ngx_buf_t           *b;
+    ngx_connection_t    *c;
+    ngx_imap_session_t  *s;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
+                   "imap proxy greeting handler");
+
+    c = rev->data;
+    s = c->data;
+
+    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_OK) {
+        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;
+
+        b = s->proxy->buffer;
+        b->pos = b->start;
+        b->last = b->start;
+
+        if (ngx_handle_read_event(s->connection->read, 0) == NGX_ERROR) {
+            ngx_imap_proxy_close_session(s);
+            return;
+        }
+
+        if (s->connection->read->ready) {
+            ngx_imap_proxy_handler(s->connection->read);
+        }
+
+        return;
+    }
+
+    /* TODO: ngx_imap_proxy_finalize_session(s, NGX_IMAP_INTERNAL_ERROR) */
+    ngx_imap_proxy_close_session(s);
+}
+
+
+static void ngx_imap_proxy_dummy_handler(ngx_event_t *ev)
+{
+    ngx_log_debug0(NGX_LOG_DEBUG_IMAP, ev->log, 0, "imap proxy dummy handler");
+}
+
+
+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;
+}
+
+
+static void ngx_imap_proxy_handler(ngx_event_t *ev)
+{
+    size_t               size;
     ssize_t              n;
     ngx_buf_t           *b;
     ngx_uint_t           data, do_write;
@@ -21,23 +207,30 @@ void ngx_imap_proxy_handler(ngx_event_t 
 
     if (c == s->connection) {
         src = c;
-        dst = s->proxy->connection;
-        b = s->proxy->downstream_buffer;
+        dst = s->proxy->upstream.connection;
+        b = s->buffer;
 
     } else {
-        src = s->proxy->connection;
-        dst = c;
-        b = s->proxy->upstream_buffer;
+        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 {
         data = 0;
 
         if (do_write == 1) {
-            if (dst->write->ready && b->pos < b->last) {
-                n = ngx_send(dst, b->pos, b->last - b->pos);
+
+            size = b->last - b->pos;
+
+            if (dst->write->ready && size) {
+                n = ngx_send(dst, b->pos, size);
 
                 if (n == NGX_ERROR) {
                     ngx_imap_proxy_close_session(s);
@@ -53,11 +246,23 @@ void ngx_imap_proxy_handler(ngx_event_t 
                         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;
+                    }
+                }
             }
         }
 
-        if (src->read->ready && b->last < b->end) {
-            n = ngx_recv(src, b->last, b->end - b->last);
+        size = b->end - b->last;
+
+        if (src->read->ready && size) {
+            n = ngx_recv(src, b->last, size);
 
             if (n == NGX_ERROR || n == 0) {
                 ngx_imap_proxy_close_session(s);
@@ -69,6 +274,13 @@ void ngx_imap_proxy_handler(ngx_event_t 
                 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 (data);
@@ -77,4 +289,10 @@ void ngx_imap_proxy_handler(ngx_event_t 
 
 static void ngx_imap_proxy_close_session(ngx_imap_session_t *s)
 {
+    if (ngx_close_socket(s->proxy->upstream.connection->fd) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno,
+                      ngx_close_socket_n " failed");
+    }
+
+    ngx_imap_close_connection(s->connection);
 }
--- a/src/os/unix/ngx_send.c
+++ b/src/os/unix/ngx_send.c
@@ -42,6 +42,8 @@ ssize_t ngx_unix_send(ngx_connection_t *
 
         if (n == 0) {
             ngx_log_error(NGX_LOG_ALERT, c->log, err, "send() returned zero");
+            wev->ready = 0;
+            return n;
         }
 
         if (err == NGX_EAGAIN || err == NGX_EINTR) {
--- a/src/os/win32/ngx_win32_config.h
+++ b/src/os/win32/ngx_win32_config.h
@@ -97,6 +97,7 @@ typedef int               ssize_t;
 typedef long              time_t;
 typedef __int64           off_t;
 typedef uint32_t          in_addr_t;
+typedef u_short           in_port_t;
 typedef int               sig_atomic_t;
 typedef uint32_t          ngx_atomic_t;