static int cgi_recv_response(server *srv, handler_ctx *hctx) { switch (http_response_read(srv, hctx->remote_conn, &hctx->opts, hctx->response, hctx->fdn)) { default: return HANDLER_GO_ON; case HANDLER_ERROR: http_response_backend_error(srv, hctx->remote_conn); /* fall through */ case HANDLER_FINISHED: cgi_connection_close(srv, hctx); return HANDLER_FINISHED; case HANDLER_COMEBACK: /* hctx->conf.local_redir */ buffer_clear(hctx->response); connection_response_reset(srv, hctx->remote_conn); /*(includes con->http_status = 0)*/ plugins_call_connection_reset(srv, hctx->remote_conn); /*cgi_connection_close(srv, hctx);*//*(already cleaned up and hctx is now invalid)*/ return HANDLER_COMEBACK; } }
int connection_reset(server *srv, connection *con) { plugins_call_connection_reset(srv, con); connection_response_reset(srv, con); con->is_readable = 1; con->bytes_written = 0; con->bytes_written_cur_second = 0; con->bytes_read = 0; con->bytes_header = 0; con->loops_per_request = 0; con->request.http_method = HTTP_METHOD_UNSET; con->request.http_version = HTTP_VERSION_UNSET; con->request.http_if_modified_since = NULL; con->request.http_if_none_match = NULL; #define CLEAN(x) \ if (con->x) buffer_reset(con->x); CLEAN(request.uri); CLEAN(request.request_line); CLEAN(request.pathinfo); CLEAN(request.request); /* CLEAN(request.orig_uri); */ CLEAN(uri.scheme); /* CLEAN(uri.authority); */ /* CLEAN(uri.path); */ CLEAN(uri.path_raw); /* CLEAN(uri.query); */ CLEAN(parse_request); CLEAN(server_name); #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT CLEAN(tlsext_server_name); #endif #undef CLEAN #define CLEAN(x) \ if (con->x) con->x->used = 0; #undef CLEAN #define CLEAN(x) \ con->request.x = NULL; CLEAN(http_host); CLEAN(http_range); CLEAN(http_content_type); #undef CLEAN con->request.content_length = 0; con->request.te_chunked = 0; array_reset(con->request.headers); array_reset(con->environment); chunkqueue_reset(con->request_content_queue); /* The cond_cache gets reset in response.c */ /* config_cond_cache_reset(srv, con); */ con->header_len = 0; con->error_handler_saved_status = 0; /*con->error_handler_saved_method = HTTP_METHOD_UNSET;*/ /*(error_handler_saved_method value is not valid unless error_handler_saved_status is set)*/ config_setup_connection(srv, con); return 0; }
int connection_state_machine(server *srv, connection *con) { int done = 0, r; if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at start", con->fd, connection_get_state(con->state)); } while (done == 0) { size_t ostate = con->state; if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } switch (con->state) { case CON_STATE_REQUEST_START: /* transient */ con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; if (con->conf.high_precision_timestamps) log_clock_gettime_realtime(&con->request_start_hp); con->request_count++; con->loops_per_request = 0; connection_set_state(srv, con, CON_STATE_READ); break; case CON_STATE_REQUEST_END: /* transient */ buffer_reset(con->uri.authority); buffer_reset(con->uri.path); buffer_reset(con->uri.query); buffer_reset(con->request.orig_uri); if (http_request_parse(srv, con)) { /* we have to read some data from the POST request */ connection_set_state(srv, con, CON_STATE_READ_POST); break; } connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case CON_STATE_READ_POST: case CON_STATE_HANDLE_REQUEST: /* * the request is parsed * * decided what to do with the request * - * * */ switch (r = http_response_prepare(srv, con)) { case HANDLER_WAIT_FOR_EVENT: if (!con->file_finished && (!con->file_started || 0 == con->conf.stream_response_body)) { break; /* come back here */ } /* response headers received from backend; fall through to start response */ case HANDLER_FINISHED: if (con->error_handler_saved_status > 0) { con->request.http_method = con->error_handler_saved_method; } if (con->mode == DIRECT) { if (con->error_handler_saved_status) { if (con->error_handler_saved_status > 0) { con->http_status = con->error_handler_saved_status; } else if (con->http_status == 404 || con->http_status == 403) { /* error-handler-404 is a 404 */ con->http_status = -con->error_handler_saved_status; } else { /* error-handler-404 is back and has generated content */ /* if Status: was set, take it otherwise use 200 */ } } else if (con->http_status >= 400) { buffer *error_handler = NULL; if (!buffer_string_is_empty(con->conf.error_handler)) { error_handler = con->conf.error_handler; } else if ((con->http_status == 404 || con->http_status == 403) && !buffer_string_is_empty(con->conf.error_handler_404)) { error_handler = con->conf.error_handler_404; } if (error_handler) { /* call error-handler */ /* set REDIRECT_STATUS to save current HTTP status code * for access by dynamic handlers * https://redmine.lighttpd.net/issues/1828 */ data_string *ds; if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string_len(ds->key, CONST_STR_LEN("REDIRECT_STATUS")); buffer_append_int(ds->value, con->http_status); array_insert_unique(con->environment, (data_unset *)ds); if (error_handler == con->conf.error_handler) { plugins_call_connection_reset(srv, con); if (con->request.content_length) { if (con->request.content_length != con->request_content_queue->bytes_in) { con->keep_alive = 0; } con->request.content_length = 0; chunkqueue_reset(con->request_content_queue); } con->is_writable = 1; con->file_finished = 0; con->file_started = 0; con->got_response = 0; con->parsed_response = 0; con->response.keep_alive = 0; con->response.content_length = -1; con->response.transfer_encoding = 0; con->error_handler_saved_status = con->http_status; con->error_handler_saved_method = con->request.http_method; con->request.http_method = HTTP_METHOD_GET; } else { /*(preserve behavior for server.error-handler-404)*/ con->error_handler_saved_status = -con->http_status; /*(negative to flag old behavior)*/ } buffer_copy_buffer(con->request.uri, error_handler); connection_handle_errdoc_init(srv, con); con->http_status = 0; /*(after connection_handle_errdoc_init())*/ done = -1; break; } } } if (con->http_status == 0) con->http_status = 200; /* we have something to send, go on */ connection_set_state(srv, con, CON_STATE_RESPONSE_START); break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; fdwaitqueue_append(srv, con); break; case HANDLER_COMEBACK: done = -1; break; case HANDLER_ERROR: /* something went wrong */ connection_set_state(srv, con, CON_STATE_ERROR); break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown ret-value: ", con->fd, r); break; } if (con->state == CON_STATE_HANDLE_REQUEST && ostate == CON_STATE_READ_POST) { ostate = CON_STATE_HANDLE_REQUEST; } break; case CON_STATE_RESPONSE_START: /* * the decision is done * - create the HTTP-Response-Header * */ if (-1 == connection_handle_write_prepare(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); break; } connection_set_state(srv, con, CON_STATE_WRITE); break; case CON_STATE_RESPONSE_END: /* transient */ case CON_STATE_ERROR: /* transient */ connection_handle_response_end_state(srv, con); break; case CON_STATE_CONNECT: chunkqueue_reset(con->read_queue); con->request_count = 0; break; case CON_STATE_CLOSE: connection_handle_close_state(srv, con); break; case CON_STATE_READ: connection_handle_read_state(srv, con); break; case CON_STATE_WRITE: do { /* only try to write if we have something in the queue */ if (!chunkqueue_is_empty(con->write_queue)) { if (con->is_writable) { if (-1 == connection_handle_write(srv, con)) { log_error_write(srv, __FILE__, __LINE__, "ds", con->fd, "handle write failed."); connection_set_state(srv, con, CON_STATE_ERROR); break; } if (con->state != CON_STATE_WRITE) break; } } else if (con->file_finished) { connection_set_state(srv, con, CON_STATE_RESPONSE_END); break; } if (con->mode != DIRECT && !con->file_finished) { switch(r = plugins_call_handle_subrequest(srv, con)) { case HANDLER_WAIT_FOR_EVENT: case HANDLER_FINISHED: case HANDLER_GO_ON: break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; fdwaitqueue_append(srv, con); break; case HANDLER_COMEBACK: default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unexpected subrequest handler ret-value: ", con->fd, r); /* fall through */ case HANDLER_ERROR: connection_set_state(srv, con, CON_STATE_ERROR); break; } } } while (con->state == CON_STATE_WRITE && (!chunkqueue_is_empty(con->write_queue) ? con->is_writable : con->file_finished)); break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown state:", con->fd, con->state); break; } if (done == -1) { done = 0; } else if (ostate == con->state) { done = 1; } } if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at exit:", con->fd, connection_get_state(con->state)); } r = 0; switch(con->state) { case CON_STATE_READ: case CON_STATE_CLOSE: r = FDEVENT_IN; break; case CON_STATE_WRITE: /* request write-fdevent only if we really need it * - if we have data to write * - if the socket is not writable yet */ if (!chunkqueue_is_empty(con->write_queue) && (con->is_writable == 0) && (con->traffic_limit_reached == 0)) { r |= FDEVENT_OUT; } /* fall through */ case CON_STATE_READ_POST: if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN) { r |= FDEVENT_IN; } break; default: break; } if (-1 != con->fd) { const int events = fdevent_event_get_interest(srv->ev, con->fd); if (con->is_readable < 0) { con->is_readable = 0; r |= FDEVENT_IN; } if (con->is_writable < 0) { con->is_writable = 0; r |= FDEVENT_OUT; } if (r != events) { /* update timestamps when enabling interest in events */ if ((r & FDEVENT_IN) && !(events & FDEVENT_IN)) { con->read_idle_ts = srv->cur_ts; } if ((r & FDEVENT_OUT) && !(events & FDEVENT_OUT)) { con->write_request_ts = srv->cur_ts; } fdevent_event_set(srv->ev, &con->fde_ndx, con->fd, r); } } return 0; }
int connection_reset(server *srv, connection *con) { size_t i; plugins_call_connection_reset(srv, con); con->is_readable = 1; con->is_writable = 1; con->http_status = 0; con->file_finished = 0; con->file_started = 0; con->got_response = 0; con->parsed_response = 0; con->bytes_written = 0; con->bytes_written_cur_second = 0; con->bytes_read = 0; con->bytes_header = 0; con->loops_per_request = 0; con->request.http_method = HTTP_METHOD_UNSET; con->request.http_version = HTTP_VERSION_UNSET; con->request.http_if_modified_since = NULL; con->request.http_if_none_match = NULL; con->response.keep_alive = 0; con->response.content_length = -1; con->response.transfer_encoding = 0; con->mode = DIRECT; #define CLEAN(x) \ if (con->x) buffer_reset(con->x); CLEAN(request.uri); CLEAN(request.request_line); CLEAN(request.pathinfo); CLEAN(request.request); /* CLEAN(request.orig_uri); */ CLEAN(uri.scheme); /* CLEAN(uri.authority); */ /* CLEAN(uri.path); */ CLEAN(uri.path_raw); /* CLEAN(uri.query); */ CLEAN(physical.doc_root); CLEAN(physical.path); CLEAN(physical.basedir); CLEAN(physical.rel_path); CLEAN(physical.etag); CLEAN(parse_request); CLEAN(server_name); CLEAN(error_handler); #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT CLEAN(tlsext_server_name); #endif #undef CLEAN #define CLEAN(x) \ if (con->x) con->x->used = 0; #undef CLEAN #define CLEAN(x) \ con->request.x = NULL; CLEAN(http_host); CLEAN(http_range); CLEAN(http_content_type); #undef CLEAN con->request.content_length = 0; array_reset(con->request.headers); array_reset(con->response.headers); array_reset(con->environment); chunkqueue_reset(con->write_queue); chunkqueue_reset(con->request_content_queue); /* the plugins should cleanup themself */ for (i = 0; i < srv->plugins.used; i++) { plugin *p = ((plugin **)(srv->plugins.ptr))[i]; plugin_data *pd = p->data; if (!pd) continue; if (con->plugin_ctx[pd->id] != NULL) { log_error_write(srv, __FILE__, __LINE__, "sb", "missing cleanup in", p->name); } con->plugin_ctx[pd->id] = NULL; } /* The cond_cache gets reset in response.c */ /* config_cond_cache_reset(srv, con); */ con->header_len = 0; con->in_error_handler = 0; config_setup_connection(srv, con); return 0; }