static ret_t do_send_socket (cherokee_socket_t *sock, cherokee_buffer_t *buffer, cherokee_socket_status_t *blocking) { ret_t ret; size_t written = 0; ret = cherokee_socket_bufwrite (sock, buffer, &written); switch (ret) { case ret_ok: break; case ret_eagain: if (written > 0) { break; } TRACE (ENTRIES, "Post write: EAGAIN, wrote nothing of %d\n", buffer->len); *blocking = socket_writing; return ret_eagain; default: return ret_error; } cherokee_buffer_move_to_begin (buffer, written); TRACE (ENTRIES, "sent=%d, remaining=%d\n", written, buffer->len); if (! cherokee_buffer_is_empty (buffer)) { return ret_eagain; } return ret_ok; }
static ret_t do_download__read_body (cherokee_downloader_t *downloader, void *param) { ret_t ret; ssize_t len; cherokee_buffer_t tmp = CHEROKEE_BUF_INIT; UNUSED(param); /* Write down */ len = write (output_fd, downloader->body.buf, downloader->body.len); if (len > 0) { ret = cherokee_buffer_move_to_begin (&downloader->body, len); if (ret != ret_ok) return ret; } /* Print info */ cherokee_buffer_add_fsize (&tmp, downloader->content_length); cherokee_buffer_add_str (&tmp, " of "); cherokee_buffer_add_fsize (&tmp, downloader->info.body_recv); if (! quiet) { fprintf (stderr, "\rDownloading: %s", tmp.buf); fflush(stderr); } cherokee_buffer_mrproper (&tmp); return ret_ok; }
ret_t cherokee_handler_proxy_conn_send (cherokee_handler_proxy_conn_t *pconn, cherokee_buffer_t *buf) { ret_t ret; size_t sent = 0; ret = cherokee_socket_bufwrite (&pconn->socket, buf, &sent); if (unlikely(ret != ret_ok)) { return ret; } if (sent >= buf->len) { cherokee_buffer_clean (buf); return ret_ok; } cherokee_buffer_move_to_begin (buf, sent); return ret_eagain; }
static ret_t reply_100_continue (cherokee_post_t *post, cherokee_connection_t *conn) { ret_t ret; size_t written; ret = cherokee_socket_bufwrite (&conn->socket, &post->read_header_100cont, &written); if (ret != ret_ok) { TRACE(ENTRIES, "Could not send a '100 Continue' response. Error=500.\n"); return ret; } if (written >= post->read_header_100cont.len) { TRACE(ENTRIES, "Sent a '100 Continue' response.\n"); return ret_ok; } TRACE(ENTRIES, "Sent partial '100 Continue' response: %d bytes\n", written); cherokee_buffer_move_to_begin (&post->read_header_100cont, written); return ret_eagain; }
static ret_t parse (cherokee_handler_ssi_t *hdl, cherokee_buffer_t *in, cherokee_buffer_t *out) { ret_t ret; char *p, *q; char *begin; int re; cuint_t len; operations_t op; path_type_t path; struct stat info; cherokee_boolean_t ignore; cherokee_buffer_t key = CHEROKEE_BUF_INIT; cherokee_buffer_t val = CHEROKEE_BUF_INIT; cherokee_buffer_t pair = CHEROKEE_BUF_INIT; cherokee_buffer_t fpath = CHEROKEE_BUF_INIT; q = in->buf; while (true) { begin = q; /* Check the end */ if (q >= in->buf + in->len) break; /* Find next SSI tag */ p = strstr (q, "<!--#"); if (p == NULL) { cherokee_buffer_add (out, begin, (in->buf + in->len) - begin); ret = ret_ok; goto out; } q = strstr (p + 5, "-->"); if (q == NULL) { ret = ret_error; goto out; } len = q - p; len -= 5; cherokee_buffer_clean (&key); cherokee_buffer_add (&key, p+5, len); cherokee_buffer_trim (&key); q += 3; TRACE(ENTRIES, "Found key '%s'\n", key.buf); /* Add the previous chunk */ cherokee_buffer_add (out, begin, p - begin); /* Check element */ op = op_none; ignore = false; if (strncmp (key.buf, "include", 7) == 0) { op = op_include; len = 7; } else if (strncmp (key.buf, "fsize", 5) == 0) { op = op_size; len = 5; } else if (strncmp (key.buf, "flastmod", 8) == 0) { op = op_lastmod; len = 8; } else { LOG_ERROR (CHEROKEE_ERROR_HANDLER_SSI_PROPERTY, key.buf); } /* Deeper parsing */ path = path_none; switch (op) { case op_size: case op_include: case op_lastmod: /* Read a property key */ cherokee_buffer_move_to_begin (&key, len); cherokee_buffer_trim (&key); cherokee_buffer_clean (&pair); get_pair (&key, &pair); cherokee_buffer_drop_ending (&key, pair.len); cherokee_buffer_trim (&key); /* Parse the property */ if (strncmp (pair.buf, "file=", 5) == 0) { path = path_file; len = 5; } else if (strncmp (pair.buf, "virtual=", 8) == 0) { path = path_virtual; len = 8; } cherokee_buffer_clean (&val); get_val (pair.buf + len, &val); cherokee_buffer_clean (&fpath); switch (path) { case path_file: cherokee_buffer_add_buffer (&fpath, &hdl->dir); cherokee_buffer_add_char (&fpath, '/'); cherokee_buffer_add_buffer (&fpath, &val); TRACE(ENTRIES, "Path: file '%s'\n", fpath.buf); break; case path_virtual: cherokee_buffer_add_buffer (&fpath, &HANDLER_VSRV(hdl)->root); cherokee_buffer_add_char (&fpath, '/'); cherokee_buffer_add_buffer (&fpath, &val); TRACE(ENTRIES, "Path: virtual '%s'\n", fpath.buf); break; default: ignore = true; SHOULDNT_HAPPEN; } /* Path security check: ensure that the file * to include is inside the document root. */ if (! cherokee_buffer_is_empty (&fpath)) { cherokee_path_short (&fpath); if (fpath.len < HANDLER_VSRV(hdl)->root.len) { ignore = true; } else { re = strncmp (fpath.buf, HANDLER_VSRV(hdl)->root.buf, HANDLER_VSRV(hdl)->root.len); if (re != 0) { ignore = true; } } } /* Perform the operation */ if (! ignore) { switch (op) { case op_include: { cherokee_buffer_t file_content = CHEROKEE_BUF_INIT; ret = cherokee_buffer_read_file (&file_content, fpath.buf); if (unlikely (ret != ret_ok)) { cherokee_buffer_mrproper (&file_content); ret = ret_error; goto out; } TRACE(ENTRIES, "Including file '%s'\n", fpath.buf); ret = parse (hdl, &file_content, out); if (unlikely (ret != ret_ok)) { cherokee_buffer_mrproper (&file_content); ret = ret_error; goto out; } cherokee_buffer_mrproper (&file_content); break; } case op_size: TRACE(ENTRIES, "Including file size '%s'\n", fpath.buf); re = cherokee_stat (fpath.buf, &info); if (re >=0) { cherokee_buffer_add_ullong10 (out, info.st_size); } break; case op_lastmod: TRACE(ENTRIES, "Including file modification date '%s'\n", fpath.buf); re = cherokee_stat (fpath.buf, &info); if (re >= 0) { struct tm *ltime; struct tm ltime_buf; char tmp[50]; ltime = cherokee_localtime (&info.st_mtime, <ime_buf); if (ltime != NULL) { strftime (tmp, sizeof(tmp), "%d-%b-%Y %H:%M", ltime); cherokee_buffer_add (out, tmp, strlen(tmp)); } } break; default: SHOULDNT_HAPPEN; } } /* !ignore */ break; default: SHOULDNT_HAPPEN; } /* switch(op) */ } /* while */ ret = ret_ok; out: cherokee_buffer_mrproper (&key); cherokee_buffer_mrproper (&val); cherokee_buffer_mrproper (&pair); cherokee_buffer_mrproper (&fpath); return ret; }
/* Methods implementation */ static ret_t process_package (cherokee_handler_fcgi_t *hdl, cherokee_buffer_t *inbuf, cherokee_buffer_t *outbuf) { FCGI_Header *header; cuint_t len; char *data; cuint_t type; cuint_t id; cuint_t padding; /* Is there enough information? */ if (inbuf->len < sizeof(FCGI_Header)) return ret_ok; /* At least there is a header */ header = (FCGI_Header *)inbuf->buf; if (header->version != 1) { cherokee_buffer_print_debug (inbuf, -1); LOG_ERROR_S (CHEROKEE_ERROR_HANDLER_FCGI_VERSION); return ret_error; } if (header->type != FCGI_STDERR && header->type != FCGI_STDOUT && header->type != FCGI_END_REQUEST) { cherokee_buffer_print_debug (inbuf, -1); LOG_ERROR_S (CHEROKEE_ERROR_HANDLER_FCGI_PARSING); return ret_error; } /* Read the header */ type = header->type; padding = header->paddingLength; id = (header->requestIdB0 | (header->requestIdB1 << 8)); len = (header->contentLengthB0 | (header->contentLengthB1 << 8)); data = inbuf->buf + FCGI_HEADER_LEN; /* printf ("have %d, hdr=%d exp_len=%d pad=%d\n", inbuf->len, FCGI_HEADER_LEN, len, padding); */ /* Is the package complete? */ if (len + padding > inbuf->len - FCGI_HEADER_LEN) { /* printf ("Incomplete: %d < %d\n", len + padding, inbuf->len - FCGI_HEADER_LEN); */ return ret_ok; } /* It has received the full package content */ switch (type) { case FCGI_STDERR: /* printf ("READ:STDERR (%d): %s", len, data?data:""); */ LOG_ERROR (CHEROKEE_ERROR_HANDLER_FCGI_STDERR, data); /* Debug mode */ if (SOURCE_INT(hdl->src_ref)->debug) { PRINT_MSG ("%.*s\n", len, data); } break; case FCGI_STDOUT: /* printf ("READ:STDOUT eof=%d: %d", HDL_CGI_BASE(hdl)->got_eof, len); */ cherokee_buffer_add (outbuf, data, len); break; case FCGI_END_REQUEST: /* printf ("READ:END"); */ HDL_CGI_BASE(hdl)->got_eof = true; break; default: SHOULDNT_HAPPEN; } cherokee_buffer_move_to_begin (inbuf, len + FCGI_HEADER_LEN + padding); /* printf ("- FCGI left %d\n", inbuf->len); */ return ret_eagain; }
ret_t cherokee_logger_writer_flush (cherokee_logger_writer_t *writer, cherokee_boolean_t locked) { int re; ret_t ret = ret_ok; /* The internal buffer might be empty */ if (cherokee_buffer_is_empty (&writer->buffer)) { return ret_ok; } if (!locked) { CHEROKEE_MUTEX_LOCK (&PRIV(writer)->mutex); } /* If not, do the proper thing */ switch (writer->type) { case cherokee_logger_writer_stderr: /* In this case we ignore errors. */ re = fwrite (writer->buffer.buf, 1, writer->buffer.len, stderr); if (re != (size_t) writer->buffer.len) { ret = ret_error; } /* Cleanup the log buffer even if there is an error, * because it's safer to go on anyway. */ cherokee_buffer_clean (&writer->buffer); break; case cherokee_logger_writer_pipe: case cherokee_logger_writer_file: { ssize_t nwr = 0; size_t buflen = writer->buffer.len; /* If there is at least 1 page to write then round * down the length to speed up write(s). */ if (buflen > LOGGER_BUF_PAGESIZE) { buflen &= ~LOGGER_BUF_PAGESIZE; } do { nwr = write (writer->fd, writer->buffer.buf, buflen); } while (nwr == -1 && errno == EINTR); if (nwr <= 0) { /* If an error occured in blocking write, then * cleanup the log buffer now because we don't * want to let it grow too much. */ cherokee_buffer_clean (&writer->buffer); ret = ret_error; goto out; } /* OK, something has been written. */ cherokee_buffer_move_to_begin (&writer->buffer, nwr); if (! cherokee_buffer_is_empty (&writer->buffer)) { ret = ret_eagain; } break; } case cherokee_logger_writer_syslog: /* Write to syslog the whole log buffer, then cleanup * it in any case. */ ret = cherokee_syslog (LOG_INFO, &writer->buffer); cherokee_buffer_clean (&writer->buffer); break; default: SHOULDNT_HAPPEN; ret = ret_error; } out: if (! locked) { CHEROKEE_MUTEX_UNLOCK (&PRIV(writer)->mutex); } return ret; }
ret_t cherokee_post_send_to_fd (cherokee_post_t *post, cherokee_socket_t *sock_in, int fd_out, cherokee_buffer_t *tmp, cherokee_socket_status_t *blocking, cherokee_boolean_t *did_IO) { ret_t ret; int r; cherokee_buffer_t *buffer = tmp ? tmp : &post->send.buffer; switch (post->send.phase) { case cherokee_post_send_phase_read: TRACE (ENTRIES, "Post send, phase: %s\n", "read"); /* Read from the client */ ret = cherokee_post_read (post, sock_in, buffer); switch (ret) { case ret_ok: break; case ret_eagain: *blocking = socket_reading; return ret_eagain; default: return ret; } /* Did something, increase timeout */ *did_IO = true; /* Write it */ TRACE (ENTRIES, "Post buffer.len %d\n", buffer->len); post->send.phase = cherokee_post_send_phase_write; case cherokee_post_send_phase_write: TRACE (ENTRIES, "Post send, phase: write. Has %d bytes to send\n", buffer->len); if (! cherokee_buffer_is_empty (buffer)) { r = write (fd_out, buffer->buf, buffer->len); if (r < 0) { if (errno == EAGAIN) { *blocking = socket_writing; return ret_eagain; } TRACE(ENTRIES, "errno %d: %s\n", errno, strerror(errno)); return ret_error; } else if (r == 0) { return ret_eagain; } cherokee_buffer_move_to_begin (buffer, r); /* Did something, increase timeout */ *did_IO = true; } /* Next iteration */ if (! cherokee_buffer_is_empty (buffer)) { return ret_eagain; } if (! cherokee_post_read_finished (post)) { post->send.phase = cherokee_post_send_phase_read; return ret_eagain; } TRACE (ENTRIES, "Post send: %s\n", "finished"); cherokee_buffer_mrproper (&post->send.buffer); return ret_ok; default: SHOULDNT_HAPPEN; } return ret_error; }
static ret_t process_chunk (cherokee_post_t *post, cherokee_buffer_t *in, cherokee_buffer_t *out) { char *p; char *begin; char *end; ssize_t content_size; TRACE (ENTRIES, "Post in-buffer len=%d\n", in->len); p = in->buf; begin = in->buf; while (true) { end = in->buf + in->len; /* Iterate through the number */ while ((p < end) && (((*p >= '0') && (*p <= '9')) || ((*p >= 'a') && (*p <= 'f')) || ((*p >= 'A') && (*p <= 'F')))) p++; if (unlikely (p+2 > end)) { return ret_ok; } /* Check the CRLF after the length */ if (p[0] != CHR_CR) { return ret_error; } if (p[1] != CHR_LF) { return ret_error; } p += 2; /* Read the length */ content_size = (size_t) strtoul (begin, NULL, 16); if (unlikely (content_size < 0)) { return ret_error; } /* Check if there's enough info */ if (content_size == 0) { post->chunked.last = true; } else if (p + content_size + 2 > end) { TRACE (ENTRIES, "Unfinished chunk(len="FMT_OFFSET"), has=%d, out->len="FMT_OFFSET"\n", content_size, (int)(end-p), out->len); break; } /* Last block check */ if (post->chunked.last) { TRACE(ENTRIES, "Last chunk: %s\n", "exiting"); if (post->chunked.retransmit) { cherokee_buffer_add_str (out, "0" CRLF); } begin = p; break; } /* Move the information */ if (post->chunked.retransmit) { cherokee_buffer_add (out, begin, (p + content_size + 2) - begin); } else { cherokee_buffer_add (out, p, content_size); } TRACE (ENTRIES, "Processing chunk len=%d\n", content_size); /* Next iteration */ begin = p + content_size + 2; p = begin; } /* Clean up in-buffer */ if (begin != in->buf) { cherokee_buffer_move_to_begin (in, begin - in->buf); } /* Very unlikely, but still possible */ if (! cherokee_buffer_is_empty(in)) { TRACE (ENTRIES, "There are %d left-over bytes in the post buffer -> incoming header", in->len); /* cherokee_buffer_add_buffer (&conn->incoming_header, in); */ /* cherokee_buffer_clean (in); */ } TRACE (ENTRIES, "Un-chunked buffer len=%d\n", out->len); return ret_ok; }
ret_t cherokee_downloader_step (cherokee_downloader_t *downloader, cherokee_buffer_t *ext_tmp1, cherokee_buffer_t *ext_tmp2) { ret_t ret; cherokee_buffer_t *tmp1; cherokee_buffer_t *tmp2; /* Set the temporary buffers */ tmp1 = (ext_tmp1) ? ext_tmp1 : &downloader->tmp1; tmp2 = (ext_tmp2) ? ext_tmp2 : &downloader->tmp2; TRACE (ENTRIES, "phase=%d\n", downloader->phase); /* Process it */ switch (downloader->phase) { case downloader_phase_init: { cherokee_request_header_t *req = &downloader->request; TRACE(ENTRIES, "Phase %s\n", "init"); /* Maybe add the post info */ if (! cherokee_buffer_is_empty (&downloader->post)) { req->method = http_post; req->post_len = downloader->post.len; } /* Build the request header */ ret = cherokee_request_header_build_string (req, &downloader->request_header, tmp1, tmp2); if (unlikely(ret < ret_ok)) return ret; /* Deal with the connection */ if (! is_connected (downloader)) { ret = cherokee_downloader_connect (downloader); if (ret < ret_ok) return ret; } /* Everything is ok, go ahead! */ downloader->phase = downloader_phase_send_headers; } case downloader_phase_send_headers: TRACE(ENTRIES, "Phase %s\n", "send_headers"); ret = downloader_send_buffer (downloader, &downloader->request_header); if (unlikely(ret != ret_ok)) return ret; BIT_SET (downloader->status, downloader_status_headers_sent); downloader->phase = downloader_phase_send_post; case downloader_phase_send_post: TRACE(ENTRIES, "Phase %s\n", "send_post"); if (! cherokee_buffer_is_empty (&downloader->post)) { size_t written = 0; ret = cherokee_socket_bufwrite (&downloader->socket, &downloader->post, &written); if (ret != ret_ok) { return ret; } cherokee_buffer_move_to_begin (&downloader->post, written); if (! cherokee_buffer_is_empty (&downloader->post)) { return ret_eagain; } } BIT_SET (downloader->status, downloader_status_post_sent); downloader->phase = downloader_phase_read_headers; break; case downloader_phase_read_headers: TRACE(ENTRIES, "Phase %s\n", "read_headers"); ret = downloader_header_read (downloader, tmp1, tmp2); if (unlikely(ret != ret_ok)) return ret; /* We have the header parsed, continue.. */ BIT_SET (downloader->status, downloader_status_headers_received); downloader->phase = downloader_phase_step; /* Does it read the full reply in the first received chunk? */ if (downloader->info.body_recv >= downloader->content_length) { BIT_SET (downloader->status, downloader_status_data_available); BIT_SET (downloader->status, downloader_status_finished); return ret_eof_have_data; } case downloader_phase_step: TRACE(ENTRIES, "Phase %s\n", "step"); ret = downloader_step (downloader); switch (ret) { case ret_error: break; case ret_ok: BIT_SET (downloader->status, downloader_status_data_available); break; case ret_eof_have_data: BIT_SET (downloader->status, downloader_status_data_available); BIT_SET (downloader->status, downloader_status_finished); break; case ret_eof: BIT_UNSET (downloader->status, downloader_status_data_available); BIT_SET (downloader->status, downloader_status_finished); break; case ret_eagain: BIT_UNSET (downloader->status, downloader_status_data_available); break; default: RET_UNKNOWN(ret); } return ret; case downloader_phase_finished: TRACE(ENTRIES, "Phase %s\n", "finished"); BIT_SET (downloader->status, downloader_status_finished); BIT_UNSET (downloader->status, downloader_status_data_available); return ret_ok; default: SHOULDNT_HAPPEN; break; } return ret_ok; }