/* WebROaR - Ruby Application Server - http://webroar.in/
* Copyright (C) 2009 WebROaR
*
* This file is part of WebROaR.
*
* WebROaR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* WebROaR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WebROaR. If not, see .
*/
#include
#include
#include
#include
http_req_t* http_req_new() {
LOG_FUNCTION
http_req_t* req = wr_malloc(http_req_t);
if(req == NULL) {
return NULL;
}
req->bytes_read = req->scgi_header_len = req->req_len = 0;
req->file = NULL;
req->scgi_req = NULL;
return req;
};
void http_req_free(http_req_t** r) {
LOG_FUNCTION
http_req_t* req = *r;
if(req) {
if(req->file) {
fclose(req->file);
unlink(req->file_name);
}
if(req->scgi_req)
scgi_request_free(req->scgi_req);
free(req);
}
*r = NULL;
}
void http_req_set(http_req_t *req) {
LOG_FUNCTION
req->bytes_read = req->scgi_header_len = req->req_len = 0;
if(req->file) {
fclose(req->file);
unlink(req->file_name);
}
if(req->scgi_req)
scgi_request_free(req->scgi_req);
req->file = NULL;
req->scgi_req = NULL;
}
/* pass an allocated buffer and the length to read. this function will try to
* fill the buffer with that length of data read from the body of the request.
* the return value says how much was actually written.
*/
int http_req_body_read(http_req_t *req, char *buf, int len) {
LOG_FUNCTION
size_t read = 0;
if(req->bytes_read >= req->req_len) {
LOG_DEBUG(DEBUG,"Request body reading complete.");
return 0;
}
if(req->file) {
read = fread(buf, sizeof(char), len, req->file);
// FILE *f=fopen("/tmp/wkr.log","a+");
// if(f){
// fwrite(buf, read, sizeof(char), f);
// fclose(f);
// }
req->bytes_read += read;
LOG_DEBUG(DEBUG,"http_req_body_read() from file asked len = %d, actual read =%d bytes sent = %d/%d",
len, read, req->bytes_read, req->req_len);
/* TODO error checking! */
return read;
} else {
char* req_body = req->buf + req->scgi_header_len;
read = wr_ramp(wr_min(len, req->req_len - req->bytes_read));
memcpy( buf, req_body + req->bytes_read, read);
// FILE *f=fopen("/tmp/wkr.log","a+");
// if(f){
// fwrite(buf, read, sizeof(char), f);
// fclose(f);
// }
req->bytes_read += read;
LOG_DEBUG(DEBUG,"http_req_body_read() from buffer asked len = %d, actual read = %d, bytes sent = %d/%d",
len, read, req->bytes_read, req->req_len);
return read;
}
}
/** Read SCGI request body in buffer */
static inline void http_req_body_read_in_buff(http_req_t *req, struct ev_loop *loop, struct ev_io *watcher) {
LOG_FUNCTION
ssize_t read = recv(watcher->fd,
req->buf + req->scgi_header_len + req->bytes_read,
req->req_len - req->bytes_read,
0);
if(read < 0) {
LOG_ERROR(SEVERE,"Error reading header:%s",strerror(errno));
return;
}
if(read == 0) {
ev_io_stop(loop, watcher);
LOG_DEBUG(DEBUG,"peer might closing");
sigproc();
return;
}
req->bytes_read += read;
if(req->bytes_read == req->req_len) {
ev_io_stop(loop, watcher);
req->bytes_read = 0;
http_req_process();
}
}
/** Read SCGI request body in file */
static inline void http_req_body_read_in_file(http_req_t *req, struct ev_loop *loop, struct ev_io *watcher) {
LOG_FUNCTION
char buffer[WR_BUF_SIZE+1];
ssize_t read, write = 0, rv;
read = recv(watcher->fd,
buffer,
wr_min(req->req_len - req->bytes_read, WR_BUF_SIZE),
0);
if(read < 0) {
LOG_ERROR(WARN,"error in request parsing");
return;
}
while(write < read) {
rv = fwrite(buffer + write, sizeof(char), read - write, req->file);
if(rv<0) {
LOG_ERROR(WARN,"error in writing request into file:%s",strerror(errno));
} else {
write += rv;
}
}
req->bytes_read += read;
if(req->bytes_read == req->req_len) {
ev_io_stop(loop, watcher);
req->bytes_read = 0;
rewind(req->file);
http_req_process();
}
}
/** Read SCGI request body */
static void http_req_body_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) {
LOG_FUNCTION
wkr_t *w = (wkr_t*) watcher->data;
http_req_t *req = w->http->req;
LOG_DEBUG(DEBUG,"read_request_body_cb() conn id=%d, Request id=%d",
w->http->conn_id, w->http->req_id);
if(EV_ERROR & revents) {
LOG_ERROR(SEVERE,"read_request_body_cb() got error event, returning.");
return;
}
if (revents & EV_READ) {
if(req->file == NULL) {
http_req_body_read_in_buff(req, loop, watcher);
} else {
http_req_body_read_in_file(req, loop, watcher);
}
}
}
/** Parse SCGI request */
static inline void parser_req_buf(http_req_t *req, struct ev_loop *loop, struct ev_io *watcher) {
LOG_FUNCTION
if(req->scgi_header_len == 0) {
// Fetch header packet length
int i;
for( i = 0 ; i < req->bytes_read; i++) {
if(req->buf[i] == ':')
break;
}
if(i >= req->bytes_read)
return;
req->scgi_header_len = atoi(req->buf) + i + 2;
LOG_DEBUG(DEBUG,"parser_req_buf() header pkt len =%d and header len = %d, rquest_ptr = %d",
req->scgi_header_len,
req->scgi_header_len - i - 2,
req->bytes_read);
}
if(req->bytes_read >= req->scgi_header_len) {
ev_io_stop(loop, watcher);
if(req->buf[req->scgi_header_len-1] != ',') {
LOG_ERROR(WARN,"error in request parsing = %c", req->buf[req->scgi_header_len-1]);
return;
} else {
req->scgi_req = scgi_request_parse(req->buf, req->scgi_header_len);
if(req->scgi_req == NULL) {
LOG_ERROR(SEVERE,"Error in preparing scgi headers");
return;
}
//first header in SCGI request must be content-length
req->req_len = atoi(req->scgi_req->header->value);
req->bytes_read -= req->scgi_header_len;
// Remove SCGI header
scgi_request_header_t *scgi_header = req->scgi_req->header->next;
req->scgi_req->header->next = scgi_header->next;
free(scgi_header);
#ifdef L_DEBUG
// Fetch Connection id and remove header
wkr_t *w = (wkr_t*) watcher->data;
scgi_header = req->scgi_req->header->next;
w->http->conn_id = atoi(scgi_header->value);
req->scgi_req->header->next = scgi_header->next;
free(scgi_header);
// Fetch Request id and remove header
scgi_header = req->scgi_req->header->next;
w->http->req_id = atoi(scgi_header->value);
req->scgi_req->header->next = scgi_header->next;
free(scgi_header);
LOG_DEBUG(DEBUG,"parser_req_buf() content len=%d, conn id=%d, Request id=%d",
req->req_len, w->http->conn_id, w->http->req_id );
#endif
if(req->bytes_read == req->req_len) {
req->bytes_read = 0;
http_req_process();
} else {
if(req->req_len + req->scgi_header_len > WR_BUF_SIZE) {
ssize_t write;
size_t processed_bytes = 0;
sprintf(req->file_name, "/tmp/proc_upload_file_%d", getpid());
req->file = fopen(req->file_name,"w+");
assert(req->file !=NULL);
//reading body part from request buffer
while(processed_bytes < req->bytes_read) {
write = fwrite(req->buf + req->scgi_header_len + processed_bytes,
sizeof(char),
req->bytes_read - processed_bytes,
req->file);
if(write < 0) {
LOG_ERROR(WARN,"Error writing into file:%s",strerror(errno));
} else {
processed_bytes += write;
}
}
}
ev_io_init(watcher, http_req_body_cb, watcher->fd,EV_READ);
ev_io_start(loop,watcher);
}
}
}
}
/** Read SCGI request headers */
void http_req_header_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) {
LOG_FUNCTION
wkr_t *w = (wkr_t*) watcher->data;
http_req_t *req = w->http->req;
if(EV_ERROR & revents) {
LOG_ERROR(SEVERE,"http_req_header_cb() got error event, returning.");
return;
}
if (revents & EV_READ) {
ssize_t read = recv(watcher->fd
, req->buf + req->bytes_read
, WR_BUF_SIZE - req->bytes_read
, 0
);
if(read < 0) {
LOG_ERROR(SEVERE,"Error reading header:%s",strerror(errno));
return;
}
if(read == 0) {
ev_io_stop(loop,watcher);
LOG_DEBUG(DEBUG,"peer might closing");
sigproc();
return;
}
req->bytes_read += read;
parser_req_buf(req, loop, watcher);
}
}