static void ev_io_on_read(struct ev_loop* mainloop, ev_io* watcher, const int events) { static char read_buf[READ_BUFFER_SIZE]; Request* request = REQUEST_FROM_WATCHER(watcher); ssize_t read_bytes = read( request->client_fd, read_buf, READ_BUFFER_SIZE ); GIL_LOCK(0); if(read_bytes <= 0) { if(errno != EAGAIN && errno != EWOULDBLOCK) { if(read_bytes == 0) DBG_REQ(request, "Client disconnected"); else DBG_REQ(request, "Hit errno %d while read()ing", errno); close(request->client_fd); Request_free(request); ev_io_stop(mainloop, &request->ev_watcher); } goto out; } Request_parse(request, read_buf, (size_t)read_bytes); if(request->state.error_code) { DBG_REQ(request, "Parse error"); request->current_chunk = PyString_FromString( http_error_messages[request->state.error_code]); assert(request->iterator == NULL); } else if(request->state.parse_finished) { if(!wsgi_call_application(request)) { assert(PyErr_Occurred()); PyErr_Print(); assert(!request->state.chunked_response); Py_XCLEAR(request->iterator); request->current_chunk = PyString_FromString( http_error_messages[HTTP_SERVER_ERROR]); } } else { /* Wait for more data */ goto out; } ev_io_stop(mainloop, &request->ev_watcher); ev_io_init(&request->ev_watcher, &ev_io_on_write, request->client_fd, EV_WRITE); ev_io_start(mainloop, &request->ev_watcher); out: GIL_UNLOCK(0); return; }
static void ev_io_on_write(struct ev_loop* mainloop, ev_io* watcher, const int events) { /* Since the response writing code is fairly complex, I'll try to give a short * overview of the different control flow paths etc.: * * On the very top level, there are two types of responses to distinguish: * A) sendfile responses * B) iterator/other responses * * These cases are handled by the 'on_write_sendfile' and 'on_write_chunk' * routines, respectively. They use the 'do_sendfile' and 'do_send_chunk' * routines to do the actual write()-ing. The 'do_*' routines return true if * there's some data left to send in the current chunk (or, in the case of * sendfile, the end of the file has not been reached yet). * * When the 'do_*' routines return false, the 'on_write_*' routines have to * figure out if there's a next chunk to send (e.g. in the case of a response iterator). */ Request* request = REQUEST_FROM_WATCHER(watcher); GIL_LOCK(0); write_state write_state; if(request->state.use_sendfile) { write_state = on_write_sendfile(mainloop, request); } else { write_state = on_write_chunk(mainloop, request); } switch(write_state) { case not_yet_done: break; case done: if(request->state.keep_alive) { DBG_REQ(request, "done, keep-alive"); ev_io_stop(mainloop, &request->ev_watcher); Request_clean(request); Request_reset(request); ev_io_init(&request->ev_watcher, &ev_io_on_read, request->client_fd, EV_READ); ev_io_start(mainloop, &request->ev_watcher); } else { DBG_REQ(request, "done, close"); close_connection(mainloop, request); } break; case aborted: /* Response was aborted due to an error. We can't do anything graceful here * because at least one chunk is already sent... just close the connection. */ close_connection(mainloop, request); break; } GIL_UNLOCK(0); }
/* XXX too many gotos */ static void ev_io_on_write(struct ev_loop* mainloop, ev_io* watcher, const int events) { Request* request = REQUEST_FROM_WATCHER(watcher); GIL_LOCK(0); if(request->state.use_sendfile) { /* sendfile */ if(request->current_chunk) { /* current_chunk contains the HTTP headers */ if(send_chunk(request)) goto out; assert(!request->current_chunk_p); /* abuse current_chunk_p to store the file fd */ request->current_chunk_p = PyObject_AsFileDescriptor(request->iterable); goto out; } if(do_sendfile(request)) goto out; } else { /* iterable */ if(send_chunk(request)) goto out; if(request->iterator) { PyObject* next_chunk; next_chunk = wsgi_iterable_get_next_chunk(request); if(next_chunk) { if(request->state.chunked_response) { request->current_chunk = wrap_http_chunk_cruft_around(next_chunk); Py_DECREF(next_chunk); } else { request->current_chunk = next_chunk; } assert(request->current_chunk_p == 0); goto out; } else { if(PyErr_Occurred()) { PyErr_Print(); /* We can't do anything graceful here because at least one * chunk is already sent... just close the connection */ DBG_REQ(request, "Exception in iterator, can not recover"); ev_io_stop(mainloop, &request->ev_watcher); close(request->client_fd); Request_free(request); goto out; } Py_CLEAR(request->iterator); } } if(request->state.chunked_response) { /* We have to send a terminating empty chunk + \r\n */ request->current_chunk = PyString_FromString("0\r\n\r\n"); assert(request->current_chunk_p == 0); request->state.chunked_response = false; goto out; } } ev_io_stop(mainloop, &request->ev_watcher); if(request->state.keep_alive) { DBG_REQ(request, "done, keep-alive"); Request_clean(request); Request_reset(request); ev_io_init(&request->ev_watcher, &ev_io_on_read, request->client_fd, EV_READ); ev_io_start(mainloop, &request->ev_watcher); } else { DBG_REQ(request, "done, close"); close(request->client_fd); Request_free(request); } out: GIL_UNLOCK(0); }
static void ev_io_on_read(struct ev_loop* mainloop, ev_io* watcher, const int events) { static char read_buf[READ_BUFFER_SIZE]; Request* request = REQUEST_FROM_WATCHER(watcher); read_state read_state; ssize_t read_bytes = read( request->client_fd, read_buf, READ_BUFFER_SIZE ); GIL_LOCK(0); if (read_bytes == 0) { /* Client disconnected */ read_state = aborted; DBG_REQ(request, "Client disconnected"); } else if (read_bytes < 0) { /* Would block or error */ if(errno == EAGAIN || errno == EWOULDBLOCK) { read_state = not_yet_done; } else { read_state = aborted; DBG_REQ(request, "Hit errno %d while read()ing", errno); } } else { /* OK, either expect more data or done reading */ Request_parse(request, read_buf, (size_t)read_bytes); if(request->state.error_code) { /* HTTP parse error */ read_state = done; DBG_REQ(request, "Parse error"); request->current_chunk = PyString_FromString( http_error_messages[request->state.error_code]); assert(request->iterator == NULL); } else if(request->state.parse_finished) { /* HTTP parse successful */ read_state = done; bool wsgi_ok = wsgi_call_application(request); if (!wsgi_ok) { /* Response is "HTTP 500 Internal Server Error" */ DBG_REQ(request, "WSGI app error"); assert(PyErr_Occurred()); PyErr_Print(); assert(!request->state.chunked_response); Py_XCLEAR(request->iterator); request->current_chunk = PyString_FromString( http_error_messages[HTTP_SERVER_ERROR]); } } else { /* Wait for more data */ read_state = not_yet_done; } } switch (read_state) { case not_yet_done: break; case done: DBG_REQ(request, "Stop read watcher, start write watcher"); ev_io_stop(mainloop, &request->ev_watcher); ev_io_init(&request->ev_watcher, &ev_io_on_write, request->client_fd, EV_WRITE); ev_io_start(mainloop, &request->ev_watcher); break; case aborted: close_connection(mainloop, request); break; } GIL_UNLOCK(0); }