Added server-side support for PROXY protocol v1 (ticket #355).

Client address specified in the PROXY protocol header is now
saved in the $proxy_protocol_addr variable and can be used in
the realip module.

This is currently not implemented for mail.
diff --git a/src/http/modules/ngx_http_realip_module.c b/src/http/modules/ngx_http_realip_module.c
index b159547..7a62118 100644
--- a/src/http/modules/ngx_http_realip_module.c
+++ b/src/http/modules/ngx_http_realip_module.c
@@ -13,6 +13,7 @@
 #define NGX_HTTP_REALIP_XREALIP  0
 #define NGX_HTTP_REALIP_XFWD     1
 #define NGX_HTTP_REALIP_HEADER   2
+#define NGX_HTTP_REALIP_PROXY    3
 
 
 typedef struct {
@@ -156,6 +157,18 @@
 
         break;
 
+    case NGX_HTTP_REALIP_PROXY:
+
+        value = &r->connection->proxy_protocol_addr;
+
+        if (value->len == 0) {
+            return NGX_DECLINED;
+        }
+
+        xfwd = NULL;
+
+        break;
+
     default: /* NGX_HTTP_REALIP_HEADER */
 
         part = &r->headers_in.headers.part;
@@ -343,6 +356,11 @@
         return NGX_CONF_OK;
     }
 
+    if (ngx_strcmp(value[1].data, "proxy_protocol") == 0) {
+        rlcf->type = NGX_HTTP_REALIP_PROXY;
+        return NGX_CONF_OK;
+    }
+
     rlcf->type = NGX_HTTP_REALIP_HEADER;
     rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len);
     rlcf->header = value[1];
diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c
index c3ac726..ce5adb7 100644
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -1849,6 +1849,7 @@
 #if (NGX_HTTP_SPDY)
         addrs[i].conf.spdy = addr[i].opt.spdy;
 #endif
+        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
 
         if (addr[i].hash.buckets == NULL
             && (addr[i].wc_head == NULL
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 74a448a..b023bdb 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -4287,6 +4287,11 @@
 #endif
         }
 
+        if (ngx_strcmp(value[n].data, "proxy_protocol") == 0) {
+            lsopt.proxy_protocol = 1;
+            continue;
+        }
+
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid parameter \"%V\"", &value[n]);
         return NGX_CONF_ERROR;
diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
index 220c94e..799d2fe 100644
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -82,6 +82,7 @@
     unsigned                   ipv6only:1;
 #endif
     unsigned                   so_keepalive:2;
+    unsigned                   proxy_protocol:1;
 
     int                        backlog;
     int                        rcvbuf;
@@ -243,6 +244,7 @@
 #if (NGX_HTTP_SPDY)
     unsigned                   spdy:1;
 #endif
+    unsigned                   proxy_protocol:1;
 };
 
 
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 5f2cf7d..4bf9d1f 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -343,6 +343,11 @@
     }
 #endif
 
+    if (hc->addr_conf->proxy_protocol) {
+        hc->proxy_protocol = 1;
+        c->log->action = "reading PROXY protocol";
+    }
+
     if (rev->ready) {
         /* the deferred accept(), rtsig, aio, iocp */
 
@@ -368,6 +373,7 @@
 static void
 ngx_http_wait_request_handler(ngx_event_t *rev)
 {
+    u_char                    *p;
     size_t                     size;
     ssize_t                    n;
     ngx_buf_t                 *b;
@@ -458,6 +464,27 @@
 
     b->last += n;
 
+    if (hc->proxy_protocol) {
+        hc->proxy_protocol = 0;
+
+        p = ngx_proxy_protocol_parse(c, b->pos, b->last);
+
+        if (p == NULL) {
+            ngx_http_close_connection(c);
+            return;
+        }
+
+        b->pos = p;
+
+        if (b->pos == b->last) {
+            c->log->action = "waiting for request";
+            b->pos = b->start;
+            b->last = b->start;
+            ngx_post_event(rev, &ngx_posted_events);
+            return;
+        }
+    }
+
     c->log->action = "reading client request line";
 
     ngx_reusable_connection(c, 0);
@@ -589,7 +616,8 @@
 static void
 ngx_http_ssl_handshake(ngx_event_t *rev)
 {
-    u_char                    buf[1];
+    u_char                   *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER + 1];
+    size_t                    size;
     ssize_t                   n;
     ngx_err_t                 err;
     ngx_int_t                 rc;
@@ -598,6 +626,7 @@
     ngx_http_ssl_srv_conf_t  *sscf;
 
     c = rev->data;
+    hc = c->data;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
                    "http check ssl handshake");
@@ -613,7 +642,9 @@
         return;
     }
 
-    n = recv(c->fd, (char *) buf, 1, MSG_PEEK);
+    size = hc->proxy_protocol ? sizeof(buf) : 1;
+
+    n = recv(c->fd, (char *) buf, size, MSG_PEEK);
 
     err = ngx_socket_errno;
 
@@ -640,12 +671,39 @@
         return;
     }
 
+    if (hc->proxy_protocol) {
+        hc->proxy_protocol = 0;
+
+        p = ngx_proxy_protocol_parse(c, buf, buf + n);
+
+        if (p == NULL) {
+            ngx_http_close_connection(c);
+            return;
+        }
+
+        size = p - buf;
+
+        if (c->recv(c, buf, size) != (ssize_t) size) {
+            ngx_http_close_connection(c);
+            return;
+        }
+
+        c->log->action = "SSL handshaking";
+
+        if (n == (ssize_t) size) {
+            ngx_post_event(rev, &ngx_posted_events);
+            return;
+        }
+
+        n = 1;
+        buf[0] = *p;
+    }
+
     if (n == 1) {
         if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) {
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0,
                            "https ssl handshake: 0x%02Xd", buf[0]);
 
-            hc = c->data;
             sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,
                                                 ngx_http_ssl_module);
 
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index 3b0858a..a1295e7 100644
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -309,8 +309,9 @@
     ngx_int_t                         nfree;
 
 #if (NGX_HTTP_SSL)
-    ngx_uint_t                        ssl;    /* unsigned  ssl:1; */
+    unsigned                          ssl:1;
 #endif
+    unsigned                          proxy_protocol:1;
 } ngx_http_connection_t;
 
 
diff --git a/src/http/ngx_http_spdy.c b/src/http/ngx_http_spdy.c
index 4005bfb..e9bad94 100644
--- a/src/http/ngx_http_spdy.c
+++ b/src/http/ngx_http_spdy.c
@@ -95,6 +95,8 @@
 static void ngx_http_spdy_write_handler(ngx_event_t *wev);
 static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc);
 
+static u_char *ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc,
+    u_char *pos, u_char *end);
 static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc,
     u_char *pos, u_char *end);
 static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc,
@@ -421,6 +423,11 @@
 
     sc->handler = ngx_http_spdy_state_head;
 
+    if (hc->proxy_protocol) {
+        c->log->action = "reading PROXY protocol";
+        sc->handler = ngx_http_spdy_proxy_protocol;
+    }
+
     sc->zstream_in.zalloc = ngx_http_spdy_zalloc;
     sc->zstream_in.zfree = ngx_http_spdy_zfree;
     sc->zstream_in.opaque = sc;
@@ -810,6 +817,22 @@
 
 
 static u_char *
+ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc, u_char *pos,
+    u_char *end)
+{
+    pos = ngx_proxy_protocol_parse(sc->connection, pos, end);
+
+    if (pos == NULL) {
+        return ngx_http_spdy_state_protocol_error(sc);
+    }
+
+    sc->connection->log->action = "processing SPDY";
+
+    return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
 ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos,
     u_char *end)
 {
diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
index 8b86d5a..f618622 100644
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -54,6 +54,8 @@
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r,
@@ -183,6 +185,9 @@
 
     { ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 },
 
+    { ngx_string("proxy_protocol_addr"), NULL,
+      ngx_http_variable_proxy_protocol_addr, 0, 0, 0 },
+
     { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },
 
     { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 },
@@ -1206,6 +1211,20 @@
 
 
 static ngx_int_t
+ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    v->len = r->connection->proxy_protocol_addr.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = r->connection->proxy_protocol_addr.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_server_addr(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {