void uwsgi_request_body_seek(struct wsgi_request *wsgi_req, off_t pos) { if (wsgi_req->post_file) { if (pos < 0) { if (fseek(wsgi_req->post_file, pos, SEEK_CUR)) { uwsgi_req_error("uwsgi_request_body_seek()/fseek()"); wsgi_req->read_errors++; } wsgi_req->post_pos = ftell(wsgi_req->post_file); return; } if (fseek(wsgi_req->post_file, pos, SEEK_SET)) { uwsgi_req_error("uwsgi_request_body_seek()/fseek()"); wsgi_req->read_errors++; } wsgi_req->post_pos = ftell(wsgi_req->post_file); return; } if (uwsgi.post_buffering) { if (pos < 0) { if ((off_t) wsgi_req->post_pos - pos < 0) { wsgi_req->post_pos = 0; return; } wsgi_req->post_pos -= pos; return; } if (pos >= (off_t) uwsgi.post_buffering) { pos = uwsgi.post_buffering - 1; } wsgi_req->post_pos = pos; } }
static ssize_t uwsgi_websockets_recv_pkt(struct wsgi_request *wsgi_req, int nb) { int ret = -1; for(;;) { ssize_t rlen = wsgi_req->socket->proto_read_body(wsgi_req, wsgi_req->websocket_buf->buf + wsgi_req->websocket_buf->pos, wsgi_req->websocket_buf->len - wsgi_req->websocket_buf->pos); if (rlen > 0) return rlen; if (rlen == 0) return -1; if (rlen < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) { if (nb) { if (uwsgi_websockets_check_pingpong(wsgi_req)) { return -1; } return 0; } goto wait; } uwsgi_req_error("uwsgi_websockets_recv_pkt()"); return -1; } wait: ret = uwsgi.wait_read_hook(wsgi_req->fd, uwsgi.websockets_ping_freq); if (ret > 0) { rlen = wsgi_req->socket->proto_read_body(wsgi_req, wsgi_req->websocket_buf->buf + wsgi_req->websocket_buf->pos, wsgi_req->websocket_buf->len - wsgi_req->websocket_buf->pos); if (rlen > 0) return rlen; if (rlen <= 0) return -1; } if (ret < 0) { uwsgi_req_error("uwsgi_websockets_recv_pkt()"); return -1; } // send unsolicited pong if (uwsgi_websockets_check_pingpong(wsgi_req)) { return -1; } } return -1; }
ssize_t uwsgi_proto_sctp_writev_header(struct wsgi_request * wsgi_req, struct iovec * iovec, size_t iov_len) { struct sctp_sndrcvinfo sinfo; memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo)); sinfo.sinfo_stream = wsgi_req->stream_id; ssize_t wlen = sctp_sendv(wsgi_req->poll.fd, iovec, iov_len, &sinfo, 0); if (wlen < 0) { uwsgi_req_error("writev()"); return 0; } return wlen; }
ssize_t uwsgi_proto_sctp_write_header(struct wsgi_request * wsgi_req, char *buf, size_t len) { struct sctp_sndrcvinfo sinfo; memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo)); sinfo.sinfo_stream = wsgi_req->stream_id; ssize_t wlen = sctp_send(wsgi_req->poll.fd, buf, len, &sinfo, 0); if (wlen < 0) { uwsgi_req_error("write()"); return 0; } return wlen; }
ssize_t uwsgi_proto_uwsgi_writev_header(struct wsgi_request *wsgi_req, struct iovec * iovec, size_t iov_len) { if (iov_len == 0) return 0; ssize_t wlen = writev(wsgi_req->poll.fd, iovec, iov_len); if (wlen < 0) { if (!uwsgi.ignore_write_errors) { uwsgi_req_error("writev()"); } wsgi_req->write_errors++; return 0; } return wlen; }
ssize_t uwsgi_proto_uwsgi_write(struct wsgi_request * wsgi_req, char *buf, size_t len) { ssize_t wlen; char *ptr = buf; if (len == 0) return 0; while(len > 0) { wlen = write(wsgi_req->poll.fd, ptr, len); if (wlen <= 0) { if (!uwsgi.ignore_write_errors) { uwsgi_req_error("write()"); } wsgi_req->write_errors++; return ptr-buf; } ptr+=wlen; len -= wlen; } return ptr-buf; }
int uwsgi_postbuffer_do_in_disk(struct wsgi_request *wsgi_req) { size_t post_remains = wsgi_req->post_cl; int ret; int upload_progress_fd = -1; char *upload_progress_filename = NULL; wsgi_req->post_file = uwsgi_tmpfile(); if (!wsgi_req->post_file) { uwsgi_req_error("uwsgi_postbuffer_do_in_disk()/uwsgi_tmpfile()"); wsgi_req->read_errors++; return -1; } if (uwsgi.upload_progress) { // first check for X-Progress-ID size // separator + 'X-Progress-ID' + '=' + uuid upload_progress_filename = uwsgi_upload_progress_create(wsgi_req, &upload_progress_fd); if (!upload_progress_filename) { uwsgi_log("invalid X-Progress-ID value: must be a UUID\n"); } } // manage buffered data and upload progress while (post_remains > 0) { // during post buffering we need to constantly reset the harakiri if (uwsgi.harakiri_options.workers > 0) { inc_harakiri(wsgi_req, uwsgi.harakiri_options.workers); } // we use the already available post buffering buffer to read chunks.... size_t remains = UMIN(post_remains, uwsgi.post_buffering); // first try to read data (there could be something already available ssize_t rlen = wsgi_req->socket->proto_read_body(wsgi_req, wsgi_req->post_buffering_buf, remains); if (rlen > 0) goto write; if (rlen == 0) { uwsgi_read_error0(remains); goto end; } if (rlen < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) { goto wait; } uwsgi_read_error(remains); wsgi_req->read_errors++; goto end; } wait: ret = uwsgi_wait_read_req(wsgi_req); if (ret > 0) { rlen = wsgi_req->socket->proto_read_body(wsgi_req, wsgi_req->post_buffering_buf, remains); if (rlen > 0) goto write; if (rlen == 0) { uwsgi_read_error0(remains); } else { uwsgi_read_error(remains); wsgi_req->read_errors++; } goto end; } if (ret < 0) { uwsgi_read_error(remains); wsgi_req->read_errors++; goto end; } uwsgi_read_timeout(remains); goto end; write: if (fwrite(wsgi_req->post_buffering_buf, rlen, 1, wsgi_req->post_file) != 1) { uwsgi_req_error("uwsgi_postbuffer_do_in_disk()/fwrite()"); wsgi_req->read_errors++; goto end; } post_remains -= rlen; if (upload_progress_filename) { // stop updating it on errors if (uwsgi_upload_progress_update(wsgi_req, upload_progress_fd, post_remains)) { uwsgi_upload_progress_destroy(upload_progress_filename, upload_progress_fd); upload_progress_filename = NULL; } } } rewind(wsgi_req->post_file); if (upload_progress_filename) { uwsgi_upload_progress_destroy(upload_progress_filename, upload_progress_fd); } return 0; end: if (upload_progress_filename) { uwsgi_upload_progress_destroy(upload_progress_filename, upload_progress_fd); } return -1; }
char *uwsgi_request_body_read(struct wsgi_request *wsgi_req, ssize_t hint, ssize_t *rlen) { int ret = -1; size_t remains = hint; // return empty if no post_cl or pos >= post_cl and no residual data if ((!wsgi_req->post_cl || wsgi_req->post_pos >= wsgi_req->post_cl ) && !wsgi_req->post_readline_pos) { return uwsgi.empty; } // return the whole input if (remains <= 0) { remains = wsgi_req->post_cl; } // some residual data ? if (wsgi_req->post_readline_pos > 0) { if (remains <= (wsgi_req->post_readline_watermark - wsgi_req->post_readline_pos)) { *rlen = remains; char *buf = wsgi_req->post_readline_buf + wsgi_req->post_readline_pos; wsgi_req->post_readline_pos += remains; return buf; } // the hint is higher than residual data, let's copy it to read() memory and go on size_t avail = wsgi_req->post_readline_watermark - wsgi_req->post_readline_pos; // check if we have enough memory... if (avail > wsgi_req->post_read_buf_size) { char *tmp_buf = realloc(wsgi_req->post_read_buf, avail); if (!tmp_buf) { uwsgi_req_error("uwsgi_request_body_read()/realloc()"); *rlen = -1; wsgi_req->read_errors++; return NULL; } wsgi_req->post_read_buf = tmp_buf; wsgi_req->post_read_buf_size = avail; if (!wsgi_req->post_warning && wsgi_req->post_read_buf_size > (uwsgi.body_read_warning * 1024*1024)) { uwsgi_log("[uwsgi-warning] you are using read() on request body allocating over than %llu MB, that is really bad and can be avoided...\n", (unsigned long long) (wsgi_req->post_read_buf_size/(1024*1024))); wsgi_req->post_warning = 1; } } // fix remains... if (remains > 0) { remains -= avail; } *rlen += avail; memcpy(wsgi_req->post_read_buf, wsgi_req->post_readline_buf + wsgi_req->post_readline_pos, avail); wsgi_req->post_readline_pos = 0; wsgi_req->post_readline_watermark = 0; } if (remains + wsgi_req->post_pos > wsgi_req->post_cl) { remains = wsgi_req->post_cl - wsgi_req->post_pos; } if (remains == 0) { if (*rlen > 0) { return wsgi_req->post_read_buf; } else { return uwsgi.empty; } } // read from post buffering memory if (uwsgi.post_buffering > 0 && !wsgi_req->post_file) { *rlen += remains; char *buf = wsgi_req->post_buffering_buf+wsgi_req->post_pos; wsgi_req->post_pos += remains; return buf; } // ok we need to check if we need to allocate memory if (!wsgi_req->post_read_buf) { wsgi_req->post_read_buf = malloc(remains); if (!wsgi_req->post_read_buf) { uwsgi_req_error("uwsgi_request_body_read()/malloc()"); wsgi_req->read_errors++; *rlen = -1; return NULL; } wsgi_req->post_read_buf_size = remains; } // need to realloc ? else { if ((remains+*rlen) > wsgi_req->post_read_buf_size) { char *tmp_buf = realloc(wsgi_req->post_read_buf, (remains+*rlen)); if (!tmp_buf) { uwsgi_req_error("uwsgi_request_body_read()/realloc()"); wsgi_req->read_errors++; *rlen = -1; return NULL; } wsgi_req->post_read_buf = tmp_buf; wsgi_req->post_read_buf_size = (remains+*rlen); if (!wsgi_req->post_warning && wsgi_req->post_read_buf_size > (uwsgi.body_read_warning * 1024*1024)) { uwsgi_log("[uwsgi-warning] you are using read() on request body allocating over than %llu MB, that is really bad and can be avoided...\n", (unsigned long long) (wsgi_req->post_read_buf_size/(1024*1024))); wsgi_req->post_warning = 1; } } } // check for disk buffered body first (they are all read in one shot) if (wsgi_req->post_file) { if (fread(wsgi_req->post_read_buf + *rlen, remains, 1, wsgi_req->post_file) != 1) { *rlen = -1; uwsgi_req_error("uwsgi_request_body_read()/fread()"); wsgi_req->read_errors++; return NULL; } *rlen += remains; wsgi_req->post_pos+= remains; return wsgi_req->post_read_buf; } // ok read all the required bytes... while(remains > 0) { // here we first try to read (as data could be already available) ssize_t len = wsgi_req->socket->proto_read_body(wsgi_req, wsgi_req->post_read_buf + *rlen , remains); if (len > 0) { wsgi_req->post_pos+=len; remains -= len; *rlen += len; continue; } // client closed connection... if (len == 0) { *rlen = -1; uwsgi_read_error0(remains); return NULL; } if (len < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) { goto wait; } *rlen = -1; uwsgi_read_error(remains); wsgi_req->read_errors++; return NULL; } wait: ret = uwsgi_wait_read_req(wsgi_req); if (ret > 0) { len = wsgi_req->socket->proto_read_body(wsgi_req, wsgi_req->post_read_buf + *rlen, remains); if (len > 0) { wsgi_req->post_pos+=len; remains -= len; *rlen += len; continue; }else if (len < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS)) { goto wait; } *rlen = -1; if (len == 0) { uwsgi_read_error0(remains); } else { uwsgi_read_error(remains); wsgi_req->read_errors++; } return NULL; } // 0 means timeout else if (ret == 0) { *rlen = 0; uwsgi_read_timeout(remains); return NULL; } *rlen = -1; uwsgi_read_error(remains); wsgi_req->read_errors++; return NULL; } return wsgi_req->post_read_buf; }
// TODO take hint into account // readline_buf is allocated when needed and freed at the end of the request char *uwsgi_request_body_readline(struct wsgi_request *wsgi_req, ssize_t hint, ssize_t *rlen) { // return 0 if no post_cl or pos >= post_cl and no residual data if ((!wsgi_req->post_cl || wsgi_req->post_pos >= wsgi_req->post_cl ) && !wsgi_req->post_readline_pos) { return uwsgi.empty; } // some residual data ? if (wsgi_req->post_readline_pos > 0) { size_t i; for(i=wsgi_req->post_readline_pos;i<wsgi_req->post_readline_watermark;i++) { // found a newline if (wsgi_req->post_readline_buf[i] == '\n') { *rlen = (i+1)-wsgi_req->post_readline_pos; char *buf = wsgi_req->post_readline_buf + wsgi_req->post_readline_pos; wsgi_req->post_readline_pos += *rlen; // all the readline buffer has been consumed if (wsgi_req->post_readline_pos >= wsgi_req->post_readline_watermark) { wsgi_req->post_readline_pos = 0; wsgi_req->post_readline_watermark = 0; } return buf; } } // ok, no newline found, continue below } // allocate memory on the first round if (!wsgi_req->post_readline_buf) { size_t amount = UMIN(uwsgi.buffer_size, wsgi_req->post_cl); wsgi_req->post_readline_buf = malloc(amount); if (!wsgi_req->post_readline_buf) { uwsgi_req_error("uwsgi_request_body_readline()/malloc()"); wsgi_req->read_errors++; *rlen = -1; return NULL; } wsgi_req->post_readline_size = amount; } // ok, no newline found, consume a bit more of memory and retry for(;;) { // no more data to consume if (wsgi_req->post_pos >= wsgi_req->post_cl) break; if (consume_body_for_readline(wsgi_req)) { wsgi_req->read_errors++; *rlen = -1; return NULL; } size_t i; for(i=wsgi_req->post_readline_pos;i<wsgi_req->post_readline_watermark;i++) { if (wsgi_req->post_readline_buf[i] == '\n') { *rlen = (i+1)-wsgi_req->post_readline_pos; char *buf = wsgi_req->post_readline_buf + wsgi_req->post_readline_pos; wsgi_req->post_readline_pos += *rlen; if (wsgi_req->post_readline_pos >= wsgi_req->post_readline_watermark) { wsgi_req->post_readline_pos = 0; wsgi_req->post_readline_watermark = 0; } return buf; } } } // no line found, let's return all *rlen = wsgi_req->post_readline_size - wsgi_req->post_readline_pos; char *buf = wsgi_req->post_readline_buf + wsgi_req->post_readline_pos; wsgi_req->post_readline_pos = 0; return buf; }
static int consume_body_for_readline(struct wsgi_request *wsgi_req) { size_t remains = UMIN(uwsgi.buffer_size, wsgi_req->post_cl - wsgi_req->post_pos); int ret; // allocate more memory if needed if (wsgi_req->post_readline_size - wsgi_req->post_readline_watermark == 0) { memmove(wsgi_req->post_readline_buf, wsgi_req->post_readline_buf + wsgi_req->post_readline_pos, wsgi_req->post_readline_watermark - wsgi_req->post_readline_pos); wsgi_req->post_readline_watermark -= wsgi_req->post_readline_pos; wsgi_req->post_readline_pos = 0; // still something to use ? if (wsgi_req->post_readline_size - wsgi_req->post_readline_watermark < remains) { char *tmp_buf = realloc(wsgi_req->post_readline_buf, wsgi_req->post_readline_size + remains); if (!tmp_buf) { uwsgi_req_error("consume_body_for_readline()/realloc()"); return -1; } wsgi_req->post_readline_buf = tmp_buf; wsgi_req->post_readline_size += remains; // INFORM THE USER HIS readline() USAGE IS FOOLISH if (!wsgi_req->post_warning && wsgi_req->post_readline_size > (uwsgi.body_read_warning * 1024*1024)) { uwsgi_log("[uwsgi-warning] you are using readline() on request body allocating over than %llu MB, that is really bad and can be avoided...\n", (unsigned long long) (wsgi_req->post_readline_size/(1024*1024))); wsgi_req->post_warning = 1; } } } remains = UMIN(wsgi_req->post_readline_size - wsgi_req->post_readline_watermark, wsgi_req->post_cl - wsgi_req->post_pos); // read from a file if (wsgi_req->post_file) { size_t ret = fread(wsgi_req->post_readline_buf + wsgi_req->post_readline_watermark, remains, 1, wsgi_req->post_file); if (ret == 0) { uwsgi_req_error("consume_body_for_readline()/fread()"); return -1; } wsgi_req->post_pos += remains; wsgi_req->post_readline_watermark += remains; return 0; } // read from post_buffering memory if (uwsgi.post_buffering) { memcpy(wsgi_req->post_readline_buf + wsgi_req->post_readline_watermark, wsgi_req->post_buffering_buf + wsgi_req->post_pos, remains); wsgi_req->post_pos += remains; wsgi_req->post_readline_watermark += remains; return 0; } // read from socket ssize_t len = wsgi_req->socket->proto_read_body(wsgi_req, wsgi_req->post_readline_buf + wsgi_req->post_readline_watermark , remains); if (len > 0) { wsgi_req->post_pos += len; wsgi_req->post_readline_watermark += len; return 0; } if (len == 0) { uwsgi_read_error(remains); wsgi_req->read_errors++; return -1; } if (len < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) { goto wait; } uwsgi_read_error(remains); wsgi_req->read_errors++; return -1; } wait: ret = uwsgi_wait_read_req(wsgi_req); if (ret > 0) { len = wsgi_req->socket->proto_read_body(wsgi_req, wsgi_req->post_readline_buf + wsgi_req->post_readline_watermark , remains); if (len > 0) { wsgi_req->post_pos += len; wsgi_req->post_readline_watermark += len; return 0; } uwsgi_read_error(remains); wsgi_req->read_errors++; return -1; } // 0 means timeout else if (ret == 0) { uwsgi_read_timeout(remains); return -1; } uwsgi_read_error(remains); wsgi_req->read_errors++; return -1; }