static write_state on_write_chunk(struct ev_loop* mainloop, Request* request) { if (do_send_chunk(request)) // data left to send in the current chunk return not_yet_done; if(request->iterator) { /* Reached the end of a chunk in the response iterator. Get next chunk. */ PyObject* next_chunk = wsgi_iterable_get_next_chunk(request); if(next_chunk) { /* We found another chunk to send. */ 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); return not_yet_done; } else { if(PyErr_Occurred()) { /* Trying to get the next chunk raised an exception. */ PyErr_Print(); DBG_REQ(request, "Exception in iterator, can not recover"); return aborted; } else { /* This was the last chunk; cleanup. */ Py_CLEAR(request->iterator); goto send_terminator_chunk; } } } else { /* We have no iterator to get more chunks from, so we're done. * Reasons we might end up in this place: * A) A parse or server error occurred * C) We just finished a chunked response with the call to 'do_send_chunk' * above and now maybe have to send the terminating empty chunk. * B) We used chunked responses earlier in the response and * are now sending the terminating empty chunk. */ goto send_terminator_chunk; } assert(0); // unreachable send_terminator_chunk: 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); // Next time we get here, don't send the terminating empty chunk again. // XXX This is kind of a hack and should be refactored for easier understanding. request->state.chunked_response = false; return not_yet_done; } else { return done; } }
static write_state on_write_sendfile(struct ev_loop* mainloop, Request* request) { /* A sendfile response is split into two phases: * Phase A) sending HTTP headers * Phase B) sending the actual file contents */ if(request->current_chunk) { /* Phase A) -- current_chunk contains the HTTP headers */ if (do_send_chunk(request)) { // data left to send in the current chunk return not_yet_done; } else { assert(request->current_chunk == NULL); assert(request->current_chunk_p == 0); /* Transition to Phase B) -- abuse current_chunk_p to store the file fd */ request->current_chunk_p = PyObject_AsFileDescriptor(request->iterable); // don't stop yet, Phase B is still missing return not_yet_done; } } else { /* Phase B) -- current_chunk_p contains file fd */ if (do_sendfile(request)) { // Haven't reached the end of file yet return not_yet_done; } else { // Done with the file return done; } } }
virtual void write(const std::string& data) { std::lock_guard<std::mutex> l(lock_); bool triggerSend = buffer_.size() == 0; // add data to buffer if (triggerSend) { do_send_chunk(); } }
void on_written(const boost::system::error_code& e, std::size_t length) { if (e) { // handle error here return; } std::lock_guard<std::mutex> l(lock_); buffer_.consume(length); if (buffer_.size() > 0) { // still has data to send do_send_chunk(); } }