Browse Source

[core] stream response to client (#949)

This replaces buffering entire response prior to sending data to client

x-ref:
  "fastcgi, cgi, flush, php5 problem."
  https://redmine.lighttpd.net/issues/949
personal/stbuehler/mod-csrf-old
Glenn Strauss 6 years ago
parent
commit
53f550b290
  1. 51
      src/connections.c
  2. 23
      src/http-header-glue.c
  3. 17
      src/mod_cgi.c
  4. 18
      src/mod_fastcgi.c
  5. 18
      src/mod_proxy.c
  6. 18
      src/mod_scgi.c
  7. 1
      src/response.h

51
src/connections.c

@ -1036,6 +1036,9 @@ int connection_state_machine(server *srv, connection *con) {
}
switch (r = http_response_prepare(srv, con)) {
case HANDLER_WAIT_FOR_EVENT:
if (!con->file_started) 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;
@ -1126,9 +1129,6 @@ int connection_state_machine(server *srv, connection *con) {
break;
case HANDLER_COMEBACK:
done = -1;
/* fallthrough */
case HANDLER_WAIT_FOR_EVENT:
/* come back here */
break;
case HANDLER_ERROR:
/* something went wrong */
@ -1278,19 +1278,44 @@ int connection_state_machine(server *srv, connection *con) {
"state for fd", con->fd, connection_get_state(con->state));
}
/* 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.");
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;
}
}
} else if (con->file_finished) {
connection_set_state(srv, con, CON_STATE_RESPONSE_END);
}
} while (con->state == CON_STATE_WRITE && (!chunkqueue_is_empty(con->write_queue) ? con->is_writable : con->file_finished));
break;
case CON_STATE_ERROR: /* transient */

23
src/http-header-glue.c

@ -726,3 +726,26 @@ void http_response_xsendfile (server *srv, connection *con, buffer *path, const
con->http_status = status;
}
}
void http_response_backend_done (server *srv, connection *con) {
/* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
* i.e. not called from handle_connection_close or connection_reset
* hooks, except maybe from errdoc handler, which later resets state)*/
switch (con->state) {
case CON_STATE_HANDLE_REQUEST:
case CON_STATE_READ_POST:
if (!con->file_started) {
/* Send an error if we haven't sent any data yet */
con->http_status = 500;
con->mode = DIRECT;
break;
} /* else fall through */
case CON_STATE_WRITE:
if (!con->file_finished) {
http_chunk_close(srv, con);
con->file_finished = 1;
}
default:
break;
}
}

17
src/mod_cgi.c

@ -662,20 +662,9 @@ static void cgi_connection_close(server *srv, handler_ctx *hctx) {
}
#endif
/* finish response (if not already finished) */
if (con->mode == p->id
&& (con->state == CON_STATE_HANDLE_REQUEST || con->state == CON_STATE_READ_POST)) {
/* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
* i.e. not called from cgi_connection_close_callback()) */
/* Send an error if we haven't sent any data yet */
if (0 == con->file_started) {
con->http_status = 500;
con->mode = DIRECT;
} else if (0 == con->file_finished) {
http_chunk_close(srv, con);
con->file_finished = 1;
}
/* finish response (if not already con->file_started, con->file_finished) */
if (con->mode == p->id) {
http_response_backend_done(srv, con);
}
}

18
src/mod_fastcgi.c

@ -1585,21 +1585,9 @@ static void fcgi_connection_close(server *srv, handler_ctx *hctx) {
handler_ctx_free(srv, hctx);
con->plugin_ctx[p->id] = NULL;
/* finish response (if not already finished) */
if (con->mode == p->id
&& (con->state == CON_STATE_HANDLE_REQUEST || con->state == CON_STATE_READ_POST)) {
/* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
* i.e. not called from fcgi_connection_reset()) */
/* Send an error if we haven't sent any data yet */
if (0 == con->file_started) {
con->http_status = 500;
con->mode = DIRECT;
}
else if (!con->file_finished) {
http_chunk_close(srv, con);
con->file_finished = 1;
}
/* finish response (if not already con->file_started, con->file_finished) */
if (con->mode == p->id) {
http_response_backend_done(srv, con);
}
}

18
src/mod_proxy.c

@ -351,21 +351,9 @@ static void proxy_connection_close(server *srv, handler_ctx *hctx) {
handler_ctx_free(hctx);
con->plugin_ctx[p->id] = NULL;
/* finish response (if not already finished) */
if (con->mode == p->id
&& (con->state == CON_STATE_HANDLE_REQUEST || con->state == CON_STATE_READ_POST)) {
/* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
* i.e. not called from proxy_connection_reset()) */
/* Send an error if we haven't sent any data yet */
if (0 == con->file_started) {
con->http_status = 500;
con->mode = DIRECT;
}
else if (!con->file_finished) {
http_chunk_close(srv, con);
con->file_finished = 1;
}
/* finish response (if not already con->file_started, con->file_finished) */
if (con->mode == p->id) {
http_response_backend_done(srv, con);
}
}

18
src/mod_scgi.c

@ -1336,21 +1336,9 @@ static void scgi_connection_close(server *srv, handler_ctx *hctx) {
handler_ctx_free(hctx);
con->plugin_ctx[p->id] = NULL;
/* finish response (if not already finished) */
if (con->mode == p->id
&& (con->state == CON_STATE_HANDLE_REQUEST || con->state == CON_STATE_READ_POST)) {
/* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
* i.e. not called from scgi_connection_reset()) */
/* Send an error if we haven't sent any data yet */
if (0 == con->file_started) {
con->http_status = 500;
con->mode = DIRECT;
}
else if (!con->file_finished) {
http_chunk_close(srv, con);
con->file_finished = 1;
}
/* finish response (if not already con->file_started, con->file_finished) */
if (con->mode == p->id) {
http_response_backend_done(srv, con);
}
}

1
src/response.h

@ -18,6 +18,7 @@ int http_response_redirect_to_directory(server *srv, connection *con);
int http_response_handle_cachable(server *srv, connection *con, buffer * mtime);
void http_response_send_file (server *srv, connection *con, buffer *path);
void http_response_xsendfile (server *srv, connection *con, buffer *path, const array *xdocroot);
void http_response_backend_done (server *srv, connection *con);
buffer * strftime_cache_get(server *srv, time_t last_mod);
#endif

Loading…
Cancel
Save