Mercurial > hg > nginx
comparison src/http/ngx_http_request.c @ 2007:b9de93d804ea
*) host in request line has priority
*) allow several Host headers
*) validate host
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Thu, 15 May 2008 14:44:47 +0000 |
parents | 22ec7da42e6f |
children | 4151c33e4dfa |
comparison
equal
deleted
inserted
replaced
2006:b52cb9bf2064 | 2007:b9de93d804ea |
---|---|
18 ngx_uint_t request_line); | 18 ngx_uint_t request_line); |
19 | 19 |
20 static ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r, | 20 static ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r, |
21 ngx_table_elt_t *h, ngx_uint_t offset); | 21 ngx_table_elt_t *h, ngx_uint_t offset); |
22 static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r, | 22 static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r, |
23 ngx_table_elt_t *h, ngx_uint_t offset); | |
24 static ngx_int_t ngx_http_process_host(ngx_http_request_t *r, | |
23 ngx_table_elt_t *h, ngx_uint_t offset); | 25 ngx_table_elt_t *h, ngx_uint_t offset); |
24 static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r, | 26 static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r, |
25 ngx_table_elt_t *h, ngx_uint_t offset); | 27 ngx_table_elt_t *h, ngx_uint_t offset); |
26 static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r, | 28 static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r, |
27 ngx_table_elt_t *h, ngx_uint_t offset); | 29 ngx_table_elt_t *h, ngx_uint_t offset); |
28 static ngx_int_t ngx_http_process_cookie(ngx_http_request_t *r, | 30 static ngx_int_t ngx_http_process_cookie(ngx_http_request_t *r, |
29 ngx_table_elt_t *h, ngx_uint_t offset); | 31 ngx_table_elt_t *h, ngx_uint_t offset); |
30 | 32 |
31 static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r); | 33 static ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r); |
32 static void ngx_http_process_request(ngx_http_request_t *r); | 34 static void ngx_http_process_request(ngx_http_request_t *r); |
33 static void ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, | 35 static ssize_t ngx_http_validate_host(u_char *host, size_t len); |
34 size_t len, ngx_uint_t hash); | 36 static ngx_int_t ngx_http_find_virtual_server(ngx_http_request_t *r, |
37 u_char *host, size_t len); | |
35 | 38 |
36 static void ngx_http_request_handler(ngx_event_t *ev); | 39 static void ngx_http_request_handler(ngx_event_t *ev); |
37 static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r); | 40 static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r); |
38 static void ngx_http_writer(ngx_http_request_t *r); | 41 static void ngx_http_writer(ngx_http_request_t *r); |
39 | 42 |
69 "client sent invalid method in HTTP/0.9 request" | 72 "client sent invalid method in HTTP/0.9 request" |
70 }; | 73 }; |
71 | 74 |
72 | 75 |
73 ngx_http_header_t ngx_http_headers_in[] = { | 76 ngx_http_header_t ngx_http_headers_in[] = { |
74 { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host), | 77 { ngx_string("Host"), 0, ngx_http_process_host }, |
75 ngx_http_process_unique_header_line }, | |
76 | 78 |
77 { ngx_string("Connection"), 0, ngx_http_process_connection }, | 79 { ngx_string("Connection"), 0, ngx_http_process_connection }, |
78 | 80 |
79 { ngx_string("If-Modified-Since"), | 81 { ngx_string("If-Modified-Since"), |
80 offsetof(ngx_http_headers_in_t, if_modified_since), | 82 offsetof(ngx_http_headers_in_t, if_modified_since), |
560 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME | 562 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME |
561 | 563 |
562 int | 564 int |
563 ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) | 565 ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg) |
564 { | 566 { |
565 u_char *p; | |
566 ngx_uint_t hash; | |
567 const char *servername; | 567 const char *servername; |
568 ngx_connection_t *c; | 568 ngx_connection_t *c; |
569 ngx_http_request_t *r; | 569 ngx_http_request_t *r; |
570 ngx_http_ssl_srv_conf_t *sscf; | 570 ngx_http_ssl_srv_conf_t *sscf; |
571 | 571 |
580 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | 580 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, |
581 "SSL server name: \"%s\"", servername); | 581 "SSL server name: \"%s\"", servername); |
582 | 582 |
583 r = c->data; | 583 r = c->data; |
584 | 584 |
585 if (r->virtual_names == NULL) { | 585 if (ngx_http_find_virtual_server(r, (u_char *) servername, |
586 ngx_strlen(servername)) | |
587 != NGX_OK) | |
588 { | |
586 return SSL_TLSEXT_ERR_NOACK; | 589 return SSL_TLSEXT_ERR_NOACK; |
587 } | 590 } |
588 | |
589 /* it seems browsers send low case server name */ | |
590 | |
591 hash = 0; | |
592 | |
593 for (p = (u_char *) servername; *p; p++) { | |
594 hash = ngx_hash(hash, *p); | |
595 } | |
596 | |
597 ngx_http_find_virtual_server(r, (u_char *) servername, | |
598 p - (u_char *) servername, hash); | |
599 | 591 |
600 sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); | 592 sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module); |
601 | 593 |
602 SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx); | 594 SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx); |
603 | 595 |
724 "http args: \"%V\"", &r->args); | 716 "http args: \"%V\"", &r->args); |
725 | 717 |
726 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | 718 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, |
727 "http exten: \"%V\"", &r->exten); | 719 "http exten: \"%V\"", &r->exten); |
728 | 720 |
721 if (r->host_start && r->host_end) { | |
722 n = ngx_http_validate_host(r->host_start, | |
723 r->host_end - r->host_start); | |
724 | |
725 if (n <= 0) { | |
726 ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
727 "client sent invalid host in request line"); | |
728 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); | |
729 return; | |
730 } | |
731 | |
732 r->headers_in.server.len = n; | |
733 r->headers_in.server.data = r->host_start; | |
734 } | |
735 | |
729 if (r->http_version < NGX_HTTP_VERSION_10) { | 736 if (r->http_version < NGX_HTTP_VERSION_10) { |
737 | |
738 if (ngx_http_find_virtual_server(r, r->headers_in.server.data, | |
739 r->headers_in.server.len) | |
740 == NGX_ERROR) | |
741 { | |
742 ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
743 return; | |
744 } | |
745 | |
730 ngx_http_process_request(r); | 746 ngx_http_process_request(r); |
731 return; | 747 return; |
732 } | 748 } |
733 | 749 |
734 | 750 |
1215 return NGX_ERROR; | 1231 return NGX_ERROR; |
1216 } | 1232 } |
1217 | 1233 |
1218 | 1234 |
1219 static ngx_int_t | 1235 static ngx_int_t |
1236 ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h, | |
1237 ngx_uint_t offset) | |
1238 { | |
1239 ssize_t len; | |
1240 | |
1241 if (r->headers_in.host == NULL) { | |
1242 r->headers_in.host = h; | |
1243 } | |
1244 | |
1245 len = ngx_http_validate_host(h->value.data, h->value.len); | |
1246 | |
1247 if (len <= 0) { | |
1248 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
1249 "client sent invalid host header"); | |
1250 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); | |
1251 return NGX_ERROR; | |
1252 } | |
1253 | |
1254 if (r->headers_in.server.len) { | |
1255 return NGX_OK; | |
1256 } | |
1257 | |
1258 r->headers_in.server.len = len; | |
1259 r->headers_in.server.data = h->value.data; | |
1260 | |
1261 return NGX_OK; | |
1262 } | |
1263 | |
1264 | |
1265 static ngx_int_t | |
1220 ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, | 1266 ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, |
1221 ngx_uint_t offset) | 1267 ngx_uint_t offset) |
1222 { | 1268 { |
1223 if (ngx_strcasestrn(h->value.data, "close", 5 - 1)) { | 1269 if (ngx_strcasestrn(h->value.data, "close", 5 - 1)) { |
1224 r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; | 1270 r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; |
1225 | 1271 |
1226 } else if (ngx_strcasestrn(h->value.data, "keep-alive", 10 - 1)) { | 1272 } else if (ngx_strcasestrn(h->value.data, "keep-alive", 10 - 1)) { |
1227 r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; | 1273 r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; |
1274 } | |
1275 | |
1276 return NGX_OK; | |
1277 } | |
1278 | |
1279 | |
1280 static ngx_int_t | |
1281 ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, | |
1282 ngx_uint_t offset) | |
1283 { | |
1284 u_char *ua, *user_agent; | |
1285 | |
1286 if (r->headers_in.user_agent) { | |
1287 return NGX_OK; | |
1288 } | |
1289 | |
1290 r->headers_in.user_agent = h; | |
1291 | |
1292 /* check some widespread browsers while the header is in CPU cache */ | |
1293 | |
1294 user_agent = h->value.data; | |
1295 | |
1296 ua = ngx_strstrn(user_agent, "MSIE", 4 - 1); | |
1297 | |
1298 if (ua && ua + 8 < user_agent + h->value.len) { | |
1299 | |
1300 r->headers_in.msie = 1; | |
1301 | |
1302 if (ua[4] == ' ' && ua[5] == '4' && ua[6] == '.') { | |
1303 r->headers_in.msie4 = 1; | |
1304 } | |
1305 | |
1306 #if 0 | |
1307 /* MSIE ignores the SSL "close notify" alert */ | |
1308 if (c->ssl) { | |
1309 c->ssl->no_send_shutdown = 1; | |
1310 } | |
1311 #endif | |
1312 } | |
1313 | |
1314 if (ngx_strstrn(user_agent, "Opera", 5 - 1)) { | |
1315 r->headers_in.opera = 1; | |
1316 r->headers_in.msie = 0; | |
1317 r->headers_in.msie4 = 0; | |
1318 } | |
1319 | |
1320 if (!r->headers_in.msie && !r->headers_in.opera) { | |
1321 | |
1322 if (ngx_strstrn(user_agent, "Gecko/", 6 - 1)) { | |
1323 r->headers_in.gecko = 1; | |
1324 | |
1325 } else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) { | |
1326 r->headers_in.konqueror = 1; | |
1327 } | |
1228 } | 1328 } |
1229 | 1329 |
1230 return NGX_OK; | 1330 return NGX_OK; |
1231 } | 1331 } |
1232 | 1332 |
1248 return NGX_ERROR; | 1348 return NGX_ERROR; |
1249 } | 1349 } |
1250 | 1350 |
1251 | 1351 |
1252 static ngx_int_t | 1352 static ngx_int_t |
1253 ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h, | |
1254 ngx_uint_t offset) | |
1255 { | |
1256 u_char *ua, *user_agent; | |
1257 | |
1258 if (r->headers_in.user_agent) { | |
1259 return NGX_OK; | |
1260 } | |
1261 | |
1262 r->headers_in.user_agent = h; | |
1263 | |
1264 /* check some widespread browsers while the header is in CPU cache */ | |
1265 | |
1266 user_agent = h->value.data; | |
1267 | |
1268 ua = ngx_strstrn(user_agent, "MSIE", 4 - 1); | |
1269 | |
1270 if (ua && ua + 8 < user_agent + h->value.len) { | |
1271 | |
1272 r->headers_in.msie = 1; | |
1273 | |
1274 if (ua[4] == ' ' && ua[5] == '4' && ua[6] == '.') { | |
1275 r->headers_in.msie4 = 1; | |
1276 } | |
1277 | |
1278 #if 0 | |
1279 /* MSIE ignores the SSL "close notify" alert */ | |
1280 if (c->ssl) { | |
1281 c->ssl->no_send_shutdown = 1; | |
1282 } | |
1283 #endif | |
1284 } | |
1285 | |
1286 if (ngx_strstrn(user_agent, "Opera", 5 - 1)) { | |
1287 r->headers_in.opera = 1; | |
1288 r->headers_in.msie = 0; | |
1289 r->headers_in.msie4 = 0; | |
1290 } | |
1291 | |
1292 if (!r->headers_in.msie && !r->headers_in.opera) { | |
1293 | |
1294 if (ngx_strstrn(user_agent, "Gecko/", 6 - 1)) { | |
1295 r->headers_in.gecko = 1; | |
1296 | |
1297 } else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) { | |
1298 r->headers_in.konqueror = 1; | |
1299 } | |
1300 } | |
1301 | |
1302 return NGX_OK; | |
1303 } | |
1304 | |
1305 | |
1306 static ngx_int_t | |
1307 ngx_http_process_request_header(ngx_http_request_t *r) | 1353 ngx_http_process_request_header(ngx_http_request_t *r) |
1308 { | 1354 { |
1309 size_t len; | 1355 if (ngx_http_find_virtual_server(r, r->headers_in.server.data, |
1310 u_char *host, ch; | 1356 r->headers_in.server.len) |
1311 ngx_uint_t hash; | 1357 == NGX_ERROR) |
1312 | 1358 { |
1313 if (r->headers_in.host) { | 1359 ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); |
1314 | 1360 return NGX_ERROR; |
1315 hash = 0; | 1361 } |
1316 | 1362 |
1317 for (len = 0; len < r->headers_in.host->value.len; len++) { | 1363 if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) { |
1318 ch = r->headers_in.host->value.data[len]; | 1364 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, |
1319 | 1365 "client sent HTTP/1.1 request without \"Host\" header"); |
1320 if (ch == ':') { | 1366 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); |
1321 break; | 1367 return NGX_ERROR; |
1322 } | |
1323 | |
1324 ch = ngx_tolower(ch); | |
1325 r->headers_in.host->value.data[len] = ch; | |
1326 hash = ngx_hash(hash, ch); | |
1327 } | |
1328 | |
1329 if (len && r->headers_in.host->value.data[len - 1] == '.') { | |
1330 len--; | |
1331 hash = ngx_hash_key(r->headers_in.host->value.data, len); | |
1332 } | |
1333 | |
1334 r->headers_in.host_name_len = len; | |
1335 | |
1336 if (r->virtual_names) { | |
1337 | |
1338 host = r->host_start; | |
1339 | |
1340 if (host == NULL) { | |
1341 host = r->headers_in.host->value.data; | |
1342 len = r->headers_in.host_name_len; | |
1343 | |
1344 } else { | |
1345 len = r->host_end - host; | |
1346 } | |
1347 | |
1348 ngx_http_find_virtual_server(r, host, len, hash); | |
1349 } | |
1350 | |
1351 } else { | |
1352 if (r->http_version > NGX_HTTP_VERSION_10) { | |
1353 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
1354 "client sent HTTP/1.1 request without \"Host\" header"); | |
1355 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); | |
1356 return NGX_ERROR; | |
1357 } | |
1358 | |
1359 r->headers_in.host_name_len = 0; | |
1360 } | 1368 } |
1361 | 1369 |
1362 if (r->headers_in.content_length) { | 1370 if (r->headers_in.content_length) { |
1363 r->headers_in.content_length_n = | 1371 r->headers_in.content_length_n = |
1364 ngx_atoof(r->headers_in.content_length->value.data, | 1372 ngx_atoof(r->headers_in.content_length->value.data, |
1487 | 1495 |
1488 return; | 1496 return; |
1489 } | 1497 } |
1490 | 1498 |
1491 | 1499 |
1492 static void | 1500 static ssize_t |
1493 ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len, | 1501 ngx_http_validate_host(u_char *host, size_t len) |
1494 ngx_uint_t hash) | 1502 { |
1495 { | 1503 u_char ch; |
1504 size_t i, last; | |
1505 ngx_uint_t dot; | |
1506 | |
1507 last = len; | |
1508 dot = 0; | |
1509 | |
1510 for (i = 0; i < len; i++) { | |
1511 ch = host[i]; | |
1512 | |
1513 if (ch == '.') { | |
1514 if (dot) { | |
1515 return -1; | |
1516 } | |
1517 | |
1518 dot = 1; | |
1519 continue; | |
1520 } | |
1521 | |
1522 dot = 0; | |
1523 | |
1524 if (ch == ':') { | |
1525 last = i; | |
1526 continue; | |
1527 } | |
1528 | |
1529 if (ch == '/' || ch == '\0') { | |
1530 return -1; | |
1531 } | |
1532 | |
1533 #if (NGX_WIN32) | |
1534 if (ch == '\\') { | |
1535 return -1; | |
1536 } | |
1537 #endif | |
1538 } | |
1539 | |
1540 if (dot) { | |
1541 last--; | |
1542 } | |
1543 | |
1544 return last; | |
1545 } | |
1546 | |
1547 | |
1548 static ngx_int_t | |
1549 ngx_http_find_virtual_server(ngx_http_request_t *r, u_char *host, size_t len) | |
1550 { | |
1551 u_char *server, ch; | |
1552 ngx_uint_t i, hash; | |
1496 ngx_http_core_loc_conf_t *clcf; | 1553 ngx_http_core_loc_conf_t *clcf; |
1497 ngx_http_core_srv_conf_t *cscf; | 1554 ngx_http_core_srv_conf_t *cscf; |
1498 | 1555 u_char buf[32]; |
1499 cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, host, len); | 1556 |
1557 if (len == 0 || r->virtual_names == NULL) { | |
1558 return NGX_DECLINED; | |
1559 } | |
1560 | |
1561 if (len <= 32) { | |
1562 server = buf; | |
1563 | |
1564 } else { | |
1565 server = ngx_palloc(r->pool, len); | |
1566 if (server == NULL) { | |
1567 return NGX_ERROR; | |
1568 } | |
1569 } | |
1570 | |
1571 hash = 0; | |
1572 | |
1573 for (i = 0; i < len; i++) { | |
1574 ch = host[i]; | |
1575 | |
1576 ch = ngx_tolower(ch); | |
1577 server[i] = ch; | |
1578 | |
1579 hash = ngx_hash(hash, ch); | |
1580 } | |
1581 | |
1582 cscf = ngx_hash_find_combined(&r->virtual_names->names, hash, server, len); | |
1500 | 1583 |
1501 if (cscf) { | 1584 if (cscf) { |
1502 goto found; | 1585 goto found; |
1503 } | 1586 } |
1504 | 1587 |
1509 ngx_uint_t i; | 1592 ngx_uint_t i; |
1510 ngx_str_t name; | 1593 ngx_str_t name; |
1511 ngx_http_server_name_t *sn; | 1594 ngx_http_server_name_t *sn; |
1512 | 1595 |
1513 name.len = len; | 1596 name.len = len; |
1514 name.data = host; | 1597 name.data = server; |
1515 | 1598 |
1516 sn = r->virtual_names->regex; | 1599 sn = r->virtual_names->regex; |
1517 | 1600 |
1518 for (i = 0; i < r->virtual_names->nregex; i++) { | 1601 for (i = 0; i < r->virtual_names->nregex; i++) { |
1519 | 1602 |
1526 if (n < 0) { | 1609 if (n < 0) { |
1527 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | 1610 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, |
1528 ngx_regex_exec_n | 1611 ngx_regex_exec_n |
1529 " failed: %d on \"%V\" using \"%V\"", | 1612 " failed: %d on \"%V\" using \"%V\"", |
1530 n, &name, &sn[i].name); | 1613 n, &name, &sn[i].name); |
1531 return; | 1614 return NGX_ERROR; |
1532 } | 1615 } |
1533 | 1616 |
1534 /* match */ | 1617 /* match */ |
1535 | 1618 |
1536 cscf = sn[i].core_srv_conf; | 1619 cscf = sn[i].core_srv_conf; |
1539 } | 1622 } |
1540 } | 1623 } |
1541 | 1624 |
1542 #endif | 1625 #endif |
1543 | 1626 |
1544 return; | 1627 return NGX_OK; |
1545 | 1628 |
1546 found: | 1629 found: |
1547 | 1630 |
1548 r->srv_conf = cscf->ctx->srv_conf; | 1631 r->srv_conf = cscf->ctx->srv_conf; |
1549 r->loc_conf = cscf->ctx->loc_conf; | 1632 r->loc_conf = cscf->ctx->loc_conf; |
1553 | 1636 |
1554 if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { | 1637 if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { |
1555 r->connection->log->log_level = clcf->err_log->log_level; | 1638 r->connection->log->log_level = clcf->err_log->log_level; |
1556 } | 1639 } |
1557 | 1640 |
1558 return; | 1641 return NGX_OK; |
1559 } | 1642 } |
1560 | 1643 |
1561 | 1644 |
1562 static void | 1645 static void |
1563 ngx_http_request_handler(ngx_event_t *ev) | 1646 ngx_http_request_handler(ngx_event_t *ev) |