static void accept_connection(nxe_loop* loop, nxweb_http_server_listening_socket* lsock) { //static int next_net_thread_idx=0; //nxweb_accept accept_msg; int client_fd; struct sockaddr_in client_addr; socklen_t client_len=sizeof(client_addr); nxe_unset_timer(loop, NXWEB_TIMER_ACCEPT_RETRY, &lsock->accept_retry_timer); while (!shutdown_in_progress) { client_fd=accept4(lsock->listen_source.fd, (struct sockaddr *)&client_addr, &client_len, SOCK_NONBLOCK); if (client_fd!=-1) { if (/*_nxweb_set_non_block(client_fd) ||*/ _nxweb_setup_client_socket(client_fd)) { _nxweb_close_bad_socket(client_fd); nxweb_log_error("failed to setup client socket"); continue; } int lconf_idx=lsock->idx; nxweb_net_thread_data* tdata=(nxweb_net_thread_data*)((char*)(lsock-lconf_idx)-offsetof(nxweb_net_thread_data, listening_sock)); nxweb_http_server_connection* conn=nxp_alloc(tdata->free_conn_pool); nxweb_http_server_connection_init(conn, tdata, lconf_idx); inet_ntop(AF_INET, &client_addr.sin_addr, conn->remote_addr, sizeof(conn->remote_addr)); nxweb_http_server_connection_connect(conn, loop, client_fd); } else { if (errno!=EAGAIN) { nxweb_log_error("accept() failed %d", errno); // retry accept after timeout nxe_set_timer(loop, NXWEB_TIMER_ACCEPT_RETRY, &lsock->accept_retry_timer); } break; } } }
static inline void start_receiving_response(nxd_http_client_proto* hcp, nxe_loop* loop) { nxe_istream_unset_ready(&hcp->data_out); nxe_unset_timer(loop, NXWEB_TIMER_WRITE, &hcp->timer_write); nxe_set_timer(loop, NXWEB_TIMER_READ, &hcp->timer_read); nxe_ostream_set_ready(loop, &hcp->data_in); hcp->state=HCP_WAITING_FOR_RESPONSE; }
static inline void wait_for_100_continue(nxd_http_client_proto* hcp, nxe_loop* loop) { nxe_istream_unset_ready(&hcp->data_out); nxe_unset_timer(loop, NXWEB_TIMER_WRITE, &hcp->timer_write); nxe_set_timer(loop, NXWEB_TIMER_100CONTINUE, &hcp->timer_100_continue); nxe_ostream_set_ready(loop, &hcp->data_in); hcp->receiving_100_continue=1; hcp->state=HCP_WAITING_FOR_RESPONSE; }
static inline void request_complete(nxd_http_client_proto* hcp, nxe_loop* loop) { nxe_publish(&hcp->events_pub, (nxe_data)NXD_HCP_REQUEST_COMPLETE); if (hcp->queued_error_message.i) nxe_publish(&hcp->events_pub, hcp->queued_error_message); nxe_ostream_unset_ready(&hcp->data_in); nxe_istream_unset_ready(&hcp->data_out); nxe_unset_timer(loop, NXWEB_TIMER_READ, &hcp->timer_read); nxe_set_timer(loop, NXWEB_TIMER_KEEP_ALIVE, &hcp->timer_keep_alive); hcp->request_complete=1; hcp->state=HCP_IDLE; hcp->request_count++; }
static nxe_ssize_t req_body_in_write(nxe_ostream* os, nxe_istream* is, int fd, nxe_data ptr, nxe_size_t size, nxe_flags_t* flags) { //nxweb_log_error("req_body_in_write(%d)", size); nxd_http_client_proto* hcp=(nxd_http_client_proto*)((char*)os-offsetof(nxd_http_client_proto, req_body_in)); nxe_loop* loop=os->super.loop; if (hcp->state!=HCP_SENDING_BODY) { nxe_ostream_unset_ready(os); nxe_istream_set_ready(loop, &hcp->data_out); // get notified when next_os ready return 0; } hcp->req_body_sending_started=1; nxe_unset_timer(loop, NXWEB_TIMER_WRITE, &hcp->timer_write); nxe_ssize_t bytes_sent=0; if (size>0) { nxe_ostream* next_os=hcp->data_out.pair; if (next_os) { nxe_flags_t wflags=*flags; if (next_os->ready) bytes_sent=OSTREAM_CLASS(next_os)->write(next_os, &hcp->data_out, 0, (nxe_data)ptr, size, &wflags); if (!next_os->ready) { //nxweb_log_error("req_body_in_write(%d) - next_os unready", size); nxe_ostream_unset_ready(os); nxe_istream_set_ready(loop, &hcp->data_out); // get notified when next_os becomes ready again } } else { nxweb_log_error("no connected device for hcp->data_out"); nxe_ostream_unset_ready(os); } } if (*flags&NXEF_EOF && bytes_sent==size) { // end of request => get response nxe_ostream_unset_ready(os); start_receiving_response(hcp, loop); return bytes_sent; } nxe_set_timer(loop, NXWEB_TIMER_WRITE, &hcp->timer_write); return bytes_sent; }
static void data_in_do_read(nxe_ostream* os, nxe_istream* is) { nxd_http_client_proto* hcp=(nxd_http_client_proto*)((char*)os-offsetof(nxd_http_client_proto, data_in)); nxe_loop* loop=os->super.loop; if (hcp->state==HCP_WAITING_FOR_RESPONSE) { nxe_unset_timer(loop, NXWEB_TIMER_READ, &hcp->timer_read); nxe_unset_timer(loop, NXWEB_TIMER_100CONTINUE, &hcp->timer_100_continue); nxe_set_timer(loop, NXWEB_TIMER_READ, &hcp->timer_read); nxb_make_room(hcp->nxb, NXWEB_MAX_REQUEST_HEADERS_SIZE); hcp->state=HCP_RECEIVING_HEADERS; } if (hcp->state==HCP_RECEIVING_HEADERS) { int size; nxe_flags_t flags=0; void* ptr=nxb_get_room(hcp->nxb, &size); int bytes_received=ISTREAM_CLASS(is)->read(is, os, ptr, size, &flags); if (bytes_received) { nxb_blank_fast(hcp->nxb, bytes_received); int read_buf_size; char* read_buf=nxb_get_unfinished(hcp->nxb, &read_buf_size); char* end_of_headers; char* start_of_body; if ((end_of_headers=_nxweb_find_end_of_http_headers(read_buf, read_buf_size, &start_of_body))) { nxb_finish_stream(hcp->nxb, 0); memset(&hcp->resp, 0, sizeof(hcp->resp)); hcp->resp.nxb=hcp->nxb; hcp->resp.cdstate.monitor_only=hcp->chunked_do_not_decode; if (_nxweb_parse_http_response(&hcp->resp, read_buf, end_of_headers)) { // bad response nxe_unset_timer(loop, NXWEB_TIMER_READ, &hcp->timer_read); nxe_ostream_unset_ready(os); nxe_publish(&hcp->events_pub, (nxe_data)NXD_HCP_BAD_RESPONSE); return; } if (hcp->receiving_100_continue) { hcp->receiving_100_continue=0; if (hcp->resp.status_code==100) { // back to sending request body nxe_ostream_unset_ready(&hcp->data_in); nxe_unset_timer(loop, NXWEB_TIMER_READ, &hcp->timer_read); nxe_set_timer(loop, NXWEB_TIMER_WRITE, &hcp->timer_write); nxe_istream_set_ready(loop, &hcp->data_out); hcp->state=HCP_SENDING_BODY; return; } else { nxe_publish(&hcp->events_pub, (nxe_data)NXD_HCP_NO_100CONTINUE); } } char* read_buf_end=read_buf+read_buf_size; if (start_of_body<read_buf_end) { hcp->first_body_chunk=start_of_body; hcp->first_body_chunk_end=read_buf_end; nxe_size_t first_body_chunk_size=hcp->first_body_chunk_end-hcp->first_body_chunk; if (hcp->resp.chunked_encoding) { int r=_nxweb_decode_chunked_stream(&hcp->resp.cdstate, hcp->first_body_chunk, &first_body_chunk_size); hcp->first_body_chunk_end=hcp->first_body_chunk+first_body_chunk_size; if (r<0) nxe_publish(&hcp->events_pub, (nxe_data)NXD_HCP_RESPONSE_CHUNKED_ENCODING_ERROR); else if (r>0) hcp->response_body_complete=1; } else if (first_body_chunk_size >= hcp->resp.content_length && hcp->resp.content_length>=0) { hcp->response_body_complete=1; } } else { hcp->first_body_chunk=0; hcp->first_body_chunk_end=0; } nxe_ostream_unset_ready(os); nxe_publish(&hcp->events_pub, (nxe_data)NXD_HCP_RESPONSE_RECEIVED); if (hcp->resp.content_length && !hcp->req->head_method) { // is body expected? hcp->state=HCP_RECEIVING_BODY; nxe_istream_set_ready(loop, &hcp->resp_body_out); if (hcp->resp_body_out.pair) { nxe_ostream_set_ready(loop, os); } } else { // rearm connection request_complete(hcp, loop); } } else { if (read_buf_size>=NXWEB_MAX_REQUEST_HEADERS_SIZE) { // bad response (headers too large) nxe_unset_timer(loop, NXWEB_TIMER_READ, &hcp->timer_read); nxe_ostream_unset_ready(os); nxe_publish(&hcp->events_pub, (nxe_data)NXD_HCP_BAD_RESPONSE); return; } } } } else if (hcp->state==HCP_RECEIVING_BODY) { nxe_ostream* next_os=hcp->resp_body_out.pair; if (next_os) { if (next_os->ready) OSTREAM_CLASS(next_os)->do_read(next_os, &hcp->resp_body_out); if (!next_os->ready) { nxe_ostream_unset_ready(os); nxe_istream_set_ready(loop, &hcp->resp_body_out); // get notified when next_os becomes ready again } } else { nxweb_log_error("no connected device for hcp->resp_body_out"); nxe_ostream_unset_ready(os); } } }
static void data_out_do_write(nxe_istream* is, nxe_ostream* os) { nxd_http_client_proto* hcp=(nxd_http_client_proto*)((char*)is-offsetof(nxd_http_client_proto, data_out)); nxe_loop* loop=is->super.loop; nxe_unset_timer(loop, NXWEB_TIMER_WRITE, &hcp->timer_write); nxe_set_timer(loop, NXWEB_TIMER_WRITE, &hcp->timer_write); if (hcp->state==HCP_CONNECTING) { nxe_publish(&hcp->events_pub, (nxe_data)NXD_HCP_CONNECTED); if (hcp->req) hcp->state=HCP_SENDING_HEADERS; else { hcp->state=HCP_IDLE; nxe_set_timer(loop, NXWEB_TIMER_KEEP_ALIVE, &hcp->timer_keep_alive); nxe_istream_unset_ready(is); return; } } if (hcp->state==HCP_SENDING_HEADERS) { if (hcp->req_headers_ptr && *hcp->req_headers_ptr) { int size=strlen(hcp->req_headers_ptr); nxe_flags_t flags=0; int bytes_sent=OSTREAM_CLASS(os)->write(os, is, 0, (nxe_data)hcp->req_headers_ptr, size, &flags); hcp->req_headers_ptr+=bytes_sent; if (bytes_sent<size) return; } // all headers sent hcp->req_headers_ptr=0; if (!hcp->req->content_length) { // no request body => get response start_receiving_response(hcp, loop); return; } if (hcp->req->expect_100_continue) { nxe_istream_unset_ready(is); wait_for_100_continue(hcp, loop); return; } hcp->state=HCP_SENDING_BODY; } if (hcp->state==HCP_SENDING_BODY) { hcp->req_body_sending_started=1; nxe_istream* prev_is=hcp->req_body_in.pair; if (prev_is) { if (prev_is->ready) { hcp->req_body_in.ready=1; ISTREAM_CLASS(prev_is)->do_write(prev_is, &hcp->req_body_in); } if (!prev_is->ready) { nxe_istream_unset_ready(is); nxe_ostream_set_ready(loop, &hcp->req_body_in); // get notified when prev_is becomes ready again } } else { nxweb_log_error("no connected device for hcp->req_body_in"); nxe_istream_unset_ready(is); } } else { // wrong state nxweb_log_error("called data_out_do_write() at wrong HCP state %d", hcp->state); nxe_istream_unset_ready(is); } }
static nxweb_result start_proxy_request(nxweb_http_server_connection* conn, nxweb_http_request* req, nxweb_http_proxy_request_data* rdata) { nxweb_log_debug("start_proxy_request"); nxe_loop* loop=conn->tdata->loop; nxweb_handler* handler=conn->handler; assert(handler->idx>=0 && handler->idx<NXWEB_MAX_PROXY_POOLS); nxd_http_proxy* hpx=nxd_http_proxy_pool_connect(&conn->tdata->proxy_pool[handler->idx]); rdata->proxy_request_complete=0; rdata->proxy_request_error=0; rdata->response_sending_started=0; if (hpx) { rdata->hpx=hpx; nxweb_http_request* preq=nxd_http_proxy_prepare(hpx); if (handler->proxy_copy_host) preq->host=req->host; preq->method=req->method; preq->head_method=req->head_method; preq->content_length=req->content_length; preq->content_type=req->content_type; /// Do not forward Accept-Encoding header if you want to process results (eg SSI) // preq->accept_encoding=req->accept_encoding; preq->expect_100_continue=!!req->content_length; if (handler->uri) { const char* path_info=req->path_info? req->path_info : req->uri; if (*handler->uri) { char* uri=nxb_alloc_obj(conn->hsp.nxb, strlen(handler->uri)+strlen(path_info)+1); strcat(strcpy(uri, handler->uri), path_info); preq->uri=uri; } else { preq->uri=path_info; } } else { preq->uri=req->uri; } preq->http11=1; preq->keep_alive=1; preq->user_agent=req->user_agent; preq->cookie=req->cookie; preq->if_modified_since=req->if_modified_since + nxd_http_proxy_pool_get_backend_time_delta(hpx->pool); preq->x_forwarded_for=conn->remote_addr; preq->x_forwarded_host=req->host; preq->x_forwarded_ssl=nxweb_server_config.listen_config[conn->lconf_idx].secure; preq->uid=req->uid; preq->parent_req=req->parent_req; preq->headers=req->headers; // need to filter these??? nxd_http_proxy_start_request(hpx, preq); nxe_init_subscriber(&rdata->proxy_events_sub, &nxweb_http_server_proxy_events_sub_class); nxe_subscribe(loop, &hpx->hcp.events_pub, &rdata->proxy_events_sub); nxd_rbuffer_init(&rdata->rb_resp, rdata->rbuf, NXWEB_RBUF_SIZE); nxe_connect_streams(loop, &hpx->hcp.resp_body_out, &rdata->rb_resp.data_in); if (req->content_length) { // receive body nxd_rbuffer_init(&rdata->rb_req, rdata->rbuf, NXWEB_RBUF_SIZE); // use same buffer area for request and response bodies, as they do not overlap in time conn->hsp.cls->connect_request_body_out(&conn->hsp, &rdata->rb_req.data_in); nxe_connect_streams(loop, &rdata->rb_req.data_out, &hpx->hcp.req_body_in); req->cdstate.monitor_only=1; conn->hsp.cls->start_receiving_request_body(&conn->hsp); } nxe_set_timer(loop, NXWEB_TIMER_BACKEND, &rdata->timer_backend); return NXWEB_OK; } else { nxweb_http_response* resp=&conn->hsp._resp; nxweb_send_http_error(resp, 502, "Bad Gateway"); return NXWEB_ERROR; } }