static inline void call_wsgi_app(client_t *client, picoev_loop* loop) { int ret; ret = process_wsgi_app(client); #ifdef DEBUG printf("call_wsgi_app result %d \n", ret); #endif switch(ret){ case -1: //Internal Server Error client->bad_request_code = 500; send_error_page(client); close_conn(client, loop); return; case 0: // suspend return; default: break; } if(client->response_closed){ //closed close_conn(client, loop); return; } ret = response_start(client); #ifdef DEBUG printf("response_start result %d \n", ret); #endif switch(ret){ case -1: // Internal Server Error client->bad_request_code = 500; send_error_page(client); close_conn(client, loop); return; case 0: // continue // set callback #ifdef DEBUG printf("set write callback %d \n", ret); #endif //clear event picoev_del(loop, client->fd); picoev_add(loop, client->fd, PICOEV_WRITE, 0, w_callback, (void *)client); return; default: // send OK close_conn(client, loop); } }
static void call_rack_app(client_t *client, picoev_loop* loop) { int ret; if(!process_rack_app(client)){ //Internal Server Error client->bad_request_code = 500; send_error_page(client); close_conn(client, loop); return; } ret = response_start(client); /* printf("response_start done: %d\n", ret); */ switch(ret){ case -1: // Internal Server Error client->bad_request_code = 500; send_error_page(client); close_conn(client, loop); return; case 0: // continue // set callback #ifdef DEBUG printf("set write callback %d \n", ret); #endif //clear event picoev_del(loop, client->fd); picoev_add(loop, client->fd, PICOEV_WRITE, WRITE_TIMEOUT_SECS, w_callback, (void *)client); return; default: // send OK close_conn(client, loop); } }
static inline int check_status_code(client_t *client) { request *req; req = client->request_queue->head; if(req->bad_request_code > 200){ //error //shift #ifdef DEBUG printf("bad_request_code \n"); #endif set_current_request(client); send_error_page(client); close_conn(client, main_loop); return 0; } return 1; }
static void r_callback(picoev_loop* loop, int fd, int events, void* cb_arg) { client_t *cli = ( client_t *)(cb_arg); if ((events & PICOEV_TIMEOUT) != 0) { #ifdef DEBUG printf("** r_callback timeout ** \n"); #endif //timeout cli->keep_alive = 0; close_conn(cli, loop); } else if ((events & PICOEV_READ) != 0) { #ifdef DEBUG printf("ready read \n"); #endif /* update timeout, and read */ int finish = 0, nread; char buf[INPUT_BUF_SIZE]; ssize_t r; if(!cli->keep_alive){ picoev_set_timeout(loop, cli->fd, SHORT_TIMEOUT_SECS); } r = read(cli->fd, buf, sizeof(buf)); switch (r) { case 0: finish = 1; break; case -1: /* error */ if (errno == EAGAIN || errno == EWOULDBLOCK) { /* try again later */ break; } else { /* fatal error */ rb_raise(rb_eException, "fatal error"); // TODO: // raise exception from errno /* rb_raise(); */ /* write_error_log(__FILE__, __LINE__); */ cli->keep_alive = 0; cli->status_code = 500; close_conn(cli, loop); return; } break; default: #ifdef DEBUG printf("read request fd %d bufsize %d \n", cli->fd, r); #endif nread = execute_parse(cli, buf, r); if(cli->bad_request_code > 0){ #ifdef DEBUG printf("fd %d bad_request code %d \n", cli->fd, cli->bad_request_code); #endif send_error_page(cli); close_conn(cli, loop); return; } if( nread != r ){ // parse error #ifdef DEBUG printf("fd %d parse error %d \n", cli->fd, cli->bad_request_code); #endif cli->bad_request_code = 400; send_error_page(cli); close_conn(cli, loop); return; } #ifdef DEBUG printf("parse ok, fd %d %d nread \n", cli->fd, nread); #endif if(parser_finish(cli) > 0){ finish = 1; } break; } if(finish == 1){ prepare_call_rack(cli); call_rack_app(cli, loop); return; } } }
static void r_callback(picoev_loop* loop, int fd, int events, void* cb_arg) { client_t *cli = ( client_t *)(cb_arg); //PyObject *body = NULL; char *key = NULL; int finish = 0, nread; if ((events & PICOEV_TIMEOUT) != 0) { #ifdef DEBUG printf("** r_callback timeout %d ** \n", fd); #endif //timeout cli->keep_alive = 0; if(cli->request_queue->size > 0){ //piplining set_bad_request_code(cli, 408); finish = 1; }else{ close_conn(cli, loop); } } else if ((events & PICOEV_READ) != 0) { char buf[INPUT_BUF_SIZE]; ssize_t r; if(!cli->keep_alive){ picoev_set_timeout(loop, cli->fd, READ_TIMEOUT_SECS); } Py_BEGIN_ALLOW_THREADS r = read(cli->fd, buf, sizeof(buf)); Py_END_ALLOW_THREADS switch (r) { case 0: cli->keep_alive = 0; //503?? if(cli->request_queue->size > 0){ //piplining set_bad_request_code(cli, 503); finish = 1; }else{ cli->status_code = 503; send_error_page(cli); close_conn(cli, loop); return; } case -1: /* error */ if (errno == EAGAIN || errno == EWOULDBLOCK) { /* try again later */ break; } else { /* fatal error */ if(cli->request_queue->size > 0){ //piplining set_bad_request_code(cli, 500); if(errno != ECONNRESET){ PyErr_SetFromErrno(PyExc_IOError); write_error_log(__FILE__, __LINE__); } finish = 1; }else{ if(cli->keep_alive && errno == ECONNRESET){ cli->keep_alive = 0; cli->status_code = 500; cli->header_done = 1; cli->response_closed = 1; }else{ PyErr_SetFromErrno(PyExc_IOError); write_error_log(__FILE__, __LINE__); cli->keep_alive = 0; cli->status_code = 500; if(errno != ECONNRESET){ send_error_page(cli); }else{ cli->header_done = 1; cli->response_closed = 1; } } close_conn(cli, loop); return; } } break; default: #ifdef DEBUG printf("********************\n%s\n", buf); #endif nread = execute_parse(cli, buf, r); #ifdef DEBUG printf("read request fd %d readed %d nread %d \n", cli->fd, r, nread); #endif if(cli->bad_request_code > 0){ #ifdef DEBUG printf("fd %d bad_request code %d \n",cli->fd, cli->bad_request_code); #endif set_bad_request_code(cli, cli->bad_request_code); ///force end finish = 1; break; } if(!cli->upgrade && nread != r){ // parse error #ifdef DEBUG printf("fd %d parse error Bad Request %d \n", cli->fd, cli->bad_request_code); #endif set_bad_request_code(cli, 400); ///force end finish = 1; break; } #ifdef DEBUG printf("parse ok, fd %d %d nread \n", cli->fd, nread); #endif if(parser_finish(cli) > 0){ if(cli->upgrade){ //WebSocket Key #ifdef DEBUG printf("upgrade websocket %d \n", cli->fd); #endif key = buf + nread + 1; buffer *b = new_buffer(r - nread -1, r - nread -1); if(write2buf(b, key, r - nread -1) == WRITE_OK){ cli->request_queue->tail->body = b; }else{ free_buffer(b); } } finish = 1; } break; } }
static inline void prepare_call_wsgi(client_t *client) { PyObject *input = NULL, *c = NULL; char *val; set_current_request(client); //check Expect if(client->http->http_minor == 1){ c = PyDict_GetItemString(client->environ, "HTTP_EXPECT"); if(c){ val = PyString_AS_STRING(c); if(!strcasecmp(val, "100-continue")){ int ret = write(client->fd, "HTTP/1.1 100 Continue\r\n\r\n", 25); if(ret < 0){ PyErr_SetFromErrno(PyExc_IOError); write_error_log(__FILE__, __LINE__); client->keep_alive = 0; client->status_code = 500; send_error_page(client); close_conn(client, main_loop); return; } }else{ //417 client->keep_alive = 0; client->status_code = 417; send_error_page(client); close_conn(client, main_loop); return; } } } if(client->body_type == BODY_TYPE_TMPFILE){ FILE *tmp = (FILE *)client->body; fflush(tmp); rewind(tmp); input = PyFile_FromFile(tmp, "<tmpfile>", "r", fclose); PyDict_SetItem((PyObject *)client->environ, wsgi_input_key, input); Py_DECREF(input); client->body = NULL; }else{ if(client->body_type == BODY_TYPE_BUFFER){ input = StringIOObject_New((buffer *)client->body); PyDict_SetItem((PyObject *)client->environ, wsgi_input_key, input); }else{ if(client->body){ input = StringIOObject_New((buffer *)client->body); }else{ input = StringIOObject_New(new_buffer(0, 0)); } PyDict_SetItem((PyObject *)client->environ, wsgi_input_key, input); } client->body = NULL;; Py_DECREF(input); } if(is_keep_alive){ //support keep-alive c = PyDict_GetItemString(client->environ, "HTTP_CONNECTION"); if(client->http->http_minor == 1){ //HTTP 1.1 if(c){ val = PyString_AS_STRING(c); if(!strcasecmp(val, "close")){ client->keep_alive = 0; }else{ client->keep_alive = 1; } }else{ client->keep_alive = 1; } }else{ //HTTP 1.0 if(c){ val = PyString_AS_STRING(c); if(!strcasecmp(val, "keep-alive")){ client->keep_alive = 1; }else{ client->keep_alive = 0; } }else{ client->keep_alive = 0; } } } }