/* WebROaR - Ruby Application Server - http://webroar.in/ * Copyright (C) 2009 Goonj LLC * * 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 // Connection count static unsigned int wr_conn_count = 0; typedef struct { wr_u_short code; char phrase[56]; char message[128]; } wr_http_status_t; static wr_http_status_t http_status [] ={ {100, "100 Continue", ""}, {400, "400 Bad Request", "The request could not be understood by the server."}, {403, "403 Forbidden", "The requested page is forbidden."}, {404, "404 Not Found", "The requested page could not be found."}, {405, "405 Method Not Allowed", "The request method is not allowed."}, {411, "411 Length Required", "The request requires 'Content-Length'."}, {413, "413 Request Entity Too Large", "The request entity is too large"}, {414, "414 Request-URI Too Large", "The request URI is too large"}, {500, "500 Internal Server Error", "The server is facing some error while processing the request. "}, {501, "501 Not Implemented", "The requested method is not implemented"} }; #define WR_RESP_BODY "\r\n\ \r\n\ %s\r\n\ \r\n\

%s

\r\n\

%s

\r\n\


%s-%s\ " #define WR_RESPONSE_ERR_BODY "\r\n\ \r\n\ %s\r\n\ \r\n\

%s

\r\n\

%s

\r\n\
error:%s

%s-%s\ " #define WR_RESP_HEADERS "HTTP/1.1 %s\r\n\ Date: %s\r\n\ Server: %s-%s\r\n\ Content-Type: text/html\r\n\ Connection: close\r\n\ Content-Length: %d\r\n\r\n%s" /** Private function */ /** The response was sent */ void wr_conn_after_write_cb(ebb_connection *connection) { LOG_FUNCTION wr_conn_t* conn = (wr_conn_t*) connection->data; LOG_DEBUG(DEBUG,"Connection = %d, Response = %d, closed = %d", conn->id, conn->resp_to_write, conn->is_closed); wr_str_arr_t *str_arr = wr_string_list_remove(conn->resp); if(str_arr) { wr_string_arr_free(str_arr); } // Check for response chunk if(conn->resp->front && !conn->is_closed) { // Send next response chunck LOG_DEBUG(DEBUG,"Writing response to ebb"); ebb_connection_write(conn->ebb_conn, conn->resp->front->str.str, conn->resp->front->str.len, wr_conn_after_write_cb); } else if(conn->resp_to_write <= 0) { LOG_DEBUG(DEBUG,"response to write is less than 1 keep alive =%d", conn->keep_alive); if(!WR_SVR_KEEP_ALIVE || !conn->keep_alive) { // Close ebb_connection if there is no any pending request LOG_DEBUG(DEBUG,"Closing Connection %d ...", conn->id); ebb_connection_schedule_close(connection); } } } /** The connection got parser error */ void wr_req_parse_err_cb(ebb_connection* connection) { LOG_FUNCTION wr_conn_t* conn = (wr_conn_t*)connection->data; LOG_DEBUG(DEBUG,"Connection id = %d",conn->id); conn->keep_alive = 0; wr_req_invalid(conn, WR_HTTP_STATUS_400); // if(conn->resp_to_write == 0) // conn->resp_to_write = 1; // wr_server_err_response(conn, WR_HTTP_STATUS_400); } /** The ebb connection goes timeout */ int wr_conn_timeout_cb(ebb_connection* connection) { LOG_FUNCTION wr_conn_t* a_conn = (wr_conn_t*) connection->data; LOG_DEBUG(DEBUG,"connection %d, response = %d", a_conn->id, a_conn->resp_to_write); if(a_conn->resp_to_write > 0 || a_conn->resp->front) { return EBB_AGAIN; } if(a_conn->req) { wr_req_free(a_conn->req); } return EBB_STOP; } /** The connection was closed */ void wr_conn_close_cb(ebb_connection* connection) { LOG_FUNCTION wr_conn_t* conn = (wr_conn_t*) connection->data; LOG_DEBUG(DEBUG,"connection %d, response %d ", conn->id, conn->resp_to_write); // Check for pending requests if(conn->resp_to_write <= 0) { // Destroy wr_connection wr_conn_free(conn); } else { // Set altas_connection to closed LOG_DEBUG(DEBUG,"closed flag set %d", conn->id); conn->is_closed = 1; conn->keep_alive = 0; } } /******************************************************** * Connection Function Definition * ********************************************************/ /** Create new Connection */ wr_conn_t* wr_conn_new(wr_svr_t *server) { LOG_FUNCTION wr_conn_t* a_connection = wr_malloc(wr_conn_t); if(a_connection == NULL) { LOG_DEBUG(SEVERE, "Error a_connection is null. Returning ..."); return NULL; } ebb_connection *connection = wr_malloc(ebb_connection); if(connection == NULL) { free(a_connection); LOG_DEBUG(SEVERE, "Error connection is null. Returning ..."); return NULL; } ebb_connection_init(connection); a_connection->id = ++wr_conn_count; a_connection->resp_to_write = 0; a_connection->ebb_conn = connection; a_connection->svr = server; a_connection->resp = wr_string_list_new(); a_connection->is_closed = FALSE; a_connection->keep_alive = FALSE; // a_connection->con_req_err = FALSE; a_connection->req = NULL; connection->data = a_connection; connection->new_request = wr_new_req_cb; connection->on_close = wr_conn_close_cb; connection->on_timeout = wr_conn_timeout_cb; connection->on_request_parse_error = wr_req_parse_err_cb; return a_connection; } /** Destroy Connection */ void wr_conn_free(wr_conn_t *conn) { LOG_FUNCTION LOG_DEBUG(DEBUG,"Connnection id %d", conn->id); if(conn) { if(conn->ebb_conn) { free(conn->ebb_conn); } else { LOG_DEBUG(SEVERE, "Error conn->ebb_conn is null."); } //if(conn->req && !conn->req->conn_err) wr_req_free(conn->req); conn->ebb_conn = NULL; wr_string_list_free(conn->resp); free(conn); } } /** Add response to Connection */ int wr_conn_resp_body_add(wr_conn_t* conn, const char* str, size_t len) { LOG_FUNCTION LOG_DEBUG(DEBUG,"Connection = %d", conn->id); if(!conn->is_closed) { if(wr_string_list_is_empty(conn->resp)) { wr_string_list_add(conn->resp, str, len); LOG_DEBUG(DEBUG, "Writing first chunk. Connection = %d",conn->id); ebb_connection_write(conn->ebb_conn, conn->resp->front->str.str, conn->resp->front->str.len, wr_conn_after_write_cb); } else { LOG_DEBUG(DEBUG, "Adding next chunk in list. Connection = %d", conn->id); wr_string_list_add(conn->resp, str, len); } } else { LOG_DEBUG(DEBUG,"Check response %d", conn->resp_to_write); if(conn->resp_to_write == 0) { wr_conn_free(conn); } } return 0; } /** Allocates and initializes an ebb_connection */ ebb_connection* wr_new_conn_cb(ebb_server* server, struct sockaddr_in* addr) { LOG_FUNCTION // Create new altas_connection wr_conn_t* conn = wr_conn_new(server->data); if(conn == NULL) { LOG_ERROR(WARN,"new_connection_cb() connection object allocation failed. Returning ..."); return NULL; } else { LOG_DEBUG(DEBUG,"new_connection_cb() connection = %d", conn->id); // Return ebb_connection return conn->ebb_conn; } } /** Response generated by Server */ void wr_conn_err_resp(wr_conn_t *conn, wr_resp_status_t resp_code) { LOG_FUNCTION char response_body[WR_LONG_LONG_STR_LEN]; char response_buff[WR_LONG_LONG_STR_LEN*2]; size_t body_len, buff_len; LOG_DEBUG(DEBUG, "response code = %s",http_status[resp_code].phrase); conn->keep_alive = 0; switch(resp_code) { case WR_HTTP_STATUS_100: body_len = 0; buff_len = sprintf(response_buff, WR_RESP_HEADERS, http_status[resp_code].phrase, WR_SERVER, WR_VERSION, body_len, http_status[resp_code].message); break; case WR_HTTP_STATUS_400: case WR_HTTP_STATUS_403: case WR_HTTP_STATUS_404: case WR_HTTP_STATUS_405: case WR_HTTP_STATUS_411: case WR_HTTP_STATUS_413: case WR_HTTP_STATUS_414: case WR_HTTP_STATUS_500: case WR_HTTP_STATUS_501: if(conn->req && conn->req->resp_buf_len > 0) { body_len = sprintf(response_body, WR_RESPONSE_ERR_BODY, http_status[resp_code].phrase,http_status[resp_code].phrase+4, http_status[resp_code].message, conn->req->resp_buf, WR_SERVER, WR_VERSION); } else { body_len = sprintf(response_body, WR_RESP_BODY, http_status[resp_code].phrase,http_status[resp_code].phrase+4, http_status[resp_code].message,WR_SERVER, WR_VERSION); } char current_date[WR_STR_LEN]; get_time(current_date, WR_STR_LEN); buff_len = sprintf(response_buff, WR_RESP_HEADERS, http_status[resp_code].phrase, current_date, WR_SERVER, WR_VERSION, body_len, response_body); break; } wr_conn_resp_body_add(conn, response_buff, buff_len); if(conn->req) { wr_req_t* req = conn->req; if(conn->svr->conf->server->flag&WR_SVR_ACCESS_LOG) { req->resp_body_len = body_len; req->resp_code = http_status[resp_code].code; wr_access_log(req); } //TODO temporary check // conn->resp_to_write --; if(req->wkr) { if(req->using_wkr) { LOG_DEBUG(DEBUG,"Request is with worker, worker would use request structure."); req->conn_err = TRUE; conn->resp_to_write --; } else { LOG_DEBUG(DEBUG,"Request is with worker, but worker would not use request structure."); wr_req_free(req); } } else if(req->app) { // Remove req from application message list LOG_DEBUG(DEBUG,"Request is inserted into Application message queue."); wr_queue_remove(conn->req->app->msg_que, conn->req); wr_req_free(req); } else { LOG_DEBUG(DEBUG,"Request is still in parsing phase."); wr_req_free(req); } } else { conn->resp_to_write --; } }