static ret_t do_read_chunked (cherokee_post_t *post, cherokee_socket_t *sock_in, cherokee_buffer_t *buffer) { ret_t ret; /* Try to read */ ret = do_read_plain (post, sock_in, &post->chunked.buffer, POST_READ_SIZE); switch(ret) { case ret_ok: break; case ret_eagain: return ret_eagain; default: RET_UNKNOWN(ret); return ret_error; } /* Process the buffer */ ret = process_chunk (post, &post->chunked.buffer, buffer); if (unlikely (ret != ret_ok)) { return ret_error; } if (post->chunked.last) { cherokee_buffer_mrproper (&post->chunked.buffer); } return ret_ok; }
static ret_t read_from_cgi (cherokee_handler_cgi_base_t *cgi_base, cherokee_buffer_t *buffer) { ret_t ret; size_t read_ = 0; cherokee_handler_cgi_t *cgi = HDL_CGI(cgi_base); /* Sanity check: pipe() accessed */ if (unlikely (cgi->pipeInput < 0)) return ret_eof; /* Read the data from the pipe: */ ret = cherokee_buffer_read_from_fd (buffer, cgi->pipeInput, 4096, &read_); TRACE (ENTRIES, "read... ret=%d %d\n", ret, read_); switch (ret) { case ret_eagain: cherokee_thread_deactive_to_polling (HANDLER_THREAD(cgi), HANDLER_CONN(cgi), cgi->pipeInput, FDPOLL_MODE_READ, false); return ret_eagain; case ret_ok: TRACE (ENTRIES, "%d bytes read\n", read_); return ret_ok; case ret_eof: case ret_error: cgi_base->got_eof = true; return ret; default: RET_UNKNOWN(ret); } SHOULDNT_HAPPEN; return ret_error; }
static ret_t read_from_fcgi (cherokee_handler_cgi_base_t *cgi, cherokee_buffer_t *buffer) { ret_t ret; size_t read = 0; cherokee_handler_fcgi_t *fcgi = HDL_FCGI(cgi); ret = cherokee_socket_bufread (&fcgi->socket, &fcgi->write_buffer, DEFAULT_READ_SIZE, &read); switch (ret) { case ret_eagain: ret = cherokee_thread_deactive_to_polling (HANDLER_THREAD(cgi), HANDLER_CONN(cgi), fcgi->socket.socket, FDPOLL_MODE_READ, false); if (unlikely (ret != ret_ok)) { cgi->got_eof = true; return ret_error; } return ret_eagain; case ret_ok: ret = process_buffer (fcgi, &fcgi->write_buffer, buffer); TRACE (ENTRIES, "%d bytes read, buffer.len %d\n", read, buffer->len); if ((ret == ret_ok) && cgi->got_eof && (buffer->len > 0)) { return ret_eof_have_data; } return ret; case ret_eof: case ret_error: cgi->got_eof = true; return ret; default: RET_UNKNOWN(ret); } SHOULDNT_HAPPEN; return ret_error; }
static ret_t match (cherokee_rule_t *rule, cherokee_connection_t *conn, cherokee_config_entry_t *ret_conf) { ret_t ret; /* Call match() in the subrule and invert the result */ ret = cherokee_rule_match (RULE_NOT(rule)->right, conn, ret_conf); switch (ret) { case ret_ok: return ret_not_found; case ret_not_found: return ret_ok; case ret_error: return ret_error; default: RET_UNKNOWN(ret); return ret_error; } }
/* WARNING: all parameters MUST be valid, * NULL pointers lead to a crash. */ ret_t cherokee_socket_read (cherokee_socket_t *socket, char *buf, int buf_size, size_t *pcnt_read) { ret_t ret; int err; ssize_t len; *pcnt_read = 0; /* There must be something to read, otherwise behaviour is undefined * and as we don't want this case, we have to enforce assertions. */ return_if_fail (buf != NULL && buf_size > 0, ret_error); if (unlikely (socket->status == socket_closed)) { TRACE(ENTRIES, "Reading a closed socket: fd=%d (TLS=%d)\n", SOCKET_FD(socket), (socket->is_tls == TLS)); return ret_eof; } if (likely (socket->is_tls != TLS)) { /* Plain read */ do { len = recv (SOCKET_FD(socket), buf, buf_size, 0); } while ((len < 0) && (errno == EINTR)); if (likely (len > 0)) { *pcnt_read = len; return ret_ok; } if (len == 0) { socket->status = socket_closed; return ret_eof; } /* Error handling */ err = SOCK_ERRNO(); TRACE(ENTRIES",read", "Socket read error fd=%d: '%s'\n", SOCKET_FD(socket), strerror(errno)); switch (err) { #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case EAGAIN: return ret_eagain; case EPIPE: #ifdef ENOTCONN case ENOTCONN: #endif case ECONNRESET: socket->status = socket_closed; case ETIMEDOUT: case EHOSTUNREACH: return ret_error; } LOG_ERRNO (errno, cherokee_err_error, CHEROKEE_ERROR_SOCKET_READ, SOCKET_FD(socket)); return ret_error; } else if (socket->cryptor != NULL) { ret = cherokee_cryptor_socket_read (socket->cryptor, buf, buf_size, pcnt_read); switch (ret) { case ret_ok: case ret_error: case ret_eagain: return ret; case ret_eof: socket->status = socket_closed; return ret_eof; default: RET_UNKNOWN(ret); return ret; } } return ret_error; }
/* WARNING: all parameters MUST be valid, * NULL pointers lead to a crash. */ ret_t cherokee_socket_write (cherokee_socket_t *socket, const char *buf, int buf_len, size_t *pcnt_written) { ret_t ret; int err; ssize_t len; *pcnt_written = 0; /* There must be something to send, otherwise behaviour is undefined * and as we don't want this case, we have to enforce assertions. */ return_if_fail (buf != NULL && buf_len > 0, ret_error); if (likely (socket->is_tls != TLS)) { do { len = send (SOCKET_FD(socket), buf, buf_len, 0); } while ((len < 0) && (errno == EINTR)); if (likely (len > 0) ) { /* Return n. of bytes sent. */ *pcnt_written = len; return ret_ok; } if (len == 0) { /* Very strange, socket is ready but nothing * has been written, retry later. */ return ret_eagain; } /* Error handling */ err = SOCK_ERRNO(); switch (err) { #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case EAGAIN: return ret_eagain; case EPIPE: case ECONNRESET: #ifdef ENOTCONN case ENOTCONN: #endif socket->status = socket_closed; case ETIMEDOUT: case EHOSTUNREACH: return ret_error; } LOG_ERRNO (errno, cherokee_err_error, CHEROKEE_ERROR_SOCKET_WRITE, SOCKET_FD(socket)); return ret_error; } else if (socket->cryptor != NULL) { ret = cherokee_cryptor_socket_write (socket->cryptor, (char *)buf, buf_len, pcnt_written); switch (ret) { case ret_ok: case ret_error: case ret_eagain: return ret; case ret_eof: socket->status = socket_closed; return ret_eof; default: RET_UNKNOWN(ret); return ret; } } return ret_error; }
ret_t cherokee_handler_proxy_conn_recv_headers (cherokee_handler_proxy_conn_t *pconn, cherokee_buffer_t *body, cherokee_boolean_t flexible) { ret_t ret; char *end; cuint_t sep_len; size_t size = 0; /* Read */ ret = cherokee_socket_bufread (&pconn->socket, &pconn->header_in_raw, DEFAULT_RECV_SIZE, &size); switch (ret) { case ret_ok: break; case ret_eof: case ret_error: return ret; case ret_eagain: if (cherokee_buffer_is_empty (&pconn->header_in_raw)) { return ret_eagain; } break; default: RET_UNKNOWN(ret); } /* Look for the end of header */ ret = cherokee_find_header_end (&pconn->header_in_raw, &end, &sep_len); switch (ret) { case ret_ok: break; case ret_not_found: return ret_eagain; default: /* Did not success */ if (! flexible) { goto error; } /* Plan B! */ TRACE (ENTRIES, "Header end not found. Being more flexible about malformed headers\n"); ret = find_header_end_flexible (&pconn->header_in_raw, &end, &sep_len); switch (ret) { case ret_ok: break; case ret_not_found: return ret_eagain; default: goto error; } } /* Copy the body if there is any */ size = (pconn->header_in_raw.buf + pconn->header_in_raw.len) - (end + sep_len); cherokee_buffer_add (body, end+sep_len, size); cherokee_buffer_drop_ending (&pconn->header_in_raw, size); return ret_ok; error: LOG_ERROR (CHEROKEE_ERROR_PROXY_HEADER_PARSE, pconn->header_in_raw.len, pconn->header_in_raw.buf); return ret_error; }
ret_t cherokee_post_send_to_socket (cherokee_post_t *post, cherokee_socket_t *sock_in, cherokee_socket_t *sock_out, cherokee_buffer_t *tmp, cherokee_socket_status_t *blocking, cherokee_boolean_t *did_IO) { ret_t ret; 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. Buffered: %d bytes\n", buffer->len); if (! cherokee_buffer_is_empty (buffer)) { ret = do_send_socket (sock_out, buffer, blocking); switch (ret) { case ret_ok: break; case ret_eagain: return ret_eagain; case ret_eof: case ret_error: return ret_error; default: RET_UNKNOWN(ret); return ret_error; } /* Did something, increase timeout */ *did_IO = true; } 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; }
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; }
static ret_t downloader_header_read (cherokee_downloader_t *downloader, cherokee_buffer_t *tmp1, cherokee_buffer_t *tmp2) { ret_t ret; cuint_t len; size_t read_ = 0; cherokee_socket_t *sock = &downloader->socket; cherokee_http_t error_code = http_bad_request; UNUSED(tmp2); ret = cherokee_socket_bufread (sock, &downloader->reply_header, DEFAULT_RECV_SIZE, &read_); switch (ret) { case ret_eof: return ret_eof; case ret_eagain: return ret_eagain; case ret_ok: /* Count */ downloader->info.headers_recv += read_; /* Check the header. Is it complete? */ ret = cherokee_header_has_header (downloader->header, &downloader->reply_header, 0); switch (ret) { case ret_ok: break; case ret_not_found: /* It needs to read more headers ... */ return ret_eagain; default: /* Too many initial CRLF */ return ret_error; } /* Parse the header */ ret = cherokee_header_parse (downloader->header, &downloader->reply_header, &error_code); if (unlikely (ret != ret_ok)) return ret_error; /* Look for the length, it will need to drop out the header from the buffer */ cherokee_header_get_length (downloader->header, &len); /* Maybe it has some body */ if (downloader->reply_header.len > len) { uint32_t body_chunk; /* Skip the CRLF separator and copy the body */ len += 2; body_chunk = downloader->reply_header.len - len; downloader->info.body_recv += body_chunk; cherokee_buffer_add (&downloader->body, downloader->reply_header.buf + len, body_chunk); cherokee_buffer_drop_ending (&downloader->reply_header, body_chunk); } /* Try to read the "Content-Length" response header */ ret = cherokee_header_has_known (downloader->header, header_content_length); if (ret == ret_ok) { cherokee_buffer_clean (tmp1); ret = cherokee_header_copy_known (downloader->header, header_content_length, tmp1); if (ret == ret_ok) { ret = cherokee_atou (tmp1->buf, &downloader->content_length); #ifdef TRACE_ENABLED if (ret == ret_ok) { TRACE (ENTRIES, "Known length: %d bytes\n", downloader->content_length); } else { TRACE (ENTRIES, "Could not parse Content-Length\n"); } #endif } } return ret_ok; case ret_error: /* Opsss.. something has failed */ return ret_error; default: RET_UNKNOWN (ret); return ret; } return ret_error; }