Return to ngx_http_upstream.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / nginx / src / http |
1.1 ! misho 1: ! 2: /* ! 3: * Copyright (C) Igor Sysoev ! 4: * Copyright (C) Nginx, Inc. ! 5: */ ! 6: ! 7: ! 8: #include <ngx_config.h> ! 9: #include <ngx_core.h> ! 10: #include <ngx_http.h> ! 11: ! 12: ! 13: #if (NGX_HTTP_CACHE) ! 14: static ngx_int_t ngx_http_upstream_cache(ngx_http_request_t *r, ! 15: ngx_http_upstream_t *u); ! 16: static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r, ! 17: ngx_http_upstream_t *u); ! 18: static ngx_int_t ngx_http_upstream_cache_status(ngx_http_request_t *r, ! 19: ngx_http_variable_value_t *v, uintptr_t data); ! 20: #endif ! 21: ! 22: static void ngx_http_upstream_init_request(ngx_http_request_t *r); ! 23: static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx); ! 24: static void ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r); ! 25: static void ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r); ! 26: static void ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, ! 27: ngx_event_t *ev); ! 28: static void ngx_http_upstream_connect(ngx_http_request_t *r, ! 29: ngx_http_upstream_t *u); ! 30: static ngx_int_t ngx_http_upstream_reinit(ngx_http_request_t *r, ! 31: ngx_http_upstream_t *u); ! 32: static void ngx_http_upstream_send_request(ngx_http_request_t *r, ! 33: ngx_http_upstream_t *u); ! 34: static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r, ! 35: ngx_http_upstream_t *u); ! 36: static void ngx_http_upstream_process_header(ngx_http_request_t *r, ! 37: ngx_http_upstream_t *u); ! 38: static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r, ! 39: ngx_http_upstream_t *u); ! 40: static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r, ! 41: ngx_http_upstream_t *u); ! 42: static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c); ! 43: static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r, ! 44: ngx_http_upstream_t *u); ! 45: static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, ! 46: ngx_http_upstream_t *u); ! 47: static void ngx_http_upstream_send_response(ngx_http_request_t *r, ! 48: ngx_http_upstream_t *u); ! 49: static void ngx_http_upstream_upgrade(ngx_http_request_t *r, ! 50: ngx_http_upstream_t *u); ! 51: static void ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r); ! 52: static void ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r); ! 53: static void ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r, ! 54: ngx_http_upstream_t *u); ! 55: static void ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r, ! 56: ngx_http_upstream_t *u); ! 57: static void ngx_http_upstream_process_upgraded(ngx_http_request_t *r, ! 58: ngx_uint_t from_upstream, ngx_uint_t do_write); ! 59: static void ! 60: ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r); ! 61: static void ! 62: ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r, ! 63: ngx_http_upstream_t *u); ! 64: static void ! 65: ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, ! 66: ngx_uint_t do_write); ! 67: static ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data); ! 68: static ngx_int_t ngx_http_upstream_non_buffered_filter(void *data, ! 69: ssize_t bytes); ! 70: static void ngx_http_upstream_process_downstream(ngx_http_request_t *r); ! 71: static void ngx_http_upstream_process_upstream(ngx_http_request_t *r, ! 72: ngx_http_upstream_t *u); ! 73: static void ngx_http_upstream_process_request(ngx_http_request_t *r); ! 74: static void ngx_http_upstream_store(ngx_http_request_t *r, ! 75: ngx_http_upstream_t *u); ! 76: static void ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ! 77: ngx_http_upstream_t *u); ! 78: static void ngx_http_upstream_next(ngx_http_request_t *r, ! 79: ngx_http_upstream_t *u, ngx_uint_t ft_type); ! 80: static void ngx_http_upstream_cleanup(void *data); ! 81: static void ngx_http_upstream_finalize_request(ngx_http_request_t *r, ! 82: ngx_http_upstream_t *u, ngx_int_t rc); ! 83: ! 84: static ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r, ! 85: ngx_table_elt_t *h, ngx_uint_t offset); ! 86: static ngx_int_t ngx_http_upstream_process_content_length(ngx_http_request_t *r, ! 87: ngx_table_elt_t *h, ngx_uint_t offset); ! 88: static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ! 89: ngx_table_elt_t *h, ngx_uint_t offset); ! 90: static ngx_int_t ! 91: ngx_http_upstream_process_cache_control(ngx_http_request_t *r, ! 92: ngx_table_elt_t *h, ngx_uint_t offset); ! 93: static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ! 94: ngx_table_elt_t *h, ngx_uint_t offset); ! 95: static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r, ! 96: ngx_table_elt_t *h, ngx_uint_t offset); ! 97: static ngx_int_t ngx_http_upstream_process_accel_expires(ngx_http_request_t *r, ! 98: ngx_table_elt_t *h, ngx_uint_t offset); ! 99: static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ! 100: ngx_table_elt_t *h, ngx_uint_t offset); ! 101: static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r, ! 102: ngx_table_elt_t *h, ngx_uint_t offset); ! 103: static ngx_int_t ngx_http_upstream_process_charset(ngx_http_request_t *r, ! 104: ngx_table_elt_t *h, ngx_uint_t offset); ! 105: static ngx_int_t ngx_http_upstream_process_connection(ngx_http_request_t *r, ! 106: ngx_table_elt_t *h, ngx_uint_t offset); ! 107: static ngx_int_t ! 108: ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r, ! 109: ngx_table_elt_t *h, ngx_uint_t offset); ! 110: static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ! 111: ngx_table_elt_t *h, ngx_uint_t offset); ! 112: static ngx_int_t ! 113: ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r, ! 114: ngx_table_elt_t *h, ngx_uint_t offset); ! 115: static ngx_int_t ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ! 116: ngx_table_elt_t *h, ngx_uint_t offset); ! 117: static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ! 118: ngx_table_elt_t *h, ngx_uint_t offset); ! 119: static ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r, ! 120: ngx_table_elt_t *h, ngx_uint_t offset); ! 121: static ngx_int_t ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r, ! 122: ngx_table_elt_t *h, ngx_uint_t offset); ! 123: static ngx_int_t ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, ! 124: ngx_table_elt_t *h, ngx_uint_t offset); ! 125: static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r, ! 126: ngx_table_elt_t *h, ngx_uint_t offset); ! 127: ! 128: #if (NGX_HTTP_GZIP) ! 129: static ngx_int_t ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r, ! 130: ngx_table_elt_t *h, ngx_uint_t offset); ! 131: #endif ! 132: ! 133: static ngx_int_t ngx_http_upstream_add_variables(ngx_conf_t *cf); ! 134: static ngx_int_t ngx_http_upstream_addr_variable(ngx_http_request_t *r, ! 135: ngx_http_variable_value_t *v, uintptr_t data); ! 136: static ngx_int_t ngx_http_upstream_status_variable(ngx_http_request_t *r, ! 137: ngx_http_variable_value_t *v, uintptr_t data); ! 138: static ngx_int_t ngx_http_upstream_response_time_variable(ngx_http_request_t *r, ! 139: ngx_http_variable_value_t *v, uintptr_t data); ! 140: static ngx_int_t ngx_http_upstream_response_length_variable( ! 141: ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); ! 142: ! 143: static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy); ! 144: static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, ! 145: void *conf); ! 146: ! 147: static ngx_addr_t *ngx_http_upstream_get_local(ngx_http_request_t *r, ! 148: ngx_http_upstream_local_t *local); ! 149: ! 150: static void *ngx_http_upstream_create_main_conf(ngx_conf_t *cf); ! 151: static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf); ! 152: ! 153: #if (NGX_HTTP_SSL) ! 154: static void ngx_http_upstream_ssl_init_connection(ngx_http_request_t *, ! 155: ngx_http_upstream_t *u, ngx_connection_t *c); ! 156: static void ngx_http_upstream_ssl_handshake(ngx_connection_t *c); ! 157: #endif ! 158: ! 159: ! 160: ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = { ! 161: ! 162: { ngx_string("Status"), ! 163: ngx_http_upstream_process_header_line, ! 164: offsetof(ngx_http_upstream_headers_in_t, status), ! 165: ngx_http_upstream_copy_header_line, 0, 0 }, ! 166: ! 167: { ngx_string("Content-Type"), ! 168: ngx_http_upstream_process_header_line, ! 169: offsetof(ngx_http_upstream_headers_in_t, content_type), ! 170: ngx_http_upstream_copy_content_type, 0, 1 }, ! 171: ! 172: { ngx_string("Content-Length"), ! 173: ngx_http_upstream_process_content_length, ! 174: offsetof(ngx_http_upstream_headers_in_t, content_length), ! 175: ngx_http_upstream_ignore_header_line, 0, 0 }, ! 176: ! 177: { ngx_string("Date"), ! 178: ngx_http_upstream_process_header_line, ! 179: offsetof(ngx_http_upstream_headers_in_t, date), ! 180: ngx_http_upstream_copy_header_line, ! 181: offsetof(ngx_http_headers_out_t, date), 0 }, ! 182: ! 183: { ngx_string("Last-Modified"), ! 184: ngx_http_upstream_process_header_line, ! 185: offsetof(ngx_http_upstream_headers_in_t, last_modified), ! 186: ngx_http_upstream_copy_last_modified, 0, 0 }, ! 187: ! 188: { ngx_string("ETag"), ! 189: ngx_http_upstream_process_header_line, ! 190: offsetof(ngx_http_upstream_headers_in_t, etag), ! 191: ngx_http_upstream_copy_header_line, ! 192: offsetof(ngx_http_headers_out_t, etag), 0 }, ! 193: ! 194: { ngx_string("Server"), ! 195: ngx_http_upstream_process_header_line, ! 196: offsetof(ngx_http_upstream_headers_in_t, server), ! 197: ngx_http_upstream_copy_header_line, ! 198: offsetof(ngx_http_headers_out_t, server), 0 }, ! 199: ! 200: { ngx_string("WWW-Authenticate"), ! 201: ngx_http_upstream_process_header_line, ! 202: offsetof(ngx_http_upstream_headers_in_t, www_authenticate), ! 203: ngx_http_upstream_copy_header_line, 0, 0 }, ! 204: ! 205: { ngx_string("Location"), ! 206: ngx_http_upstream_process_header_line, ! 207: offsetof(ngx_http_upstream_headers_in_t, location), ! 208: ngx_http_upstream_rewrite_location, 0, 0 }, ! 209: ! 210: { ngx_string("Refresh"), ! 211: ngx_http_upstream_ignore_header_line, 0, ! 212: ngx_http_upstream_rewrite_refresh, 0, 0 }, ! 213: ! 214: { ngx_string("Set-Cookie"), ! 215: ngx_http_upstream_process_set_cookie, 0, ! 216: ngx_http_upstream_rewrite_set_cookie, 0, 1 }, ! 217: ! 218: { ngx_string("Content-Disposition"), ! 219: ngx_http_upstream_ignore_header_line, 0, ! 220: ngx_http_upstream_copy_header_line, 0, 1 }, ! 221: ! 222: { ngx_string("Cache-Control"), ! 223: ngx_http_upstream_process_cache_control, 0, ! 224: ngx_http_upstream_copy_multi_header_lines, ! 225: offsetof(ngx_http_headers_out_t, cache_control), 1 }, ! 226: ! 227: { ngx_string("Expires"), ! 228: ngx_http_upstream_process_expires, 0, ! 229: ngx_http_upstream_copy_header_line, ! 230: offsetof(ngx_http_headers_out_t, expires), 1 }, ! 231: ! 232: { ngx_string("Accept-Ranges"), ! 233: ngx_http_upstream_process_header_line, ! 234: offsetof(ngx_http_upstream_headers_in_t, accept_ranges), ! 235: ngx_http_upstream_copy_allow_ranges, ! 236: offsetof(ngx_http_headers_out_t, accept_ranges), 1 }, ! 237: ! 238: { ngx_string("Connection"), ! 239: ngx_http_upstream_process_connection, 0, ! 240: ngx_http_upstream_ignore_header_line, 0, 0 }, ! 241: ! 242: { ngx_string("Keep-Alive"), ! 243: ngx_http_upstream_ignore_header_line, 0, ! 244: ngx_http_upstream_ignore_header_line, 0, 0 }, ! 245: ! 246: { ngx_string("X-Powered-By"), ! 247: ngx_http_upstream_ignore_header_line, 0, ! 248: ngx_http_upstream_copy_header_line, 0, 0 }, ! 249: ! 250: { ngx_string("X-Accel-Expires"), ! 251: ngx_http_upstream_process_accel_expires, 0, ! 252: ngx_http_upstream_copy_header_line, 0, 0 }, ! 253: ! 254: { ngx_string("X-Accel-Redirect"), ! 255: ngx_http_upstream_process_header_line, ! 256: offsetof(ngx_http_upstream_headers_in_t, x_accel_redirect), ! 257: ngx_http_upstream_copy_header_line, 0, 0 }, ! 258: ! 259: { ngx_string("X-Accel-Limit-Rate"), ! 260: ngx_http_upstream_process_limit_rate, 0, ! 261: ngx_http_upstream_copy_header_line, 0, 0 }, ! 262: ! 263: { ngx_string("X-Accel-Buffering"), ! 264: ngx_http_upstream_process_buffering, 0, ! 265: ngx_http_upstream_copy_header_line, 0, 0 }, ! 266: ! 267: { ngx_string("X-Accel-Charset"), ! 268: ngx_http_upstream_process_charset, 0, ! 269: ngx_http_upstream_copy_header_line, 0, 0 }, ! 270: ! 271: { ngx_string("Transfer-Encoding"), ! 272: ngx_http_upstream_process_transfer_encoding, 0, ! 273: ngx_http_upstream_ignore_header_line, 0, 0 }, ! 274: ! 275: #if (NGX_HTTP_GZIP) ! 276: { ngx_string("Content-Encoding"), ! 277: ngx_http_upstream_process_header_line, ! 278: offsetof(ngx_http_upstream_headers_in_t, content_encoding), ! 279: ngx_http_upstream_copy_content_encoding, 0, 0 }, ! 280: #endif ! 281: ! 282: { ngx_null_string, NULL, 0, NULL, 0, 0 } ! 283: }; ! 284: ! 285: ! 286: static ngx_command_t ngx_http_upstream_commands[] = { ! 287: ! 288: { ngx_string("upstream"), ! 289: NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1, ! 290: ngx_http_upstream, ! 291: 0, ! 292: 0, ! 293: NULL }, ! 294: ! 295: { ngx_string("server"), ! 296: NGX_HTTP_UPS_CONF|NGX_CONF_1MORE, ! 297: ngx_http_upstream_server, ! 298: NGX_HTTP_SRV_CONF_OFFSET, ! 299: 0, ! 300: NULL }, ! 301: ! 302: ngx_null_command ! 303: }; ! 304: ! 305: ! 306: static ngx_http_module_t ngx_http_upstream_module_ctx = { ! 307: ngx_http_upstream_add_variables, /* preconfiguration */ ! 308: NULL, /* postconfiguration */ ! 309: ! 310: ngx_http_upstream_create_main_conf, /* create main configuration */ ! 311: ngx_http_upstream_init_main_conf, /* init main configuration */ ! 312: ! 313: NULL, /* create server configuration */ ! 314: NULL, /* merge server configuration */ ! 315: ! 316: NULL, /* create location configuration */ ! 317: NULL /* merge location configuration */ ! 318: }; ! 319: ! 320: ! 321: ngx_module_t ngx_http_upstream_module = { ! 322: NGX_MODULE_V1, ! 323: &ngx_http_upstream_module_ctx, /* module context */ ! 324: ngx_http_upstream_commands, /* module directives */ ! 325: NGX_HTTP_MODULE, /* module type */ ! 326: NULL, /* init master */ ! 327: NULL, /* init module */ ! 328: NULL, /* init process */ ! 329: NULL, /* init thread */ ! 330: NULL, /* exit thread */ ! 331: NULL, /* exit process */ ! 332: NULL, /* exit master */ ! 333: NGX_MODULE_V1_PADDING ! 334: }; ! 335: ! 336: ! 337: static ngx_http_variable_t ngx_http_upstream_vars[] = { ! 338: ! 339: { ngx_string("upstream_addr"), NULL, ! 340: ngx_http_upstream_addr_variable, 0, ! 341: NGX_HTTP_VAR_NOCACHEABLE, 0 }, ! 342: ! 343: { ngx_string("upstream_status"), NULL, ! 344: ngx_http_upstream_status_variable, 0, ! 345: NGX_HTTP_VAR_NOCACHEABLE, 0 }, ! 346: ! 347: { ngx_string("upstream_response_time"), NULL, ! 348: ngx_http_upstream_response_time_variable, 0, ! 349: NGX_HTTP_VAR_NOCACHEABLE, 0 }, ! 350: ! 351: { ngx_string("upstream_response_length"), NULL, ! 352: ngx_http_upstream_response_length_variable, 0, ! 353: NGX_HTTP_VAR_NOCACHEABLE, 0 }, ! 354: ! 355: #if (NGX_HTTP_CACHE) ! 356: ! 357: { ngx_string("upstream_cache_status"), NULL, ! 358: ngx_http_upstream_cache_status, 0, ! 359: NGX_HTTP_VAR_NOCACHEABLE, 0 }, ! 360: ! 361: #endif ! 362: ! 363: { ngx_null_string, NULL, NULL, 0, 0, 0 } ! 364: }; ! 365: ! 366: ! 367: static ngx_http_upstream_next_t ngx_http_upstream_next_errors[] = { ! 368: { 500, NGX_HTTP_UPSTREAM_FT_HTTP_500 }, ! 369: { 502, NGX_HTTP_UPSTREAM_FT_HTTP_502 }, ! 370: { 503, NGX_HTTP_UPSTREAM_FT_HTTP_503 }, ! 371: { 504, NGX_HTTP_UPSTREAM_FT_HTTP_504 }, ! 372: { 404, NGX_HTTP_UPSTREAM_FT_HTTP_404 }, ! 373: { 0, 0 } ! 374: }; ! 375: ! 376: ! 377: ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[] = { ! 378: { ngx_string("GET"), NGX_HTTP_GET}, ! 379: { ngx_string("HEAD"), NGX_HTTP_HEAD }, ! 380: { ngx_string("POST"), NGX_HTTP_POST }, ! 381: { ngx_null_string, 0 } ! 382: }; ! 383: ! 384: ! 385: ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[] = { ! 386: { ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT }, ! 387: { ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES }, ! 388: { ngx_string("X-Accel-Limit-Rate"), NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE }, ! 389: { ngx_string("X-Accel-Buffering"), NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING }, ! 390: { ngx_string("X-Accel-Charset"), NGX_HTTP_UPSTREAM_IGN_XA_CHARSET }, ! 391: { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES }, ! 392: { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL }, ! 393: { ngx_string("Set-Cookie"), NGX_HTTP_UPSTREAM_IGN_SET_COOKIE }, ! 394: { ngx_null_string, 0 } ! 395: }; ! 396: ! 397: ! 398: ngx_int_t ! 399: ngx_http_upstream_create(ngx_http_request_t *r) ! 400: { ! 401: ngx_http_upstream_t *u; ! 402: ! 403: u = r->upstream; ! 404: ! 405: if (u && u->cleanup) { ! 406: r->main->count++; ! 407: ngx_http_upstream_cleanup(r); ! 408: } ! 409: ! 410: u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t)); ! 411: if (u == NULL) { ! 412: return NGX_ERROR; ! 413: } ! 414: ! 415: r->upstream = u; ! 416: ! 417: u->peer.log = r->connection->log; ! 418: u->peer.log_error = NGX_ERROR_ERR; ! 419: #if (NGX_THREADS) ! 420: u->peer.lock = &r->connection->lock; ! 421: #endif ! 422: ! 423: #if (NGX_HTTP_CACHE) ! 424: r->cache = NULL; ! 425: #endif ! 426: ! 427: u->headers_in.content_length_n = -1; ! 428: ! 429: return NGX_OK; ! 430: } ! 431: ! 432: ! 433: void ! 434: ngx_http_upstream_init(ngx_http_request_t *r) ! 435: { ! 436: ngx_connection_t *c; ! 437: ! 438: c = r->connection; ! 439: ! 440: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 441: "http init upstream, client timer: %d", c->read->timer_set); ! 442: ! 443: #if (NGX_HTTP_SPDY) ! 444: if (r->spdy_stream) { ! 445: ngx_http_upstream_init_request(r); ! 446: return; ! 447: } ! 448: #endif ! 449: ! 450: if (c->read->timer_set) { ! 451: ngx_del_timer(c->read); ! 452: } ! 453: ! 454: if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { ! 455: ! 456: if (!c->write->active) { ! 457: if (ngx_add_event(c->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT) ! 458: == NGX_ERROR) ! 459: { ! 460: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); ! 461: return; ! 462: } ! 463: } ! 464: } ! 465: ! 466: ngx_http_upstream_init_request(r); ! 467: } ! 468: ! 469: ! 470: static void ! 471: ngx_http_upstream_init_request(ngx_http_request_t *r) ! 472: { ! 473: ngx_str_t *host; ! 474: ngx_uint_t i; ! 475: ngx_resolver_ctx_t *ctx, temp; ! 476: ngx_http_cleanup_t *cln; ! 477: ngx_http_upstream_t *u; ! 478: ngx_http_core_loc_conf_t *clcf; ! 479: ngx_http_upstream_srv_conf_t *uscf, **uscfp; ! 480: ngx_http_upstream_main_conf_t *umcf; ! 481: ! 482: if (r->aio) { ! 483: return; ! 484: } ! 485: ! 486: u = r->upstream; ! 487: ! 488: #if (NGX_HTTP_CACHE) ! 489: ! 490: if (u->conf->cache) { ! 491: ngx_int_t rc; ! 492: ! 493: rc = ngx_http_upstream_cache(r, u); ! 494: ! 495: if (rc == NGX_BUSY) { ! 496: r->write_event_handler = ngx_http_upstream_init_request; ! 497: return; ! 498: } ! 499: ! 500: r->write_event_handler = ngx_http_request_empty_handler; ! 501: ! 502: if (rc == NGX_DONE) { ! 503: return; ! 504: } ! 505: ! 506: if (rc != NGX_DECLINED) { ! 507: ngx_http_finalize_request(r, rc); ! 508: return; ! 509: } ! 510: } ! 511: ! 512: #endif ! 513: ! 514: u->store = (u->conf->store || u->conf->store_lengths); ! 515: ! 516: if (!u->store && !r->post_action && !u->conf->ignore_client_abort) { ! 517: r->read_event_handler = ngx_http_upstream_rd_check_broken_connection; ! 518: r->write_event_handler = ngx_http_upstream_wr_check_broken_connection; ! 519: } ! 520: ! 521: if (r->request_body) { ! 522: u->request_bufs = r->request_body->bufs; ! 523: } ! 524: ! 525: if (u->create_request(r) != NGX_OK) { ! 526: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); ! 527: return; ! 528: } ! 529: ! 530: u->peer.local = ngx_http_upstream_get_local(r, u->conf->local); ! 531: ! 532: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ! 533: ! 534: u->output.alignment = clcf->directio_alignment; ! 535: u->output.pool = r->pool; ! 536: u->output.bufs.num = 1; ! 537: u->output.bufs.size = clcf->client_body_buffer_size; ! 538: u->output.output_filter = ngx_chain_writer; ! 539: u->output.filter_ctx = &u->writer; ! 540: ! 541: u->writer.pool = r->pool; ! 542: ! 543: if (r->upstream_states == NULL) { ! 544: ! 545: r->upstream_states = ngx_array_create(r->pool, 1, ! 546: sizeof(ngx_http_upstream_state_t)); ! 547: if (r->upstream_states == NULL) { ! 548: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); ! 549: return; ! 550: } ! 551: ! 552: } else { ! 553: ! 554: u->state = ngx_array_push(r->upstream_states); ! 555: if (u->state == NULL) { ! 556: ngx_http_upstream_finalize_request(r, u, ! 557: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 558: return; ! 559: } ! 560: ! 561: ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t)); ! 562: } ! 563: ! 564: cln = ngx_http_cleanup_add(r, 0); ! 565: if (cln == NULL) { ! 566: ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); ! 567: return; ! 568: } ! 569: ! 570: cln->handler = ngx_http_upstream_cleanup; ! 571: cln->data = r; ! 572: u->cleanup = &cln->handler; ! 573: ! 574: if (u->resolved == NULL) { ! 575: ! 576: uscf = u->conf->upstream; ! 577: ! 578: } else { ! 579: ! 580: if (u->resolved->sockaddr) { ! 581: ! 582: if (ngx_http_upstream_create_round_robin_peer(r, u->resolved) ! 583: != NGX_OK) ! 584: { ! 585: ngx_http_upstream_finalize_request(r, u, ! 586: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 587: return; ! 588: } ! 589: ! 590: ngx_http_upstream_connect(r, u); ! 591: ! 592: return; ! 593: } ! 594: ! 595: host = &u->resolved->host; ! 596: ! 597: umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); ! 598: ! 599: uscfp = umcf->upstreams.elts; ! 600: ! 601: for (i = 0; i < umcf->upstreams.nelts; i++) { ! 602: ! 603: uscf = uscfp[i]; ! 604: ! 605: if (uscf->host.len == host->len ! 606: && ((uscf->port == 0 && u->resolved->no_port) ! 607: || uscf->port == u->resolved->port) ! 608: && ngx_memcmp(uscf->host.data, host->data, host->len) == 0) ! 609: { ! 610: goto found; ! 611: } ! 612: } ! 613: ! 614: if (u->resolved->port == 0) { ! 615: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ! 616: "no port in upstream \"%V\"", host); ! 617: ngx_http_upstream_finalize_request(r, u, ! 618: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 619: return; ! 620: } ! 621: ! 622: temp.name = *host; ! 623: ! 624: ctx = ngx_resolve_start(clcf->resolver, &temp); ! 625: if (ctx == NULL) { ! 626: ngx_http_upstream_finalize_request(r, u, ! 627: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 628: return; ! 629: } ! 630: ! 631: if (ctx == NGX_NO_RESOLVER) { ! 632: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ! 633: "no resolver defined to resolve %V", host); ! 634: ! 635: ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY); ! 636: return; ! 637: } ! 638: ! 639: ctx->name = *host; ! 640: ctx->type = NGX_RESOLVE_A; ! 641: ctx->handler = ngx_http_upstream_resolve_handler; ! 642: ctx->data = r; ! 643: ctx->timeout = clcf->resolver_timeout; ! 644: ! 645: u->resolved->ctx = ctx; ! 646: ! 647: if (ngx_resolve_name(ctx) != NGX_OK) { ! 648: u->resolved->ctx = NULL; ! 649: ngx_http_upstream_finalize_request(r, u, ! 650: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 651: return; ! 652: } ! 653: ! 654: return; ! 655: } ! 656: ! 657: found: ! 658: ! 659: if (uscf == NULL) { ! 660: ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, ! 661: "no upstream configuration"); ! 662: ngx_http_upstream_finalize_request(r, u, ! 663: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 664: return; ! 665: } ! 666: ! 667: if (uscf->peer.init(r, uscf) != NGX_OK) { ! 668: ngx_http_upstream_finalize_request(r, u, ! 669: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 670: return; ! 671: } ! 672: ! 673: ngx_http_upstream_connect(r, u); ! 674: } ! 675: ! 676: ! 677: #if (NGX_HTTP_CACHE) ! 678: ! 679: static ngx_int_t ! 680: ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 681: { ! 682: ngx_int_t rc; ! 683: ngx_http_cache_t *c; ! 684: ! 685: c = r->cache; ! 686: ! 687: if (c == NULL) { ! 688: ! 689: if (!(r->method & u->conf->cache_methods)) { ! 690: return NGX_DECLINED; ! 691: } ! 692: ! 693: if (r->method & NGX_HTTP_HEAD) { ! 694: u->method = ngx_http_core_get_method; ! 695: } ! 696: ! 697: if (ngx_http_file_cache_new(r) != NGX_OK) { ! 698: return NGX_ERROR; ! 699: } ! 700: ! 701: if (u->create_key(r) != NGX_OK) { ! 702: return NGX_ERROR; ! 703: } ! 704: ! 705: /* TODO: add keys */ ! 706: ! 707: ngx_http_file_cache_create_key(r); ! 708: ! 709: if (r->cache->header_start + 256 >= u->conf->buffer_size) { ! 710: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ! 711: "%V_buffer_size %uz is not enough for cache key, " ! 712: "it should increased at least to %uz", ! 713: &u->conf->module, u->conf->buffer_size, ! 714: ngx_align(r->cache->header_start + 256, 1024)); ! 715: ! 716: r->cache = NULL; ! 717: return NGX_DECLINED; ! 718: } ! 719: ! 720: u->cacheable = 1; ! 721: ! 722: switch (ngx_http_test_predicates(r, u->conf->cache_bypass)) { ! 723: ! 724: case NGX_ERROR: ! 725: return NGX_ERROR; ! 726: ! 727: case NGX_DECLINED: ! 728: u->cache_status = NGX_HTTP_CACHE_BYPASS; ! 729: return NGX_DECLINED; ! 730: ! 731: default: /* NGX_OK */ ! 732: break; ! 733: } ! 734: ! 735: c = r->cache; ! 736: ! 737: c->min_uses = u->conf->cache_min_uses; ! 738: c->body_start = u->conf->buffer_size; ! 739: c->file_cache = u->conf->cache->data; ! 740: ! 741: c->lock = u->conf->cache_lock; ! 742: c->lock_timeout = u->conf->cache_lock_timeout; ! 743: ! 744: u->cache_status = NGX_HTTP_CACHE_MISS; ! 745: } ! 746: ! 747: rc = ngx_http_file_cache_open(r); ! 748: ! 749: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 750: "http upstream cache: %i", rc); ! 751: ! 752: switch (rc) { ! 753: ! 754: case NGX_HTTP_CACHE_UPDATING: ! 755: ! 756: if (u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) { ! 757: u->cache_status = rc; ! 758: rc = NGX_OK; ! 759: ! 760: } else { ! 761: rc = NGX_HTTP_CACHE_STALE; ! 762: } ! 763: ! 764: break; ! 765: ! 766: case NGX_OK: ! 767: u->cache_status = NGX_HTTP_CACHE_HIT; ! 768: } ! 769: ! 770: switch (rc) { ! 771: ! 772: case NGX_OK: ! 773: ! 774: rc = ngx_http_upstream_cache_send(r, u); ! 775: ! 776: if (rc != NGX_HTTP_UPSTREAM_INVALID_HEADER) { ! 777: return rc; ! 778: } ! 779: ! 780: break; ! 781: ! 782: case NGX_HTTP_CACHE_STALE: ! 783: ! 784: c->valid_sec = 0; ! 785: u->buffer.start = NULL; ! 786: u->cache_status = NGX_HTTP_CACHE_EXPIRED; ! 787: ! 788: break; ! 789: ! 790: case NGX_DECLINED: ! 791: ! 792: if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) { ! 793: u->buffer.start = NULL; ! 794: ! 795: } else { ! 796: u->buffer.pos = u->buffer.start + c->header_start; ! 797: u->buffer.last = u->buffer.pos; ! 798: } ! 799: ! 800: break; ! 801: ! 802: case NGX_HTTP_CACHE_SCARCE: ! 803: ! 804: u->cacheable = 0; ! 805: ! 806: break; ! 807: ! 808: case NGX_AGAIN: ! 809: ! 810: return NGX_BUSY; ! 811: ! 812: case NGX_ERROR: ! 813: ! 814: return NGX_ERROR; ! 815: ! 816: default: ! 817: ! 818: /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */ ! 819: ! 820: u->cache_status = NGX_HTTP_CACHE_HIT; ! 821: ! 822: return rc; ! 823: } ! 824: ! 825: r->cached = 0; ! 826: ! 827: return NGX_DECLINED; ! 828: } ! 829: ! 830: ! 831: static ngx_int_t ! 832: ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 833: { ! 834: ngx_int_t rc; ! 835: ngx_http_cache_t *c; ! 836: ! 837: r->cached = 1; ! 838: c = r->cache; ! 839: ! 840: if (c->header_start == c->body_start) { ! 841: r->http_version = NGX_HTTP_VERSION_9; ! 842: return ngx_http_cache_send(r); ! 843: } ! 844: ! 845: /* TODO: cache stack */ ! 846: ! 847: u->buffer = *c->buf; ! 848: u->buffer.pos += c->header_start; ! 849: ! 850: ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); ! 851: u->headers_in.content_length_n = -1; ! 852: ! 853: if (ngx_list_init(&u->headers_in.headers, r->pool, 8, ! 854: sizeof(ngx_table_elt_t)) ! 855: != NGX_OK) ! 856: { ! 857: return NGX_ERROR; ! 858: } ! 859: ! 860: rc = u->process_header(r); ! 861: ! 862: if (rc == NGX_OK) { ! 863: ! 864: if (ngx_http_upstream_process_headers(r, u) != NGX_OK) { ! 865: return NGX_DONE; ! 866: } ! 867: ! 868: return ngx_http_cache_send(r); ! 869: } ! 870: ! 871: if (rc == NGX_ERROR) { ! 872: return NGX_ERROR; ! 873: } ! 874: ! 875: /* rc == NGX_HTTP_UPSTREAM_INVALID_HEADER */ ! 876: ! 877: /* TODO: delete file */ ! 878: ! 879: return rc; ! 880: } ! 881: ! 882: #endif ! 883: ! 884: ! 885: static void ! 886: ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx) ! 887: { ! 888: ngx_connection_t *c; ! 889: ngx_http_request_t *r; ! 890: ngx_http_upstream_t *u; ! 891: ngx_http_upstream_resolved_t *ur; ! 892: ! 893: r = ctx->data; ! 894: c = r->connection; ! 895: ! 896: u = r->upstream; ! 897: ur = u->resolved; ! 898: ! 899: if (ctx->state) { ! 900: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ! 901: "%V could not be resolved (%i: %s)", ! 902: &ctx->name, ctx->state, ! 903: ngx_resolver_strerror(ctx->state)); ! 904: ! 905: ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY); ! 906: goto failed; ! 907: } ! 908: ! 909: ur->naddrs = ctx->naddrs; ! 910: ur->addrs = ctx->addrs; ! 911: ! 912: #if (NGX_DEBUG) ! 913: { ! 914: in_addr_t addr; ! 915: ngx_uint_t i; ! 916: ! 917: for (i = 0; i < ctx->naddrs; i++) { ! 918: addr = ntohl(ur->addrs[i]); ! 919: ! 920: ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 921: "name was resolved to %ud.%ud.%ud.%ud", ! 922: (addr >> 24) & 0xff, (addr >> 16) & 0xff, ! 923: (addr >> 8) & 0xff, addr & 0xff); ! 924: } ! 925: } ! 926: #endif ! 927: ! 928: if (ngx_http_upstream_create_round_robin_peer(r, ur) != NGX_OK) { ! 929: ngx_http_upstream_finalize_request(r, u, ! 930: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 931: goto failed; ! 932: } ! 933: ! 934: ngx_resolve_name_done(ctx); ! 935: ur->ctx = NULL; ! 936: ! 937: ngx_http_upstream_connect(r, u); ! 938: ! 939: failed: ! 940: ! 941: ngx_http_run_posted_requests(c); ! 942: } ! 943: ! 944: ! 945: static void ! 946: ngx_http_upstream_handler(ngx_event_t *ev) ! 947: { ! 948: ngx_connection_t *c; ! 949: ngx_http_request_t *r; ! 950: ngx_http_log_ctx_t *ctx; ! 951: ngx_http_upstream_t *u; ! 952: ! 953: c = ev->data; ! 954: r = c->data; ! 955: ! 956: u = r->upstream; ! 957: c = r->connection; ! 958: ! 959: ctx = c->log->data; ! 960: ctx->current_request = r; ! 961: ! 962: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 963: "http upstream request: \"%V?%V\"", &r->uri, &r->args); ! 964: ! 965: if (ev->write) { ! 966: u->write_event_handler(r, u); ! 967: ! 968: } else { ! 969: u->read_event_handler(r, u); ! 970: } ! 971: ! 972: ngx_http_run_posted_requests(c); ! 973: } ! 974: ! 975: ! 976: static void ! 977: ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r) ! 978: { ! 979: ngx_http_upstream_check_broken_connection(r, r->connection->read); ! 980: } ! 981: ! 982: ! 983: static void ! 984: ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r) ! 985: { ! 986: ngx_http_upstream_check_broken_connection(r, r->connection->write); ! 987: } ! 988: ! 989: ! 990: static void ! 991: ngx_http_upstream_check_broken_connection(ngx_http_request_t *r, ! 992: ngx_event_t *ev) ! 993: { ! 994: int n; ! 995: char buf[1]; ! 996: ngx_err_t err; ! 997: ngx_int_t event; ! 998: ngx_connection_t *c; ! 999: ngx_http_upstream_t *u; ! 1000: ! 1001: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0, ! 1002: "http upstream check client, write event:%d, \"%V\"", ! 1003: ev->write, &r->uri); ! 1004: ! 1005: c = r->connection; ! 1006: u = r->upstream; ! 1007: ! 1008: if (c->error) { ! 1009: if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { ! 1010: ! 1011: event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT; ! 1012: ! 1013: if (ngx_del_event(ev, event, 0) != NGX_OK) { ! 1014: ngx_http_upstream_finalize_request(r, u, ! 1015: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1016: return; ! 1017: } ! 1018: } ! 1019: ! 1020: if (!u->cacheable) { ! 1021: ngx_http_upstream_finalize_request(r, u, ! 1022: NGX_HTTP_CLIENT_CLOSED_REQUEST); ! 1023: } ! 1024: ! 1025: return; ! 1026: } ! 1027: ! 1028: #if (NGX_HTTP_SPDY) ! 1029: if (r->spdy_stream) { ! 1030: return; ! 1031: } ! 1032: #endif ! 1033: ! 1034: #if (NGX_HAVE_KQUEUE) ! 1035: ! 1036: if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { ! 1037: ! 1038: if (!ev->pending_eof) { ! 1039: return; ! 1040: } ! 1041: ! 1042: ev->eof = 1; ! 1043: c->error = 1; ! 1044: ! 1045: if (ev->kq_errno) { ! 1046: ev->error = 1; ! 1047: } ! 1048: ! 1049: if (!u->cacheable && u->peer.connection) { ! 1050: ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, ! 1051: "kevent() reported that client prematurely closed " ! 1052: "connection, so upstream connection is closed too"); ! 1053: ngx_http_upstream_finalize_request(r, u, ! 1054: NGX_HTTP_CLIENT_CLOSED_REQUEST); ! 1055: return; ! 1056: } ! 1057: ! 1058: ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno, ! 1059: "kevent() reported that client prematurely closed " ! 1060: "connection"); ! 1061: ! 1062: if (u->peer.connection == NULL) { ! 1063: ngx_http_upstream_finalize_request(r, u, ! 1064: NGX_HTTP_CLIENT_CLOSED_REQUEST); ! 1065: } ! 1066: ! 1067: return; ! 1068: } ! 1069: ! 1070: #endif ! 1071: ! 1072: n = recv(c->fd, buf, 1, MSG_PEEK); ! 1073: ! 1074: err = ngx_socket_errno; ! 1075: ! 1076: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err, ! 1077: "http upstream recv(): %d", n); ! 1078: ! 1079: if (ev->write && (n >= 0 || err == NGX_EAGAIN)) { ! 1080: return; ! 1081: } ! 1082: ! 1083: if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) { ! 1084: ! 1085: event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT; ! 1086: ! 1087: if (ngx_del_event(ev, event, 0) != NGX_OK) { ! 1088: ngx_http_upstream_finalize_request(r, u, ! 1089: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1090: return; ! 1091: } ! 1092: } ! 1093: ! 1094: if (n > 0) { ! 1095: return; ! 1096: } ! 1097: ! 1098: if (n == -1) { ! 1099: if (err == NGX_EAGAIN) { ! 1100: return; ! 1101: } ! 1102: ! 1103: ev->error = 1; ! 1104: ! 1105: } else { /* n == 0 */ ! 1106: err = 0; ! 1107: } ! 1108: ! 1109: ev->eof = 1; ! 1110: c->error = 1; ! 1111: ! 1112: if (!u->cacheable && u->peer.connection) { ! 1113: ngx_log_error(NGX_LOG_INFO, ev->log, err, ! 1114: "client prematurely closed connection, " ! 1115: "so upstream connection is closed too"); ! 1116: ngx_http_upstream_finalize_request(r, u, ! 1117: NGX_HTTP_CLIENT_CLOSED_REQUEST); ! 1118: return; ! 1119: } ! 1120: ! 1121: ngx_log_error(NGX_LOG_INFO, ev->log, err, ! 1122: "client prematurely closed connection"); ! 1123: ! 1124: if (u->peer.connection == NULL) { ! 1125: ngx_http_upstream_finalize_request(r, u, ! 1126: NGX_HTTP_CLIENT_CLOSED_REQUEST); ! 1127: } ! 1128: } ! 1129: ! 1130: ! 1131: static void ! 1132: ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 1133: { ! 1134: ngx_int_t rc; ! 1135: ngx_time_t *tp; ! 1136: ngx_connection_t *c; ! 1137: ! 1138: r->connection->log->action = "connecting to upstream"; ! 1139: ! 1140: if (u->state && u->state->response_sec) { ! 1141: tp = ngx_timeofday(); ! 1142: u->state->response_sec = tp->sec - u->state->response_sec; ! 1143: u->state->response_msec = tp->msec - u->state->response_msec; ! 1144: } ! 1145: ! 1146: u->state = ngx_array_push(r->upstream_states); ! 1147: if (u->state == NULL) { ! 1148: ngx_http_upstream_finalize_request(r, u, ! 1149: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1150: return; ! 1151: } ! 1152: ! 1153: ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t)); ! 1154: ! 1155: tp = ngx_timeofday(); ! 1156: u->state->response_sec = tp->sec; ! 1157: u->state->response_msec = tp->msec; ! 1158: ! 1159: rc = ngx_event_connect_peer(&u->peer); ! 1160: ! 1161: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 1162: "http upstream connect: %i", rc); ! 1163: ! 1164: if (rc == NGX_ERROR) { ! 1165: ngx_http_upstream_finalize_request(r, u, ! 1166: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1167: return; ! 1168: } ! 1169: ! 1170: u->state->peer = u->peer.name; ! 1171: ! 1172: if (rc == NGX_BUSY) { ! 1173: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams"); ! 1174: ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE); ! 1175: return; ! 1176: } ! 1177: ! 1178: if (rc == NGX_DECLINED) { ! 1179: ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); ! 1180: return; ! 1181: } ! 1182: ! 1183: /* rc == NGX_OK || rc == NGX_AGAIN */ ! 1184: ! 1185: c = u->peer.connection; ! 1186: ! 1187: c->data = r; ! 1188: ! 1189: c->write->handler = ngx_http_upstream_handler; ! 1190: c->read->handler = ngx_http_upstream_handler; ! 1191: ! 1192: u->write_event_handler = ngx_http_upstream_send_request_handler; ! 1193: u->read_event_handler = ngx_http_upstream_process_header; ! 1194: ! 1195: c->sendfile &= r->connection->sendfile; ! 1196: u->output.sendfile = c->sendfile; ! 1197: ! 1198: if (c->pool == NULL) { ! 1199: ! 1200: /* we need separate pool here to be able to cache SSL connections */ ! 1201: ! 1202: c->pool = ngx_create_pool(128, r->connection->log); ! 1203: if (c->pool == NULL) { ! 1204: ngx_http_upstream_finalize_request(r, u, ! 1205: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1206: return; ! 1207: } ! 1208: } ! 1209: ! 1210: c->log = r->connection->log; ! 1211: c->pool->log = c->log; ! 1212: c->read->log = c->log; ! 1213: c->write->log = c->log; ! 1214: ! 1215: /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */ ! 1216: ! 1217: u->writer.out = NULL; ! 1218: u->writer.last = &u->writer.out; ! 1219: u->writer.connection = c; ! 1220: u->writer.limit = 0; ! 1221: ! 1222: if (u->request_sent) { ! 1223: if (ngx_http_upstream_reinit(r, u) != NGX_OK) { ! 1224: ngx_http_upstream_finalize_request(r, u, ! 1225: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1226: return; ! 1227: } ! 1228: } ! 1229: ! 1230: if (r->request_body ! 1231: && r->request_body->buf ! 1232: && r->request_body->temp_file ! 1233: && r == r->main) ! 1234: { ! 1235: /* ! 1236: * the r->request_body->buf can be reused for one request only, ! 1237: * the subrequests should allocate their own temporary bufs ! 1238: */ ! 1239: ! 1240: u->output.free = ngx_alloc_chain_link(r->pool); ! 1241: if (u->output.free == NULL) { ! 1242: ngx_http_upstream_finalize_request(r, u, ! 1243: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1244: return; ! 1245: } ! 1246: ! 1247: u->output.free->buf = r->request_body->buf; ! 1248: u->output.free->next = NULL; ! 1249: u->output.allocated = 1; ! 1250: ! 1251: r->request_body->buf->pos = r->request_body->buf->start; ! 1252: r->request_body->buf->last = r->request_body->buf->start; ! 1253: r->request_body->buf->tag = u->output.tag; ! 1254: } ! 1255: ! 1256: u->request_sent = 0; ! 1257: ! 1258: if (rc == NGX_AGAIN) { ! 1259: ngx_add_timer(c->write, u->conf->connect_timeout); ! 1260: return; ! 1261: } ! 1262: ! 1263: #if (NGX_HTTP_SSL) ! 1264: ! 1265: if (u->ssl && c->ssl == NULL) { ! 1266: ngx_http_upstream_ssl_init_connection(r, u, c); ! 1267: return; ! 1268: } ! 1269: ! 1270: #endif ! 1271: ! 1272: ngx_http_upstream_send_request(r, u); ! 1273: } ! 1274: ! 1275: ! 1276: #if (NGX_HTTP_SSL) ! 1277: ! 1278: static void ! 1279: ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r, ! 1280: ngx_http_upstream_t *u, ngx_connection_t *c) ! 1281: { ! 1282: ngx_int_t rc; ! 1283: ! 1284: if (ngx_ssl_create_connection(u->conf->ssl, c, ! 1285: NGX_SSL_BUFFER|NGX_SSL_CLIENT) ! 1286: != NGX_OK) ! 1287: { ! 1288: ngx_http_upstream_finalize_request(r, u, ! 1289: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1290: return; ! 1291: } ! 1292: ! 1293: c->sendfile = 0; ! 1294: u->output.sendfile = 0; ! 1295: ! 1296: if (u->conf->ssl_session_reuse) { ! 1297: if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) { ! 1298: ngx_http_upstream_finalize_request(r, u, ! 1299: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1300: return; ! 1301: } ! 1302: } ! 1303: ! 1304: r->connection->log->action = "SSL handshaking to upstream"; ! 1305: ! 1306: rc = ngx_ssl_handshake(c); ! 1307: ! 1308: if (rc == NGX_AGAIN) { ! 1309: c->ssl->handler = ngx_http_upstream_ssl_handshake; ! 1310: return; ! 1311: } ! 1312: ! 1313: ngx_http_upstream_ssl_handshake(c); ! 1314: } ! 1315: ! 1316: ! 1317: static void ! 1318: ngx_http_upstream_ssl_handshake(ngx_connection_t *c) ! 1319: { ! 1320: ngx_http_request_t *r; ! 1321: ngx_http_upstream_t *u; ! 1322: ! 1323: r = c->data; ! 1324: u = r->upstream; ! 1325: ! 1326: if (c->ssl->handshaked) { ! 1327: ! 1328: if (u->conf->ssl_session_reuse) { ! 1329: u->peer.save_session(&u->peer, u->peer.data); ! 1330: } ! 1331: ! 1332: c->write->handler = ngx_http_upstream_handler; ! 1333: c->read->handler = ngx_http_upstream_handler; ! 1334: ! 1335: ngx_http_upstream_send_request(r, u); ! 1336: ! 1337: return; ! 1338: } ! 1339: ! 1340: ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); ! 1341: ! 1342: } ! 1343: ! 1344: #endif ! 1345: ! 1346: ! 1347: static ngx_int_t ! 1348: ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 1349: { ! 1350: ngx_chain_t *cl; ! 1351: ! 1352: if (u->reinit_request(r) != NGX_OK) { ! 1353: return NGX_ERROR; ! 1354: } ! 1355: ! 1356: u->keepalive = 0; ! 1357: u->upgrade = 0; ! 1358: ! 1359: ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t)); ! 1360: u->headers_in.content_length_n = -1; ! 1361: ! 1362: if (ngx_list_init(&u->headers_in.headers, r->pool, 8, ! 1363: sizeof(ngx_table_elt_t)) ! 1364: != NGX_OK) ! 1365: { ! 1366: return NGX_ERROR; ! 1367: } ! 1368: ! 1369: /* reinit the request chain */ ! 1370: ! 1371: for (cl = u->request_bufs; cl; cl = cl->next) { ! 1372: cl->buf->pos = cl->buf->start; ! 1373: cl->buf->file_pos = 0; ! 1374: } ! 1375: ! 1376: /* reinit the subrequest's ngx_output_chain() context */ ! 1377: ! 1378: if (r->request_body && r->request_body->temp_file ! 1379: && r != r->main && u->output.buf) ! 1380: { ! 1381: u->output.free = ngx_alloc_chain_link(r->pool); ! 1382: if (u->output.free == NULL) { ! 1383: return NGX_ERROR; ! 1384: } ! 1385: ! 1386: u->output.free->buf = u->output.buf; ! 1387: u->output.free->next = NULL; ! 1388: ! 1389: u->output.buf->pos = u->output.buf->start; ! 1390: u->output.buf->last = u->output.buf->start; ! 1391: } ! 1392: ! 1393: u->output.buf = NULL; ! 1394: u->output.in = NULL; ! 1395: u->output.busy = NULL; ! 1396: ! 1397: /* reinit u->buffer */ ! 1398: ! 1399: u->buffer.pos = u->buffer.start; ! 1400: ! 1401: #if (NGX_HTTP_CACHE) ! 1402: ! 1403: if (r->cache) { ! 1404: u->buffer.pos += r->cache->header_start; ! 1405: } ! 1406: ! 1407: #endif ! 1408: ! 1409: u->buffer.last = u->buffer.pos; ! 1410: ! 1411: return NGX_OK; ! 1412: } ! 1413: ! 1414: ! 1415: static void ! 1416: ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 1417: { ! 1418: ngx_int_t rc; ! 1419: ngx_connection_t *c; ! 1420: ! 1421: c = u->peer.connection; ! 1422: ! 1423: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 1424: "http upstream send request"); ! 1425: ! 1426: if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) { ! 1427: ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); ! 1428: return; ! 1429: } ! 1430: ! 1431: c->log->action = "sending request to upstream"; ! 1432: ! 1433: rc = ngx_output_chain(&u->output, u->request_sent ? NULL : u->request_bufs); ! 1434: ! 1435: u->request_sent = 1; ! 1436: ! 1437: if (rc == NGX_ERROR) { ! 1438: ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); ! 1439: return; ! 1440: } ! 1441: ! 1442: if (c->write->timer_set) { ! 1443: ngx_del_timer(c->write); ! 1444: } ! 1445: ! 1446: if (rc == NGX_AGAIN) { ! 1447: ngx_add_timer(c->write, u->conf->send_timeout); ! 1448: ! 1449: if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) { ! 1450: ngx_http_upstream_finalize_request(r, u, ! 1451: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1452: return; ! 1453: } ! 1454: ! 1455: return; ! 1456: } ! 1457: ! 1458: /* rc == NGX_OK */ ! 1459: ! 1460: if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) { ! 1461: if (ngx_tcp_push(c->fd) == NGX_ERROR) { ! 1462: ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno, ! 1463: ngx_tcp_push_n " failed"); ! 1464: ngx_http_upstream_finalize_request(r, u, ! 1465: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1466: return; ! 1467: } ! 1468: ! 1469: c->tcp_nopush = NGX_TCP_NOPUSH_UNSET; ! 1470: } ! 1471: ! 1472: ngx_add_timer(c->read, u->conf->read_timeout); ! 1473: ! 1474: #if 1 ! 1475: if (c->read->ready) { ! 1476: ! 1477: /* post aio operation */ ! 1478: ! 1479: /* ! 1480: * TODO comment ! 1481: * although we can post aio operation just in the end ! 1482: * of ngx_http_upstream_connect() CHECK IT !!! ! 1483: * it's better to do here because we postpone header buffer allocation ! 1484: */ ! 1485: ! 1486: ngx_http_upstream_process_header(r, u); ! 1487: return; ! 1488: } ! 1489: #endif ! 1490: ! 1491: u->write_event_handler = ngx_http_upstream_dummy_handler; ! 1492: ! 1493: if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ! 1494: ngx_http_upstream_finalize_request(r, u, ! 1495: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1496: return; ! 1497: } ! 1498: } ! 1499: ! 1500: ! 1501: static void ! 1502: ngx_http_upstream_send_request_handler(ngx_http_request_t *r, ! 1503: ngx_http_upstream_t *u) ! 1504: { ! 1505: ngx_connection_t *c; ! 1506: ! 1507: c = u->peer.connection; ! 1508: ! 1509: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 1510: "http upstream send request handler"); ! 1511: ! 1512: if (c->write->timedout) { ! 1513: ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); ! 1514: return; ! 1515: } ! 1516: ! 1517: #if (NGX_HTTP_SSL) ! 1518: ! 1519: if (u->ssl && c->ssl == NULL) { ! 1520: ngx_http_upstream_ssl_init_connection(r, u, c); ! 1521: return; ! 1522: } ! 1523: ! 1524: #endif ! 1525: ! 1526: if (u->header_sent) { ! 1527: u->write_event_handler = ngx_http_upstream_dummy_handler; ! 1528: ! 1529: (void) ngx_handle_write_event(c->write, 0); ! 1530: ! 1531: return; ! 1532: } ! 1533: ! 1534: ngx_http_upstream_send_request(r, u); ! 1535: } ! 1536: ! 1537: ! 1538: static void ! 1539: ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 1540: { ! 1541: ssize_t n; ! 1542: ngx_int_t rc; ! 1543: ngx_connection_t *c; ! 1544: ! 1545: c = u->peer.connection; ! 1546: ! 1547: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 1548: "http upstream process header"); ! 1549: ! 1550: c->log->action = "reading response header from upstream"; ! 1551: ! 1552: if (c->read->timedout) { ! 1553: ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT); ! 1554: return; ! 1555: } ! 1556: ! 1557: if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) { ! 1558: ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); ! 1559: return; ! 1560: } ! 1561: ! 1562: if (u->buffer.start == NULL) { ! 1563: u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size); ! 1564: if (u->buffer.start == NULL) { ! 1565: ngx_http_upstream_finalize_request(r, u, ! 1566: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1567: return; ! 1568: } ! 1569: ! 1570: u->buffer.pos = u->buffer.start; ! 1571: u->buffer.last = u->buffer.start; ! 1572: u->buffer.end = u->buffer.start + u->conf->buffer_size; ! 1573: u->buffer.temporary = 1; ! 1574: ! 1575: u->buffer.tag = u->output.tag; ! 1576: ! 1577: if (ngx_list_init(&u->headers_in.headers, r->pool, 8, ! 1578: sizeof(ngx_table_elt_t)) ! 1579: != NGX_OK) ! 1580: { ! 1581: ngx_http_upstream_finalize_request(r, u, ! 1582: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1583: return; ! 1584: } ! 1585: ! 1586: #if (NGX_HTTP_CACHE) ! 1587: ! 1588: if (r->cache) { ! 1589: u->buffer.pos += r->cache->header_start; ! 1590: u->buffer.last = u->buffer.pos; ! 1591: } ! 1592: #endif ! 1593: } ! 1594: ! 1595: for ( ;; ) { ! 1596: ! 1597: n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); ! 1598: ! 1599: if (n == NGX_AGAIN) { ! 1600: #if 0 ! 1601: ngx_add_timer(rev, u->read_timeout); ! 1602: #endif ! 1603: ! 1604: if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ! 1605: ngx_http_upstream_finalize_request(r, u, ! 1606: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1607: return; ! 1608: } ! 1609: ! 1610: return; ! 1611: } ! 1612: ! 1613: if (n == 0) { ! 1614: ngx_log_error(NGX_LOG_ERR, c->log, 0, ! 1615: "upstream prematurely closed connection"); ! 1616: } ! 1617: ! 1618: if (n == NGX_ERROR || n == 0) { ! 1619: ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); ! 1620: return; ! 1621: } ! 1622: ! 1623: u->buffer.last += n; ! 1624: ! 1625: #if 0 ! 1626: u->valid_header_in = 0; ! 1627: ! 1628: u->peer.cached = 0; ! 1629: #endif ! 1630: ! 1631: rc = u->process_header(r); ! 1632: ! 1633: if (rc == NGX_AGAIN) { ! 1634: ! 1635: if (u->buffer.last == u->buffer.end) { ! 1636: ngx_log_error(NGX_LOG_ERR, c->log, 0, ! 1637: "upstream sent too big header"); ! 1638: ! 1639: ngx_http_upstream_next(r, u, ! 1640: NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); ! 1641: return; ! 1642: } ! 1643: ! 1644: continue; ! 1645: } ! 1646: ! 1647: break; ! 1648: } ! 1649: ! 1650: if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { ! 1651: ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); ! 1652: return; ! 1653: } ! 1654: ! 1655: if (rc == NGX_ERROR) { ! 1656: ngx_http_upstream_finalize_request(r, u, ! 1657: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1658: return; ! 1659: } ! 1660: ! 1661: /* rc == NGX_OK */ ! 1662: ! 1663: if (u->headers_in.status_n > NGX_HTTP_SPECIAL_RESPONSE) { ! 1664: ! 1665: if (r->subrequest_in_memory) { ! 1666: u->buffer.last = u->buffer.pos; ! 1667: } ! 1668: ! 1669: if (ngx_http_upstream_test_next(r, u) == NGX_OK) { ! 1670: return; ! 1671: } ! 1672: ! 1673: if (ngx_http_upstream_intercept_errors(r, u) == NGX_OK) { ! 1674: return; ! 1675: } ! 1676: } ! 1677: ! 1678: if (ngx_http_upstream_process_headers(r, u) != NGX_OK) { ! 1679: return; ! 1680: } ! 1681: ! 1682: if (!r->subrequest_in_memory) { ! 1683: ngx_http_upstream_send_response(r, u); ! 1684: return; ! 1685: } ! 1686: ! 1687: /* subrequest content in memory */ ! 1688: ! 1689: if (u->input_filter == NULL) { ! 1690: u->input_filter_init = ngx_http_upstream_non_buffered_filter_init; ! 1691: u->input_filter = ngx_http_upstream_non_buffered_filter; ! 1692: u->input_filter_ctx = r; ! 1693: } ! 1694: ! 1695: if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) { ! 1696: ngx_http_upstream_finalize_request(r, u, ! 1697: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1698: return; ! 1699: } ! 1700: ! 1701: n = u->buffer.last - u->buffer.pos; ! 1702: ! 1703: if (n) { ! 1704: u->buffer.last -= n; ! 1705: ! 1706: u->state->response_length += n; ! 1707: ! 1708: if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ! 1709: ngx_http_upstream_finalize_request(r, u, NGX_ERROR); ! 1710: return; ! 1711: } ! 1712: ! 1713: if (u->length == 0) { ! 1714: ngx_http_upstream_finalize_request(r, u, 0); ! 1715: return; ! 1716: } ! 1717: } ! 1718: ! 1719: u->read_event_handler = ngx_http_upstream_process_body_in_memory; ! 1720: ! 1721: ngx_http_upstream_process_body_in_memory(r, u); ! 1722: } ! 1723: ! 1724: ! 1725: static ngx_int_t ! 1726: ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 1727: { ! 1728: ngx_uint_t status; ! 1729: ngx_http_upstream_next_t *un; ! 1730: ! 1731: status = u->headers_in.status_n; ! 1732: ! 1733: for (un = ngx_http_upstream_next_errors; un->status; un++) { ! 1734: ! 1735: if (status != un->status) { ! 1736: continue; ! 1737: } ! 1738: ! 1739: if (u->peer.tries > 1 && (u->conf->next_upstream & un->mask)) { ! 1740: ngx_http_upstream_next(r, u, un->mask); ! 1741: return NGX_OK; ! 1742: } ! 1743: ! 1744: #if (NGX_HTTP_CACHE) ! 1745: ! 1746: if (u->cache_status == NGX_HTTP_CACHE_EXPIRED ! 1747: && (u->conf->cache_use_stale & un->mask)) ! 1748: { ! 1749: ngx_int_t rc; ! 1750: ! 1751: rc = u->reinit_request(r); ! 1752: ! 1753: if (rc == NGX_OK) { ! 1754: u->cache_status = NGX_HTTP_CACHE_STALE; ! 1755: rc = ngx_http_upstream_cache_send(r, u); ! 1756: } ! 1757: ! 1758: ngx_http_upstream_finalize_request(r, u, rc); ! 1759: return NGX_OK; ! 1760: } ! 1761: ! 1762: #endif ! 1763: } ! 1764: ! 1765: return NGX_DECLINED; ! 1766: } ! 1767: ! 1768: ! 1769: static ngx_int_t ! 1770: ngx_http_upstream_intercept_errors(ngx_http_request_t *r, ! 1771: ngx_http_upstream_t *u) ! 1772: { ! 1773: ngx_int_t status; ! 1774: ngx_uint_t i; ! 1775: ngx_table_elt_t *h; ! 1776: ngx_http_err_page_t *err_page; ! 1777: ngx_http_core_loc_conf_t *clcf; ! 1778: ! 1779: status = u->headers_in.status_n; ! 1780: ! 1781: if (status == NGX_HTTP_NOT_FOUND && u->conf->intercept_404) { ! 1782: ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND); ! 1783: return NGX_OK; ! 1784: } ! 1785: ! 1786: if (!u->conf->intercept_errors) { ! 1787: return NGX_DECLINED; ! 1788: } ! 1789: ! 1790: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ! 1791: ! 1792: if (clcf->error_pages == NULL) { ! 1793: return NGX_DECLINED; ! 1794: } ! 1795: ! 1796: err_page = clcf->error_pages->elts; ! 1797: for (i = 0; i < clcf->error_pages->nelts; i++) { ! 1798: ! 1799: if (err_page[i].status == status) { ! 1800: ! 1801: if (status == NGX_HTTP_UNAUTHORIZED ! 1802: && u->headers_in.www_authenticate) ! 1803: { ! 1804: h = ngx_list_push(&r->headers_out.headers); ! 1805: ! 1806: if (h == NULL) { ! 1807: ngx_http_upstream_finalize_request(r, u, ! 1808: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1809: return NGX_OK; ! 1810: } ! 1811: ! 1812: *h = *u->headers_in.www_authenticate; ! 1813: ! 1814: r->headers_out.www_authenticate = h; ! 1815: } ! 1816: ! 1817: #if (NGX_HTTP_CACHE) ! 1818: ! 1819: if (r->cache) { ! 1820: time_t valid; ! 1821: ! 1822: valid = ngx_http_file_cache_valid(u->conf->cache_valid, status); ! 1823: ! 1824: if (valid) { ! 1825: r->cache->valid_sec = ngx_time() + valid; ! 1826: r->cache->error = status; ! 1827: } ! 1828: ! 1829: ngx_http_file_cache_free(r->cache, u->pipe->temp_file); ! 1830: } ! 1831: #endif ! 1832: ngx_http_upstream_finalize_request(r, u, status); ! 1833: ! 1834: return NGX_OK; ! 1835: } ! 1836: } ! 1837: ! 1838: return NGX_DECLINED; ! 1839: } ! 1840: ! 1841: ! 1842: static ngx_int_t ! 1843: ngx_http_upstream_test_connect(ngx_connection_t *c) ! 1844: { ! 1845: int err; ! 1846: socklen_t len; ! 1847: ! 1848: #if (NGX_HAVE_KQUEUE) ! 1849: ! 1850: if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { ! 1851: if (c->write->pending_eof || c->read->pending_eof) { ! 1852: if (c->write->pending_eof) { ! 1853: err = c->write->kq_errno; ! 1854: ! 1855: } else { ! 1856: err = c->read->kq_errno; ! 1857: } ! 1858: ! 1859: c->log->action = "connecting to upstream"; ! 1860: (void) ngx_connection_error(c, err, ! 1861: "kevent() reported that connect() failed"); ! 1862: return NGX_ERROR; ! 1863: } ! 1864: ! 1865: } else ! 1866: #endif ! 1867: { ! 1868: err = 0; ! 1869: len = sizeof(int); ! 1870: ! 1871: /* ! 1872: * BSDs and Linux return 0 and set a pending error in err ! 1873: * Solaris returns -1 and sets errno ! 1874: */ ! 1875: ! 1876: if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) ! 1877: == -1) ! 1878: { ! 1879: err = ngx_errno; ! 1880: } ! 1881: ! 1882: if (err) { ! 1883: c->log->action = "connecting to upstream"; ! 1884: (void) ngx_connection_error(c, err, "connect() failed"); ! 1885: return NGX_ERROR; ! 1886: } ! 1887: } ! 1888: ! 1889: return NGX_OK; ! 1890: } ! 1891: ! 1892: ! 1893: static ngx_int_t ! 1894: ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 1895: { ! 1896: ngx_str_t *uri, args; ! 1897: ngx_uint_t i, flags; ! 1898: ngx_list_part_t *part; ! 1899: ngx_table_elt_t *h; ! 1900: ngx_http_upstream_header_t *hh; ! 1901: ngx_http_upstream_main_conf_t *umcf; ! 1902: ! 1903: umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); ! 1904: ! 1905: if (u->headers_in.x_accel_redirect ! 1906: && !(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT)) ! 1907: { ! 1908: ngx_http_upstream_finalize_request(r, u, NGX_DECLINED); ! 1909: ! 1910: part = &u->headers_in.headers.part; ! 1911: h = part->elts; ! 1912: ! 1913: for (i = 0; /* void */; i++) { ! 1914: ! 1915: if (i >= part->nelts) { ! 1916: if (part->next == NULL) { ! 1917: break; ! 1918: } ! 1919: ! 1920: part = part->next; ! 1921: h = part->elts; ! 1922: i = 0; ! 1923: } ! 1924: ! 1925: hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash, ! 1926: h[i].lowcase_key, h[i].key.len); ! 1927: ! 1928: if (hh && hh->redirect) { ! 1929: if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) { ! 1930: ngx_http_finalize_request(r, ! 1931: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1932: return NGX_DONE; ! 1933: } ! 1934: } ! 1935: } ! 1936: ! 1937: uri = &u->headers_in.x_accel_redirect->value; ! 1938: ngx_str_null(&args); ! 1939: flags = NGX_HTTP_LOG_UNSAFE; ! 1940: ! 1941: if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) { ! 1942: ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND); ! 1943: return NGX_DONE; ! 1944: } ! 1945: ! 1946: if (r->method != NGX_HTTP_HEAD) { ! 1947: r->method = NGX_HTTP_GET; ! 1948: } ! 1949: ! 1950: ngx_http_internal_redirect(r, uri, &args); ! 1951: ngx_http_finalize_request(r, NGX_DONE); ! 1952: return NGX_DONE; ! 1953: } ! 1954: ! 1955: part = &u->headers_in.headers.part; ! 1956: h = part->elts; ! 1957: ! 1958: for (i = 0; /* void */; i++) { ! 1959: ! 1960: if (i >= part->nelts) { ! 1961: if (part->next == NULL) { ! 1962: break; ! 1963: } ! 1964: ! 1965: part = part->next; ! 1966: h = part->elts; ! 1967: i = 0; ! 1968: } ! 1969: ! 1970: if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash, ! 1971: h[i].lowcase_key, h[i].key.len)) ! 1972: { ! 1973: continue; ! 1974: } ! 1975: ! 1976: hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash, ! 1977: h[i].lowcase_key, h[i].key.len); ! 1978: ! 1979: if (hh) { ! 1980: if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) { ! 1981: ngx_http_upstream_finalize_request(r, u, ! 1982: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1983: return NGX_DONE; ! 1984: } ! 1985: ! 1986: continue; ! 1987: } ! 1988: ! 1989: if (ngx_http_upstream_copy_header_line(r, &h[i], 0) != NGX_OK) { ! 1990: ngx_http_upstream_finalize_request(r, u, ! 1991: NGX_HTTP_INTERNAL_SERVER_ERROR); ! 1992: return NGX_DONE; ! 1993: } ! 1994: } ! 1995: ! 1996: if (r->headers_out.server && r->headers_out.server->value.data == NULL) { ! 1997: r->headers_out.server->hash = 0; ! 1998: } ! 1999: ! 2000: if (r->headers_out.date && r->headers_out.date->value.data == NULL) { ! 2001: r->headers_out.date->hash = 0; ! 2002: } ! 2003: ! 2004: r->headers_out.status = u->headers_in.status_n; ! 2005: r->headers_out.status_line = u->headers_in.status_line; ! 2006: ! 2007: r->headers_out.content_length_n = u->headers_in.content_length_n; ! 2008: ! 2009: u->length = u->headers_in.content_length_n; ! 2010: ! 2011: return NGX_OK; ! 2012: } ! 2013: ! 2014: ! 2015: static void ! 2016: ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r, ! 2017: ngx_http_upstream_t *u) ! 2018: { ! 2019: size_t size; ! 2020: ssize_t n; ! 2021: ngx_buf_t *b; ! 2022: ngx_event_t *rev; ! 2023: ngx_connection_t *c; ! 2024: ! 2025: c = u->peer.connection; ! 2026: rev = c->read; ! 2027: ! 2028: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 2029: "http upstream process body on memory"); ! 2030: ! 2031: if (rev->timedout) { ! 2032: ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); ! 2033: ngx_http_upstream_finalize_request(r, u, NGX_ETIMEDOUT); ! 2034: return; ! 2035: } ! 2036: ! 2037: b = &u->buffer; ! 2038: ! 2039: for ( ;; ) { ! 2040: ! 2041: size = b->end - b->last; ! 2042: ! 2043: if (size == 0) { ! 2044: ngx_log_error(NGX_LOG_ALERT, c->log, 0, ! 2045: "upstream buffer is too small to read response"); ! 2046: ngx_http_upstream_finalize_request(r, u, NGX_ERROR); ! 2047: return; ! 2048: } ! 2049: ! 2050: n = c->recv(c, b->last, size); ! 2051: ! 2052: if (n == NGX_AGAIN) { ! 2053: break; ! 2054: } ! 2055: ! 2056: if (n == 0 || n == NGX_ERROR) { ! 2057: ngx_http_upstream_finalize_request(r, u, n); ! 2058: return; ! 2059: } ! 2060: ! 2061: u->state->response_length += n; ! 2062: ! 2063: if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ! 2064: ngx_http_upstream_finalize_request(r, u, NGX_ERROR); ! 2065: return; ! 2066: } ! 2067: ! 2068: if (!rev->ready) { ! 2069: break; ! 2070: } ! 2071: } ! 2072: ! 2073: if (u->length == 0) { ! 2074: ngx_http_upstream_finalize_request(r, u, 0); ! 2075: return; ! 2076: } ! 2077: ! 2078: if (ngx_handle_read_event(rev, 0) != NGX_OK) { ! 2079: ngx_http_upstream_finalize_request(r, u, NGX_ERROR); ! 2080: return; ! 2081: } ! 2082: ! 2083: if (rev->active) { ! 2084: ngx_add_timer(rev, u->conf->read_timeout); ! 2085: ! 2086: } else if (rev->timer_set) { ! 2087: ngx_del_timer(rev); ! 2088: } ! 2089: } ! 2090: ! 2091: ! 2092: static void ! 2093: ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 2094: { ! 2095: int tcp_nodelay; ! 2096: ssize_t n; ! 2097: ngx_int_t rc; ! 2098: ngx_event_pipe_t *p; ! 2099: ngx_connection_t *c; ! 2100: ngx_http_core_loc_conf_t *clcf; ! 2101: ! 2102: rc = ngx_http_send_header(r); ! 2103: ! 2104: if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) { ! 2105: ngx_http_upstream_finalize_request(r, u, rc); ! 2106: return; ! 2107: } ! 2108: ! 2109: if (u->upgrade) { ! 2110: ngx_http_upstream_upgrade(r, u); ! 2111: return; ! 2112: } ! 2113: ! 2114: c = r->connection; ! 2115: ! 2116: if (r->header_only) { ! 2117: ! 2118: if (u->cacheable || u->store) { ! 2119: ! 2120: if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { ! 2121: ngx_connection_error(c, ngx_socket_errno, ! 2122: ngx_shutdown_socket_n " failed"); ! 2123: } ! 2124: ! 2125: r->read_event_handler = ngx_http_request_empty_handler; ! 2126: r->write_event_handler = ngx_http_request_empty_handler; ! 2127: c->error = 1; ! 2128: ! 2129: } else { ! 2130: ngx_http_upstream_finalize_request(r, u, rc); ! 2131: return; ! 2132: } ! 2133: } ! 2134: ! 2135: u->header_sent = 1; ! 2136: ! 2137: if (r->request_body && r->request_body->temp_file) { ! 2138: ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd); ! 2139: r->request_body->temp_file->file.fd = NGX_INVALID_FILE; ! 2140: } ! 2141: ! 2142: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ! 2143: ! 2144: if (!u->buffering) { ! 2145: ! 2146: if (u->input_filter == NULL) { ! 2147: u->input_filter_init = ngx_http_upstream_non_buffered_filter_init; ! 2148: u->input_filter = ngx_http_upstream_non_buffered_filter; ! 2149: u->input_filter_ctx = r; ! 2150: } ! 2151: ! 2152: u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream; ! 2153: r->write_event_handler = ! 2154: ngx_http_upstream_process_non_buffered_downstream; ! 2155: ! 2156: r->limit_rate = 0; ! 2157: ! 2158: if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) { ! 2159: ngx_http_upstream_finalize_request(r, u, 0); ! 2160: return; ! 2161: } ! 2162: ! 2163: if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { ! 2164: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay"); ! 2165: ! 2166: tcp_nodelay = 1; ! 2167: ! 2168: if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, ! 2169: (const void *) &tcp_nodelay, sizeof(int)) == -1) ! 2170: { ! 2171: ngx_connection_error(c, ngx_socket_errno, ! 2172: "setsockopt(TCP_NODELAY) failed"); ! 2173: ngx_http_upstream_finalize_request(r, u, 0); ! 2174: return; ! 2175: } ! 2176: ! 2177: c->tcp_nodelay = NGX_TCP_NODELAY_SET; ! 2178: } ! 2179: ! 2180: n = u->buffer.last - u->buffer.pos; ! 2181: ! 2182: if (n) { ! 2183: u->buffer.last = u->buffer.pos; ! 2184: ! 2185: u->state->response_length += n; ! 2186: ! 2187: if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ! 2188: ngx_http_upstream_finalize_request(r, u, 0); ! 2189: return; ! 2190: } ! 2191: ! 2192: ngx_http_upstream_process_non_buffered_downstream(r); ! 2193: ! 2194: } else { ! 2195: u->buffer.pos = u->buffer.start; ! 2196: u->buffer.last = u->buffer.start; ! 2197: ! 2198: if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) { ! 2199: ngx_http_upstream_finalize_request(r, u, 0); ! 2200: return; ! 2201: } ! 2202: ! 2203: if (u->peer.connection->read->ready || u->length == 0) { ! 2204: ngx_http_upstream_process_non_buffered_upstream(r, u); ! 2205: } ! 2206: } ! 2207: ! 2208: return; ! 2209: } ! 2210: ! 2211: /* TODO: preallocate event_pipe bufs, look "Content-Length" */ ! 2212: ! 2213: #if (NGX_HTTP_CACHE) ! 2214: ! 2215: if (r->cache && r->cache->file.fd != NGX_INVALID_FILE) { ! 2216: ngx_pool_run_cleanup_file(r->pool, r->cache->file.fd); ! 2217: r->cache->file.fd = NGX_INVALID_FILE; ! 2218: } ! 2219: ! 2220: switch (ngx_http_test_predicates(r, u->conf->no_cache)) { ! 2221: ! 2222: case NGX_ERROR: ! 2223: ngx_http_upstream_finalize_request(r, u, 0); ! 2224: return; ! 2225: ! 2226: case NGX_DECLINED: ! 2227: u->cacheable = 0; ! 2228: break; ! 2229: ! 2230: default: /* NGX_OK */ ! 2231: ! 2232: if (u->cache_status == NGX_HTTP_CACHE_BYPASS) { ! 2233: ! 2234: r->cache->min_uses = u->conf->cache_min_uses; ! 2235: r->cache->body_start = u->conf->buffer_size; ! 2236: r->cache->file_cache = u->conf->cache->data; ! 2237: ! 2238: if (ngx_http_file_cache_create(r) != NGX_OK) { ! 2239: ngx_http_upstream_finalize_request(r, u, 0); ! 2240: return; ! 2241: } ! 2242: } ! 2243: ! 2244: break; ! 2245: } ! 2246: ! 2247: if (u->cacheable) { ! 2248: time_t now, valid; ! 2249: ! 2250: now = ngx_time(); ! 2251: ! 2252: valid = r->cache->valid_sec; ! 2253: ! 2254: if (valid == 0) { ! 2255: valid = ngx_http_file_cache_valid(u->conf->cache_valid, ! 2256: u->headers_in.status_n); ! 2257: if (valid) { ! 2258: r->cache->valid_sec = now + valid; ! 2259: } ! 2260: } ! 2261: ! 2262: if (valid) { ! 2263: r->cache->last_modified = r->headers_out.last_modified_time; ! 2264: r->cache->date = now; ! 2265: r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start); ! 2266: ! 2267: ngx_http_file_cache_set_header(r, u->buffer.start); ! 2268: ! 2269: } else { ! 2270: u->cacheable = 0; ! 2271: r->headers_out.last_modified_time = -1; ! 2272: } ! 2273: } ! 2274: ! 2275: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 2276: "http cacheable: %d", u->cacheable); ! 2277: ! 2278: if (u->cacheable == 0 && r->cache) { ! 2279: ngx_http_file_cache_free(r->cache, u->pipe->temp_file); ! 2280: } ! 2281: ! 2282: #endif ! 2283: ! 2284: p = u->pipe; ! 2285: ! 2286: p->output_filter = (ngx_event_pipe_output_filter_pt) ngx_http_output_filter; ! 2287: p->output_ctx = r; ! 2288: p->tag = u->output.tag; ! 2289: p->bufs = u->conf->bufs; ! 2290: p->busy_size = u->conf->busy_buffers_size; ! 2291: p->upstream = u->peer.connection; ! 2292: p->downstream = c; ! 2293: p->pool = r->pool; ! 2294: p->log = c->log; ! 2295: ! 2296: p->cacheable = u->cacheable || u->store; ! 2297: ! 2298: p->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); ! 2299: if (p->temp_file == NULL) { ! 2300: ngx_http_upstream_finalize_request(r, u, 0); ! 2301: return; ! 2302: } ! 2303: ! 2304: p->temp_file->file.fd = NGX_INVALID_FILE; ! 2305: p->temp_file->file.log = c->log; ! 2306: p->temp_file->path = u->conf->temp_path; ! 2307: p->temp_file->pool = r->pool; ! 2308: ! 2309: if (p->cacheable) { ! 2310: p->temp_file->persistent = 1; ! 2311: ! 2312: } else { ! 2313: p->temp_file->log_level = NGX_LOG_WARN; ! 2314: p->temp_file->warn = "an upstream response is buffered " ! 2315: "to a temporary file"; ! 2316: } ! 2317: ! 2318: p->max_temp_file_size = u->conf->max_temp_file_size; ! 2319: p->temp_file_write_size = u->conf->temp_file_write_size; ! 2320: ! 2321: p->preread_bufs = ngx_alloc_chain_link(r->pool); ! 2322: if (p->preread_bufs == NULL) { ! 2323: ngx_http_upstream_finalize_request(r, u, 0); ! 2324: return; ! 2325: } ! 2326: ! 2327: p->preread_bufs->buf = &u->buffer; ! 2328: p->preread_bufs->next = NULL; ! 2329: u->buffer.recycled = 1; ! 2330: ! 2331: p->preread_size = u->buffer.last - u->buffer.pos; ! 2332: ! 2333: if (u->cacheable) { ! 2334: ! 2335: p->buf_to_file = ngx_calloc_buf(r->pool); ! 2336: if (p->buf_to_file == NULL) { ! 2337: ngx_http_upstream_finalize_request(r, u, 0); ! 2338: return; ! 2339: } ! 2340: ! 2341: p->buf_to_file->start = u->buffer.start; ! 2342: p->buf_to_file->pos = u->buffer.start; ! 2343: p->buf_to_file->last = u->buffer.pos; ! 2344: p->buf_to_file->temporary = 1; ! 2345: } ! 2346: ! 2347: if (ngx_event_flags & NGX_USE_AIO_EVENT) { ! 2348: /* the posted aio operation may corrupt a shadow buffer */ ! 2349: p->single_buf = 1; ! 2350: } ! 2351: ! 2352: /* TODO: p->free_bufs = 0 if use ngx_create_chain_of_bufs() */ ! 2353: p->free_bufs = 1; ! 2354: ! 2355: /* ! 2356: * event_pipe would do u->buffer.last += p->preread_size ! 2357: * as though these bytes were read ! 2358: */ ! 2359: u->buffer.last = u->buffer.pos; ! 2360: ! 2361: if (u->conf->cyclic_temp_file) { ! 2362: ! 2363: /* ! 2364: * we need to disable the use of sendfile() if we use cyclic temp file ! 2365: * because the writing a new data may interfere with sendfile() ! 2366: * that uses the same kernel file pages (at least on FreeBSD) ! 2367: */ ! 2368: ! 2369: p->cyclic_temp_file = 1; ! 2370: c->sendfile = 0; ! 2371: ! 2372: } else { ! 2373: p->cyclic_temp_file = 0; ! 2374: } ! 2375: ! 2376: p->read_timeout = u->conf->read_timeout; ! 2377: p->send_timeout = clcf->send_timeout; ! 2378: p->send_lowat = clcf->send_lowat; ! 2379: ! 2380: p->length = -1; ! 2381: ! 2382: if (u->input_filter_init ! 2383: && u->input_filter_init(p->input_ctx) != NGX_OK) ! 2384: { ! 2385: ngx_http_upstream_finalize_request(r, u, 0); ! 2386: return; ! 2387: } ! 2388: ! 2389: u->read_event_handler = ngx_http_upstream_process_upstream; ! 2390: r->write_event_handler = ngx_http_upstream_process_downstream; ! 2391: ! 2392: ngx_http_upstream_process_upstream(r, u); ! 2393: } ! 2394: ! 2395: ! 2396: static void ! 2397: ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 2398: { ! 2399: int tcp_nodelay; ! 2400: ngx_connection_t *c; ! 2401: ngx_http_core_loc_conf_t *clcf; ! 2402: ! 2403: c = r->connection; ! 2404: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ! 2405: ! 2406: /* TODO: prevent upgrade if not requested or not possible */ ! 2407: ! 2408: r->keepalive = 0; ! 2409: c->log->action = "proxying upgraded connection"; ! 2410: ! 2411: u->read_event_handler = ngx_http_upstream_upgraded_read_upstream; ! 2412: u->write_event_handler = ngx_http_upstream_upgraded_write_upstream; ! 2413: r->read_event_handler = ngx_http_upstream_upgraded_read_downstream; ! 2414: r->write_event_handler = ngx_http_upstream_upgraded_write_downstream; ! 2415: ! 2416: if (clcf->tcp_nodelay) { ! 2417: tcp_nodelay = 1; ! 2418: ! 2419: if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { ! 2420: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay"); ! 2421: ! 2422: if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, ! 2423: (const void *) &tcp_nodelay, sizeof(int)) == -1) ! 2424: { ! 2425: ngx_connection_error(c, ngx_socket_errno, ! 2426: "setsockopt(TCP_NODELAY) failed"); ! 2427: ngx_http_upstream_finalize_request(r, u, 0); ! 2428: return; ! 2429: } ! 2430: ! 2431: c->tcp_nodelay = NGX_TCP_NODELAY_SET; ! 2432: } ! 2433: ! 2434: if (u->peer.connection->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { ! 2435: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->peer.connection->log, 0, ! 2436: "tcp_nodelay"); ! 2437: ! 2438: if (setsockopt(u->peer.connection->fd, IPPROTO_TCP, TCP_NODELAY, ! 2439: (const void *) &tcp_nodelay, sizeof(int)) == -1) ! 2440: { ! 2441: ngx_connection_error(u->peer.connection, ngx_socket_errno, ! 2442: "setsockopt(TCP_NODELAY) failed"); ! 2443: ngx_http_upstream_finalize_request(r, u, 0); ! 2444: return; ! 2445: } ! 2446: ! 2447: u->peer.connection->tcp_nodelay = NGX_TCP_NODELAY_SET; ! 2448: } ! 2449: } ! 2450: ! 2451: if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) { ! 2452: ngx_http_upstream_finalize_request(r, u, 0); ! 2453: return; ! 2454: } ! 2455: ! 2456: if (u->peer.connection->read->ready ! 2457: || u->buffer.pos != u->buffer.last) ! 2458: { ! 2459: ngx_http_upstream_process_upgraded(r, 1, 1); ! 2460: } ! 2461: ! 2462: if (c->read->ready ! 2463: || r->header_in->pos != r->header_in->last) ! 2464: { ! 2465: ngx_http_upstream_process_upgraded(r, 0, 1); ! 2466: } ! 2467: } ! 2468: ! 2469: ! 2470: static void ! 2471: ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r) ! 2472: { ! 2473: ngx_http_upstream_process_upgraded(r, 0, 0); ! 2474: } ! 2475: ! 2476: ! 2477: static void ! 2478: ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r) ! 2479: { ! 2480: ngx_http_upstream_process_upgraded(r, 1, 1); ! 2481: } ! 2482: ! 2483: ! 2484: static void ! 2485: ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r, ! 2486: ngx_http_upstream_t *u) ! 2487: { ! 2488: ngx_http_upstream_process_upgraded(r, 1, 0); ! 2489: } ! 2490: ! 2491: ! 2492: static void ! 2493: ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r, ! 2494: ngx_http_upstream_t *u) ! 2495: { ! 2496: ngx_http_upstream_process_upgraded(r, 0, 1); ! 2497: } ! 2498: ! 2499: ! 2500: static void ! 2501: ngx_http_upstream_process_upgraded(ngx_http_request_t *r, ! 2502: ngx_uint_t from_upstream, ngx_uint_t do_write) ! 2503: { ! 2504: size_t size; ! 2505: ssize_t n; ! 2506: ngx_buf_t *b; ! 2507: ngx_connection_t *c, *downstream, *upstream, *dst, *src; ! 2508: ngx_http_upstream_t *u; ! 2509: ngx_http_core_loc_conf_t *clcf; ! 2510: ! 2511: c = r->connection; ! 2512: u = r->upstream; ! 2513: ! 2514: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 2515: "http upstream process upgraded, fu:%ui", from_upstream); ! 2516: ! 2517: downstream = c; ! 2518: upstream = u->peer.connection; ! 2519: ! 2520: if (downstream->write->timedout) { ! 2521: c->timedout = 1; ! 2522: ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); ! 2523: ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT); ! 2524: return; ! 2525: } ! 2526: ! 2527: if (upstream->read->timedout || upstream->write->timedout) { ! 2528: ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); ! 2529: ngx_http_upstream_finalize_request(r, u, 0); ! 2530: return; ! 2531: } ! 2532: ! 2533: if (from_upstream) { ! 2534: src = upstream; ! 2535: dst = downstream; ! 2536: b = &u->buffer; ! 2537: ! 2538: } else { ! 2539: src = downstream; ! 2540: dst = upstream; ! 2541: b = &u->from_client; ! 2542: ! 2543: if (r->header_in->last > r->header_in->pos) { ! 2544: b = r->header_in; ! 2545: b->end = b->last; ! 2546: do_write = 1; ! 2547: } ! 2548: ! 2549: if (b->start == NULL) { ! 2550: b->start = ngx_palloc(r->pool, u->conf->buffer_size); ! 2551: if (b->start == NULL) { ! 2552: ngx_http_upstream_finalize_request(r, u, 0); ! 2553: return; ! 2554: } ! 2555: ! 2556: b->pos = b->start; ! 2557: b->last = b->start; ! 2558: b->end = b->start + u->conf->buffer_size; ! 2559: b->temporary = 1; ! 2560: b->tag = u->output.tag; ! 2561: } ! 2562: } ! 2563: ! 2564: for ( ;; ) { ! 2565: ! 2566: if (do_write) { ! 2567: ! 2568: size = b->last - b->pos; ! 2569: ! 2570: if (size && dst->write->ready) { ! 2571: ! 2572: n = dst->send(dst, b->pos, size); ! 2573: ! 2574: if (n == NGX_ERROR) { ! 2575: ngx_http_upstream_finalize_request(r, u, 0); ! 2576: return; ! 2577: } ! 2578: ! 2579: if (n > 0) { ! 2580: b->pos += n; ! 2581: ! 2582: if (b->pos == b->last) { ! 2583: b->pos = b->start; ! 2584: b->last = b->start; ! 2585: } ! 2586: } ! 2587: } ! 2588: } ! 2589: ! 2590: size = b->end - b->last; ! 2591: ! 2592: if (size && src->read->ready) { ! 2593: ! 2594: n = src->recv(src, b->last, size); ! 2595: ! 2596: if (n == NGX_AGAIN || n == 0) { ! 2597: break; ! 2598: } ! 2599: ! 2600: if (n > 0) { ! 2601: do_write = 1; ! 2602: b->last += n; ! 2603: ! 2604: continue; ! 2605: } ! 2606: ! 2607: if (n == NGX_ERROR) { ! 2608: src->read->eof = 1; ! 2609: } ! 2610: } ! 2611: ! 2612: break; ! 2613: } ! 2614: ! 2615: if ((upstream->read->eof && u->buffer.pos == u->buffer.last) ! 2616: || (downstream->read->eof && u->from_client.pos == u->from_client.last) ! 2617: || (downstream->read->eof && upstream->read->eof)) ! 2618: { ! 2619: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 2620: "http upstream upgraded done"); ! 2621: ngx_http_upstream_finalize_request(r, u, 0); ! 2622: return; ! 2623: } ! 2624: ! 2625: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ! 2626: ! 2627: if (ngx_handle_write_event(upstream->write, u->conf->send_lowat) ! 2628: != NGX_OK) ! 2629: { ! 2630: ngx_http_upstream_finalize_request(r, u, 0); ! 2631: return; ! 2632: } ! 2633: ! 2634: if (upstream->write->active && !upstream->write->ready) { ! 2635: ngx_add_timer(upstream->write, u->conf->send_timeout); ! 2636: ! 2637: } else if (upstream->write->timer_set) { ! 2638: ngx_del_timer(upstream->write); ! 2639: } ! 2640: ! 2641: if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) { ! 2642: ngx_http_upstream_finalize_request(r, u, 0); ! 2643: return; ! 2644: } ! 2645: ! 2646: if (upstream->read->active && !upstream->read->ready) { ! 2647: ngx_add_timer(upstream->read, u->conf->read_timeout); ! 2648: ! 2649: } else if (upstream->read->timer_set) { ! 2650: ngx_del_timer(upstream->read); ! 2651: } ! 2652: ! 2653: if (ngx_handle_write_event(downstream->write, clcf->send_lowat) ! 2654: != NGX_OK) ! 2655: { ! 2656: ngx_http_upstream_finalize_request(r, u, 0); ! 2657: return; ! 2658: } ! 2659: ! 2660: if (ngx_handle_read_event(downstream->read, 0) != NGX_OK) { ! 2661: ngx_http_upstream_finalize_request(r, u, 0); ! 2662: return; ! 2663: } ! 2664: ! 2665: if (downstream->write->active && !downstream->write->ready) { ! 2666: ngx_add_timer(downstream->write, clcf->send_timeout); ! 2667: ! 2668: } else if (downstream->write->timer_set) { ! 2669: ngx_del_timer(downstream->write); ! 2670: } ! 2671: } ! 2672: ! 2673: ! 2674: static void ! 2675: ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r) ! 2676: { ! 2677: ngx_event_t *wev; ! 2678: ngx_connection_t *c; ! 2679: ngx_http_upstream_t *u; ! 2680: ! 2681: c = r->connection; ! 2682: u = r->upstream; ! 2683: wev = c->write; ! 2684: ! 2685: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 2686: "http upstream process non buffered downstream"); ! 2687: ! 2688: c->log->action = "sending to client"; ! 2689: ! 2690: if (wev->timedout) { ! 2691: c->timedout = 1; ! 2692: ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); ! 2693: ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT); ! 2694: return; ! 2695: } ! 2696: ! 2697: ngx_http_upstream_process_non_buffered_request(r, 1); ! 2698: } ! 2699: ! 2700: ! 2701: static void ! 2702: ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r, ! 2703: ngx_http_upstream_t *u) ! 2704: { ! 2705: ngx_connection_t *c; ! 2706: ! 2707: c = u->peer.connection; ! 2708: ! 2709: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 2710: "http upstream process non buffered upstream"); ! 2711: ! 2712: c->log->action = "reading upstream"; ! 2713: ! 2714: if (c->read->timedout) { ! 2715: ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); ! 2716: ngx_http_upstream_finalize_request(r, u, 0); ! 2717: return; ! 2718: } ! 2719: ! 2720: ngx_http_upstream_process_non_buffered_request(r, 0); ! 2721: } ! 2722: ! 2723: ! 2724: static void ! 2725: ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r, ! 2726: ngx_uint_t do_write) ! 2727: { ! 2728: size_t size; ! 2729: ssize_t n; ! 2730: ngx_buf_t *b; ! 2731: ngx_int_t rc; ! 2732: ngx_connection_t *downstream, *upstream; ! 2733: ngx_http_upstream_t *u; ! 2734: ngx_http_core_loc_conf_t *clcf; ! 2735: ! 2736: u = r->upstream; ! 2737: downstream = r->connection; ! 2738: upstream = u->peer.connection; ! 2739: ! 2740: b = &u->buffer; ! 2741: ! 2742: do_write = do_write || u->length == 0; ! 2743: ! 2744: for ( ;; ) { ! 2745: ! 2746: if (do_write) { ! 2747: ! 2748: if (u->out_bufs || u->busy_bufs) { ! 2749: rc = ngx_http_output_filter(r, u->out_bufs); ! 2750: ! 2751: if (rc == NGX_ERROR) { ! 2752: ngx_http_upstream_finalize_request(r, u, 0); ! 2753: return; ! 2754: } ! 2755: ! 2756: ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs, ! 2757: &u->out_bufs, u->output.tag); ! 2758: } ! 2759: ! 2760: if (u->busy_bufs == NULL) { ! 2761: ! 2762: if (u->length == 0 ! 2763: || upstream->read->eof ! 2764: || upstream->read->error) ! 2765: { ! 2766: ngx_http_upstream_finalize_request(r, u, 0); ! 2767: return; ! 2768: } ! 2769: ! 2770: b->pos = b->start; ! 2771: b->last = b->start; ! 2772: } ! 2773: } ! 2774: ! 2775: size = b->end - b->last; ! 2776: ! 2777: if (size && upstream->read->ready) { ! 2778: ! 2779: n = upstream->recv(upstream, b->last, size); ! 2780: ! 2781: if (n == NGX_AGAIN) { ! 2782: break; ! 2783: } ! 2784: ! 2785: if (n > 0) { ! 2786: u->state->response_length += n; ! 2787: ! 2788: if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) { ! 2789: ngx_http_upstream_finalize_request(r, u, 0); ! 2790: return; ! 2791: } ! 2792: } ! 2793: ! 2794: do_write = 1; ! 2795: ! 2796: continue; ! 2797: } ! 2798: ! 2799: break; ! 2800: } ! 2801: ! 2802: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ! 2803: ! 2804: if (downstream->data == r) { ! 2805: if (ngx_handle_write_event(downstream->write, clcf->send_lowat) ! 2806: != NGX_OK) ! 2807: { ! 2808: ngx_http_upstream_finalize_request(r, u, 0); ! 2809: return; ! 2810: } ! 2811: } ! 2812: ! 2813: if (downstream->write->active && !downstream->write->ready) { ! 2814: ngx_add_timer(downstream->write, clcf->send_timeout); ! 2815: ! 2816: } else if (downstream->write->timer_set) { ! 2817: ngx_del_timer(downstream->write); ! 2818: } ! 2819: ! 2820: if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) { ! 2821: ngx_http_upstream_finalize_request(r, u, 0); ! 2822: return; ! 2823: } ! 2824: ! 2825: if (upstream->read->active && !upstream->read->ready) { ! 2826: ngx_add_timer(upstream->read, u->conf->read_timeout); ! 2827: ! 2828: } else if (upstream->read->timer_set) { ! 2829: ngx_del_timer(upstream->read); ! 2830: } ! 2831: } ! 2832: ! 2833: ! 2834: static ngx_int_t ! 2835: ngx_http_upstream_non_buffered_filter_init(void *data) ! 2836: { ! 2837: return NGX_OK; ! 2838: } ! 2839: ! 2840: ! 2841: static ngx_int_t ! 2842: ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes) ! 2843: { ! 2844: ngx_http_request_t *r = data; ! 2845: ! 2846: ngx_buf_t *b; ! 2847: ngx_chain_t *cl, **ll; ! 2848: ngx_http_upstream_t *u; ! 2849: ! 2850: u = r->upstream; ! 2851: ! 2852: for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) { ! 2853: ll = &cl->next; ! 2854: } ! 2855: ! 2856: cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs); ! 2857: if (cl == NULL) { ! 2858: return NGX_ERROR; ! 2859: } ! 2860: ! 2861: *ll = cl; ! 2862: ! 2863: cl->buf->flush = 1; ! 2864: cl->buf->memory = 1; ! 2865: ! 2866: b = &u->buffer; ! 2867: ! 2868: cl->buf->pos = b->last; ! 2869: b->last += bytes; ! 2870: cl->buf->last = b->last; ! 2871: cl->buf->tag = u->output.tag; ! 2872: ! 2873: if (u->length == -1) { ! 2874: return NGX_OK; ! 2875: } ! 2876: ! 2877: u->length -= bytes; ! 2878: ! 2879: return NGX_OK; ! 2880: } ! 2881: ! 2882: ! 2883: static void ! 2884: ngx_http_upstream_process_downstream(ngx_http_request_t *r) ! 2885: { ! 2886: ngx_event_t *wev; ! 2887: ngx_connection_t *c; ! 2888: ngx_event_pipe_t *p; ! 2889: ngx_http_upstream_t *u; ! 2890: ! 2891: c = r->connection; ! 2892: u = r->upstream; ! 2893: p = u->pipe; ! 2894: wev = c->write; ! 2895: ! 2896: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 2897: "http upstream process downstream"); ! 2898: ! 2899: c->log->action = "sending to client"; ! 2900: ! 2901: if (wev->timedout) { ! 2902: ! 2903: if (wev->delayed) { ! 2904: ! 2905: wev->timedout = 0; ! 2906: wev->delayed = 0; ! 2907: ! 2908: if (!wev->ready) { ! 2909: ngx_add_timer(wev, p->send_timeout); ! 2910: ! 2911: if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { ! 2912: ngx_http_upstream_finalize_request(r, u, 0); ! 2913: } ! 2914: ! 2915: return; ! 2916: } ! 2917: ! 2918: if (ngx_event_pipe(p, wev->write) == NGX_ABORT) { ! 2919: ngx_http_upstream_finalize_request(r, u, 0); ! 2920: return; ! 2921: } ! 2922: ! 2923: } else { ! 2924: p->downstream_error = 1; ! 2925: c->timedout = 1; ! 2926: ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); ! 2927: } ! 2928: ! 2929: } else { ! 2930: ! 2931: if (wev->delayed) { ! 2932: ! 2933: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 2934: "http downstream delayed"); ! 2935: ! 2936: if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) { ! 2937: ngx_http_upstream_finalize_request(r, u, 0); ! 2938: } ! 2939: ! 2940: return; ! 2941: } ! 2942: ! 2943: if (ngx_event_pipe(p, 1) == NGX_ABORT) { ! 2944: ngx_http_upstream_finalize_request(r, u, 0); ! 2945: return; ! 2946: } ! 2947: } ! 2948: ! 2949: ngx_http_upstream_process_request(r); ! 2950: } ! 2951: ! 2952: ! 2953: static void ! 2954: ngx_http_upstream_process_upstream(ngx_http_request_t *r, ! 2955: ngx_http_upstream_t *u) ! 2956: { ! 2957: ngx_connection_t *c; ! 2958: ! 2959: c = u->peer.connection; ! 2960: ! 2961: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, ! 2962: "http upstream process upstream"); ! 2963: ! 2964: c->log->action = "reading upstream"; ! 2965: ! 2966: if (c->read->timedout) { ! 2967: u->pipe->upstream_error = 1; ! 2968: ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out"); ! 2969: ! 2970: } else { ! 2971: if (ngx_event_pipe(u->pipe, 0) == NGX_ABORT) { ! 2972: ngx_http_upstream_finalize_request(r, u, 0); ! 2973: return; ! 2974: } ! 2975: } ! 2976: ! 2977: ngx_http_upstream_process_request(r); ! 2978: } ! 2979: ! 2980: ! 2981: static void ! 2982: ngx_http_upstream_process_request(ngx_http_request_t *r) ! 2983: { ! 2984: ngx_temp_file_t *tf; ! 2985: ngx_event_pipe_t *p; ! 2986: ngx_http_upstream_t *u; ! 2987: ! 2988: u = r->upstream; ! 2989: p = u->pipe; ! 2990: ! 2991: if (u->peer.connection) { ! 2992: ! 2993: if (u->store) { ! 2994: ! 2995: if (p->upstream_eof || p->upstream_done) { ! 2996: ! 2997: tf = u->pipe->temp_file; ! 2998: ! 2999: if (u->headers_in.status_n == NGX_HTTP_OK ! 3000: && (u->headers_in.content_length_n == -1 ! 3001: || (u->headers_in.content_length_n == tf->offset))) ! 3002: { ! 3003: ngx_http_upstream_store(r, u); ! 3004: u->store = 0; ! 3005: } ! 3006: } ! 3007: } ! 3008: ! 3009: #if (NGX_HTTP_CACHE) ! 3010: ! 3011: if (u->cacheable) { ! 3012: ! 3013: if (p->upstream_done) { ! 3014: ngx_http_file_cache_update(r, u->pipe->temp_file); ! 3015: ! 3016: } else if (p->upstream_eof) { ! 3017: ! 3018: tf = u->pipe->temp_file; ! 3019: ! 3020: if (u->headers_in.content_length_n == -1 ! 3021: || u->headers_in.content_length_n ! 3022: == tf->offset - (off_t) r->cache->body_start) ! 3023: { ! 3024: ngx_http_file_cache_update(r, tf); ! 3025: ! 3026: } else { ! 3027: ngx_http_file_cache_free(r->cache, tf); ! 3028: } ! 3029: ! 3030: } else if (p->upstream_error) { ! 3031: ngx_http_file_cache_free(r->cache, u->pipe->temp_file); ! 3032: } ! 3033: } ! 3034: ! 3035: #endif ! 3036: ! 3037: if (p->upstream_done || p->upstream_eof || p->upstream_error) { ! 3038: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3039: "http upstream exit: %p", p->out); ! 3040: #if 0 ! 3041: ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock); ! 3042: #endif ! 3043: ngx_http_upstream_finalize_request(r, u, 0); ! 3044: return; ! 3045: } ! 3046: } ! 3047: ! 3048: if (p->downstream_error) { ! 3049: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3050: "http upstream downstream error"); ! 3051: ! 3052: if (!u->cacheable && !u->store && u->peer.connection) { ! 3053: ngx_http_upstream_finalize_request(r, u, 0); ! 3054: } ! 3055: } ! 3056: } ! 3057: ! 3058: ! 3059: static void ! 3060: ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 3061: { ! 3062: size_t root; ! 3063: time_t lm; ! 3064: ngx_str_t path; ! 3065: ngx_temp_file_t *tf; ! 3066: ngx_ext_rename_file_t ext; ! 3067: ! 3068: tf = u->pipe->temp_file; ! 3069: ! 3070: if (tf->file.fd == NGX_INVALID_FILE) { ! 3071: ! 3072: /* create file for empty 200 response */ ! 3073: ! 3074: tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); ! 3075: if (tf == NULL) { ! 3076: return; ! 3077: } ! 3078: ! 3079: tf->file.fd = NGX_INVALID_FILE; ! 3080: tf->file.log = r->connection->log; ! 3081: tf->path = u->conf->temp_path; ! 3082: tf->pool = r->pool; ! 3083: tf->persistent = 1; ! 3084: ! 3085: if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, ! 3086: tf->persistent, tf->clean, tf->access) ! 3087: != NGX_OK) ! 3088: { ! 3089: return; ! 3090: } ! 3091: ! 3092: u->pipe->temp_file = tf; ! 3093: } ! 3094: ! 3095: ext.access = u->conf->store_access; ! 3096: ext.path_access = u->conf->store_access; ! 3097: ext.time = -1; ! 3098: ext.create_path = 1; ! 3099: ext.delete_file = 1; ! 3100: ext.log = r->connection->log; ! 3101: ! 3102: if (u->headers_in.last_modified) { ! 3103: ! 3104: lm = ngx_http_parse_time(u->headers_in.last_modified->value.data, ! 3105: u->headers_in.last_modified->value.len); ! 3106: ! 3107: if (lm != NGX_ERROR) { ! 3108: ext.time = lm; ! 3109: ext.fd = tf->file.fd; ! 3110: } ! 3111: } ! 3112: ! 3113: if (u->conf->store_lengths == NULL) { ! 3114: ! 3115: ngx_http_map_uri_to_path(r, &path, &root, 0); ! 3116: ! 3117: } else { ! 3118: if (ngx_http_script_run(r, &path, u->conf->store_lengths->elts, 0, ! 3119: u->conf->store_values->elts) ! 3120: == NULL) ! 3121: { ! 3122: return; ! 3123: } ! 3124: } ! 3125: ! 3126: path.len--; ! 3127: ! 3128: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3129: "upstream stores \"%s\" to \"%s\"", ! 3130: tf->file.name.data, path.data); ! 3131: ! 3132: (void) ngx_ext_rename_file(&tf->file.name, &path, &ext); ! 3133: } ! 3134: ! 3135: ! 3136: static void ! 3137: ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u) ! 3138: { ! 3139: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3140: "http upstream dummy handler"); ! 3141: } ! 3142: ! 3143: ! 3144: static void ! 3145: ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, ! 3146: ngx_uint_t ft_type) ! 3147: { ! 3148: ngx_uint_t status, state; ! 3149: ! 3150: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3151: "http next upstream, %xi", ft_type); ! 3152: ! 3153: #if 0 ! 3154: ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock); ! 3155: #endif ! 3156: ! 3157: if (u->peer.sockaddr) { ! 3158: ! 3159: if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404) { ! 3160: state = NGX_PEER_NEXT; ! 3161: } else { ! 3162: state = NGX_PEER_FAILED; ! 3163: } ! 3164: ! 3165: u->peer.free(&u->peer, u->peer.data, state); ! 3166: u->peer.sockaddr = NULL; ! 3167: } ! 3168: ! 3169: if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) { ! 3170: ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT, ! 3171: "upstream timed out"); ! 3172: } ! 3173: ! 3174: if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) { ! 3175: status = 0; ! 3176: ! 3177: /* TODO: inform balancer instead */ ! 3178: ! 3179: u->peer.tries++; ! 3180: ! 3181: } else { ! 3182: switch(ft_type) { ! 3183: ! 3184: case NGX_HTTP_UPSTREAM_FT_TIMEOUT: ! 3185: status = NGX_HTTP_GATEWAY_TIME_OUT; ! 3186: break; ! 3187: ! 3188: case NGX_HTTP_UPSTREAM_FT_HTTP_500: ! 3189: status = NGX_HTTP_INTERNAL_SERVER_ERROR; ! 3190: break; ! 3191: ! 3192: case NGX_HTTP_UPSTREAM_FT_HTTP_404: ! 3193: status = NGX_HTTP_NOT_FOUND; ! 3194: break; ! 3195: ! 3196: /* ! 3197: * NGX_HTTP_UPSTREAM_FT_BUSY_LOCK and NGX_HTTP_UPSTREAM_FT_MAX_WAITING ! 3198: * never reach here ! 3199: */ ! 3200: ! 3201: default: ! 3202: status = NGX_HTTP_BAD_GATEWAY; ! 3203: } ! 3204: } ! 3205: ! 3206: if (r->connection->error) { ! 3207: ngx_http_upstream_finalize_request(r, u, ! 3208: NGX_HTTP_CLIENT_CLOSED_REQUEST); ! 3209: return; ! 3210: } ! 3211: ! 3212: if (status) { ! 3213: u->state->status = status; ! 3214: ! 3215: if (u->peer.tries == 0 || !(u->conf->next_upstream & ft_type)) { ! 3216: ! 3217: #if (NGX_HTTP_CACHE) ! 3218: ! 3219: if (u->cache_status == NGX_HTTP_CACHE_EXPIRED ! 3220: && (u->conf->cache_use_stale & ft_type)) ! 3221: { ! 3222: ngx_int_t rc; ! 3223: ! 3224: rc = u->reinit_request(r); ! 3225: ! 3226: if (rc == NGX_OK) { ! 3227: u->cache_status = NGX_HTTP_CACHE_STALE; ! 3228: rc = ngx_http_upstream_cache_send(r, u); ! 3229: } ! 3230: ! 3231: ngx_http_upstream_finalize_request(r, u, rc); ! 3232: return; ! 3233: } ! 3234: #endif ! 3235: ! 3236: ngx_http_upstream_finalize_request(r, u, status); ! 3237: return; ! 3238: } ! 3239: } ! 3240: ! 3241: if (u->peer.connection) { ! 3242: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3243: "close http upstream connection: %d", ! 3244: u->peer.connection->fd); ! 3245: #if (NGX_HTTP_SSL) ! 3246: ! 3247: if (u->peer.connection->ssl) { ! 3248: u->peer.connection->ssl->no_wait_shutdown = 1; ! 3249: u->peer.connection->ssl->no_send_shutdown = 1; ! 3250: ! 3251: (void) ngx_ssl_shutdown(u->peer.connection); ! 3252: } ! 3253: #endif ! 3254: ! 3255: if (u->peer.connection->pool) { ! 3256: ngx_destroy_pool(u->peer.connection->pool); ! 3257: } ! 3258: ! 3259: ngx_close_connection(u->peer.connection); ! 3260: u->peer.connection = NULL; ! 3261: } ! 3262: ! 3263: #if 0 ! 3264: if (u->conf->busy_lock && !u->busy_locked) { ! 3265: ngx_http_upstream_busy_lock(p); ! 3266: return; ! 3267: } ! 3268: #endif ! 3269: ! 3270: ngx_http_upstream_connect(r, u); ! 3271: } ! 3272: ! 3273: ! 3274: static void ! 3275: ngx_http_upstream_cleanup(void *data) ! 3276: { ! 3277: ngx_http_request_t *r = data; ! 3278: ! 3279: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3280: "cleanup http upstream request: \"%V\"", &r->uri); ! 3281: ! 3282: ngx_http_upstream_finalize_request(r, r->upstream, NGX_DONE); ! 3283: } ! 3284: ! 3285: ! 3286: static void ! 3287: ngx_http_upstream_finalize_request(ngx_http_request_t *r, ! 3288: ngx_http_upstream_t *u, ngx_int_t rc) ! 3289: { ! 3290: ngx_time_t *tp; ! 3291: ! 3292: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3293: "finalize http upstream request: %i", rc); ! 3294: ! 3295: if (u->cleanup) { ! 3296: *u->cleanup = NULL; ! 3297: u->cleanup = NULL; ! 3298: } ! 3299: ! 3300: if (u->resolved && u->resolved->ctx) { ! 3301: ngx_resolve_name_done(u->resolved->ctx); ! 3302: u->resolved->ctx = NULL; ! 3303: } ! 3304: ! 3305: if (u->state && u->state->response_sec) { ! 3306: tp = ngx_timeofday(); ! 3307: u->state->response_sec = tp->sec - u->state->response_sec; ! 3308: u->state->response_msec = tp->msec - u->state->response_msec; ! 3309: ! 3310: if (u->pipe && u->pipe->read_length) { ! 3311: u->state->response_length = u->pipe->read_length; ! 3312: } ! 3313: } ! 3314: ! 3315: u->finalize_request(r, rc); ! 3316: ! 3317: if (u->peer.free && u->peer.sockaddr) { ! 3318: u->peer.free(&u->peer, u->peer.data, 0); ! 3319: u->peer.sockaddr = NULL; ! 3320: } ! 3321: ! 3322: if (u->peer.connection) { ! 3323: ! 3324: #if (NGX_HTTP_SSL) ! 3325: ! 3326: /* TODO: do not shutdown persistent connection */ ! 3327: ! 3328: if (u->peer.connection->ssl) { ! 3329: ! 3330: /* ! 3331: * We send the "close notify" shutdown alert to the upstream only ! 3332: * and do not wait its "close notify" shutdown alert. ! 3333: * It is acceptable according to the TLS standard. ! 3334: */ ! 3335: ! 3336: u->peer.connection->ssl->no_wait_shutdown = 1; ! 3337: ! 3338: (void) ngx_ssl_shutdown(u->peer.connection); ! 3339: } ! 3340: #endif ! 3341: ! 3342: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3343: "close http upstream connection: %d", ! 3344: u->peer.connection->fd); ! 3345: ! 3346: if (u->peer.connection->pool) { ! 3347: ngx_destroy_pool(u->peer.connection->pool); ! 3348: } ! 3349: ! 3350: ngx_close_connection(u->peer.connection); ! 3351: } ! 3352: ! 3353: u->peer.connection = NULL; ! 3354: ! 3355: if (u->pipe && u->pipe->temp_file) { ! 3356: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3357: "http upstream temp fd: %d", ! 3358: u->pipe->temp_file->file.fd); ! 3359: } ! 3360: ! 3361: if (u->store && u->pipe && u->pipe->temp_file ! 3362: && u->pipe->temp_file->file.fd != NGX_INVALID_FILE) ! 3363: { ! 3364: if (ngx_delete_file(u->pipe->temp_file->file.name.data) ! 3365: == NGX_FILE_ERROR) ! 3366: { ! 3367: ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, ! 3368: ngx_delete_file_n " \"%s\" failed", ! 3369: u->pipe->temp_file->file.name.data); ! 3370: } ! 3371: } ! 3372: ! 3373: #if (NGX_HTTP_CACHE) ! 3374: ! 3375: if (r->cache) { ! 3376: ! 3377: if (u->cacheable) { ! 3378: ! 3379: if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT) { ! 3380: time_t valid; ! 3381: ! 3382: valid = ngx_http_file_cache_valid(u->conf->cache_valid, rc); ! 3383: ! 3384: if (valid) { ! 3385: r->cache->valid_sec = ngx_time() + valid; ! 3386: r->cache->error = rc; ! 3387: } ! 3388: } ! 3389: } ! 3390: ! 3391: ngx_http_file_cache_free(r->cache, u->pipe->temp_file); ! 3392: } ! 3393: ! 3394: #endif ! 3395: ! 3396: if (u->header_sent ! 3397: && rc != NGX_HTTP_REQUEST_TIME_OUT ! 3398: && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE)) ! 3399: { ! 3400: rc = 0; ! 3401: } ! 3402: ! 3403: if (rc == NGX_DECLINED) { ! 3404: return; ! 3405: } ! 3406: ! 3407: r->connection->log->action = "sending to client"; ! 3408: ! 3409: if (rc == 0 ! 3410: && !r->header_only ! 3411: #if (NGX_HTTP_CACHE) ! 3412: && !r->cached ! 3413: #endif ! 3414: ) ! 3415: { ! 3416: rc = ngx_http_send_special(r, NGX_HTTP_LAST); ! 3417: } ! 3418: ! 3419: ngx_http_finalize_request(r, rc); ! 3420: } ! 3421: ! 3422: ! 3423: static ngx_int_t ! 3424: ngx_http_upstream_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3425: ngx_uint_t offset) ! 3426: { ! 3427: ngx_table_elt_t **ph; ! 3428: ! 3429: ph = (ngx_table_elt_t **) ((char *) &r->upstream->headers_in + offset); ! 3430: ! 3431: if (*ph == NULL) { ! 3432: *ph = h; ! 3433: } ! 3434: ! 3435: return NGX_OK; ! 3436: } ! 3437: ! 3438: ! 3439: static ngx_int_t ! 3440: ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3441: ngx_uint_t offset) ! 3442: { ! 3443: return NGX_OK; ! 3444: } ! 3445: ! 3446: ! 3447: static ngx_int_t ! 3448: ngx_http_upstream_process_content_length(ngx_http_request_t *r, ! 3449: ngx_table_elt_t *h, ngx_uint_t offset) ! 3450: { ! 3451: ngx_http_upstream_t *u; ! 3452: ! 3453: u = r->upstream; ! 3454: ! 3455: u->headers_in.content_length = h; ! 3456: u->headers_in.content_length_n = ngx_atoof(h->value.data, h->value.len); ! 3457: ! 3458: return NGX_OK; ! 3459: } ! 3460: ! 3461: ! 3462: static ngx_int_t ! 3463: ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3464: ngx_uint_t offset) ! 3465: { ! 3466: #if (NGX_HTTP_CACHE) ! 3467: ngx_http_upstream_t *u; ! 3468: ! 3469: u = r->upstream; ! 3470: ! 3471: if (!(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_SET_COOKIE)) { ! 3472: u->cacheable = 0; ! 3473: } ! 3474: #endif ! 3475: ! 3476: return NGX_OK; ! 3477: } ! 3478: ! 3479: ! 3480: static ngx_int_t ! 3481: ngx_http_upstream_process_cache_control(ngx_http_request_t *r, ! 3482: ngx_table_elt_t *h, ngx_uint_t offset) ! 3483: { ! 3484: ngx_array_t *pa; ! 3485: ngx_table_elt_t **ph; ! 3486: ngx_http_upstream_t *u; ! 3487: ! 3488: u = r->upstream; ! 3489: pa = &u->headers_in.cache_control; ! 3490: ! 3491: if (pa->elts == NULL) { ! 3492: if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK) ! 3493: { ! 3494: return NGX_ERROR; ! 3495: } ! 3496: } ! 3497: ! 3498: ph = ngx_array_push(pa); ! 3499: if (ph == NULL) { ! 3500: return NGX_ERROR; ! 3501: } ! 3502: ! 3503: *ph = h; ! 3504: ! 3505: #if (NGX_HTTP_CACHE) ! 3506: { ! 3507: u_char *p, *last; ! 3508: ngx_int_t n; ! 3509: ! 3510: if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL) { ! 3511: return NGX_OK; ! 3512: } ! 3513: ! 3514: if (r->cache == NULL) { ! 3515: return NGX_OK; ! 3516: } ! 3517: ! 3518: if (r->cache->valid_sec != 0) { ! 3519: return NGX_OK; ! 3520: } ! 3521: ! 3522: p = h->value.data; ! 3523: last = p + h->value.len; ! 3524: ! 3525: if (ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL ! 3526: || ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1) != NULL ! 3527: || ngx_strlcasestrn(p, last, (u_char *) "private", 7 - 1) != NULL) ! 3528: { ! 3529: u->cacheable = 0; ! 3530: return NGX_OK; ! 3531: } ! 3532: ! 3533: p = ngx_strlcasestrn(p, last, (u_char *) "max-age=", 8 - 1); ! 3534: ! 3535: if (p == NULL) { ! 3536: return NGX_OK; ! 3537: } ! 3538: ! 3539: n = 0; ! 3540: ! 3541: for (p += 8; p < last; p++) { ! 3542: if (*p == ',' || *p == ';' || *p == ' ') { ! 3543: break; ! 3544: } ! 3545: ! 3546: if (*p >= '0' && *p <= '9') { ! 3547: n = n * 10 + *p - '0'; ! 3548: continue; ! 3549: } ! 3550: ! 3551: u->cacheable = 0; ! 3552: return NGX_OK; ! 3553: } ! 3554: ! 3555: if (n == 0) { ! 3556: u->cacheable = 0; ! 3557: return NGX_OK; ! 3558: } ! 3559: ! 3560: r->cache->valid_sec = ngx_time() + n; ! 3561: } ! 3562: #endif ! 3563: ! 3564: return NGX_OK; ! 3565: } ! 3566: ! 3567: ! 3568: static ngx_int_t ! 3569: ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3570: ngx_uint_t offset) ! 3571: { ! 3572: ngx_http_upstream_t *u; ! 3573: ! 3574: u = r->upstream; ! 3575: u->headers_in.expires = h; ! 3576: ! 3577: #if (NGX_HTTP_CACHE) ! 3578: { ! 3579: time_t expires; ! 3580: ! 3581: if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) { ! 3582: return NGX_OK; ! 3583: } ! 3584: ! 3585: if (r->cache == NULL) { ! 3586: return NGX_OK; ! 3587: } ! 3588: ! 3589: if (r->cache->valid_sec != 0) { ! 3590: return NGX_OK; ! 3591: } ! 3592: ! 3593: expires = ngx_http_parse_time(h->value.data, h->value.len); ! 3594: ! 3595: if (expires == NGX_ERROR || expires < ngx_time()) { ! 3596: u->cacheable = 0; ! 3597: return NGX_OK; ! 3598: } ! 3599: ! 3600: r->cache->valid_sec = expires; ! 3601: } ! 3602: #endif ! 3603: ! 3604: return NGX_OK; ! 3605: } ! 3606: ! 3607: ! 3608: static ngx_int_t ! 3609: ngx_http_upstream_process_accel_expires(ngx_http_request_t *r, ! 3610: ngx_table_elt_t *h, ngx_uint_t offset) ! 3611: { ! 3612: ngx_http_upstream_t *u; ! 3613: ! 3614: u = r->upstream; ! 3615: u->headers_in.x_accel_expires = h; ! 3616: ! 3617: #if (NGX_HTTP_CACHE) ! 3618: { ! 3619: u_char *p; ! 3620: size_t len; ! 3621: ngx_int_t n; ! 3622: ! 3623: if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES) { ! 3624: return NGX_OK; ! 3625: } ! 3626: ! 3627: if (r->cache == NULL) { ! 3628: return NGX_OK; ! 3629: } ! 3630: ! 3631: len = h->value.len; ! 3632: p = h->value.data; ! 3633: ! 3634: if (p[0] != '@') { ! 3635: n = ngx_atoi(p, len); ! 3636: ! 3637: switch (n) { ! 3638: case 0: ! 3639: u->cacheable = 0; ! 3640: /* fall through */ ! 3641: ! 3642: case NGX_ERROR: ! 3643: return NGX_OK; ! 3644: ! 3645: default: ! 3646: r->cache->valid_sec = ngx_time() + n; ! 3647: return NGX_OK; ! 3648: } ! 3649: } ! 3650: ! 3651: p++; ! 3652: len--; ! 3653: ! 3654: n = ngx_atoi(p, len); ! 3655: ! 3656: if (n != NGX_ERROR) { ! 3657: r->cache->valid_sec = n; ! 3658: } ! 3659: } ! 3660: #endif ! 3661: ! 3662: return NGX_OK; ! 3663: } ! 3664: ! 3665: ! 3666: static ngx_int_t ! 3667: ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3668: ngx_uint_t offset) ! 3669: { ! 3670: ngx_int_t n; ! 3671: ngx_http_upstream_t *u; ! 3672: ! 3673: u = r->upstream; ! 3674: u->headers_in.x_accel_limit_rate = h; ! 3675: ! 3676: if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE) { ! 3677: return NGX_OK; ! 3678: } ! 3679: ! 3680: n = ngx_atoi(h->value.data, h->value.len); ! 3681: ! 3682: if (n != NGX_ERROR) { ! 3683: r->limit_rate = (size_t) n; ! 3684: } ! 3685: ! 3686: return NGX_OK; ! 3687: } ! 3688: ! 3689: ! 3690: static ngx_int_t ! 3691: ngx_http_upstream_process_buffering(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3692: ngx_uint_t offset) ! 3693: { ! 3694: u_char c0, c1, c2; ! 3695: ngx_http_upstream_t *u; ! 3696: ! 3697: u = r->upstream; ! 3698: ! 3699: if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING) { ! 3700: return NGX_OK; ! 3701: } ! 3702: ! 3703: if (u->conf->change_buffering) { ! 3704: ! 3705: if (h->value.len == 2) { ! 3706: c0 = ngx_tolower(h->value.data[0]); ! 3707: c1 = ngx_tolower(h->value.data[1]); ! 3708: ! 3709: if (c0 == 'n' && c1 == 'o') { ! 3710: u->buffering = 0; ! 3711: } ! 3712: ! 3713: } else if (h->value.len == 3) { ! 3714: c0 = ngx_tolower(h->value.data[0]); ! 3715: c1 = ngx_tolower(h->value.data[1]); ! 3716: c2 = ngx_tolower(h->value.data[2]); ! 3717: ! 3718: if (c0 == 'y' && c1 == 'e' && c2 == 's') { ! 3719: u->buffering = 1; ! 3720: } ! 3721: } ! 3722: } ! 3723: ! 3724: return NGX_OK; ! 3725: } ! 3726: ! 3727: ! 3728: static ngx_int_t ! 3729: ngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3730: ngx_uint_t offset) ! 3731: { ! 3732: if (r->upstream->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_CHARSET) { ! 3733: return NGX_OK; ! 3734: } ! 3735: ! 3736: r->headers_out.override_charset = &h->value; ! 3737: ! 3738: return NGX_OK; ! 3739: } ! 3740: ! 3741: ! 3742: static ngx_int_t ! 3743: ngx_http_upstream_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3744: ngx_uint_t offset) ! 3745: { ! 3746: r->upstream->headers_in.connection = h; ! 3747: ! 3748: if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len, ! 3749: (u_char *) "close", 5 - 1) ! 3750: != NULL) ! 3751: { ! 3752: r->upstream->headers_in.connection_close = 1; ! 3753: } ! 3754: ! 3755: return NGX_OK; ! 3756: } ! 3757: ! 3758: ! 3759: static ngx_int_t ! 3760: ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r, ! 3761: ngx_table_elt_t *h, ngx_uint_t offset) ! 3762: { ! 3763: r->upstream->headers_in.transfer_encoding = h; ! 3764: ! 3765: if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len, ! 3766: (u_char *) "chunked", 7 - 1) ! 3767: != NULL) ! 3768: { ! 3769: r->upstream->headers_in.chunked = 1; ! 3770: } ! 3771: ! 3772: return NGX_OK; ! 3773: } ! 3774: ! 3775: ! 3776: static ngx_int_t ! 3777: ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3778: ngx_uint_t offset) ! 3779: { ! 3780: ngx_table_elt_t *ho, **ph; ! 3781: ! 3782: ho = ngx_list_push(&r->headers_out.headers); ! 3783: if (ho == NULL) { ! 3784: return NGX_ERROR; ! 3785: } ! 3786: ! 3787: *ho = *h; ! 3788: ! 3789: if (offset) { ! 3790: ph = (ngx_table_elt_t **) ((char *) &r->headers_out + offset); ! 3791: *ph = ho; ! 3792: } ! 3793: ! 3794: return NGX_OK; ! 3795: } ! 3796: ! 3797: ! 3798: static ngx_int_t ! 3799: ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r, ! 3800: ngx_table_elt_t *h, ngx_uint_t offset) ! 3801: { ! 3802: ngx_array_t *pa; ! 3803: ngx_table_elt_t *ho, **ph; ! 3804: ! 3805: pa = (ngx_array_t *) ((char *) &r->headers_out + offset); ! 3806: ! 3807: if (pa->elts == NULL) { ! 3808: if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK) ! 3809: { ! 3810: return NGX_ERROR; ! 3811: } ! 3812: } ! 3813: ! 3814: ph = ngx_array_push(pa); ! 3815: if (ph == NULL) { ! 3816: return NGX_ERROR; ! 3817: } ! 3818: ! 3819: ho = ngx_list_push(&r->headers_out.headers); ! 3820: if (ho == NULL) { ! 3821: return NGX_ERROR; ! 3822: } ! 3823: ! 3824: *ho = *h; ! 3825: *ph = ho; ! 3826: ! 3827: return NGX_OK; ! 3828: } ! 3829: ! 3830: ! 3831: static ngx_int_t ! 3832: ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3833: ngx_uint_t offset) ! 3834: { ! 3835: u_char *p, *last; ! 3836: ! 3837: r->headers_out.content_type_len = h->value.len; ! 3838: r->headers_out.content_type = h->value; ! 3839: r->headers_out.content_type_lowcase = NULL; ! 3840: ! 3841: for (p = h->value.data; *p; p++) { ! 3842: ! 3843: if (*p != ';') { ! 3844: continue; ! 3845: } ! 3846: ! 3847: last = p; ! 3848: ! 3849: while (*++p == ' ') { /* void */ } ! 3850: ! 3851: if (*p == '\0') { ! 3852: return NGX_OK; ! 3853: } ! 3854: ! 3855: if (ngx_strncasecmp(p, (u_char *) "charset=", 8) != 0) { ! 3856: continue; ! 3857: } ! 3858: ! 3859: p += 8; ! 3860: ! 3861: r->headers_out.content_type_len = last - h->value.data; ! 3862: ! 3863: if (*p == '"') { ! 3864: p++; ! 3865: } ! 3866: ! 3867: last = h->value.data + h->value.len; ! 3868: ! 3869: if (*(last - 1) == '"') { ! 3870: last--; ! 3871: } ! 3872: ! 3873: r->headers_out.charset.len = last - p; ! 3874: r->headers_out.charset.data = p; ! 3875: ! 3876: return NGX_OK; ! 3877: } ! 3878: ! 3879: return NGX_OK; ! 3880: } ! 3881: ! 3882: ! 3883: static ngx_int_t ! 3884: ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3885: ngx_uint_t offset) ! 3886: { ! 3887: ngx_table_elt_t *ho; ! 3888: ! 3889: ho = ngx_list_push(&r->headers_out.headers); ! 3890: if (ho == NULL) { ! 3891: return NGX_ERROR; ! 3892: } ! 3893: ! 3894: *ho = *h; ! 3895: ! 3896: r->headers_out.last_modified = ho; ! 3897: ! 3898: #if (NGX_HTTP_CACHE) ! 3899: ! 3900: if (r->upstream->cacheable) { ! 3901: r->headers_out.last_modified_time = ngx_http_parse_time(h->value.data, ! 3902: h->value.len); ! 3903: } ! 3904: ! 3905: #endif ! 3906: ! 3907: return NGX_OK; ! 3908: } ! 3909: ! 3910: ! 3911: static ngx_int_t ! 3912: ngx_http_upstream_rewrite_location(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3913: ngx_uint_t offset) ! 3914: { ! 3915: ngx_int_t rc; ! 3916: ngx_table_elt_t *ho; ! 3917: ! 3918: ho = ngx_list_push(&r->headers_out.headers); ! 3919: if (ho == NULL) { ! 3920: return NGX_ERROR; ! 3921: } ! 3922: ! 3923: *ho = *h; ! 3924: ! 3925: if (r->upstream->rewrite_redirect) { ! 3926: rc = r->upstream->rewrite_redirect(r, ho, 0); ! 3927: ! 3928: if (rc == NGX_DECLINED) { ! 3929: return NGX_OK; ! 3930: } ! 3931: ! 3932: if (rc == NGX_OK) { ! 3933: r->headers_out.location = ho; ! 3934: ! 3935: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3936: "rewritten location: \"%V\"", &ho->value); ! 3937: } ! 3938: ! 3939: return rc; ! 3940: } ! 3941: ! 3942: if (ho->value.data[0] != '/') { ! 3943: r->headers_out.location = ho; ! 3944: } ! 3945: ! 3946: /* ! 3947: * we do not set r->headers_out.location here to avoid the handling ! 3948: * the local redirects without a host name by ngx_http_header_filter() ! 3949: */ ! 3950: ! 3951: return NGX_OK; ! 3952: } ! 3953: ! 3954: ! 3955: static ngx_int_t ! 3956: ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r, ngx_table_elt_t *h, ! 3957: ngx_uint_t offset) ! 3958: { ! 3959: u_char *p; ! 3960: ngx_int_t rc; ! 3961: ngx_table_elt_t *ho; ! 3962: ! 3963: ho = ngx_list_push(&r->headers_out.headers); ! 3964: if (ho == NULL) { ! 3965: return NGX_ERROR; ! 3966: } ! 3967: ! 3968: *ho = *h; ! 3969: ! 3970: if (r->upstream->rewrite_redirect) { ! 3971: ! 3972: p = ngx_strcasestrn(ho->value.data, "url=", 4 - 1); ! 3973: ! 3974: if (p) { ! 3975: rc = r->upstream->rewrite_redirect(r, ho, p + 4 - ho->value.data); ! 3976: ! 3977: } else { ! 3978: return NGX_OK; ! 3979: } ! 3980: ! 3981: if (rc == NGX_DECLINED) { ! 3982: return NGX_OK; ! 3983: } ! 3984: ! 3985: if (rc == NGX_OK) { ! 3986: r->headers_out.refresh = ho; ! 3987: ! 3988: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 3989: "rewritten refresh: \"%V\"", &ho->value); ! 3990: } ! 3991: ! 3992: return rc; ! 3993: } ! 3994: ! 3995: r->headers_out.refresh = ho; ! 3996: ! 3997: return NGX_OK; ! 3998: } ! 3999: ! 4000: ! 4001: static ngx_int_t ! 4002: ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h, ! 4003: ngx_uint_t offset) ! 4004: { ! 4005: ngx_int_t rc; ! 4006: ngx_table_elt_t *ho; ! 4007: ! 4008: ho = ngx_list_push(&r->headers_out.headers); ! 4009: if (ho == NULL) { ! 4010: return NGX_ERROR; ! 4011: } ! 4012: ! 4013: *ho = *h; ! 4014: ! 4015: if (r->upstream->rewrite_cookie) { ! 4016: rc = r->upstream->rewrite_cookie(r, ho); ! 4017: ! 4018: if (rc == NGX_DECLINED) { ! 4019: return NGX_OK; ! 4020: } ! 4021: ! 4022: #if (NGX_DEBUG) ! 4023: if (rc == NGX_OK) { ! 4024: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, ! 4025: "rewritten cookie: \"%V\"", &ho->value); ! 4026: } ! 4027: #endif ! 4028: ! 4029: return rc; ! 4030: } ! 4031: ! 4032: return NGX_OK; ! 4033: } ! 4034: ! 4035: ! 4036: static ngx_int_t ! 4037: ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r, ! 4038: ngx_table_elt_t *h, ngx_uint_t offset) ! 4039: { ! 4040: ngx_table_elt_t *ho; ! 4041: ! 4042: #if (NGX_HTTP_CACHE) ! 4043: ! 4044: if (r->cached) { ! 4045: r->allow_ranges = 1; ! 4046: return NGX_OK; ! 4047: ! 4048: } ! 4049: ! 4050: #endif ! 4051: ! 4052: ho = ngx_list_push(&r->headers_out.headers); ! 4053: if (ho == NULL) { ! 4054: return NGX_ERROR; ! 4055: } ! 4056: ! 4057: *ho = *h; ! 4058: ! 4059: r->headers_out.accept_ranges = ho; ! 4060: ! 4061: return NGX_OK; ! 4062: } ! 4063: ! 4064: ! 4065: #if (NGX_HTTP_GZIP) ! 4066: ! 4067: static ngx_int_t ! 4068: ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r, ! 4069: ngx_table_elt_t *h, ngx_uint_t offset) ! 4070: { ! 4071: ngx_table_elt_t *ho; ! 4072: ! 4073: ho = ngx_list_push(&r->headers_out.headers); ! 4074: if (ho == NULL) { ! 4075: return NGX_ERROR; ! 4076: } ! 4077: ! 4078: *ho = *h; ! 4079: ! 4080: r->headers_out.content_encoding = ho; ! 4081: ! 4082: return NGX_OK; ! 4083: } ! 4084: ! 4085: #endif ! 4086: ! 4087: ! 4088: static ngx_int_t ! 4089: ngx_http_upstream_add_variables(ngx_conf_t *cf) ! 4090: { ! 4091: ngx_http_variable_t *var, *v; ! 4092: ! 4093: for (v = ngx_http_upstream_vars; v->name.len; v++) { ! 4094: var = ngx_http_add_variable(cf, &v->name, v->flags); ! 4095: if (var == NULL) { ! 4096: return NGX_ERROR; ! 4097: } ! 4098: ! 4099: var->get_handler = v->get_handler; ! 4100: var->data = v->data; ! 4101: } ! 4102: ! 4103: return NGX_OK; ! 4104: } ! 4105: ! 4106: ! 4107: static ngx_int_t ! 4108: ngx_http_upstream_addr_variable(ngx_http_request_t *r, ! 4109: ngx_http_variable_value_t *v, uintptr_t data) ! 4110: { ! 4111: u_char *p; ! 4112: size_t len; ! 4113: ngx_uint_t i; ! 4114: ngx_http_upstream_state_t *state; ! 4115: ! 4116: v->valid = 1; ! 4117: v->no_cacheable = 0; ! 4118: v->not_found = 0; ! 4119: ! 4120: if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { ! 4121: v->not_found = 1; ! 4122: return NGX_OK; ! 4123: } ! 4124: ! 4125: len = 0; ! 4126: state = r->upstream_states->elts; ! 4127: ! 4128: for (i = 0; i < r->upstream_states->nelts; i++) { ! 4129: if (state[i].peer) { ! 4130: len += state[i].peer->len + 2; ! 4131: ! 4132: } else { ! 4133: len += 3; ! 4134: } ! 4135: } ! 4136: ! 4137: p = ngx_pnalloc(r->pool, len); ! 4138: if (p == NULL) { ! 4139: return NGX_ERROR; ! 4140: } ! 4141: ! 4142: v->data = p; ! 4143: ! 4144: i = 0; ! 4145: ! 4146: for ( ;; ) { ! 4147: if (state[i].peer) { ! 4148: p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len); ! 4149: } ! 4150: ! 4151: if (++i == r->upstream_states->nelts) { ! 4152: break; ! 4153: } ! 4154: ! 4155: if (state[i].peer) { ! 4156: *p++ = ','; ! 4157: *p++ = ' '; ! 4158: ! 4159: } else { ! 4160: *p++ = ' '; ! 4161: *p++ = ':'; ! 4162: *p++ = ' '; ! 4163: ! 4164: if (++i == r->upstream_states->nelts) { ! 4165: break; ! 4166: } ! 4167: ! 4168: continue; ! 4169: } ! 4170: } ! 4171: ! 4172: v->len = p - v->data; ! 4173: ! 4174: return NGX_OK; ! 4175: } ! 4176: ! 4177: ! 4178: static ngx_int_t ! 4179: ngx_http_upstream_status_variable(ngx_http_request_t *r, ! 4180: ngx_http_variable_value_t *v, uintptr_t data) ! 4181: { ! 4182: u_char *p; ! 4183: size_t len; ! 4184: ngx_uint_t i; ! 4185: ngx_http_upstream_state_t *state; ! 4186: ! 4187: v->valid = 1; ! 4188: v->no_cacheable = 0; ! 4189: v->not_found = 0; ! 4190: ! 4191: if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { ! 4192: v->not_found = 1; ! 4193: return NGX_OK; ! 4194: } ! 4195: ! 4196: len = r->upstream_states->nelts * (3 + 2); ! 4197: ! 4198: p = ngx_pnalloc(r->pool, len); ! 4199: if (p == NULL) { ! 4200: return NGX_ERROR; ! 4201: } ! 4202: ! 4203: v->data = p; ! 4204: ! 4205: i = 0; ! 4206: state = r->upstream_states->elts; ! 4207: ! 4208: for ( ;; ) { ! 4209: if (state[i].status) { ! 4210: p = ngx_sprintf(p, "%ui", state[i].status); ! 4211: ! 4212: } else { ! 4213: *p++ = '-'; ! 4214: } ! 4215: ! 4216: if (++i == r->upstream_states->nelts) { ! 4217: break; ! 4218: } ! 4219: ! 4220: if (state[i].peer) { ! 4221: *p++ = ','; ! 4222: *p++ = ' '; ! 4223: ! 4224: } else { ! 4225: *p++ = ' '; ! 4226: *p++ = ':'; ! 4227: *p++ = ' '; ! 4228: ! 4229: if (++i == r->upstream_states->nelts) { ! 4230: break; ! 4231: } ! 4232: ! 4233: continue; ! 4234: } ! 4235: } ! 4236: ! 4237: v->len = p - v->data; ! 4238: ! 4239: return NGX_OK; ! 4240: } ! 4241: ! 4242: ! 4243: static ngx_int_t ! 4244: ngx_http_upstream_response_time_variable(ngx_http_request_t *r, ! 4245: ngx_http_variable_value_t *v, uintptr_t data) ! 4246: { ! 4247: u_char *p; ! 4248: size_t len; ! 4249: ngx_uint_t i; ! 4250: ngx_msec_int_t ms; ! 4251: ngx_http_upstream_state_t *state; ! 4252: ! 4253: v->valid = 1; ! 4254: v->no_cacheable = 0; ! 4255: v->not_found = 0; ! 4256: ! 4257: if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { ! 4258: v->not_found = 1; ! 4259: return NGX_OK; ! 4260: } ! 4261: ! 4262: len = r->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2); ! 4263: ! 4264: p = ngx_pnalloc(r->pool, len); ! 4265: if (p == NULL) { ! 4266: return NGX_ERROR; ! 4267: } ! 4268: ! 4269: v->data = p; ! 4270: ! 4271: i = 0; ! 4272: state = r->upstream_states->elts; ! 4273: ! 4274: for ( ;; ) { ! 4275: if (state[i].status) { ! 4276: ms = (ngx_msec_int_t) ! 4277: (state[i].response_sec * 1000 + state[i].response_msec); ! 4278: ms = ngx_max(ms, 0); ! 4279: p = ngx_sprintf(p, "%d.%03d", ms / 1000, ms % 1000); ! 4280: ! 4281: } else { ! 4282: *p++ = '-'; ! 4283: } ! 4284: ! 4285: if (++i == r->upstream_states->nelts) { ! 4286: break; ! 4287: } ! 4288: ! 4289: if (state[i].peer) { ! 4290: *p++ = ','; ! 4291: *p++ = ' '; ! 4292: ! 4293: } else { ! 4294: *p++ = ' '; ! 4295: *p++ = ':'; ! 4296: *p++ = ' '; ! 4297: ! 4298: if (++i == r->upstream_states->nelts) { ! 4299: break; ! 4300: } ! 4301: ! 4302: continue; ! 4303: } ! 4304: } ! 4305: ! 4306: v->len = p - v->data; ! 4307: ! 4308: return NGX_OK; ! 4309: } ! 4310: ! 4311: ! 4312: static ngx_int_t ! 4313: ngx_http_upstream_response_length_variable(ngx_http_request_t *r, ! 4314: ngx_http_variable_value_t *v, uintptr_t data) ! 4315: { ! 4316: u_char *p; ! 4317: size_t len; ! 4318: ngx_uint_t i; ! 4319: ngx_http_upstream_state_t *state; ! 4320: ! 4321: v->valid = 1; ! 4322: v->no_cacheable = 0; ! 4323: v->not_found = 0; ! 4324: ! 4325: if (r->upstream_states == NULL || r->upstream_states->nelts == 0) { ! 4326: v->not_found = 1; ! 4327: return NGX_OK; ! 4328: } ! 4329: ! 4330: len = r->upstream_states->nelts * (NGX_OFF_T_LEN + 2); ! 4331: ! 4332: p = ngx_pnalloc(r->pool, len); ! 4333: if (p == NULL) { ! 4334: return NGX_ERROR; ! 4335: } ! 4336: ! 4337: v->data = p; ! 4338: ! 4339: i = 0; ! 4340: state = r->upstream_states->elts; ! 4341: ! 4342: for ( ;; ) { ! 4343: p = ngx_sprintf(p, "%O", state[i].response_length); ! 4344: ! 4345: if (++i == r->upstream_states->nelts) { ! 4346: break; ! 4347: } ! 4348: ! 4349: if (state[i].peer) { ! 4350: *p++ = ','; ! 4351: *p++ = ' '; ! 4352: ! 4353: } else { ! 4354: *p++ = ' '; ! 4355: *p++ = ':'; ! 4356: *p++ = ' '; ! 4357: ! 4358: if (++i == r->upstream_states->nelts) { ! 4359: break; ! 4360: } ! 4361: ! 4362: continue; ! 4363: } ! 4364: } ! 4365: ! 4366: v->len = p - v->data; ! 4367: ! 4368: return NGX_OK; ! 4369: } ! 4370: ! 4371: ! 4372: ngx_int_t ! 4373: ngx_http_upstream_header_variable(ngx_http_request_t *r, ! 4374: ngx_http_variable_value_t *v, uintptr_t data) ! 4375: { ! 4376: if (r->upstream == NULL) { ! 4377: v->not_found = 1; ! 4378: return NGX_OK; ! 4379: } ! 4380: ! 4381: return ngx_http_variable_unknown_header(v, (ngx_str_t *) data, ! 4382: &r->upstream->headers_in.headers.part, ! 4383: sizeof("upstream_http_") - 1); ! 4384: } ! 4385: ! 4386: ! 4387: #if (NGX_HTTP_CACHE) ! 4388: ! 4389: ngx_int_t ! 4390: ngx_http_upstream_cache_status(ngx_http_request_t *r, ! 4391: ngx_http_variable_value_t *v, uintptr_t data) ! 4392: { ! 4393: ngx_uint_t n; ! 4394: ! 4395: if (r->upstream == NULL || r->upstream->cache_status == 0) { ! 4396: v->not_found = 1; ! 4397: return NGX_OK; ! 4398: } ! 4399: ! 4400: n = r->upstream->cache_status - 1; ! 4401: ! 4402: v->valid = 1; ! 4403: v->no_cacheable = 0; ! 4404: v->not_found = 0; ! 4405: v->len = ngx_http_cache_status[n].len; ! 4406: v->data = ngx_http_cache_status[n].data; ! 4407: ! 4408: return NGX_OK; ! 4409: } ! 4410: ! 4411: #endif ! 4412: ! 4413: ! 4414: static char * ! 4415: ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) ! 4416: { ! 4417: char *rv; ! 4418: void *mconf; ! 4419: ngx_str_t *value; ! 4420: ngx_url_t u; ! 4421: ngx_uint_t m; ! 4422: ngx_conf_t pcf; ! 4423: ngx_http_module_t *module; ! 4424: ngx_http_conf_ctx_t *ctx, *http_ctx; ! 4425: ngx_http_upstream_srv_conf_t *uscf; ! 4426: ! 4427: ngx_memzero(&u, sizeof(ngx_url_t)); ! 4428: ! 4429: value = cf->args->elts; ! 4430: u.host = value[1]; ! 4431: u.no_resolve = 1; ! 4432: u.no_port = 1; ! 4433: ! 4434: uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE ! 4435: |NGX_HTTP_UPSTREAM_WEIGHT ! 4436: |NGX_HTTP_UPSTREAM_MAX_FAILS ! 4437: |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT ! 4438: |NGX_HTTP_UPSTREAM_DOWN ! 4439: |NGX_HTTP_UPSTREAM_BACKUP); ! 4440: if (uscf == NULL) { ! 4441: return NGX_CONF_ERROR; ! 4442: } ! 4443: ! 4444: ! 4445: ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); ! 4446: if (ctx == NULL) { ! 4447: return NGX_CONF_ERROR; ! 4448: } ! 4449: ! 4450: http_ctx = cf->ctx; ! 4451: ctx->main_conf = http_ctx->main_conf; ! 4452: ! 4453: /* the upstream{}'s srv_conf */ ! 4454: ! 4455: ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); ! 4456: if (ctx->srv_conf == NULL) { ! 4457: return NGX_CONF_ERROR; ! 4458: } ! 4459: ! 4460: ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf; ! 4461: ! 4462: uscf->srv_conf = ctx->srv_conf; ! 4463: ! 4464: ! 4465: /* the upstream{}'s loc_conf */ ! 4466: ! 4467: ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); ! 4468: if (ctx->loc_conf == NULL) { ! 4469: return NGX_CONF_ERROR; ! 4470: } ! 4471: ! 4472: for (m = 0; ngx_modules[m]; m++) { ! 4473: if (ngx_modules[m]->type != NGX_HTTP_MODULE) { ! 4474: continue; ! 4475: } ! 4476: ! 4477: module = ngx_modules[m]->ctx; ! 4478: ! 4479: if (module->create_srv_conf) { ! 4480: mconf = module->create_srv_conf(cf); ! 4481: if (mconf == NULL) { ! 4482: return NGX_CONF_ERROR; ! 4483: } ! 4484: ! 4485: ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf; ! 4486: } ! 4487: ! 4488: if (module->create_loc_conf) { ! 4489: mconf = module->create_loc_conf(cf); ! 4490: if (mconf == NULL) { ! 4491: return NGX_CONF_ERROR; ! 4492: } ! 4493: ! 4494: ctx->loc_conf[ngx_modules[m]->ctx_index] = mconf; ! 4495: } ! 4496: } ! 4497: ! 4498: ! 4499: /* parse inside upstream{} */ ! 4500: ! 4501: pcf = *cf; ! 4502: cf->ctx = ctx; ! 4503: cf->cmd_type = NGX_HTTP_UPS_CONF; ! 4504: ! 4505: rv = ngx_conf_parse(cf, NULL); ! 4506: ! 4507: *cf = pcf; ! 4508: ! 4509: if (rv != NGX_CONF_OK) { ! 4510: return rv; ! 4511: } ! 4512: ! 4513: if (uscf->servers == NULL) { ! 4514: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ! 4515: "no servers are inside upstream"); ! 4516: return NGX_CONF_ERROR; ! 4517: } ! 4518: ! 4519: return rv; ! 4520: } ! 4521: ! 4522: ! 4523: static char * ! 4524: ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) ! 4525: { ! 4526: ngx_http_upstream_srv_conf_t *uscf = conf; ! 4527: ! 4528: time_t fail_timeout; ! 4529: ngx_str_t *value, s; ! 4530: ngx_url_t u; ! 4531: ngx_int_t weight, max_fails; ! 4532: ngx_uint_t i; ! 4533: ngx_http_upstream_server_t *us; ! 4534: ! 4535: if (uscf->servers == NULL) { ! 4536: uscf->servers = ngx_array_create(cf->pool, 4, ! 4537: sizeof(ngx_http_upstream_server_t)); ! 4538: if (uscf->servers == NULL) { ! 4539: return NGX_CONF_ERROR; ! 4540: } ! 4541: } ! 4542: ! 4543: us = ngx_array_push(uscf->servers); ! 4544: if (us == NULL) { ! 4545: return NGX_CONF_ERROR; ! 4546: } ! 4547: ! 4548: ngx_memzero(us, sizeof(ngx_http_upstream_server_t)); ! 4549: ! 4550: value = cf->args->elts; ! 4551: ! 4552: ngx_memzero(&u, sizeof(ngx_url_t)); ! 4553: ! 4554: u.url = value[1]; ! 4555: u.default_port = 80; ! 4556: ! 4557: if (ngx_parse_url(cf->pool, &u) != NGX_OK) { ! 4558: if (u.err) { ! 4559: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ! 4560: "%s in upstream \"%V\"", u.err, &u.url); ! 4561: } ! 4562: ! 4563: return NGX_CONF_ERROR; ! 4564: } ! 4565: ! 4566: weight = 1; ! 4567: max_fails = 1; ! 4568: fail_timeout = 10; ! 4569: ! 4570: for (i = 2; i < cf->args->nelts; i++) { ! 4571: ! 4572: if (ngx_strncmp(value[i].data, "weight=", 7) == 0) { ! 4573: ! 4574: if (!(uscf->flags & NGX_HTTP_UPSTREAM_WEIGHT)) { ! 4575: goto invalid; ! 4576: } ! 4577: ! 4578: weight = ngx_atoi(&value[i].data[7], value[i].len - 7); ! 4579: ! 4580: if (weight == NGX_ERROR || weight == 0) { ! 4581: goto invalid; ! 4582: } ! 4583: ! 4584: continue; ! 4585: } ! 4586: ! 4587: if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) { ! 4588: ! 4589: if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) { ! 4590: goto invalid; ! 4591: } ! 4592: ! 4593: max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10); ! 4594: ! 4595: if (max_fails == NGX_ERROR) { ! 4596: goto invalid; ! 4597: } ! 4598: ! 4599: continue; ! 4600: } ! 4601: ! 4602: if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) { ! 4603: ! 4604: if (!(uscf->flags & NGX_HTTP_UPSTREAM_FAIL_TIMEOUT)) { ! 4605: goto invalid; ! 4606: } ! 4607: ! 4608: s.len = value[i].len - 13; ! 4609: s.data = &value[i].data[13]; ! 4610: ! 4611: fail_timeout = ngx_parse_time(&s, 1); ! 4612: ! 4613: if (fail_timeout == (time_t) NGX_ERROR) { ! 4614: goto invalid; ! 4615: } ! 4616: ! 4617: continue; ! 4618: } ! 4619: ! 4620: if (ngx_strncmp(value[i].data, "backup", 6) == 0) { ! 4621: ! 4622: if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) { ! 4623: goto invalid; ! 4624: } ! 4625: ! 4626: us->backup = 1; ! 4627: ! 4628: continue; ! 4629: } ! 4630: ! 4631: if (ngx_strncmp(value[i].data, "down", 4) == 0) { ! 4632: ! 4633: if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) { ! 4634: goto invalid; ! 4635: } ! 4636: ! 4637: us->down = 1; ! 4638: ! 4639: continue; ! 4640: } ! 4641: ! 4642: goto invalid; ! 4643: } ! 4644: ! 4645: us->addrs = u.addrs; ! 4646: us->naddrs = u.naddrs; ! 4647: us->weight = weight; ! 4648: us->max_fails = max_fails; ! 4649: us->fail_timeout = fail_timeout; ! 4650: ! 4651: return NGX_CONF_OK; ! 4652: ! 4653: invalid: ! 4654: ! 4655: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ! 4656: "invalid parameter \"%V\"", &value[i]); ! 4657: ! 4658: return NGX_CONF_ERROR; ! 4659: } ! 4660: ! 4661: ! 4662: ngx_http_upstream_srv_conf_t * ! 4663: ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags) ! 4664: { ! 4665: ngx_uint_t i; ! 4666: ngx_http_upstream_server_t *us; ! 4667: ngx_http_upstream_srv_conf_t *uscf, **uscfp; ! 4668: ngx_http_upstream_main_conf_t *umcf; ! 4669: ! 4670: if (!(flags & NGX_HTTP_UPSTREAM_CREATE)) { ! 4671: ! 4672: if (ngx_parse_url(cf->pool, u) != NGX_OK) { ! 4673: if (u->err) { ! 4674: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ! 4675: "%s in upstream \"%V\"", u->err, &u->url); ! 4676: } ! 4677: ! 4678: return NULL; ! 4679: } ! 4680: } ! 4681: ! 4682: umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module); ! 4683: ! 4684: uscfp = umcf->upstreams.elts; ! 4685: ! 4686: for (i = 0; i < umcf->upstreams.nelts; i++) { ! 4687: ! 4688: if (uscfp[i]->host.len != u->host.len ! 4689: || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len) ! 4690: != 0) ! 4691: { ! 4692: continue; ! 4693: } ! 4694: ! 4695: if ((flags & NGX_HTTP_UPSTREAM_CREATE) ! 4696: && (uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE)) ! 4697: { ! 4698: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ! 4699: "duplicate upstream \"%V\"", &u->host); ! 4700: return NULL; ! 4701: } ! 4702: ! 4703: if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port) { ! 4704: ngx_conf_log_error(NGX_LOG_WARN, cf, 0, ! 4705: "upstream \"%V\" may not have port %d", ! 4706: &u->host, u->port); ! 4707: return NULL; ! 4708: } ! 4709: ! 4710: if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port) { ! 4711: ngx_log_error(NGX_LOG_WARN, cf->log, 0, ! 4712: "upstream \"%V\" may not have port %d in %s:%ui", ! 4713: &u->host, uscfp[i]->port, ! 4714: uscfp[i]->file_name, uscfp[i]->line); ! 4715: return NULL; ! 4716: } ! 4717: ! 4718: if (uscfp[i]->port && u->port ! 4719: && uscfp[i]->port != u->port) ! 4720: { ! 4721: continue; ! 4722: } ! 4723: ! 4724: if (uscfp[i]->default_port && u->default_port ! 4725: && uscfp[i]->default_port != u->default_port) ! 4726: { ! 4727: continue; ! 4728: } ! 4729: ! 4730: if (flags & NGX_HTTP_UPSTREAM_CREATE) { ! 4731: uscfp[i]->flags = flags; ! 4732: } ! 4733: ! 4734: return uscfp[i]; ! 4735: } ! 4736: ! 4737: uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t)); ! 4738: if (uscf == NULL) { ! 4739: return NULL; ! 4740: } ! 4741: ! 4742: uscf->flags = flags; ! 4743: uscf->host = u->host; ! 4744: uscf->file_name = cf->conf_file->file.name.data; ! 4745: uscf->line = cf->conf_file->line; ! 4746: uscf->port = u->port; ! 4747: uscf->default_port = u->default_port; ! 4748: uscf->no_port = u->no_port; ! 4749: ! 4750: if (u->naddrs == 1) { ! 4751: uscf->servers = ngx_array_create(cf->pool, 1, ! 4752: sizeof(ngx_http_upstream_server_t)); ! 4753: if (uscf->servers == NULL) { ! 4754: return NULL; ! 4755: } ! 4756: ! 4757: us = ngx_array_push(uscf->servers); ! 4758: if (us == NULL) { ! 4759: return NULL; ! 4760: } ! 4761: ! 4762: ngx_memzero(us, sizeof(ngx_http_upstream_server_t)); ! 4763: ! 4764: us->addrs = u->addrs; ! 4765: us->naddrs = 1; ! 4766: } ! 4767: ! 4768: uscfp = ngx_array_push(&umcf->upstreams); ! 4769: if (uscfp == NULL) { ! 4770: return NULL; ! 4771: } ! 4772: ! 4773: *uscfp = uscf; ! 4774: ! 4775: return uscf; ! 4776: } ! 4777: ! 4778: ! 4779: char * ! 4780: ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, ! 4781: void *conf) ! 4782: { ! 4783: char *p = conf; ! 4784: ! 4785: ngx_int_t rc; ! 4786: ngx_str_t *value; ! 4787: ngx_http_complex_value_t cv; ! 4788: ngx_http_upstream_local_t **plocal, *local; ! 4789: ngx_http_compile_complex_value_t ccv; ! 4790: ! 4791: plocal = (ngx_http_upstream_local_t **) (p + cmd->offset); ! 4792: ! 4793: if (*plocal != NGX_CONF_UNSET_PTR) { ! 4794: return "is duplicate"; ! 4795: } ! 4796: ! 4797: value = cf->args->elts; ! 4798: ! 4799: if (ngx_strcmp(value[1].data, "off") == 0) { ! 4800: *plocal = NULL; ! 4801: return NGX_CONF_OK; ! 4802: } ! 4803: ! 4804: ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ! 4805: ! 4806: ccv.cf = cf; ! 4807: ccv.value = &value[1]; ! 4808: ccv.complex_value = &cv; ! 4809: ! 4810: if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { ! 4811: return NGX_CONF_ERROR; ! 4812: } ! 4813: ! 4814: local = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_local_t)); ! 4815: if (local == NULL) { ! 4816: return NGX_CONF_ERROR; ! 4817: } ! 4818: ! 4819: *plocal = local; ! 4820: ! 4821: if (cv.lengths) { ! 4822: local->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); ! 4823: if (local->value == NULL) { ! 4824: return NGX_CONF_ERROR; ! 4825: } ! 4826: ! 4827: *local->value = cv; ! 4828: ! 4829: return NGX_CONF_OK; ! 4830: } ! 4831: ! 4832: local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t)); ! 4833: if (local->addr == NULL) { ! 4834: return NGX_CONF_ERROR; ! 4835: } ! 4836: ! 4837: rc = ngx_parse_addr(cf->pool, local->addr, value[1].data, value[1].len); ! 4838: ! 4839: switch (rc) { ! 4840: case NGX_OK: ! 4841: local->addr->name = value[1]; ! 4842: return NGX_CONF_OK; ! 4843: ! 4844: case NGX_DECLINED: ! 4845: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ! 4846: "invalid address \"%V\"", &value[1]); ! 4847: /* fall through */ ! 4848: ! 4849: default: ! 4850: return NGX_CONF_ERROR; ! 4851: } ! 4852: } ! 4853: ! 4854: ! 4855: static ngx_addr_t * ! 4856: ngx_http_upstream_get_local(ngx_http_request_t *r, ! 4857: ngx_http_upstream_local_t *local) ! 4858: { ! 4859: ngx_int_t rc; ! 4860: ngx_str_t val; ! 4861: ngx_addr_t *addr; ! 4862: ! 4863: if (local == NULL) { ! 4864: return NULL; ! 4865: } ! 4866: ! 4867: if (local->value == NULL) { ! 4868: return local->addr; ! 4869: } ! 4870: ! 4871: if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) { ! 4872: return NULL; ! 4873: } ! 4874: ! 4875: if (val.len == 0) { ! 4876: return NULL; ! 4877: } ! 4878: ! 4879: addr = ngx_palloc(r->pool, sizeof(ngx_addr_t)); ! 4880: if (addr == NULL) { ! 4881: return NULL; ! 4882: } ! 4883: ! 4884: rc = ngx_parse_addr(r->pool, addr, val.data, val.len); ! 4885: ! 4886: switch (rc) { ! 4887: case NGX_OK: ! 4888: addr->name = val; ! 4889: return addr; ! 4890: ! 4891: case NGX_DECLINED: ! 4892: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, ! 4893: "invalid local address \"%V\"", &val); ! 4894: /* fall through */ ! 4895: ! 4896: default: ! 4897: return NULL; ! 4898: } ! 4899: } ! 4900: ! 4901: ! 4902: char * ! 4903: ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, ! 4904: void *conf) ! 4905: { ! 4906: char *p = conf; ! 4907: ! 4908: ngx_str_t *value; ! 4909: ngx_array_t **a; ! 4910: ngx_http_upstream_param_t *param; ! 4911: ! 4912: a = (ngx_array_t **) (p + cmd->offset); ! 4913: ! 4914: if (*a == NULL) { ! 4915: *a = ngx_array_create(cf->pool, 4, sizeof(ngx_http_upstream_param_t)); ! 4916: if (*a == NULL) { ! 4917: return NGX_CONF_ERROR; ! 4918: } ! 4919: } ! 4920: ! 4921: param = ngx_array_push(*a); ! 4922: if (param == NULL) { ! 4923: return NGX_CONF_ERROR; ! 4924: } ! 4925: ! 4926: value = cf->args->elts; ! 4927: ! 4928: param->key = value[1]; ! 4929: param->value = value[2]; ! 4930: param->skip_empty = 0; ! 4931: ! 4932: if (cf->args->nelts == 4) { ! 4933: if (ngx_strcmp(value[3].data, "if_not_empty") != 0) { ! 4934: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, ! 4935: "invalid parameter \"%V\"", &value[3]); ! 4936: return NGX_CONF_ERROR; ! 4937: } ! 4938: ! 4939: param->skip_empty = 1; ! 4940: } ! 4941: ! 4942: return NGX_CONF_OK; ! 4943: } ! 4944: ! 4945: ! 4946: ngx_int_t ! 4947: ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf, ! 4948: ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev, ! 4949: ngx_str_t *default_hide_headers, ngx_hash_init_t *hash) ! 4950: { ! 4951: ngx_str_t *h; ! 4952: ngx_uint_t i, j; ! 4953: ngx_array_t hide_headers; ! 4954: ngx_hash_key_t *hk; ! 4955: ! 4956: if (conf->hide_headers == NGX_CONF_UNSET_PTR ! 4957: && conf->pass_headers == NGX_CONF_UNSET_PTR) ! 4958: { ! 4959: conf->hide_headers = prev->hide_headers; ! 4960: conf->pass_headers = prev->pass_headers; ! 4961: ! 4962: conf->hide_headers_hash = prev->hide_headers_hash; ! 4963: ! 4964: if (conf->hide_headers_hash.buckets ! 4965: #if (NGX_HTTP_CACHE) ! 4966: && ((conf->cache == NULL) == (prev->cache == NULL)) ! 4967: #endif ! 4968: ) ! 4969: { ! 4970: return NGX_OK; ! 4971: } ! 4972: ! 4973: } else { ! 4974: if (conf->hide_headers == NGX_CONF_UNSET_PTR) { ! 4975: conf->hide_headers = prev->hide_headers; ! 4976: } ! 4977: ! 4978: if (conf->pass_headers == NGX_CONF_UNSET_PTR) { ! 4979: conf->pass_headers = prev->pass_headers; ! 4980: } ! 4981: } ! 4982: ! 4983: if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t)) ! 4984: != NGX_OK) ! 4985: { ! 4986: return NGX_ERROR; ! 4987: } ! 4988: ! 4989: for (h = default_hide_headers; h->len; h++) { ! 4990: hk = ngx_array_push(&hide_headers); ! 4991: if (hk == NULL) { ! 4992: return NGX_ERROR; ! 4993: } ! 4994: ! 4995: hk->key = *h; ! 4996: hk->key_hash = ngx_hash_key_lc(h->data, h->len); ! 4997: hk->value = (void *) 1; ! 4998: } ! 4999: ! 5000: if (conf->hide_headers != NGX_CONF_UNSET_PTR) { ! 5001: ! 5002: h = conf->hide_headers->elts; ! 5003: ! 5004: for (i = 0; i < conf->hide_headers->nelts; i++) { ! 5005: ! 5006: hk = hide_headers.elts; ! 5007: ! 5008: for (j = 0; j < hide_headers.nelts; j++) { ! 5009: if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) { ! 5010: goto exist; ! 5011: } ! 5012: } ! 5013: ! 5014: hk = ngx_array_push(&hide_headers); ! 5015: if (hk == NULL) { ! 5016: return NGX_ERROR; ! 5017: } ! 5018: ! 5019: hk->key = h[i]; ! 5020: hk->key_hash = ngx_hash_key_lc(h[i].data, h[i].len); ! 5021: hk->value = (void *) 1; ! 5022: ! 5023: exist: ! 5024: ! 5025: continue; ! 5026: } ! 5027: } ! 5028: ! 5029: if (conf->pass_headers != NGX_CONF_UNSET_PTR) { ! 5030: ! 5031: h = conf->pass_headers->elts; ! 5032: hk = hide_headers.elts; ! 5033: ! 5034: for (i = 0; i < conf->pass_headers->nelts; i++) { ! 5035: for (j = 0; j < hide_headers.nelts; j++) { ! 5036: ! 5037: if (hk[j].key.data == NULL) { ! 5038: continue; ! 5039: } ! 5040: ! 5041: if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) { ! 5042: hk[j].key.data = NULL; ! 5043: break; ! 5044: } ! 5045: } ! 5046: } ! 5047: } ! 5048: ! 5049: hash->hash = &conf->hide_headers_hash; ! 5050: hash->key = ngx_hash_key_lc; ! 5051: hash->pool = cf->pool; ! 5052: hash->temp_pool = NULL; ! 5053: ! 5054: return ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts); ! 5055: } ! 5056: ! 5057: ! 5058: static void * ! 5059: ngx_http_upstream_create_main_conf(ngx_conf_t *cf) ! 5060: { ! 5061: ngx_http_upstream_main_conf_t *umcf; ! 5062: ! 5063: umcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_main_conf_t)); ! 5064: if (umcf == NULL) { ! 5065: return NULL; ! 5066: } ! 5067: ! 5068: if (ngx_array_init(&umcf->upstreams, cf->pool, 4, ! 5069: sizeof(ngx_http_upstream_srv_conf_t *)) ! 5070: != NGX_OK) ! 5071: { ! 5072: return NULL; ! 5073: } ! 5074: ! 5075: return umcf; ! 5076: } ! 5077: ! 5078: ! 5079: static char * ! 5080: ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf) ! 5081: { ! 5082: ngx_http_upstream_main_conf_t *umcf = conf; ! 5083: ! 5084: ngx_uint_t i; ! 5085: ngx_array_t headers_in; ! 5086: ngx_hash_key_t *hk; ! 5087: ngx_hash_init_t hash; ! 5088: ngx_http_upstream_init_pt init; ! 5089: ngx_http_upstream_header_t *header; ! 5090: ngx_http_upstream_srv_conf_t **uscfp; ! 5091: ! 5092: uscfp = umcf->upstreams.elts; ! 5093: ! 5094: for (i = 0; i < umcf->upstreams.nelts; i++) { ! 5095: ! 5096: init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream: ! 5097: ngx_http_upstream_init_round_robin; ! 5098: ! 5099: if (init(cf, uscfp[i]) != NGX_OK) { ! 5100: return NGX_CONF_ERROR; ! 5101: } ! 5102: } ! 5103: ! 5104: ! 5105: /* upstream_headers_in_hash */ ! 5106: ! 5107: if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t)) ! 5108: != NGX_OK) ! 5109: { ! 5110: return NGX_CONF_ERROR; ! 5111: } ! 5112: ! 5113: for (header = ngx_http_upstream_headers_in; header->name.len; header++) { ! 5114: hk = ngx_array_push(&headers_in); ! 5115: if (hk == NULL) { ! 5116: return NGX_CONF_ERROR; ! 5117: } ! 5118: ! 5119: hk->key = header->name; ! 5120: hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len); ! 5121: hk->value = header; ! 5122: } ! 5123: ! 5124: hash.hash = &umcf->headers_in_hash; ! 5125: hash.key = ngx_hash_key_lc; ! 5126: hash.max_size = 512; ! 5127: hash.bucket_size = ngx_align(64, ngx_cacheline_size); ! 5128: hash.name = "upstream_headers_in_hash"; ! 5129: hash.pool = cf->pool; ! 5130: hash.temp_pool = NULL; ! 5131: ! 5132: if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) { ! 5133: return NGX_CONF_ERROR; ! 5134: } ! 5135: ! 5136: return NGX_CONF_OK; ! 5137: }