#include "uwsgi.h" extern struct uwsgi_server uwsgi; int uwsgi_response_add_content_length(struct wsgi_request *wsgi_req, uint64_t cl) { char buf[sizeof(UMAX64_STR)+1]; int ret = snprintf(buf, sizeof(UMAX64_STR)+1, "%llu", (unsigned long long) cl); if (ret <= 0 || ret >= (int) (sizeof(UMAX64_STR)+1)) { wsgi_req->write_errors++; return -1; } return uwsgi_response_add_header(wsgi_req, "Content-Length", 14, buf, ret); } int uwsgi_response_add_expires(struct wsgi_request *wsgi_req, uint64_t t) { // 30+1 char expires[31]; int len = uwsgi_http_date((time_t) t, expires); if (!len) { wsgi_req->write_errors++; return -1; } return uwsgi_response_add_header(wsgi_req, "Expires", 7, expires, len); } int uwsgi_response_add_date(struct wsgi_request *wsgi_req, char *hkey, uint16_t hlen, uint64_t t) { // 30+1 char d[31]; int len = uwsgi_http_date((time_t) t, d); if (!len) { wsgi_req->write_errors++; return -1; } return uwsgi_response_add_header(wsgi_req, hkey, hlen, d, len); } int uwsgi_response_add_last_modified(struct wsgi_request *wsgi_req, uint64_t t) { // 30+1 char lm[31]; int len = uwsgi_http_date((time_t) t, lm); if (!len) { wsgi_req->write_errors++; return -1; } return uwsgi_response_add_header(wsgi_req, "Last-Modified", 13, lm, len); } int uwsgi_response_add_content_range(struct wsgi_request *wsgi_req, int64_t start, int64_t end, int64_t cl) { char buf[6+(sizeof(UMAX64_STR)*3)+4]; int ret = -1; if (start == -1 && end == -1 && cl >= 0) { ret = snprintf(buf, sizeof(buf), "bytes */%lld", (long long) cl); } else if (start < 0 || end < start || end >= cl) { uwsgi_log("uwsgi_response_add_content_range is called with wrong arguments:" "start=%lld end=%lld content-length=%lld\n", start, end, cl); wsgi_req->write_errors++; return -1; } else { ret = snprintf(buf, sizeof(buf), "bytes %lld-%lld/%lld", (long long) start, (long long) end, (long long) cl); } if (ret <= 0 || ret >= (int) (6+(sizeof(UMAX64_STR)*3)+4)) { wsgi_req->write_errors++; return -1; } return uwsgi_response_add_header(wsgi_req, "Content-Range", 13, buf, ret); } int uwsgi_response_prepare_headers_int(struct wsgi_request *wsgi_req, int status) { char status_str[11]; uwsgi_num2str2(status, status_str); return uwsgi_response_prepare_headers(wsgi_req, status_str, 3); } // status could be NNN or NNN message int uwsgi_response_prepare_headers(struct wsgi_request *wsgi_req, char *status, uint16_t status_len) { if (wsgi_req->headers_sent || wsgi_req->headers_size || wsgi_req->response_size || status_len < 3 || wsgi_req->write_errors) return -1; if (!wsgi_req->headers) { wsgi_req->headers = uwsgi_buffer_new(uwsgi.page_size); wsgi_req->headers->limit = uwsgi.response_header_limit; } // reset the buffer (could be useful for rollbacks...) wsgi_req->headers->pos = 0; // reset headers count wsgi_req->header_cnt = 0; struct uwsgi_buffer *hh = NULL; wsgi_req->status = uwsgi_str3_num(status); #ifdef UWSGI_ROUTING // apply error routes if (wsgi_req->is_error_routing == 0) { if (uwsgi_apply_error_routes(wsgi_req) == UWSGI_ROUTE_BREAK) { // from now on ignore write body requests... wsgi_req->ignore_body = 1; return -1; } wsgi_req->is_error_routing = 0; } #endif if (status_len <= 4) { char *new_sc = NULL; size_t new_sc_len = 0; uint16_t sc_len = 0; const char *sc = uwsgi_http_status_msg(status, &sc_len); if (sc) { new_sc = uwsgi_concat3n(status, 3, " ", 1, (char *)sc, sc_len); new_sc_len = 4+sc_len; } else { new_sc = uwsgi_concat2n(status, 3, " Unknown", 8); new_sc_len = 11; } hh = wsgi_req->socket->proto_prepare_headers(wsgi_req, new_sc, new_sc_len); free(new_sc); } else { hh = wsgi_req->socket->proto_prepare_headers(wsgi_req, status, status_len); } if (!hh) {wsgi_req->write_errors++; return -1;} if (uwsgi_buffer_append(wsgi_req->headers, hh->buf, hh->pos)) goto error; uwsgi_buffer_destroy(hh); return 0; error: uwsgi_buffer_destroy(hh); wsgi_req->write_errors++; return -1; } //each protocol has its header generator static int uwsgi_response_add_header_do(struct wsgi_request *wsgi_req, char *key, uint16_t key_len, char *value, uint16_t value_len) { // pull/collect the header ? struct uwsgi_string_list *usl = NULL; uwsgi_foreach(usl, uwsgi.pull_headers) { if (!uwsgi_strnicmp(key, key_len, usl->value, usl->custom)) { if (!uwsgi_req_append(wsgi_req, usl->custom_ptr, usl->custom2, value, value_len)) { wsgi_req->write_errors++ ; return -1; } return 0; } } uwsgi_foreach(usl, uwsgi.collect_headers) { if (!uwsgi_strnicmp(key, key_len, usl->value, usl->custom)) { if (!uwsgi_req_append(wsgi_req, usl->custom_ptr, usl->custom2, value, value_len)) { wsgi_req->write_errors++ ; return -1; } } } if (!wsgi_req->headers) { wsgi_req->headers = uwsgi_buffer_new(uwsgi.page_size); wsgi_req->headers->limit = uwsgi.response_header_limit; } struct uwsgi_buffer *hh = wsgi_req->socket->proto_add_header(wsgi_req, key, key_len, value, value_len); if (!hh) { wsgi_req->write_errors++ ; return -1;} if (uwsgi_buffer_append(wsgi_req->headers, hh->buf, hh->pos)) goto error; wsgi_req->header_cnt++; uwsgi_buffer_destroy(hh); return 0; error: uwsgi_buffer_destroy(hh); wsgi_req->write_errors++; return -1; } int uwsgi_response_add_header(struct wsgi_request *wsgi_req, char *key, uint16_t key_len, char *value, uint16_t value_len) { if (wsgi_req->headers_sent || wsgi_req->headers_size || wsgi_req->response_size || wsgi_req->write_errors) return -1; struct uwsgi_string_list *rh = uwsgi.remove_headers; while(rh) { if (!uwsgi_strnicmp(key, key_len, rh->value, rh->len)) { return 0; } rh = rh->next; } rh = wsgi_req->remove_headers; while(rh) { if (!uwsgi_strnicmp(key, key_len, rh->value, rh->len)) { return 0; } rh = rh->next; } return uwsgi_response_add_header_do(wsgi_req, key, key_len, value, value_len); } int uwsgi_response_add_header_force(struct wsgi_request *wsgi_req, char *key, uint16_t key_len, char *value, uint16_t value_len) { if (wsgi_req->headers_sent || wsgi_req->headers_size || wsgi_req->response_size || wsgi_req->write_errors) return -1; return uwsgi_response_add_header_do(wsgi_req, key, key_len, value, value_len); } static int uwsgi_response_write_headers_do0(struct wsgi_request *wsgi_req) { if (wsgi_req->headers_sent || !wsgi_req->headers || wsgi_req->response_size || wsgi_req->write_errors) { return UWSGI_OK; } #ifdef UWSGI_ROUTING // apply response routes if (wsgi_req->is_response_routing == 0) { if (uwsgi_apply_response_routes(wsgi_req) == UWSGI_ROUTE_BREAK) { // from now on ignore write body requests... wsgi_req->ignore_body = 1; return -1; } wsgi_req->is_response_routing = 0; } #endif struct uwsgi_string_list *ah = uwsgi.additional_headers; while(ah) { if (uwsgi_response_add_header(wsgi_req, NULL, 0, ah->value, ah->len)) return -1; ah = ah->next; } ah = wsgi_req->additional_headers; while(ah) { if (uwsgi_response_add_header(wsgi_req, NULL, 0, ah->value, ah->len)) return -1; ah = ah->next; } if (wsgi_req->socket->proto_fix_headers(wsgi_req)) { wsgi_req->write_errors++ ; return -1;} return UWSGI_AGAIN; } int uwsgi_response_write_headers_do(struct wsgi_request *wsgi_req) { int ret = uwsgi_response_write_headers_do0(wsgi_req); if (ret != UWSGI_AGAIN) return ret; for(;;) { errno = 0; int ret = wsgi_req->socket->proto_write_headers(wsgi_req, wsgi_req->headers->buf, wsgi_req->headers->pos); if (ret < 0) { if (!uwsgi.ignore_write_errors) { uwsgi_req_error("uwsgi_response_write_headers_do()"); } wsgi_req->write_errors++; return -1; } if (ret == UWSGI_OK) { break; } if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; return -1;} if (ret == 0) { uwsgi_log("uwsgi_response_write_headers_do() TIMEOUT !!!\n"); wsgi_req->write_errors++; return -1; } } wsgi_req->headers_size += wsgi_req->write_pos; // reset for the next write wsgi_req->write_pos = 0; wsgi_req->headers_sent = 1; return UWSGI_OK; } /* private function for highly optimized writes (1 single syscall for headers and body) */ static int uwsgi_response_writev_headers_and_body_do(struct wsgi_request *wsgi_req, char *buf, size_t len) { struct iovec iov[2]; int ret = uwsgi_response_write_headers_do0(wsgi_req); if (ret != UWSGI_AGAIN) return ret; iov[0].iov_base = wsgi_req->headers->buf; iov[0].iov_len = wsgi_req->headers->pos; iov[1].iov_base = buf; iov[1].iov_len = len; size_t iov_len = 2; for(;;) { errno = 0; // no need to use writev if a single iovec remains if (iov_len == 1) { buf = iov[0].iov_base; len = iov[0].iov_len; // update counters wsgi_req->headers_size += wsgi_req->headers->pos; wsgi_req->headers_sent = 1; wsgi_req->response_size += wsgi_req->write_pos - wsgi_req->headers_size; wsgi_req->write_pos = 0; goto fallback; } int ret = wsgi_req->socket->proto_writev(wsgi_req, iov, &iov_len); if (ret < 0) { if (!uwsgi.ignore_write_errors) { uwsgi_req_error("uwsgi_response_writev_headers_and_body_do()"); } wsgi_req->write_errors++; return -1; } if (ret == UWSGI_OK) { break; } if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; return -1;} if (ret == 0) { uwsgi_log("uwsgi_response_writev_headers_and_body_do() TIMEOUT !!!\n"); wsgi_req->write_errors++; return -1; } } wsgi_req->headers_size += wsgi_req->headers->pos; wsgi_req->response_size += len; wsgi_req->headers_sent = 1; // reset for the next write wsgi_req->write_pos = 0; return UWSGI_OK; fallback: for(;;) { errno = 0; int ret = wsgi_req->socket->proto_write(wsgi_req, buf, len); if (ret < 0) { if (!uwsgi.ignore_write_errors) { // here we use the parent name uwsgi_req_error("uwsgi_response_write_body_do()"); } wsgi_req->write_errors++; return -1; } if (ret == UWSGI_OK) { break; } if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; return -1;} if (ret == 0) { // here we use the parent name uwsgi_log("uwsgi_response_write_body_do() TIMEOUT !!!\n"); wsgi_req->write_errors++; return -1; } } wsgi_req->response_size += wsgi_req->write_pos; // reset for the next write wsgi_req->write_pos = 0; return UWSGI_OK; } // this is the function called by all request plugins to send chunks to the client int uwsgi_response_write_body_do(struct wsgi_request *wsgi_req, char *buf, size_t len) { if (wsgi_req->write_errors) return -1; if (wsgi_req->ignore_body) return UWSGI_OK; #ifdef UWSGI_ROUTING // special case here, we could need to set transformations before if (!wsgi_req->headers_sent) { // apply response routes if (wsgi_req->is_response_routing == 0) { if (uwsgi_apply_response_routes(wsgi_req) == UWSGI_ROUTE_BREAK) { // from now on ignore write body requests... wsgi_req->ignore_body = 1; return -1; } wsgi_req->is_response_routing = 0; } } #endif // if the transformation chain returns 1, we are in buffering mode if (wsgi_req->transformed_chunk_len == 0 && wsgi_req->transformations) { int t_ret = uwsgi_apply_transformations(wsgi_req, buf, len); if (t_ret == 0) { buf = wsgi_req->transformed_chunk; len = wsgi_req->transformed_chunk_len; // reset transformation wsgi_req->transformed_chunk = NULL; wsgi_req->transformed_chunk_len = 0; goto write; } if (t_ret == 1) { return UWSGI_OK; } wsgi_req->write_errors++; return -1; } write: // send headers if not already sent if (!wsgi_req->headers_sent) { if (wsgi_req->socket->proto_writev && len > 0 && wsgi_req->headers) { return uwsgi_response_writev_headers_and_body_do(wsgi_req, buf, len); } int ret = uwsgi_response_write_headers_do(wsgi_req); if (ret == UWSGI_OK) goto sendbody; if (ret == UWSGI_AGAIN) return UWSGI_AGAIN; wsgi_req->write_errors++; return -1; } sendbody: if (len == 0) return UWSGI_OK; for(;;) { errno = 0; int ret = wsgi_req->socket->proto_write(wsgi_req, buf, len); if (ret < 0) { if (!uwsgi.ignore_write_errors) { uwsgi_req_error("uwsgi_response_write_body_do()"); } wsgi_req->write_errors++; return -1; } if (ret == UWSGI_OK) { break; } if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; return -1;} if (ret == 0) { uwsgi_log("uwsgi_response_write_body_do() TIMEOUT !!!\n"); wsgi_req->write_errors++; return -1; } } wsgi_req->response_size += wsgi_req->write_pos; // reset for the next write wsgi_req->write_pos = 0; return UWSGI_OK; } int uwsgi_response_writev_body_do(struct wsgi_request *wsgi_req, struct iovec *iov, size_t len) { if (wsgi_req->write_errors) return -1; if (wsgi_req->ignore_body) return UWSGI_OK; #ifdef UWSGI_ROUTING // special case here, we could need to set transformations before if (!wsgi_req->headers_sent) { // apply response routes if (wsgi_req->is_response_routing == 0) { if (uwsgi_apply_response_routes(wsgi_req) == UWSGI_ROUTE_BREAK) { // from now on ignore write body requests... wsgi_req->ignore_body = 1; return -1; } wsgi_req->is_response_routing = 0; } } #endif size_t i; int buffering = 0; // transformations apply to every vector for(i=0;itransformed_chunk_len == 0 && wsgi_req->transformations) { int t_ret = uwsgi_apply_transformations(wsgi_req, iov[i].iov_base, iov[i].iov_len); if (t_ret == 0) { iov[i].iov_base = wsgi_req->transformed_chunk; iov[i].iov_len = wsgi_req->transformed_chunk_len; // reset transformation wsgi_req->transformed_chunk = NULL; wsgi_req->transformed_chunk_len = 0; goto write; } if (t_ret == 1) { buffering = 1; continue; } wsgi_req->write_errors++; return -1; } } if (buffering) return UWSGI_OK; write: // send headers if not already sent if (!wsgi_req->headers_sent) { int ret = uwsgi_response_write_headers_do(wsgi_req); if (ret == UWSGI_OK) goto sendbody; if (ret == UWSGI_AGAIN) return UWSGI_AGAIN; wsgi_req->write_errors++; return -1; } sendbody: if (len == 0) return UWSGI_OK; // unfortunately vector based I/O cannot be accomplished on all protocols if (!wsgi_req->socket->proto_writev) goto fallback; // we use a copy to avoid mess size_t iov_len = len; for(;;) { errno = 0; int ret = wsgi_req->socket->proto_writev(wsgi_req, iov, &iov_len); if (ret < 0) { if (!uwsgi.ignore_write_errors) { uwsgi_req_error("uwsgi_response_writev_body_do()"); } wsgi_req->write_errors++; return -1; } if (ret == UWSGI_OK) { break; } if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; return -1;} if (ret == 0) { uwsgi_log("uwsgi_response_writev_body_do() TIMEOUT !!!\n"); wsgi_req->write_errors++; return -1; } } goto done; fallback: for(i=0;isocket->proto_write(wsgi_req, iov[i].iov_base, iov[i].iov_len); if (ret < 0) { if (!uwsgi.ignore_write_errors) { uwsgi_req_error("uwsgi_response_writev_body_do()"); } wsgi_req->write_errors++; return -1; } if (ret == UWSGI_OK) { break; } if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; return -1;} if (ret == 0) { uwsgi_log("uwsgi_response_writev_body_do() TIMEOUT !!!\n"); wsgi_req->write_errors++; return -1; } } } done: wsgi_req->response_size += wsgi_req->write_pos; // reset for the next write wsgi_req->write_pos = 0; return UWSGI_OK; } int uwsgi_response_sendfile_do(struct wsgi_request *wsgi_req, int fd, size_t pos, size_t len) { return uwsgi_response_sendfile_do_can_close(wsgi_req, fd, pos, len, 1); } int uwsgi_response_sendfile_do_can_close(struct wsgi_request *wsgi_req, int fd, size_t pos, size_t len, int can_close) { if (fd == wsgi_req->sendfile_fd) can_close = 0; if (wsgi_req->write_errors) { if (can_close) close(fd); return -1; } if (wsgi_req->ignore_body) { if (can_close) close(fd); return UWSGI_OK; } if (!wsgi_req->headers_sent) { int ret = uwsgi_response_write_headers_do(wsgi_req); if (ret == UWSGI_OK) goto sendfile; if (ret == UWSGI_AGAIN) return UWSGI_AGAIN; wsgi_req->write_errors++; if (can_close) close(fd); return -1; } sendfile: if (len == 0) { struct stat st; if (fstat(fd, &st)) { uwsgi_req_error("uwsgi_response_sendfile_do()/fstat()"); wsgi_req->write_errors++; if (can_close) close(fd); return -1; } if (pos >= (size_t)st.st_size) { if (can_close) close(fd); return UWSGI_OK; } len = st.st_size; } if (wsgi_req->socket->can_offload) { // of we cannot close the socket (before the app will close it later) // let's dup it if (!can_close) { int tmp_fd = dup(fd); if (tmp_fd < 0) { uwsgi_req_error("uwsgi_response_sendfile_do()/dup()"); wsgi_req->write_errors++; return -1; } fd = tmp_fd; can_close = 1; } if (!uwsgi_offload_request_sendfile_do(wsgi_req, fd, pos, len)) { wsgi_req->via = UWSGI_VIA_OFFLOAD; wsgi_req->response_size += len; return 0; } wsgi_req->write_errors++; if (can_close) close(fd); return -1; } wsgi_req->via = UWSGI_VIA_SENDFILE; for(;;) { errno = 0; int ret = wsgi_req->socket->proto_sendfile(wsgi_req, fd, pos, len); if (ret < 0) { if (!uwsgi.ignore_write_errors) { uwsgi_req_error("uwsgi_response_sendfile_do()"); } wsgi_req->write_errors++; if (can_close) close(fd); return -1; } if (ret == UWSGI_OK) { break; } if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; if (can_close) close(fd); return -1; } if (ret == 0) { uwsgi_log("uwsgi_response_sendfile_do() TIMEOUT !!!\n"); wsgi_req->write_errors++; if (can_close) close(fd); return -1; } } wsgi_req->response_size += wsgi_req->write_pos; // reset for the next write wsgi_req->write_pos = 0; // close the file descriptor if (can_close) close(fd); return UWSGI_OK; } int uwsgi_simple_wait_write_hook(int fd, int timeout) { struct pollfd upoll; timeout = timeout * 1000; upoll.fd = fd; upoll.events = POLLOUT; upoll.revents = 0; int ret = poll(&upoll, 1, timeout); if (ret > 0) { if (upoll.revents & POLLOUT) { return 1; } return -1; } if (ret < 0) { uwsgi_error("uwsgi_simple_wait_write_hook()/poll()"); } return ret; } /* simplified write to client (generally used as fallback) */ int uwsgi_simple_write(struct wsgi_request *wsgi_req, char *buf, size_t len) { wsgi_req->write_pos = 0; for(;;) { errno = 0; int ret = wsgi_req->socket->proto_write(wsgi_req, buf, len); if (ret < 0) { if (!uwsgi.ignore_write_errors) { uwsgi_req_error("uwsgi_simple_write()"); } wsgi_req->write_errors++; return -1; } if (ret == UWSGI_OK) { break; } if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; return -1;} if (ret == 0) { uwsgi_log("uwsgi_simple_write() TIMEOUT !!!\n"); wsgi_req->write_errors++; return -1; } } return 0; }