SSL: do not treat SSL handshake as request.

The request object will not be created until SSL handshake is complete.
This simplifies adding another connection handler that does not need
request object right after handshake (e.g., SPDY).

There are also a few more intentional effects:

 - the "client_header_buffer_size" directive will be taken from the
   server configuration that was negotiated by SNI;

 - SSL handshake errors and timeouts are not logged into access log
   as bad requests;

 - ngx_ssl_create_connection() is not called until the first byte of
   ClientHello message was received.  This also decreases memory
   consumption if plain HTTP request is sent to SSL socket.
This commit is contained in:
Valentin Bartenev 2013-02-27 17:21:21 +00:00
parent 167aabf2b3
commit d281d0ba8b
2 changed files with 101 additions and 91 deletions

View File

@ -316,6 +316,31 @@ ngx_http_init_connection(ngx_connection_t *c)
rev->handler = ngx_http_init_request; rev->handler = ngx_http_init_request;
c->write->handler = ngx_http_empty_handler; c->write->handler = ngx_http_empty_handler;
#if (NGX_HTTP_SSL)
{
ngx_http_ssl_srv_conf_t *sscf;
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
if (sscf->enable || hc->addr_conf->ssl) {
c->log->action = "SSL handshaking";
if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"no \"ssl_certificate\" is defined "
"in server listening on SSL port");
ngx_http_close_connection(c);
return;
}
hc->ssl = 1;
rev->handler = ngx_http_ssl_handshake;
}
}
#endif
if (rev->ready) { if (rev->ready) {
/* the deferred accept(), rtsig, aio, iocp */ /* the deferred accept(), rtsig, aio, iocp */
@ -324,7 +349,7 @@ ngx_http_init_connection(ngx_connection_t *c)
return; return;
} }
ngx_http_init_request(rev); rev->handler(rev);
return; return;
} }
@ -395,45 +420,8 @@ ngx_http_init_request(ngx_event_t *rev)
r->srv_conf = hc->conf_ctx->srv_conf; r->srv_conf = hc->conf_ctx->srv_conf;
r->loc_conf = hc->conf_ctx->loc_conf; r->loc_conf = hc->conf_ctx->loc_conf;
rev->handler = ngx_http_process_request_line;
r->read_event_handler = ngx_http_block_reading; r->read_event_handler = ngx_http_block_reading;
#if (NGX_HTTP_SSL)
{
ngx_http_ssl_srv_conf_t *sscf;
sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
if (sscf->enable || hc->addr_conf->ssl) {
if (c->ssl == NULL) {
c->log->action = "SSL handshaking";
if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) {
ngx_log_error(NGX_LOG_ERR, c->log, 0,
"no \"ssl_certificate\" is defined "
"in server listening on SSL port");
ngx_http_close_connection(c);
return;
}
if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)
!= NGX_OK)
{
ngx_http_close_connection(c);
return;
}
rev->handler = ngx_http_ssl_handshake;
}
r->main_filter_need_in_memory = 1;
}
}
#endif
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_http_set_connection_log(r->connection, clcf->error_log); ngx_http_set_connection_log(r->connection, clcf->error_log);
@ -489,6 +477,12 @@ ngx_http_init_request(ngx_event_t *rev)
c->single_connection = 1; c->single_connection = 1;
c->destroyed = 0; c->destroyed = 0;
#if (NGX_HTTP_SSL)
if (c->ssl) {
r->main_filter_need_in_memory = 1;
}
#endif
r->main = r; r->main = r;
r->count = 1; r->count = 1;
@ -519,7 +513,8 @@ ngx_http_init_request(ngx_event_t *rev)
(void) ngx_atomic_fetch_add(ngx_stat_requests, 1); (void) ngx_atomic_fetch_add(ngx_stat_requests, 1);
#endif #endif
rev->handler(rev); rev->handler = ngx_http_process_request_line;
ngx_http_process_request_line(rev);
} }
@ -530,43 +525,65 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
{ {
u_char buf[1]; u_char buf[1];
ssize_t n; ssize_t n;
ngx_err_t err;
ngx_int_t rc; ngx_int_t rc;
ngx_connection_t *c; ngx_connection_t *c;
ngx_http_request_t *r; ngx_http_connection_t *hc;
ngx_http_ssl_srv_conf_t *sscf;
c = rev->data; c = rev->data;
r = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"http check ssl handshake"); "http check ssl handshake");
if (rev->timedout) { if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
c->timedout = 1; ngx_http_close_connection(c);
ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
return; return;
} }
n = recv(c->fd, (char *) buf, 1, MSG_PEEK); n = recv(c->fd, (char *) buf, 1, MSG_PEEK);
if (n == -1 && ngx_socket_errno == NGX_EAGAIN) { err = ngx_socket_errno;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http recv(): %d", n);
if (n == -1) {
if (err == NGX_EAGAIN) {
if (!rev->timer_set) { if (!rev->timer_set) {
ngx_add_timer(rev, c->listening->post_accept_timeout); ngx_add_timer(rev, c->listening->post_accept_timeout);
} }
if (ngx_handle_read_event(rev, 0) != NGX_OK) { if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); ngx_http_close_connection(c);
} }
return; return;
} }
ngx_connection_error(c, err, "recv() failed");
ngx_http_close_connection(c);
return;
}
if (n == 1) { if (n == 1) {
if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) { if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"https ssl handshake: 0x%02Xd", buf[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);
if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)
!= NGX_OK)
{
ngx_http_close_connection(c);
return;
}
rc = ngx_ssl_handshake(c); rc = ngx_ssl_handshake(c);
if (rc == NGX_AGAIN) { if (rc == NGX_AGAIN) {
@ -582,27 +599,26 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
ngx_http_ssl_handshake_handler(c); ngx_http_ssl_handshake_handler(c);
return; return;
} else {
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"plain http");
r->plain_http = 1;
}
} }
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "plain http");
c->log->action = "reading client request line"; c->log->action = "reading client request line";
rev->handler = ngx_http_process_request_line; rev->handler = ngx_http_init_request;
ngx_http_process_request_line(rev); ngx_http_init_request(rev);
return;
}
ngx_log_error(NGX_LOG_INFO, c->log, 0, "client closed connection");
ngx_http_close_connection(c);
} }
static void static void
ngx_http_ssl_handshake_handler(ngx_connection_t *c) ngx_http_ssl_handshake_handler(ngx_connection_t *c)
{ {
ngx_http_request_t *r;
if (c->ssl->handshaked) { if (c->ssl->handshaked) {
/* /*
@ -617,19 +633,19 @@ ngx_http_ssl_handshake_handler(ngx_connection_t *c)
c->log->action = "reading client request line"; c->log->action = "reading client request line";
c->read->handler = ngx_http_process_request_line; c->read->handler = ngx_http_init_request;
/* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler; /* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler;
ngx_http_process_request_line(c->read); ngx_http_init_request(c->read);
return; return;
} }
r = c->data; if (c->read->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
}
ngx_http_close_request(r, NGX_HTTP_BAD_REQUEST); ngx_http_close_connection(c);
return;
} }
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
@ -640,7 +656,6 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
ngx_str_t host; ngx_str_t host;
const char *servername; const char *servername;
ngx_connection_t *c; ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_connection_t *hc; ngx_http_connection_t *hc;
ngx_http_ssl_srv_conf_t *sscf; ngx_http_ssl_srv_conf_t *sscf;
ngx_http_core_loc_conf_t *clcf; ngx_http_core_loc_conf_t *clcf;
@ -663,15 +678,13 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
return SSL_TLSEXT_ERR_NOACK; return SSL_TLSEXT_ERR_NOACK;
} }
r = c->data;
host.data = (u_char *) servername; host.data = (u_char *) servername;
if (ngx_http_validate_host(&host, r->pool, 1) != NGX_OK) { if (ngx_http_validate_host(&host, c->pool, 1) != NGX_OK) {
return SSL_TLSEXT_ERR_NOACK; return SSL_TLSEXT_ERR_NOACK;
} }
hc = r->http_connection; hc = c->data;
if (ngx_http_find_virtual_server(c, hc->addr_conf->virtual_names, &host, if (ngx_http_find_virtual_server(c, hc->addr_conf->virtual_names, &host,
NULL, &cscf) NULL, &cscf)
@ -682,14 +695,11 @@ ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
hc->conf_ctx = cscf->ctx; hc->conf_ctx = cscf->ctx;
r->srv_conf = cscf->ctx->srv_conf; clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module);
r->loc_conf = cscf->ctx->loc_conf;
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_http_set_connection_log(c, clcf->error_log); ngx_http_set_connection_log(c, clcf->error_log);
sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
if (sscf->ssl.ctx) { if (sscf->ssl.ctx) {
SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx); SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx);
@ -1631,20 +1641,20 @@ ngx_http_process_request(ngx_http_request_t *r)
c = r->connection; c = r->connection;
if (r->plain_http) { #if (NGX_HTTP_SSL)
if (r->http_connection->ssl) {
long rc;
X509 *cert;
ngx_http_ssl_srv_conf_t *sscf;
if (c->ssl == NULL) {
ngx_log_error(NGX_LOG_INFO, c->log, 0, ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client sent plain HTTP request to HTTPS port"); "client sent plain HTTP request to HTTPS port");
ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS); ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
return; return;
} }
#if (NGX_HTTP_SSL)
if (c->ssl) {
long rc;
X509 *cert;
ngx_http_ssl_srv_conf_t *sscf;
sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
if (sscf->verify) { if (sscf->verify) {

View File

@ -303,7 +303,8 @@ typedef struct {
ngx_buf_t **free; ngx_buf_t **free;
ngx_int_t nfree; ngx_int_t nfree;
ngx_uint_t pipeline; /* unsigned pipeline:1; */ unsigned pipeline:1;
unsigned ssl:1;
} ngx_http_connection_t; } ngx_http_connection_t;
@ -492,7 +493,6 @@ struct ngx_http_request_s {
#endif #endif
unsigned pipeline:1; unsigned pipeline:1;
unsigned plain_http:1;
unsigned chunked:1; unsigned chunked:1;
unsigned header_only:1; unsigned header_only:1;
unsigned keepalive:1; unsigned keepalive:1;