static filter_lua_state* filter_lua_state_new(liVRequest *vr, filter_lua_config *config) { int object_ref = LUA_NOREF; liServer *srv = vr->wrk->srv; lua_State *L = config->LL->L; li_lua_lock(config->LL); lua_rawgeti(L, LUA_REGISTRYINDEX, config->class_ref); /* +1 */ li_lua_push_vrequest(L, vr); /* +1 */ if (li_lua_call_object(srv, vr, L, "new", 2, 1, FALSE)) { /* -2, +1 on success */ if (!lua_isnil(L, -1)) { object_ref = luaL_ref(L, LUA_REGISTRYINDEX); /* -1 */ } else { /* no error; nil is interpreted as "don't need this filter for this request" */ lua_pop(L, 1); /* -1 */ } } else { VR_ERROR(vr, "%s", "li_lua_call_object failed"); li_vrequest_error(vr); } li_lua_unlock(config->LL); if (LUA_NOREF != object_ref) { filter_lua_state *state = g_slice_new0(filter_lua_state); state->LL = config->LL; state->object_ref = object_ref; return state; } else { return NULL; } }
static void stream_http_response_data(liStreamHttpResponse* shr) { if (NULL == shr->stream.source) return; if (!shr->response_headers_finished) { switch (li_http_response_parse(shr->vr, &shr->parse_response_ctx)) { case LI_HANDLER_GO_ON: check_response_header(shr); if (NULL == shr->stream.source) return; break; case LI_HANDLER_ERROR: VR_ERROR(shr->vr, "%s", "Parsing response header failed"); li_vrequest_error(shr->vr); return; case LI_HANDLER_WAIT_FOR_EVENT: if (shr->stream.source->out->is_closed) { VR_ERROR(shr->vr, "%s", "Parsing response header failed (eos)"); li_vrequest_error(shr->vr); } return; default: return; } } if (shr->transfer_encoding_chunked) { if (!li_filter_chunked_decode(shr->vr, shr->stream.out, shr->stream.source->out, &shr->chunked_decode_state)) { if (NULL != shr->vr) { VR_ERROR(shr->vr, "%s", "Decoding chunks failed"); li_vrequest_error(shr->vr); } else { li_stream_reset(&shr->stream); } } if (shr->stream.source->out->is_closed) { li_stream_disconnect(&shr->stream); } } else { li_chunkqueue_steal_all(shr->stream.out, shr->stream.source->out); if (shr->stream.source->out->is_closed) { shr->stream.out->is_closed = TRUE; li_stream_disconnect(&shr->stream); } } li_stream_notify(&shr->stream); }
static gboolean fastcgi_parse_response(fastcgi_connection *fcon) { liVRequest *vr = fcon->vr; liPlugin *p = fcon->ctx->plugin; gint len; while (fastcgi_get_packet(fcon)) { if (fcon->fcgi_in_record.version != FCGI_VERSION_1) { VR_ERROR(vr, "(%s) Unknown fastcgi protocol version %i", fcon->ctx->socket_str->str, (gint) fcon->fcgi_in_record.version); close(fcon->fd); fcon->fd = -1; li_vrequest_error(vr); return FALSE; } switch (fcon->fcgi_in_record.type) { case FCGI_END_REQUEST: li_chunkqueue_skip(fcon->fcgi_in, fastcgi_available(fcon)); fcon->stdout->is_closed = TRUE; break; case FCGI_STDOUT: if (0 == fcon->fcgi_in_record.contentLength) { fcon->stdout->is_closed = TRUE; } else { li_chunkqueue_steal_len(fcon->stdout, fcon->fcgi_in, fastcgi_available(fcon)); } break; case FCGI_STDERR: len = fastcgi_available(fcon); li_chunkqueue_extract_to(vr, fcon->fcgi_in, len, vr->wrk->tmp_str); if (OPTION(FASTCGI_OPTION_LOG_PLAIN_ERRORS).boolean) { li_log_split_lines(vr->wrk->srv, vr, LI_LOG_LEVEL_BACKEND, 0, vr->wrk->tmp_str->str, ""); } else { VR_BACKEND_LINES(vr, vr->wrk->tmp_str->str, "(fcgi-stderr %s) ", fcon->ctx->socket_str->str); } li_chunkqueue_skip(fcon->fcgi_in, len); break; default: if (fcon->fcgi_in_record.first) VR_WARNING(vr, "(%s) Unhandled fastcgi record type %i", fcon->ctx->socket_str->str, (gint) fcon->fcgi_in_record.type); li_chunkqueue_skip(fcon->fcgi_in, fastcgi_available(fcon)); break; } fcon->fcgi_in_record.first = FALSE; } return TRUE; }
static void check_response_header(liStreamHttpResponse* shr) { liResponse *resp = &shr->vr->response; GList *l; shr->transfer_encoding_chunked = FALSE; /* Transfer-Encoding: chunked */ l = li_http_header_find_first(resp->headers, CONST_STR_LEN("transfer-encoding")); if (l) { for ( ; l ; l = li_http_header_find_next(l, CONST_STR_LEN("transfer-encoding")) ) { liHttpHeader *hh = (liHttpHeader*) l->data; if (0 == g_ascii_strcasecmp( LI_HEADER_VALUE(hh), "identity" )) { /* ignore */ continue; } if (0 == g_ascii_strcasecmp( LI_HEADER_VALUE(hh), "chunked" )) { if (shr->transfer_encoding_chunked) { VR_ERROR(shr->vr, "%s", "Response is chunked encoded twice"); li_vrequest_error(shr->vr); return; } shr->transfer_encoding_chunked = TRUE; } else { VR_ERROR(shr->vr, "Response has unsupported Transfer-Encoding: %s", LI_HEADER_VALUE(hh)); li_vrequest_error(shr->vr); return; } } li_http_header_remove(resp->headers, CONST_STR_LEN("transfer-encoding")); /* any non trivial transfer-encoding overwrites content-length */ if (shr->transfer_encoding_chunked) { li_http_header_remove(resp->headers, CONST_STR_LEN("content-length")); } } /* Upgrade: */ l = li_http_header_find_first(resp->headers, CONST_STR_LEN("upgrade")); if (l) { gboolean have_connection_upgrade = FALSE; liHttpHeaderTokenizer header_tokenizer; GString *token; if (101 != resp->http_status) { VR_ERROR(shr->vr, "Upgrade but status is %i instead of 101 'Switching Protocols'", resp->http_status); li_vrequest_error(shr->vr); return; } if (shr->transfer_encoding_chunked) { VR_ERROR(shr->vr, "%s", "Upgrade with Transfer-Encoding: chunked"); li_vrequest_error(shr->vr); return; } /* requires Connection: Upgrade header */ token = g_string_sized_new(15); li_http_header_tokenizer_start(&header_tokenizer, resp->headers, CONST_STR_LEN("Connection")); while (li_http_header_tokenizer_next(&header_tokenizer, token)) { VR_ERROR(shr->vr, "Parsing header '%s'", ((liHttpHeader*)header_tokenizer.cur->data)->data->str); VR_ERROR(shr->vr, "Connection token '%s'", token->str); if (0 == g_ascii_strcasecmp(token->str, "Upgrade")) { have_connection_upgrade = TRUE; break; } } g_string_free(token, TRUE); token = NULL; if (!have_connection_upgrade) { VR_ERROR(shr->vr, "%s", "Upgrade without Connection: Upgrade Transfer"); li_vrequest_error(shr->vr); return; } shr->response_headers_finished = TRUE; shr->vr->backend_drain->out->is_closed = FALSE; { /* li_vrequest_connection_upgrade releases vr->backend_drain; keep our own reference */ liStream *backend_drain = shr->vr->backend_drain; shr->vr->backend_drain = NULL; li_vrequest_connection_upgrade(shr->vr, backend_drain, &shr->stream); li_stream_release(backend_drain); } return; } shr->response_headers_finished = TRUE; li_vrequest_indirect_headers_ready(shr->vr); return; }
static void proxy_fd_cb(struct ev_loop *loop, ev_io *w, int revents) { proxy_connection *pcon = (proxy_connection*) w->data; if (pcon->state == SS_CONNECTING) { if (LI_HANDLER_GO_ON != proxy_statemachine(pcon->vr, pcon)) { li_vrequest_error(pcon->vr); } return; } if (revents & EV_READ) { if (pcon->proxy_in->is_closed) { li_ev_io_rem_events(loop, w, EV_READ); } else { switch (li_network_read(pcon->vr, w->fd, pcon->proxy_in, &pcon->proxy_in_buffer)) { case LI_NETWORK_STATUS_SUCCESS: break; case LI_NETWORK_STATUS_FATAL_ERROR: VR_ERROR(pcon->vr, "(%s) network read fatal error", pcon->ctx->socket_str->str); li_vrequest_error(pcon->vr); return; case LI_NETWORK_STATUS_CONNECTION_CLOSE: pcon->proxy_in->is_closed = TRUE; ev_io_stop(loop, w); close(pcon->fd); pcon->fd = -1; li_vrequest_backend_finished(pcon->vr); break; case LI_NETWORK_STATUS_WAIT_FOR_EVENT: break; } } } if (pcon->fd != -1 && (revents & EV_WRITE)) { if (pcon->proxy_out->length > 0) { switch (li_network_write(pcon->vr, w->fd, pcon->proxy_out, 256*1024)) { case LI_NETWORK_STATUS_SUCCESS: break; case LI_NETWORK_STATUS_FATAL_ERROR: VR_ERROR(pcon->vr, "(%s) network write fatal error", pcon->ctx->socket_str->str); li_vrequest_error(pcon->vr); return; case LI_NETWORK_STATUS_CONNECTION_CLOSE: pcon->proxy_in->is_closed = TRUE; ev_io_stop(loop, w); close(pcon->fd); pcon->fd = -1; li_vrequest_backend_finished(pcon->vr); break; case LI_NETWORK_STATUS_WAIT_FOR_EVENT: break; } } if (pcon->proxy_out->length == 0) { li_ev_io_rem_events(loop, w, EV_WRITE); } } if (!pcon->response_headers_finished && LI_HANDLER_GO_ON == li_http_response_parse(pcon->vr, &pcon->parse_response_ctx)) { /* "ignore" 1xx response headers */ if (!(pcon->vr->response.http_status >= 100 && pcon->vr->response.http_status < 200)) { pcon->response_headers_finished = TRUE; li_vrequest_handle_response_headers(pcon->vr); } } if (pcon->response_headers_finished) { li_chunkqueue_steal_all(pcon->vr->out, pcon->proxy_in); pcon->vr->out->is_closed = pcon->proxy_in->is_closed; li_vrequest_handle_response_body(pcon->vr); } /* only possible if we didn't found a header */ if (pcon->proxy_in->is_closed && !pcon->vr->out->is_closed) { VR_ERROR(pcon->vr, "(%s) unexpected end-of-file (perhaps the proxy process died)", pcon->ctx->socket_str->str); li_vrequest_error(pcon->vr); } }
static void fastcgi_fd_cb(struct ev_loop *loop, ev_io *w, int revents) { fastcgi_connection *fcon = (fastcgi_connection*) w->data; if (fcon->state == FS_CONNECTING) { if (LI_HANDLER_GO_ON != fastcgi_statemachine(fcon->vr, fcon)) { li_vrequest_error(fcon->vr); } return; } if (revents & EV_READ) { if (fcon->fcgi_in->is_closed) { li_ev_io_rem_events(loop, w, EV_READ); } else { switch (li_network_read(fcon->vr, w->fd, fcon->fcgi_in, &fcon->fcgi_in_buffer)) { case LI_NETWORK_STATUS_SUCCESS: break; case LI_NETWORK_STATUS_FATAL_ERROR: VR_ERROR(fcon->vr, "(%s) network read fatal error", fcon->ctx->socket_str->str); li_vrequest_error(fcon->vr); return; case LI_NETWORK_STATUS_CONNECTION_CLOSE: fcon->fcgi_in->is_closed = TRUE; ev_io_stop(loop, w); close(fcon->fd); fcon->fd = -1; li_vrequest_backend_finished(fcon->vr); break; case LI_NETWORK_STATUS_WAIT_FOR_EVENT: break; } } } if (fcon->fd != -1 && (revents & EV_WRITE)) { if (fcon->fcgi_out->length > 0) { switch (li_network_write(fcon->vr, w->fd, fcon->fcgi_out, 256*1024)) { case LI_NETWORK_STATUS_SUCCESS: break; case LI_NETWORK_STATUS_FATAL_ERROR: VR_ERROR(fcon->vr, "(%s) network write fatal error", fcon->ctx->socket_str->str); li_vrequest_error(fcon->vr); return; case LI_NETWORK_STATUS_CONNECTION_CLOSE: fcon->fcgi_in->is_closed = TRUE; ev_io_stop(loop, w); close(fcon->fd); fcon->fd = -1; li_vrequest_backend_finished(fcon->vr); break; case LI_NETWORK_STATUS_WAIT_FOR_EVENT: break; } } if (fcon->fcgi_out->length == 0) { li_ev_io_rem_events(loop, w, EV_WRITE); } } if (!fastcgi_parse_response(fcon)) return; if (!fcon->response_headers_finished) { switch (li_http_response_parse(fcon->vr, &fcon->parse_response_ctx)) { case LI_HANDLER_GO_ON: fcon->response_headers_finished = TRUE; li_vrequest_handle_response_headers(fcon->vr); break; case LI_HANDLER_ERROR: VR_ERROR(fcon->vr, "Parsing response header failed for: %s", fcon->ctx->socket_str->str); li_vrequest_error(fcon->vr); break; default: break; } } if (fcon->response_headers_finished) { li_chunkqueue_steal_all(fcon->vr->out, fcon->stdout); fcon->vr->out->is_closed = fcon->stdout->is_closed; li_vrequest_handle_response_body(fcon->vr); } if (fcon->fcgi_in->is_closed && !fcon->vr->out->is_closed) { VR_ERROR(fcon->vr, "(%s) unexpected end-of-file (perhaps the fastcgi process died)", fcon->ctx->socket_str->str); li_vrequest_error(fcon->vr); } }