From b0691bfc7943532575dba0cee285838d45072535 Mon Sep 17 00:00:00 2001 From: snowdrop Date: Fri, 15 Oct 2004 13:29:36 +0000 Subject: added mime support and https stream object --- nanohttp/nanohttp-client.c | 959 ++++++++++----------------------------------- nanohttp/nanohttp-client.h | 131 ++++--- nanohttp/nanohttp-common.c | 617 +++++++++++------------------ nanohttp/nanohttp-common.h | 368 +++++++++++++++-- nanohttp/nanohttp-server.c | 587 ++++++++++++++++++++------- nanohttp/nanohttp-server.h | 57 ++- nanohttp/nanohttp-socket.c | 355 +++++++---------- nanohttp/nanohttp-socket.h | 282 ++++++------- 8 files changed, 1608 insertions(+), 1748 deletions(-) diff --git a/nanohttp/nanohttp-client.c b/nanohttp/nanohttp-client.c index 13972a1..3c3ce7f 100644 --- a/nanohttp/nanohttp-client.c +++ b/nanohttp/nanohttp-client.c @@ -1,5 +1,5 @@ /****************************************************************** -* $Id: nanohttp-client.c,v 1.19 2004/09/19 07:05:03 snowdrop Exp $ +* $Id: nanohttp-client.c,v 1.20 2004/10/15 13:29:36 snowdrop Exp $ * * CSOAP Project: A http client/server library in C * Copyright (C) 2003 Ferhat Ayaz @@ -19,10 +19,11 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * -* Email: ayaz@jprogrammer.net +* Email: ferhatayaz@yahoo.com ******************************************************************/ #include + #ifdef HAVE_CONFIG_H #include #endif @@ -31,6 +32,15 @@ #include #include +#ifdef MEM_DEBUG +#include +#endif + +static +int httpc_send_data(httpc_conn_t *conn, const unsigned char* data, size_t size) +{ + return -1; +} /*-------------------------------------------------- FUNCTION: httpc_init @@ -53,15 +63,16 @@ to communicate via http. httpc_conn_t * httpc_new() { + static int counter = 10000; httpc_conn_t *res = (httpc_conn_t *) malloc(sizeof(httpc_conn_t)); hsocket_init(&res->sock); res->header = NULL; - res->url = NULL; res->version = HTTP_1_1; + res->out = NULL; res->_dime_package_nr = 0; res->_dime_sent_bytes = 0; - res->_is_chunked = 0; + res->id = counter++; return res; } @@ -75,17 +86,26 @@ httpc_free(httpc_conn_t * conn) { hpair_t *tmp; - if (conn != NULL) { - hsocket_free(conn->sock); + if (conn == NULL) + return; - while (conn->header != NULL) { - tmp = conn->header; - conn->header = conn->header->next; - hpairnode_free(tmp); - } - free(conn); + while (conn->header != NULL) + { + tmp = conn->header; + conn->header = conn->header->next; + hpairnode_free(tmp); + } + + if (conn->out != NULL) + { + http_output_stream_free(conn->out); + conn->out = NULL; } + + hsocket_free(conn->sock); + free(conn); + } @@ -122,6 +142,23 @@ httpc_set_header(httpc_conn_t * conn, const char *key, const char *value) } +/*-------------------------------------------------- +FUNCTION: httpc_header_add_date +DESC: Adds the current date to the header. +----------------------------------------------------*/ +static +void _httpc_set_error(httpc_conn_t *conn, int errcode, + const char *format, ...) +{ + va_list ap; + + conn->errcode = errcode; + + va_start(ap, format); + vsnprintf(conn->errmsg, 149, format, ap); + va_end(ap); +} + /*-------------------------------------------------- FUNCTION: httpc_header_add_date DESC: Adds the current date to the header. @@ -160,7 +197,7 @@ httpc_send_header(httpc_conn_t * conn) if (p->key && p->value) { sprintf(buffer, "%s: %s\r\n", p->key, p->value); status = hsocket_send(conn->sock, buffer); - if (status != HSOCKET_OK) + if (status != H_OK) return status; } p = p->next; @@ -170,443 +207,6 @@ httpc_send_header(httpc_conn_t * conn) return status; } -/*-------------------------------------------------- -FUNCTION: httpc_set_transfer_encoding -DESC: -----------------------------------------------------*/ -void httpc_set_transfer_encoding(httpc_conn_t *conn, const char* encoding) -{ - httpc_set_header(conn, HEADER_TRANSFER_ENCODING, encoding); - - if (!strcmp(encoding, TRANSFER_ENCODING_CHUNKED)) - conn->_is_chunked = 1; -} - -/*-------------------------------------------------- -FUNCTION: httpc_set_transfer_encoding -DESC: -----------------------------------------------------*/ -int httpc_send_data(httpc_conn_t *conn, const unsigned char* bytes, size_t size) -{ - int status; - char chunked[15]; - - if (conn->_is_chunked) - { - sprintf(chunked,"%x\r\n",size); - status = hsocket_send(conn->sock, chunked); - if (status != HSOCKET_OK) - return status; - } - - status = hsocket_nsend(conn->sock, bytes, size); - - if (conn->_is_chunked) - { - status = hsocket_send(conn->sock, "\r\n"); - if (status != HSOCKET_OK) - return status; - } - - return status; -} - - - -static -hresponse_t * -httpc_receive_header(hsocket_t sock) -{ - hresponse_t *res; - int done; - int i; - int status; - char buffer[HSOCKET_MAX_BUFSIZE]; - char *response; - char *rest; - int rsize; - int restsize; - - /* Receive Response incl. header */ - rsize = restsize = 0; - response = rest = NULL; - done = 0; - - while (!done) { - - status = hsocket_read(sock, buffer, HSOCKET_MAX_BUFSIZE, 0); - - if (status <= 0) { - log_error2("Can not receive response (status:%d)", status); - return NULL; - } - for (i = 0; i < status - 2; i++) { - - if (buffer[i] == '\n') { - if (buffer[i + 1] == '\n') { - - response = (char *) realloc(response, rsize + i + 1); - strncpy(&response[rsize], buffer, i); - response[rsize + i] = '\0'; - rsize += i; - - restsize = status - i - 2; - rest = (char *) malloc(restsize + 1); - strncpy(rest, &buffer[i + 2], restsize); - rest[restsize] = '\0'; - done = 1; - break; - - } else if (buffer[i + 1] == '\r' && buffer[i + 2] == '\n') { - - response = (char *) realloc(response, rsize + i + 1); - strncpy(&response[rsize], buffer, i); - response[rsize + i] = '\0'; - rsize += i; - - restsize = status - i - 3; - rest = (char *) malloc(restsize + 1); - strncpy(rest, &buffer[i + 3], restsize); - rest[restsize] = '\0'; - done = 1; - break; - } - } - } - - if (!done) - rsize += status; - } - - - if (response == NULL) { - log_error1("Header too long!"); - return NULL; - } - res = hresponse_new_from_buffer(response); - if (res == NULL) { - log_error1("Can't create response"); - return NULL; - } - res->bodysize = restsize; - res->body = rest; - - if (res->errcode == 100) { /* continue */ - hresponse_free(res); - res = httpc_receive_header(sock); - } - return res; -} - - -static -int -httpc_receive_with_connection_closed(httpc_conn_t * conn, - hresponse_t * res, - httpc_response_callback cb, - void *userdata) -{ - /* connection closed */ - char *connection_status; - int status; - char buffer[HSOCKET_MAX_BUFSIZE]; - int counter; - - counter = 0; - - /* ================================================= */ - /* Retreive with only "Connection: close" */ - /* ================================================= */ - connection_status = - hpairnode_get(res->header, HEADER_CONNECTION); - - if (connection_status != NULL && - !strcmp(connection_status, "close")) { - - log_debug1("Server communicates with 'Connection: close' !"); - - /* Invoke callback for rest */ - if (res->bodysize > 0) - cb(0, conn, userdata, res->bodysize, res->body); - - - while (1) { - - status = hsocket_read(conn->sock, buffer, HSOCKET_MAX_BUFSIZE, 0); - - if (status == 0) { /* close connection */ - close(conn->sock); - return 0; - } - if (status < 0) { /* error */ - log_error2("Can nor read from socket (status: %d)", status); - return 11; - } - /* Invoke callback */ - cb(counter++, conn, userdata, status, buffer); - } - - return 0; - } - return -1; -} - -static -int -httpc_receive_with_chunked_encoding(httpc_conn_t * conn, - hresponse_t * res, - httpc_response_callback cb, - void *userdata) -{ - /* chunked encoding */ - char *transfer_encoding; - char *chunk_buffer; - int chunk_size; - hbufsocket_t bufsock; - char chunk_size_str[25]; - int chunk_size_cur; - int counter; - char buffer[2]; - - counter = 0; - /* ================================================= */ - /* Retreive with chunked encoding */ - /* ================================================= */ - - /* Check if server communicates with chunked encoding */ - transfer_encoding = - hpairnode_get(res->header, HEADER_TRANSFER_ENCODING); - - if (transfer_encoding != NULL && - !strcmp(transfer_encoding, "chunked")) { - - log_debug1("Server communicates with chunked encoding !"); - - /* initialize buffered socket */ - bufsock.sock = conn->sock; - bufsock.cur = 0; - bufsock.buffer = (char *) res->body; - bufsock.bufsize = res->bodysize; - - chunk_size = 1; - while (chunk_size > 0) { - - /* read chunk size */ - chunk_size_cur = 0; - while (1) { - - if (hbufsocket_read(&bufsock, &chunk_size_str[chunk_size_cur], 1)) { - log_error1("Can not read from socket"); - return 9; - } - - log_debug2("chunk_size_str[chunk_size_cur] = '%c'", - chunk_size_str[chunk_size_cur]); - - if (chunk_size_str[chunk_size_cur] == '\n') { - chunk_size_str[chunk_size_cur] = '\0'; - break; - } - if (chunk_size_str[chunk_size_cur] != '\r' - && chunk_size_str[chunk_size_cur] != ';') { - chunk_size_cur++; - } - /* TODO (#1#): check for chunk_size_cur >= 25 */ - - } /* while (1) */ - - chunk_size = strtol(chunk_size_str, (char **) NULL, 16); /* hex to dec */ - log_debug3("chunk_size: '%s' as dec: '%d'", - chunk_size_str, chunk_size); - - if (chunk_size <= 0) - break; - - chunk_buffer = (char *) malloc(chunk_size + 1); - if (hbufsocket_read(&bufsock, chunk_buffer, chunk_size)) { - log_error1("Can not read from socket"); - return 9; - } - cb(counter++, conn, userdata, chunk_size, chunk_buffer); - free(chunk_buffer); - - /* skip new line */ - buffer[0] = 0; /* reset buffer[0] */ - while (buffer[0] != '\n') { - hbufsocket_read(&bufsock, &buffer[0], 1); - } - - } /* while (chunk_size > 0) */ - - /* rest and response are no longer required */ - - return 0; - - } /* if transfer_encodig */ - return -1; -} - -/* - * returns -1 if server does not communicate with content-length; - */ -static -int -httpc_receive_with_content_length(httpc_conn_t * conn, - hresponse_t * res, - httpc_response_callback cb, - void *userdata) -{ - int counter; - int content_length; - int remain_length; - int recvSize; - char *content_length_str; - char buffer[HSOCKET_MAX_BUFSIZE]; - - /* ================================================= */ - /* Retreive with content-length */ - /* ================================================= */ - - /* Check if server communicates with content-length */ - content_length_str = - hpairnode_get_ignore_case(res->header, HEADER_CONTENT_LENGTH); - - if (content_length_str != NULL) { - - log_debug1("Server communicates with content-length!"); - - /* Invoke callback for rest */ - if (res->bodysize > 0) - cb(0, conn, userdata, res->bodysize, (char *) res->body); - - /* content length */ - content_length = atol(content_length_str); - - counter = 1; - remain_length = content_length - res->bodysize; - while (remain_length > 0) { - if (remain_length >= HSOCKET_MAX_BUFSIZE) { - recvSize = HSOCKET_MAX_BUFSIZE; - } else { - recvSize = remain_length; - } - - if (hsocket_read(conn->sock, buffer, recvSize, 1)) { - log_error1("Can not read from socket!"); - return 9; - } else { - cb(counter++, conn, userdata, recvSize, buffer); - } - - remain_length -= HSOCKET_MAX_BUFSIZE; - - } /* while */ - - /* rest and response are no longer required */ - - return 0; - - } /* if content length */ - return -1; -} - -static -int -httpc_receive_response(httpc_conn_t * conn, - httpc_response_start_callback start_cb, - httpc_response_callback cb, void *userdata) -{ - hresponse_t *res; - int status; - - /* check if chunked */ - if (conn->_is_chunked) - { - status = hsocket_send(conn->sock, "0\r\n\r\n"); - if (status != HSOCKET_OK) - return status; - } - - /* receive header */ - log_verbose1("receiving header"); - res = httpc_receive_header(conn->sock); - if (res == NULL) - return 1; - log_verbose1("header ok"); - - /* Invoke callback */ - start_cb(conn, userdata, res->header, res->spec, - res->errcode, res->desc); - - /* try to receive with content length */ - status = httpc_receive_with_content_length(conn, res, - cb, userdata); - if (status != -1) { - hresponse_free(res); - return status; - } - status = httpc_receive_with_chunked_encoding(conn, res, - cb, userdata); - - if (status != -1) { - hresponse_free(res); - return status; - } - status = httpc_receive_with_connection_closed(conn, res, - cb, userdata); - if (status != -1) { - hresponse_free(res); - return status; - } - log_error1("Unknown server response retreive type!"); - return -1; -} - -static -int -_httpc_receive_response(httpc_conn_t * conn, - httpc_response_start_callback start_cb, - httpc_response_callback cb, void *userdata) -{ - hresponse_t *res; - int status, counter=1; - byte_t buffer[MAX_SOCKET_BUFFER_SIZE+1]; - - /* check if chunked */ - if (conn->_is_chunked) - { - status = hsocket_send(conn->sock, "0\r\n\r\n"); - if (status != HSOCKET_OK) - return status; - } - - /* Create response object */ - res = hresponse_new_from_socket(conn->sock); - if (res == NULL) - { - log_error1("hresponse_new_from_socket() failed!"); - return -1; - } - - /* Invoke callback */ - start_cb(conn, userdata, res->header, res->spec, - res->errcode, res->desc); - - while (http_input_stream_is_ready(res->in)) - { - status = http_input_stream_read(res->in, buffer, MAX_SOCKET_BUFFER_SIZE); - if (status < 0) - { - log_error2("Stream read error: %d", status); - return -1; - } - - cb(counter++, conn, userdata, status, buffer); - } - - return HSOCKET_OK; -} - /*-------------------------------------------------- FUNCTION: httpc_talk_to_server DESC: This function is the heart of the httpc @@ -654,10 +254,10 @@ If success, this function will return 0. ----------------------------------------------------*/ static int -httpc_talk_to_server(hreq_method method, httpc_conn_t * conn, +httpc_talk_to_server(hreq_method_t method, httpc_conn_t * conn, const char *urlstr) { - hurl_t *url; + hurl_t url; char buffer[4096]; int status; @@ -669,23 +269,25 @@ httpc_talk_to_server(hreq_method method, httpc_conn_t * conn, httpc_header_add_date(conn); /* Create url */ - url = hurl_new(urlstr); - if (url == NULL) { + status = hurl_parse(&url, urlstr); + if (status != H_OK) { log_error2("Can not parse URL '%s'", SAVE_STR(urlstr)); - return 2; + return status; } +/* TODO (#1#): Check for HTTP protocol in URL */ + /* Set hostname */ - httpc_set_header(conn, HEADER_HOST, url->host); + httpc_set_header(conn, HEADER_HOST, url.host); /* Open connection */ - status = hsocket_open(&conn->sock, url->host, url->port); - if (status != HSOCKET_OK) { + status = hsocket_open(&conn->sock, url.host, url.port); + if (status != H_OK) { log_error3("Can not open connection to '%s' (status:%d)", - SAVE_STR(url->host), status); + SAVE_STR(url.host), status); return 3; } - status = hsocket_makenonblock(conn->sock); - if (status != HSOCKET_OK) { + status = hsocket_block(conn->sock, 0); + if (status != H_OK) { log_error1("Cannot make socket non-blocking"); return status; } @@ -694,14 +296,14 @@ httpc_talk_to_server(hreq_method method, httpc_conn_t * conn, /* Set GET Header */ sprintf(buffer, "GET %s HTTP/%s\r\n", - (url->context) ? url->context : ("/"), + (url.context[0] != '\0') ? url.context : ("/"), (conn->version == HTTP_1_0)?"1.0":"1.1"); } else if (method == HTTP_REQUEST_POST) { /* Set POST Header */ sprintf(buffer, "POST %s HTTP/%s\r\n", - (url->context) ? url->context : ("/"), + (url.context[0] != '\0') ? url.context : ("/"), (conn->version == HTTP_1_0)?"1.0":"1.1"); } else { @@ -710,14 +312,14 @@ httpc_talk_to_server(hreq_method method, httpc_conn_t * conn, } status = hsocket_send(conn->sock, buffer); - if (status != HSOCKET_OK) { + if (status != H_OK) { log_error2("Can not send request (status:%d)", status); hsocket_close(conn->sock); return 4; } /* Send Header */ status = httpc_send_header(conn); - if (status != HSOCKET_OK) { + if (status != H_OK) { log_error2("Can not send header (status:%d)", status); hsocket_close(conn->sock); return 5; @@ -726,178 +328,100 @@ httpc_talk_to_server(hreq_method method, httpc_conn_t * conn, } - /*-------------------------------------------------- -FUNCTION: httpc_get_cb -DESC: Wraps the httpc_talk_to_server() function -to communicate with GET method. - -See the documentation of httpc_talk_to_server() -for more information. +FUNCTION: httpc_get +DESC: ----------------------------------------------------*/ -int -httpc_get_cb(httpc_conn_t * conn, const char *urlstr, - httpc_response_start_callback start_cb, - httpc_response_callback cb, void *userdata) +hresponse_t * +httpc_get(httpc_conn_t *conn, const char *urlstr) { int status; + hresponse_t *res; status = httpc_talk_to_server(HTTP_REQUEST_GET, conn, urlstr); - if (status != HSOCKET_OK) - return status; + if (status != H_OK) + { + _httpc_set_error(conn, status, "Can not comunicate: %s", urlstr); + return NULL; + } - status = _httpc_receive_response(conn, start_cb, cb, userdata); - return status; + res = hresponse_new_from_socket(conn->sock); + if (res == NULL) + { + _httpc_set_error(conn, -1, "Can not get response from '%s'", urlstr); + return NULL; + } + + return res; } - /*-------------------------------------------------- -FUNCTION: httpc_post_cb -DESC: Wraps the httpc_talk_to_server() function -to communicate with POST method. - -See the documentation of httpc_talk_to_server() -for more information. - -TODO: Add post content rutine +FUNCTION: httpc_post_begin +DESC: Returns H_OK if success ----------------------------------------------------*/ -int -httpc_post_cb(httpc_conn_t * conn, const char *urlstr, - httpc_response_start_callback start_cb, - httpc_response_callback cb, int content_size, - const char *content, void *userdata) +int httpc_post_begin(httpc_conn_t *conn, const char *url) { + int status; - char buffer[255]; - - sprintf(buffer, "%d", content_size); - httpc_set_header(conn, HEADER_CONTENT_LENGTH, buffer); - - status = httpc_talk_to_server(HTTP_REQUEST_POST, conn, urlstr); - if (status != HSOCKET_OK) - return status; - status = hsocket_nsend(conn->sock, content, content_size); - if (status != HSOCKET_OK) + status = httpc_talk_to_server(HTTP_REQUEST_POST, conn, url); + if (status != H_OK) return status; - status = httpc_receive_response(conn, start_cb, cb, userdata); - return status; -} - - -/* ====================================================== - The following - functions are used internally to wrap the httpc_x_cb -(x = get|post) functions. -======================================================*/ -static -void -httpc_custom_res_callback(int counter, httpc_conn_t * conn, - void *userdata, int size, char *buffer) -{ - hresponse_t *res; - - /* get response object */ - res = (hresponse_t *) userdata; - - /* allocate buffersize */ + conn->out = http_output_stream_new(conn->sock, conn->header); - res->bodysize += size; - res->body = (char *) realloc(res->body, res->bodysize + 1); - - memcpy(&(res->body[res->bodysize - size]), buffer, size); - res->body[res->bodysize] = '\0'; -} - - -static -void -httpc_custom_start_callback(httpc_conn_t * conn, void *userdata, - hpair_t * header, const char *spec, - int errcode, const char *desc) -{ - hresponse_t *res; - - /* get response object */ - res = (hresponse_t *) userdata; - - /* set spec */ - if (spec != NULL) { - strcpy(res->spec, spec); - } - /* set errcode */ - res->errcode = errcode; - - /* set desc */ - if (desc != NULL) { - res->desc = (char *) malloc(strlen(desc) + 1); - strcpy(res->desc, desc); - } - /* set header */ - if (header == NULL) { - log_warn1("header is NULL!"); - } - res->header = hpairnode_copy_deep(header); + return H_OK; } /*-------------------------------------------------- -FUNCTION: httpc_get +FUNCTION: httpc_post_begin +DESC: End a "POST" method and receive the response. + You MUST call httpc_post_end() before! ----------------------------------------------------*/ -hresponse_t * -httpc_get(httpc_conn_t * conn, const char *url) +hresponse_t *httpc_post_end(httpc_conn_t *conn) { - int status; - hresponse_t *res; - - res = hresponse_new(); - status = httpc_get_cb(conn, url, httpc_custom_start_callback, - httpc_custom_res_callback, res); - - if (status != 0) { - hresponse_free(res); - return NULL; - } - return res; -} - + int status; + hresponse_t *res; -/*-------------------------------------------------- -FUNCTION: httpc_post -----------------------------------------------------*/ -hresponse_t * -httpc_post(httpc_conn_t * conn, const char *url, - int content_size, const char *content) -{ - int status; - hresponse_t *res; + status = http_output_stream_flush(conn->out); - res = hresponse_new(); - status = httpc_post_cb(conn, url, httpc_custom_start_callback, - httpc_custom_res_callback, content_size, - (char *) content, res); + if (status != H_OK) + { + _httpc_set_error(conn, status, "Can not flush output stream"); + return NULL; + } - if (status != 0) { - hresponse_free(res); - return NULL; + res = hresponse_new_from_socket(conn->sock); + if (res == NULL) + { + _httpc_set_error(conn, -1, "Can not get response "); + return NULL; } + return res; } + /* --------------------------------------------------- DIME support functions httpc_dime_* function set -----------------------------------------------------*/ int httpc_dime_begin(httpc_conn_t *conn, const char *url) { int status; + httpc_set_header(conn, HEADER_CONTENT_TYPE, "application/dime"); status = httpc_talk_to_server(HTTP_REQUEST_POST, conn, url); - return status; + if (status != H_OK) + return status; + + conn->out = http_output_stream_new(conn->sock, conn->header); + + return H_OK; } static _print_binary_ascii(int n) @@ -946,7 +470,7 @@ int httpc_dime_next(httpc_conn_t* conn, long content_length, const char *dime_options, int last) { int status, tmp; - unsigned char header[12]; + byte_t header[12]; for (tmp=0;tmp<12;tmp++) header[tmp]=0; @@ -983,55 +507,47 @@ int httpc_dime_next(httpc_conn_t* conn, long content_length, _print_binary_ascii32(header[4], header[5], header[6], header[7]); _print_binary_ascii32(header[8], header[9], header[10], header[11]); - status = httpc_send_data(conn, header, 12); - if (status != HSOCKET_OK) + status = http_output_stream_write(conn->out, header, 12); + if (status != H_OK) return status; - status = httpc_send_data(conn, (const unsigned char*)dime_options, strlen(dime_options)); - if (status != HSOCKET_OK) + status = http_output_stream_write(conn->out, (const byte_t*)dime_options, strlen(dime_options)); + if (status != H_OK) return status; - status = httpc_send_data(conn, (const unsigned char*)id, strlen(id)); - if (status != HSOCKET_OK) + status = http_output_stream_write(conn->out, (const byte_t*)id, strlen(id)); + if (status != H_OK) return status; - status = httpc_send_data(conn, (const unsigned char*)content_type, strlen(content_type)); - if (status != HSOCKET_OK) + status = http_output_stream_write(conn->out, (const byte_t*)content_type, strlen(content_type)); + if (status != H_OK) return status; return status; } -int httpc_dime_send_data(httpc_conn_t* conn, int size, unsigned char* data) -{ - return httpc_send_data(conn, data, size); -} - -int httpc_dime_get_response_cb(httpc_conn_t *conn, - httpc_response_start_callback start_cb, - httpc_response_callback cb, void *userdata) -{ - int status; - status = httpc_receive_response(conn, start_cb, cb, userdata); - return status; -} - - -hresponse_t* httpc_dime_get_response(httpc_conn_t *conn) +hresponse_t* httpc_dime_end(httpc_conn_t *conn) { int status; hresponse_t *res; - res = hresponse_new(); - status = httpc_dime_get_response_cb(conn, httpc_custom_start_callback, - httpc_custom_res_callback, res); + /* Flush put stream */ + status = http_output_stream_flush(conn->out); - if (status != 0) { - hresponse_free(res); - return NULL; - } + if (status != H_OK) + { + _httpc_set_error(conn, status, "Can not flush output stream"); + return NULL; + } + res = hresponse_new_from_socket(conn->sock); + if (res == NULL) + { + _httpc_set_error(conn, -1, "Can not get response "); + return NULL; + } + return res; } @@ -1043,11 +559,11 @@ hresponse_t* httpc_dime_get_response(httpc_conn_t *conn) static void _httpc_mime_get_boundary(httpc_conn_t *conn, char *dest) { - sprintf(dest, "---=_NH_%p", conn); + sprintf(dest, "---=.Part_NH_%d", conn->id); log_verbose2("boundary= \"%s\"", dest); } -int httpc_mime_post_begin(httpc_conn_t *conn, const char *url, +int httpc_mime_begin(httpc_conn_t *conn, const char *url, const char* related_start, const char* related_start_info, const char* related_type) @@ -1086,12 +602,12 @@ int httpc_mime_post_begin(httpc_conn_t *conn, const char *url, httpc_set_header(conn, HEADER_CONTENT_TYPE, buffer); - status = httpc_talk_to_server(HTTP_REQUEST_POST, conn, url); + status = httpc_post_begin(conn, url); return status; } -int httpc_mime_post_next(httpc_conn_t *conn, +int httpc_mime_next(httpc_conn_t *conn, const char* content_id, const char* content_type, const char* transfer_encoding) @@ -1105,9 +621,10 @@ int httpc_mime_post_next(httpc_conn_t *conn, sprintf(buffer, "\r\n--%s\r\n", boundary); /* Send boundary */ - status = httpc_send_data(conn, (const unsigned char*)buffer, strlen(buffer)); - /* status = hsocket_send(conn->sock, buffer);*/ - if (status != HSOCKET_OK) + status = http_output_stream_write(conn->out, + (const byte_t*)buffer, strlen(buffer)); + + if (status != H_OK) return status; /* Send Content header */ @@ -1116,152 +633,96 @@ int httpc_mime_post_next(httpc_conn_t *conn, HEADER_CONTENT_TRANSFER_ENCODING, transfer_encoding, HEADER_CONTENT_ID, content_id); - status = httpc_send_data(conn, (const unsigned char*)buffer, strlen(buffer)); + status = http_output_stream_write(conn->out, + (const byte_t*)buffer, strlen(buffer)); return status; } -int httpc_mime_post_send(httpc_conn_t *conn, size_t size, const unsigned char* data) +hresponse_t *httpc_mime_end(httpc_conn_t *conn) { - int status; - char buffer[15]; - - status = httpc_send_data(conn, (const unsigned char*)data, size); - if (status != HSOCKET_OK) - return status; - - - return status; -} - - -int httpc_mime_post_end_cb(httpc_conn_t *conn, - httpc_response_start_callback start_cb, - httpc_response_callback cb, void *userdata) -{ - + hresponse_t *res; int status; char buffer[512]; char boundary[75]; - char chunked[15]; /* Get the boundary string */ _httpc_mime_get_boundary(conn, boundary); sprintf(buffer, "\r\n--%s--\r\n\r\n", boundary); /* Send boundary */ - status = httpc_send_data(conn, (unsigned char*)buffer, strlen(buffer)); - if (status != HSOCKET_OK) - return status; - - /*status = hsocket_send(conn->sock, buffer);*/ - - status = httpc_receive_response(conn, start_cb, cb, userdata); - return status; -} + status = http_output_stream_write(conn->out, + (const byte_t*)buffer, strlen(buffer)); + if (status != H_OK) + return NULL; -hresponse_t *httpc_mime_post_end(httpc_conn_t *conn) -{ - int status; - hresponse_t *res; + /* Flush put stream */ + status = http_output_stream_flush(conn->out); - res = hresponse_new(); - status = httpc_mime_post_end_cb(conn, httpc_custom_start_callback, - httpc_custom_res_callback, res); + if (status != H_OK) + { + _httpc_set_error(conn, status, "Can not flush output stream"); + return NULL; + } - if (status != 0) { - hresponse_free(res); - return NULL; + res = hresponse_new_from_socket(conn->sock); + if (res == NULL) + { + _httpc_set_error(conn, -1, "Can not get response "); + return NULL; } - + return res; -} - - -/* - * POST Module - */ - - -/*-------------------------------------------------- -FUNCTION: httpc_post_open -----------------------------------------------------*/ -int -httpc_post_open(httpc_conn_t * conn, const char *urlstr) -{ - int status; - - httpc_set_header(conn, HEADER_TRANSFER_ENCODING, "chunked"); - - status = httpc_talk_to_server(HTTP_REQUEST_POST, conn, urlstr); - return status; } -/*-------------------------------------------------- -FUNCTION: httpc_post_send -----------------------------------------------------*/ -int -httpc_post_send(httpc_conn_t * conn, - const char *buffer, - int bufsize) +/** + Send boundary and part header and continue + with next part +*/ +int +httpc_mime_send_file (httpc_conn_t * conn, + const char *content_id, + const char *content_type, + const char *transfer_encoding, const char *filename) { - char hexsize[100]; /* chunk size in hex */ - int status; - - sprintf(hexsize, "%x\r\n", bufsize); - - status = hsocket_send(conn->sock, hexsize); - if (status != HSOCKET_OK) - return status; - - status = hsocket_nsend(conn->sock, buffer, bufsize); - if (status != HSOCKET_OK) - return status; + int status; + FILE *fd = fopen (filename, "rb"); + byte_t buffer[MAX_FILE_BUFFER_SIZE]; + size_t size; - status = hsocket_send(conn->sock, "\r\n"); + if (fd == NULL) + return FILE_ERROR_OPEN; - return status; -} + status = + httpc_mime_next(conn, content_id, content_type, transfer_encoding); + if (status != H_OK) + { + fclose (fd); + return status; + } -/*-------------------------------------------------- -FUNCTION: httpc_post_finish_cb -----------------------------------------------------*/ -int -httpc_post_finish_cb(httpc_conn_t * conn, - httpc_response_start_callback start_cb, - httpc_response_callback cb, - void *userdata) -{ - int status; + while (!feof (fd)) + { + size = fread (buffer, 1, MAX_FILE_BUFFER_SIZE, fd); + if (size == -1) + { + fclose (fd); + return FILE_ERROR_READ; + } - status = hsocket_send(conn->sock, "0\r\n\r\n"); - if (status != HSOCKET_OK) - return status; + status = http_output_stream_write (conn->out, buffer, size); + if (status != H_OK) { + fclose (fd); + return status; + } + } - status = httpc_receive_response(conn, start_cb, cb, userdata); - return status; + fclose (fd); + return H_OK; } -/*-------------------------------------------------- -FUNCTION: httpc_post_finish -----------------------------------------------------*/ -hresponse_t * -httpc_post_finish(httpc_conn_t * conn) -{ - int status; - hresponse_t *res; - - res = hresponse_new(); - status = httpc_post_finish_cb(conn, httpc_custom_start_callback, - httpc_custom_res_callback, res); - if (status != 0) { - hresponse_free(res); - return NULL; - } - return res; -} diff --git a/nanohttp/nanohttp-client.h b/nanohttp/nanohttp-client.h index 6a53f9d..8bd8579 100644 --- a/nanohttp/nanohttp-client.h +++ b/nanohttp/nanohttp-client.h @@ -1,5 +1,5 @@ /****************************************************************** - * $Id: nanohttp-client.h,v 1.8 2004/09/19 07:05:03 snowdrop Exp $ + * $Id: nanohttp-client.h,v 1.9 2004/10/15 13:29:36 snowdrop Exp $ * * CSOAP Project: A http client/server library in C * Copyright (C) 2003 Ferhat Ayaz @@ -19,7 +19,7 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * - * Email: ayaz@jprogrammer.net + * Email: ferhatayaz@yahoo.com ******************************************************************/ #ifndef NANO_HTTP_CLIENT_H #define NANO_HTTP_CLIENT_H @@ -27,13 +27,13 @@ #include #include -#include +#include typedef struct httpc_conn { hsocket_t sock; hpair_t *header; - hurl_t *url; + hurl_t url; http_version_t version; /* -1 : last dime package @@ -42,44 +42,58 @@ typedef struct httpc_conn */ int _dime_package_nr; long _dime_sent_bytes; - int _is_chunked; + int errcode; + char errmsg[150]; + http_output_stream_t *out; + int id; /* uniq id */ }httpc_conn_t; -/* - PROTOTYPE: - void my_callback(int counter, httpc_conn_t* conn, - void *userdata, int size, char *buffer) - */ -typedef void (*httpc_response_callback)(int, httpc_conn_t*, void*,int,char*); -/* - void my_start_callback(httpc_conn_t *conn, void *userdata, - hpair_t *header, const char *spec, - int errcode, const char *desc) - */ -typedef void (*httpc_response_start_callback)(httpc_conn_t*, void*, hpair_t*, - const char*, int, const char*); - -int httpc_init(int argc, char *argv[]); +/* -------------------------------------------------------------- + HTTP CLIENT MODULE RELATED FUNCTIONS + ---------------------------------------------------------------*/ + +/** + initialize the httpc_* module +*/ +hstatus_t httpc_init(int argc, char *argv[]); + +/** + Creates a new connection +*/ httpc_conn_t* httpc_new(); + +/** + Release a connections +*/ void httpc_free(httpc_conn_t* conn); +/** + Set header element (key,value) pair. +*/ int httpc_set_header(httpc_conn_t *conn, const char* key, const char* value); -void httpc_set_transfer_encoding(httpc_conn_t *conn, const char* encoding); +/** + Invoke a "GET" method request and receive the response +*/ hresponse_t *httpc_get(httpc_conn_t *conn, const char *url); -hresponse_t *httpc_post(httpc_conn_t *conn, const char *url, - int conten_size, const char *content); -int httpc_get_cb(httpc_conn_t *conn, const char *url, - httpc_response_start_callback start_cb, - httpc_response_callback cb, void *userdata); - +/** + Start a "POST" method request + Returns: HSOCKET_OK or error flag +*/ +int httpc_post_begin(httpc_conn_t *conn, const char *url); + +/** + End a "POST" method and receive the response. + You MUST call httpc_post_end() before! +*/ +hresponse_t *httpc_post_end(httpc_conn_t *conn); + -int httpc_post_cb(httpc_conn_t *conn, const char *url, - httpc_response_start_callback start_cb, - httpc_response_callback cb, int content_size, - const char *content, void *userdata); +/* -------------------------------------------------------------- + DIME RELATED FUNCTIONS + ---------------------------------------------------------------*/ /* DIME support httpc_dime_* function set @@ -88,47 +102,50 @@ int httpc_dime_begin(httpc_conn_t *conn, const char *url); int httpc_dime_next(httpc_conn_t* conn, long content_length, const char *content_type, const char *id, const char *dime_options, int last); -int httpc_dime_send_data(httpc_conn_t* conn, int size, unsigned char* data); -hresponse_t* httpc_dime_get_response(httpc_conn_t *conn); -int httpc_dime_get_response_cb(httpc_conn_t *conn, - httpc_response_start_callback start_cb, - httpc_response_callback cb, void *userdata); +hresponse_t* httpc_dime_end(httpc_conn_t *conn); + +/* -------------------------------------------------------------- + MIME RELATED FUNCTIONS + ---------------------------------------------------------------*/ /* MIME support httpc_mime_* function set */ -int httpc_mime_post_begin(httpc_conn_t *conn, const char *url, +/** + Begin MIME multipart/related POST request + Returns: HSOCKET_OK or error flag +*/ +int httpc_mime_begin(httpc_conn_t *conn, const char *url, const char* related_start, const char* related_start_info, const char* related_type); -int httpc_mime_post_next(httpc_conn_t *conn, +/** + Send boundary and part header and continue + with next part +*/ +int httpc_mime_next(httpc_conn_t *conn, const char* content_id, const char* content_type, const char* transfer_encoding); -int httpc_mime_post_send(httpc_conn_t *conn, size_t size, const unsigned char* data); -hresponse_t *httpc_mime_post_end(httpc_conn_t *conn); -int httpc_mime_post_end_cb(httpc_conn_t *conn, - httpc_response_start_callback start_cb, - httpc_response_callback cb, void *userdata); - +/** + Finish MIME request and get the response +*/ +hresponse_t *httpc_mime_end(httpc_conn_t *conn); -/* - Chunked POST Module - */ - -/* Returns 0 if success, >0 otherwise */ -/* do not use this -int httpc_post_open(httpc_conn_t *conn, const char *url); - -int httpc_post_send(httpc_conn_t *conn, const char* buffer, int bufsize); -hresponse_t *httpc_post_finish(httpc_conn_t *conn); -int httpc_post_finish_cb(httpc_conn_t *conn, - httpc_response_start_callback start_cb, - httpc_response_callback cb, void *userdata); +/** + Send boundary and part header and continue + with next part */ +int +httpc_mime_send_file (httpc_conn_t * conn, + const char *content_id, + const char *content_type, + const char *transfer_encoding, + const char *filename); #endif + diff --git a/nanohttp/nanohttp-common.c b/nanohttp/nanohttp-common.c index fb67595..e31c20f 100644 --- a/nanohttp/nanohttp-common.c +++ b/nanohttp/nanohttp-common.c @@ -1,5 +1,5 @@ /****************************************************************** -* $Id: nanohttp-common.c,v 1.12 2004/09/19 07:05:03 snowdrop Exp $ +* $Id: nanohttp-common.c,v 1.13 2004/10/15 13:29:36 snowdrop Exp $ * * CSOAP Project: A http client/server library in C * Copyright (C) 2003 Ferhat Ayaz @@ -23,40 +23,15 @@ ******************************************************************/ #include -#include + #include #include #include - -#ifdef WIN32 #include -static char * -strtok_r(char *s, const char *delim, char **save_ptr) -{ - char *token; - - if (s == NULL) - s = *save_ptr; - /* Scan leading delimiters. */ - s += strspn(s, delim); - if (*s == '\0') - return NULL; - - /* Find the end of the token. */ - token = s; - s = strpbrk(token, delim); - if (s == NULL) - /* This token finishes the string. */ - *save_ptr = strchr(token, '\0'); - else { - /* Terminate the token and make *SAVE_PTR point past it. */ - *s = '\0'; - *save_ptr = s + 1; - } - return token; -} +#ifdef MEM_DEBUG +#include #endif static log_level_t loglevel = HLOG_DEBUG; @@ -144,10 +119,9 @@ log_error(const char *FUNC, const char *format,...) } -/* - * ----------------------------------------- FUNCTION: strcmpigcase - * ------------------------------------------ - */ +/* ----------------------------------------- + FUNCTION: strcmpigcase + ------------------------------------------*/ int strcmpigcase(const char *s1, const char *s2) { @@ -364,23 +338,22 @@ hurl_dump(const hurl_t * url) log_error1("url is NULL!"); return; } - log_verbose2("PROTOCOL : %s", SAVE_STR(url->protocol)); - log_verbose2(" HOST : %s", SAVE_STR(url->host)); + log_verbose2("PROTOCOL : %d", url->protocol); + log_verbose2(" HOST : %s", url->host); log_verbose2(" PORT : %d", url->port); - log_verbose2(" CONTEXT : %s", SAVE_STR(url->context)); + log_verbose2(" CONTEXT : %s", url->context); } -hurl_t * -hurl_new(const char *urlstr) +int hurl_parse(hurl_t* url, const char *urlstr) { int iprotocol; int ihost; int iport; int len; int size; - hurl_t *url; char tmp[8]; + char protocol[1024]; iprotocol = 0; len = strlen(urlstr); @@ -392,17 +365,17 @@ hurl_new(const char *urlstr) if (iprotocol == 0) { log_error1("no protocol"); - return NULL; + return URL_ERROR_NO_PROTOCOL; } if (iprotocol + 3 >= len) { log_error1("no host"); - return NULL; + return URL_ERROR_NO_HOST; } if (urlstr[iprotocol] != ':' && urlstr[iprotocol + 1] != '/' && urlstr[iprotocol + 2] != '/') { log_error1("no protocol"); - return NULL; + return URL_ERROR_NO_PROTOCOL; } /* find host */ ihost = iprotocol + 3; @@ -414,7 +387,7 @@ hurl_new(const char *urlstr) if (ihost == iprotocol + 1) { log_error1("no host"); - return NULL; + return URL_ERROR_NO_HOST; } /* find port */ iport = ihost; @@ -425,14 +398,20 @@ hurl_new(const char *urlstr) } } } - url = (hurl_t *) malloc(sizeof(hurl_t)); - - url->protocol = (char *) malloc(sizeof(char) * iprotocol + 1); - strncpy(url->protocol, urlstr, iprotocol); - url->protocol[iprotocol] = '\0'; - size = ihost - iprotocol - 3; - url->host = (char *) malloc(sizeof(char) * size + 1); + /* find protocol */ + strncpy(protocol, urlstr, iprotocol); + protocol[iprotocol] = '\0'; + if (strcmpigcase(protocol, "http")) + url->protocol = PROTOCOL_HTTP; + else if (strcmpigcase(protocol, "https")) + url->protocol = PROTOCOL_HTTPS; + else if (strcmpigcase(protocol, "ftp")) + url->protocol = PROTOCOL_FTP; + else return URL_ERROR_UNKNOWN_PROTOCOL; + + /* TODO (#1#): add max of size and URL_MAX_HOST_SIZE */ + size = ihost - iprotocol - 3; strncpy(url->host, &urlstr[iprotocol + 3], size); url->host[size] = '\0'; @@ -441,402 +420,250 @@ hurl_new(const char *urlstr) strncpy(tmp, &urlstr[ihost + 1], size); url->port = atoi(tmp); } else { - url->port = 80; + switch (url->protocol) { + case PROTOCOL_HTTP: + url->port = URL_DEFAULT_PORT_HTTP; + break; + case PROTOCOL_HTTPS: + url->port = URL_DEFAULT_PORT_HTTPS; + break; + case PROTOCOL_FTP: + url->port = URL_DEFAULT_PORT_FTP; + break; + } } len = strlen(urlstr); if (len > iport) { - size = len - iport; - url->context = (char *) malloc(sizeof(char) * size + 1); + /* TODO (#1#): find max of size and URL_MAX_CONTEXT_SIZE */ + size = len - iport; strncpy(url->context, &urlstr[iport], size); url->context[size] = '\0'; } else { - url->context = NULL; + url->context[0] = '\0'; } hurl_dump(url); - return url; -} - - -void -hurl_free(hurl_t * url) -{ - if (url == NULL) - return; - - free(url->protocol); - free(url->host); - free(url->context); - - free(url); + return H_OK; } -/* request stuff */ +/* Content-type stuff */ -/* - * ----------------------------------------------------- FUNCTION: - * hrequest_new_from_buffer - * ----------------------------------------------------- - */ -hrequest_t * -hrequest_new_from_buffer(char *data) +content_type_t *content_type_new(const char* content_type_str) { - hrequest_t *req; - hpair_t *hpair = NULL, *qpair = NULL, *tmppair = NULL; - - char *tmp; - char *tmp2; - char *saveptr; - char *saveptr2; - char *saveptr3; - char *result; - char *key; - char *value; - char *opt_key; - char *opt_value; - int firstline = 1; - - req = (hrequest_t *) malloc(sizeof(hrequest_t)); - - req->method = NULL; - req->spec = NULL; - req->path = NULL; - req->query = NULL; - req->header = NULL; - - tmp = data; - - for (;;) { - result = (char *) strtok_r(tmp, "\n", &saveptr); - tmp = saveptr; - - if (result == NULL) - break; - - if (firstline) { - firstline = 0; - tmp2 = result; - - /* parse [GET|POST] [PATH] [SPEC] */ - key = (char *) strtok_r(tmp2, " ", &saveptr2); - - /* save method (get or post) */ - tmp2 = saveptr2; - if (key != NULL) { - req->method = (char *) malloc(strlen(key) + 1); - strcpy(req->method, key); - } - /* below is key the path and tmp2 the spec */ - key = (char *) strtok_r(tmp2, " ", &saveptr2); - - /* save spec */ - tmp2 = saveptr2; - if (tmp2 != NULL) { - req->spec = (char *) malloc(strlen(tmp2) + 1); - strcpy(req->spec, tmp2); - } - /* - * parse and save path+query parse: - * /path/of/target?key1=value1&key2=value2... - */ - - if (key != NULL) { - tmp2 = key; - key = (char *) strtok_r(tmp2, "?", &saveptr2); - tmp2 = saveptr2; - - /* save path */ - req->path = (char *) malloc(strlen(key) + 1); - strcpy(req->path, key); - - /* parse options */ - for (;;) { - key = (char *) strtok_r(tmp2, "&", &saveptr2); - tmp2 = saveptr2; - - if (key == NULL) - break; - - opt_key = (char *) strtok_r(key, "=", &saveptr3); - opt_value = saveptr3; - - if (opt_value == NULL) - opt_value = ""; - - /* create option pair */ - if (opt_key != NULL) { - tmppair = (hpair_t *) malloc(sizeof(hpair_t)); - - if (req->query == NULL) { - req->query = qpair = tmppair; - } else { - qpair->next = tmppair; - qpair = tmppair; - } - - /* fill hpairnode_t struct */ - qpair->next = NULL; - qpair->key = (char *) malloc(strlen(opt_key) + 1); - qpair->value = (char *) malloc(strlen(opt_value) + 1); - - strcpy(qpair->key, opt_key); - strcpy(qpair->value, opt_value); - - } - } - } - } else { + hpair_t *pair = NULL, *last = NULL; + content_type_t * ct; + char ch, key[256], value[256]; + int inQuote = 0, i=0, c=0, begin=0, len; + int mode = 0; + /* 0: searching ';' + 1: process key + 2: process value + */ + + + /* Create object */ + ct = (content_type_t*)malloc(sizeof(content_type_t)); + ct->params = NULL; + + len = strlen(content_type_str); + while (i <= len) + { + if (i != len) + ch = content_type_str[i++]; + else + { + ch = ' '; i++; + } - /* parse "key: value" */ - tmp2 = result; - key = (char *) strtok_r(tmp2, ": ", &saveptr2); - value = saveptr2; + switch (mode) + { + case 0: + + if (ch == ';') + { + ct->type[c] = '\0'; + c = 0; + mode = 1; + } + else if (ch != ' ' && ch != '\t' && ch != '\r') + ct->type[c++] = ch; + break; + + case 1: + + if (ch == '=') + { + key[c] = '\0'; + c = 0; + mode = 2; + } + else if (ch != ' ' && ch != '\t' && ch != '\r') + key[c++] = ch; + break; + + case 2: + + if (ch != ' ') begin = 1; + + if ((ch == ' ' || ch == ';') && !inQuote && begin) + { + value[c] = '\0'; + + pair = hpairnode_new(key, value, NULL); + if (ct->params == NULL) + ct->params = pair; + else + last->next = pair; + last = pair; + + c = 0; + begin = 0; + mode = 1; + } + else if (ch == '"') + inQuote = !inQuote; + else if (begin && ch != '\r') + value[c++] = ch; + + break; - /* create pair */ - tmppair = (hpair_t *) malloc(sizeof(hpair_t)); + } + } - if (req->header == NULL) { - req->header = hpair = tmppair; - } else { - hpair->next = tmppair; - hpair = tmppair; - } + return ct; +} - /* fill pairnode_t struct */ - hpair->next = NULL; - hpair->key = (char *) malloc(strlen(key) + 1); - hpair->value = (char *) malloc(strlen(value) + 1); - strcpy(hpair->key, key); - strcpy(hpair->value, value); - } - } +void content_type_free(content_type_t *ct) +{ + if (!ct) return; - return req; + hpairnode_free_deep(ct->params); + free(ct); } -void -hrequest_free(hrequest_t * req) +part_t *part_new(const char *id, const char* filename, + const char* content_type, const char* transfer_encoding, part_t *next) { - if (req == NULL) - return; - - free(req->method); - free(req->path); - free(req->spec); - - hpairnode_free_deep(req->header); - hpairnode_free_deep(req->query); + part_t *part = (part_t*)malloc(sizeof(part_t)); + part->header = NULL; + part->next = next; + strcpy(part->id, id); + strcpy(part->filename, filename); + if (content_type) + strcpy(part->content_type, content_type); + else + part->content_type[0] = '\0'; + + part->header = hpairnode_new(HEADER_CONTENT_ID, id, part->header); + /* TODO (#1#): encoding is always binary. implement also others! */ +/* part->header = hpairnode_new(HEADER_CONTENT_TRANSFER_ENCODING, "binary", part->header);*/ + + strcpy(part->transfer_encoding, transfer_encoding?transfer_encoding:"binary"); + + if (content_type) { + part->header = hpairnode_new(HEADER_CONTENT_TYPE, content_type, part->header); + } else { + /* TODO (#1#): get content-type from mime type list */ + } + return part; } -/* response stuff */ - -/* - * ------------------------------------------- FUNCTION: hresponse_new - * --------------------------------------------- - */ -hresponse_t * -hresponse_new() +attachments_t *attachments_new() /* should be used internally */ { - hresponse_t *res; - - /* create response object */ - res = (hresponse_t *) malloc(sizeof(hresponse_t)); - res->spec[0] = '\0'; - res->errcode = -1; - res->desc = NULL; - res->header = NULL; - res->body = NULL; - res->bodysize = 0; - - return res; + attachments_t *attachments= (attachments_t*)malloc(sizeof(attachments_t)); + attachments->parts = NULL; + attachments->last = NULL; + attachments->root_part = NULL; + + return attachments; } -static -hresponse_t * -_hresponse_parse_header(const char *buffer) +void attachments_add_part(attachments_t *attachments, part_t *part) { - hresponse_t *res; - char *s1, *s2, *str; - - /* create response object */ - res = hresponse_new(); - - /* *** parse spec *** */ - /* [HTTP/1.1 | 1.2] [CODE] [DESC] */ - - /* stage 1: HTTP spec */ - str = (char *) strtok_r((char *) buffer, " ", &s2); - s1 = s2; - if (str == NULL) { - log_error1("Parse error"); - return NULL; - } - strncpy(res->spec, str, 10); - - /* stage 2: http code */ - str = (char *) strtok_r(s1, " ", &s2); - s1 = s2; - if (str == NULL) { - log_error1("Parse error"); - return NULL; - } - res->errcode = atoi(str); - - /* stage 3: description text */ - str = (char *) strtok_r(s1, "\r\n", &s2); - s1 = s2; - if (str == NULL) { - log_error1("Parse error"); - return NULL; - } - res->desc = (char *) malloc(strlen(str) + 1); - strcpy(res->desc, str); - res->desc[strlen(str)] = '\0'; - - /* *** parse header *** */ - /* [key]: [value] */ - for (;;) { - str = strtok_r(s1, "\n", &s2); - s1 = s2; - - /* check if header ends without body */ - if (str == NULL) { - return res; - } - /* check also for end of header */ - if (!strcmp(str, "\r")) { - break; - } - str[strlen(str) - 1] = '\0'; - res->header = hpairnode_parse(str, ":", res->header); - } - - /* return response object */ - return res; + /* paranoya check */ + if (!attachments) + return; + + if (attachments->last) + attachments->last->next = part; + else + attachments->parts = part; + + attachments->last = part; } -hresponse_t * -hresponse_new_from_socket(hsocket_t sock) +/* + Free a mime message +*/ +void attachments_free(attachments_t *message) { - int i=0, status; - hresponse_t *res; - char buffer[MAX_HEADER_SIZE+1]; + part_t *tmp, *part; - /* Read header */ - while (i 3) - { - if (!strcmp(&(buffer[i-1]), "\n\n") || - !strcmp(&(buffer[i-2]), "\n\r\n")) - break; - } - i++; - } + if (message == NULL) + return; - /* Create response */ - res = _hresponse_parse_header(buffer); - if (res == NULL) - { - log_error1("Header parse error"); - return NULL; + part = message->parts; + while (part) { + tmp = part->next; + mime_part_free(part); + part= tmp; } - /* Create input stream */ - res->in = http_input_stream_new(sock, res->header); - - return res; +/* TODO (#1#): HERE IS A BUG!!!! */ +/* free(message);*/ } -/* - * ------------------------------------------- FUNCTION: - * hresponse_new_from_buffer --------------------------------------------- - */ -hresponse_t * -hresponse_new_from_buffer(const char *buffer) -{ - hresponse_t *res; - char *s1, *s2, *str; - /* create response object */ - res = hresponse_new(); +#ifdef WIN32 - /* *** parse spec *** */ - /* [HTTP/1.1 | 1.2] [CODE] [DESC] */ +/* strtok_r() */ +char * +strtok_r(char *s, const char *delim, char **save_ptr) +{ + char *token; - /* stage 1: HTTP spec */ - str = (char *) strtok_r((char *) buffer, " ", &s2); - s1 = s2; - if (str == NULL) { - log_error1("Parse error"); - return NULL; - } - strncpy(res->spec, str, 10); + if (s == NULL) + s = *save_ptr; - /* stage 2: http code */ - str = (char *) strtok_r(s1, " ", &s2); - s1 = s2; - if (str == NULL) { - log_error1("Parse error"); + /* Scan leading delimiters. */ + s += strspn(s, delim); + if (*s == '\0') return NULL; - } - res->errcode = atoi(str); - /* stage 3: description text */ - str = (char *) strtok_r(s1, "\r\n", &s2); - s1 = s2; - if (str == NULL) { - log_error1("Parse error"); - return NULL; - } - res->desc = (char *) malloc(strlen(str) + 1); - strcpy(res->desc, str); - res->desc[strlen(str)] = '\0'; - - /* *** parse header *** */ - /* [key]: [value] */ - for (;;) { - str = strtok_r(s1, "\n", &s2); - s1 = s2; - - /* check if header ends without body */ - if (str == NULL) { - return res; - } - /* check also for end of header */ - if (!strcmp(str, "\r")) { - break; - } - str[strlen(str) - 1] = '\0'; - res->header = hpairnode_parse(str, ":", res->header); + /* Find the end of the token. */ + token = s; + s = strpbrk(token, delim); + if (s == NULL) + /* This token finishes the string. */ + *save_ptr = strchr(token, '\0'); + else { + /* Terminate the token and make *SAVE_PTR point past it. */ + *s = '\0'; + *save_ptr = s + 1; } - - /* *** Save body *** */ - res->body = s1; - - /* return response object */ - return res; + return token; } - -void -hresponse_free(hresponse_t * res) +/* localtime_r() */ +struct tm * +localtime_r (const time_t * const timep, struct tm *p_tm) { - /* not implemented yet! */ + static struct tm *tmp; + tmp = localtime (timep); + if (tmp) + { + memcpy (p_tm, tmp, sizeof (struct tm)); + tmp = p_tm; + } + return tmp; } + +#endif + diff --git a/nanohttp/nanohttp-common.h b/nanohttp/nanohttp-common.h index 0d765d8..d37c43a 100644 --- a/nanohttp/nanohttp-common.h +++ b/nanohttp/nanohttp-common.h @@ -1,8 +1,8 @@ /****************************************************************** - * $Id: nanohttp-common.h,v 1.11 2004/09/19 07:05:03 snowdrop Exp $ - * + * $Id: nanohttp-common.h,v 1.12 2004/10/15 13:29:36 snowdrop Exp $ + * * CSOAP Project: A http client/server library in C - * Copyright (C) 2003 Ferhat Ayaz + * Copyright (C) 2003-2004 Ferhat Ayaz * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -19,11 +19,13 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * - * Email: ayaz@jprogrammer.net + * Email: ferhatayaz@yahoo.com ******************************************************************/ #ifndef NANO_HTTP_COMMON_H #define NANO_HTTP_COMMON_H +#include +#include #define HEADER_CONTENT_LENGTH "Content-Length" #define HEADER_CONTENT_TYPE "Content-Type" @@ -42,8 +44,75 @@ #define TRANSFER_ENCODING_CHUNKED "chunked" +#define BOUNDARY_LENGTH 18 + #define MAX_HEADER_SIZE 4256 #define MAX_SOCKET_BUFFER_SIZE 4256 +#define MAX_FILE_BUFFER_SIZE 4256 + +#define URL_MAX_HOST_SIZE 120 +#define URL_MAX_CONTEXT_SIZE 1024 + +#define HSOCKET_MAX_BUFSIZE 1024 + +#define REQUEST_MAX_PATH_SIZE 1024 +#define RESPONSE_MAX_DESC_SIZE 1024 + +/* + DIME common stuff +*/ +#define DIME_VERSION_1 0x08 +#define DIME_FIRST_PACKAGE 0x04 +#define DIME_LAST_PACKAGE 0x02 +#define DIME_CHUNKED 0x01 +#define DIME_TYPE_URI 0x2 + + + +/* TODO (#1#): find proper ports */ +#define URL_DEFAULT_PORT_HTTP 80 +#define URL_DEFAULT_PORT_HTTPS 81 +#define URL_DEFAULT_PORT_FTP 120 + +/* Success flag */ +#define H_OK 0 + +/* File errors */ +#define FILE_ERROR_OPEN 8000 +#define FILE_ERROR_READ 8001 + +/* Socket errors */ +#define HSOCKET_ERROR_CREATE 1001 +#define HSOCKET_ERROR_GET_HOSTNAME 1002 +#define HSOCKET_ERROR_CONNECT 1003 +#define HSOCKET_ERROR_SEND 1004 +#define HSOCKET_ERROR_RECEIVE 1005 +#define HSOCKET_ERROR_BIND 1006 +#define HSOCKET_ERROR_LISTEN 1007 +#define HSOCKET_ERROR_ACCEPT 1008 +#define HSOCKET_ERROR_NOT_INITIALIZED 1009 +#define HSOCKET_ERROR_IOCTL 1010 + +/* URL errors */ +#define URL_ERROR_UNKNOWN_PROTOCOL 1101 +#define URL_ERROR_NO_PROTOCOL 1102 +#define URL_ERROR_NO_HOST 1103 + +/* Stream errors */ +#define STREAM_ERROR_INVALID_TYPE 1201 +#define STREAM_ERROR_SOCKET_ERROR 1202 +#define STREAM_ERROR_NO_CHUNK_SIZE 1203 +#define STREAM_ERROR_WRONG_CHUNK_SIZE 1204 + + +/* MIME errors */ +#define MIME_ERROR_NO_BOUNDARY_PARAM 1301 +#define MIME_ERROR_NO_START_PARAM 1302 +#define MIME_ERROR_PARSE_ERROR 1303 +#define MIME_ERROR_NO_ROOT_PART 1304 +#define MIME_ERROR_NOT_MIME_MESSAGE 1305 + + /* Set Sleep function platform depended @@ -54,7 +123,36 @@ Set Sleep function platform depended #define system_sleep(seconds) sleep(seconds); #endif +#ifdef WIN32 +#include +char* strtok_r(char *s, const char *delim, char **save_ptr); +struct tm *localtime_r(const time_t *const timep, struct tm *p_tm); +#endif + typedef unsigned char byte_t; +typedef int hstatus_t; + + +/** + Indicates the version of the + used HTTP protocol. +*/ +typedef enum _http_version { + HTTP_1_0, + HTTP_1_1 /* default */ +}http_version_t; + + +/** + Indicates the used method +*/ +typedef enum _hreq_method +{ + HTTP_REQUEST_POST, + HTTP_REQUEST_GET +}hreq_method_t ; + + /* string function to compare strings ignoring case @@ -77,68 +175,255 @@ struct hpair }; +/** + Creates a new pair with the given parameters. Both strings + key and value will be cloned while creating the pair. + + @param key the key of the (key,value) pair + @param value the value of the (key,value) pair + @param next next pair node in the linked list + + @returns A newly crated hpair_t object. Use hpair_free() + or hpair_free_deep() to free the pair. +*/ hpair_t *hpairnode_new(const char* key, const char* value, hpair_t* next); + + +/** + Creates a new pair from a given string. This function + will split 'str' with the found first delimiter 'delim'. + The 'value' field of the newly created pair will have + the value "", if no delimiter was found/ + Whitespaces (' ') will be removed from the beginnig of + the parsed value. + + @param str A string to parse + @param delim a delimiter to use while splitting into key,value + @param next next pair node in the linked list + + @returns A newly crated hpair_t object. Use hpair_free() + or hpair_free_deep() to free the pair. +*/ +hpair_t *hpairnode_parse(const char *str, const char *delim, hpair_t * next); + + +/** + Frees a given pair. + + @param pair the pair to free +*/ void hpairnode_free(hpair_t *pair); + + +/** + Makes a deep free operation. All pairnodes, + beginning with the given pari, in the + linked list will be destroyed. + + @param pair the pair to start to free the linked list +*/ +void hpairnode_free_deep(hpair_t * pair); + + +/** + Returns the (key,value) pair, which key is the + given 'key'. + + @param pair the first pair to start to search from. + @param key key to find the in the pair. + @returns if a value will be found, this function will + return the value (do not free this string) or NULL + if no pair was found with the key 'key'. +*/ char *hpairnode_get(hpair_t *pair, const char* key); + + +/** + Returns the (key,value) pair, which key is the + given 'key'. The case will be ignored while + comparing the key strings. + + @param pair the first pair to start to search from. + @param key key to find the in the pair. + @returns if a value will be found, this function will + return the value (do not free this string) or NULL + if no pair was found with the key 'key'. +*/ char *hpairnode_get_ignore_case(hpair_t *pair, const char* key); + + +/** + This function will create a new pair and fills the + (key,value) fields of a given pair. Note that the 'next' + field will not be copied. + + @param src the source pair object to copy. + + @returns a newly created pair with the same (key,value) + pairs as in 'src'. This fields will be cloned. The'next' + field will be set to NULL. + + @see hpairnode_copy_deep +*/ hpair_t* hpairnode_copy(const hpair_t *src); + + +/** + Clones the hole linked list. + + @param src the source pair object to copy from + + @returns the first object in the linked list. + + @see hpairnode_copy +*/ hpair_t* hpairnode_copy_deep(const hpair_t *src); + +/* Debug functions */ void hpairnode_dump_deep(hpair_t *pair); void hpairnode_dump(hpair_t *pair); -typedef enum http_version { - HTTP_1_0, - HTTP_1_1 /* default */ -}http_version_t; +/** + The protocol types in enumeration + format. Used in some other nanohttp objects + like hurl_t. -typedef enum hreq_method + @see hurl_t +*/ +typedef enum _hprotocol { - HTTP_REQUEST_POST, - HTTP_REQUEST_GET -}hreq_method ; + PROTOCOL_HTTP, + PROTOCOL_HTTPS, + PROTOCOL_FTP +}hprotocol_t; -typedef struct hurl + +/** + The URL object. A representation + of an URL like:

+ + [protocol]://[host]:[port]/[context] + +*/ +typedef struct _hurl { - char *protocol; - char *host; + /** + The transfer protocol. + Note that only PROTOCOL_HTTP is supported by nanohttp. + */ + hprotocol_t protocol; + + /** + The port number. If no port number was given in the URL, + one of the default port numbers will be selected. + URL_HTTP_DEFAULT_PORT + URL_HTTPS_DEFAULT_PORT + URL_FTP_DEFAULT_PORT + */ int port; - char *context; + + /** The hostname */ + char host[URL_MAX_HOST_SIZE]; + + /** The string after the hostname. */ + char context[URL_MAX_CONTEXT_SIZE]; }hurl_t; -hurl_t* hurl_new(const char* urlstr); -void hurl_free(hurl_t *url); +/** + Parses the given 'urlstr' and fills the given hurl_t object. + + @param obj the destination URL object to fill + @param url the URL in string format + @returns H_OK on success or one of the following otherwise + URL_ERROR_UNKNOWN_PROTOCOL + URL_ERROR_NO_PROTOCOL + URL_ERROR_NO_HOST +*/ +hstatus_t hurl_parse(hurl_t *obj, const char* url); /* - DIME common stuff + Object representation of the content-type field + in a HTTP header: +

+ Example:

+ + text/xml; key="value" key2="value2' ... */ -#define DIME_VERSION_1 0x08 -#define DIME_FIRST_PACKAGE 0x04 -#define DIME_LAST_PACKAGE 0x02 -#define DIME_CHUNKED 0x01 -#define DIME_TYPE_URI 0x2 +typedef struct _content_type +{ + char type[128]; + hpair_t *params; +}content_type_t; + + +/** + Parses the given string and creates a new ccontent_type_t + object. + + @param content_type_str the string representation of the + content-type field in a HTTP header. + + @returns A newly created content_type_t object. Free this + object with content_type_free(); + + @see content_type_free +*/ +content_type_t *content_type_new(const char* content_type_str); + + +/** + Frees the given content_type_t object +*/ +void content_type_free(content_type_t *ct); + + + +/* + part. Attachment +*/ +typedef struct _part +{ + char id[250]; + hpair_t *header; + char content_type[128]; + char transfer_encoding[128]; + char filename[250]; + struct _part *next; +}part_t; + + +part_t *part_new(const char *id, const char* filename, + const char* content_type, const char* transfer_encoding, part_t *next); + -typedef struct _DIME_PACKAGE +/* + Attachments +*/ +typedef struct _attachments { - char version; /* Specifies the version of the DIME message */ - char first_record; /* Specifies that this record is the first record of the message */ - char last_recored; /* Specifies that this record is the last record of the message */ - char chunked; /* Specifies that the contents of the message have been chunked */ - char type_t; /* Specifies the structure and format of the TYPE field */ - char *options; /* Contains any optional information used by a DIME parser */ - char *id; /* Contains a URI for uniquely identifying a DIME payload with any - additional padding; */ - char *type; /* Specifies the encoding for the record based on a type reference URI or a MIME media-type */ - int data_size; /* Specifies the length (in bytes) of the DATA */ - unsigned char* data; /* Contains the actual data payload for the record; - format of the data depends on the type specified for the record */ -}DIME_PACKAGE; - - -/* logging stuff*/ + part_t *parts; + part_t *last; + part_t *root_part; +}attachments_t; + +attachments_t *attachments_new(); /* should be used internally */ + +/* + Free a attachment. Create attachments with MIME + and DIME (DIME is not supported yet). + + @see mime_get_attachments +*/ +void attachments_free(attachments_t *message); + + + + +/* logging stuff */ typedef enum log_level { HLOG_VERBOSE, @@ -199,7 +484,6 @@ void log_error(const char* FUNC, const char *format, ...); - #endif diff --git a/nanohttp/nanohttp-server.c b/nanohttp/nanohttp-server.c index 9fecc1a..ad4f02c 100644 --- a/nanohttp/nanohttp-server.c +++ b/nanohttp/nanohttp-server.c @@ -1,5 +1,5 @@ /****************************************************************** -* $Id: nanohttp-server.c,v 1.25 2004/09/19 07:05:03 snowdrop Exp $ +* $Id: nanohttp-server.c,v 1.26 2004/10/15 13:29:36 snowdrop Exp $ * * CSOAP Project: A http client/server library in C * Copyright (C) 2003 Ferhat Ayaz @@ -42,6 +42,22 @@ #endif +#ifdef MEM_DEBUG +#include +#endif + +typedef struct _conndata +{ + hsocket_t sock; +#ifdef WIN32 + HANDLE tid; +#else + pthread_t tid; + pthread_attr_t attr; +#endif + time_t atime; +}conndata_t; + /* * ----------------------------------------------------- * nano httpd @@ -62,8 +78,9 @@ static int _httpd_terminate_signal = SIGTERM; #endif static conndata_t *_httpd_connection; + #ifdef WIN32 -#include +static void WSAReaper(void *x); #endif /* @@ -106,7 +123,7 @@ httpd_init (int argc, char *argv[]) /* init built-in services */ - /* httpd_register("/httpd/list", service_list);*/ + /* httpd_register("/httpd/list", service_list); */ _httpd_connection = calloc (_httpd_max_connections, sizeof (conndata_t)); for (i = 0; i < _httpd_max_connections; i++) @@ -171,6 +188,16 @@ httpd_services () return _httpd_services_head; } +/* + * ----------------------------------------------------- + * FUNCTION: httpd_services + * ----------------------------------------------------- + */ +static +void hservice_free(hservice_t *service) +{ + free(service); +} /* * ----------------------------------------------------- @@ -213,8 +240,7 @@ httpd_response_set_content_type (httpd_conn_t * res, const char *content_type) * ----------------------------------------------------- */ int -httpd_send_header (httpd_conn_t * res, - int code, const char *text, hpair_t * pair) +httpd_send_header (httpd_conn_t * res, int code, const char *text) { struct tm stm; time_t nw; @@ -247,7 +273,7 @@ httpd_send_header (httpd_conn_t * res, strcat (header, "Connection: close\r\n"); /* add pairs */ - cur = pair; + cur = res->header; while (cur != NULL) { sprintf (buffer, "%s: %s\r\n", cur->key, cur->value); @@ -261,6 +287,7 @@ httpd_send_header (httpd_conn_t * res, /* send header */ status = hsocket_nsend (res->sock, header, strlen (header)); + res->out = http_output_stream_new (res->sock, res->header); return status; } @@ -273,7 +300,7 @@ httpd_send_internal_error (httpd_conn_t * conn, const char *errmsg) char buffer[4064]; sprintf (buffer, template1, errmsg); - httpd_send_header (conn, 500, "INTERNAL", NULL); + httpd_send_header (conn, 500, "INTERNAL"); return send (conn->sock, buffer, strlen (buffer), 0); } @@ -288,9 +315,9 @@ httpd_request_print (hrequest_t * req) hpair_t *pair; log_verbose1 ("++++++ Request +++++++++"); - log_verbose2 (" Method : '%s'", req->method); + log_verbose2 (" Method : '%s'", (req->method == HTTP_REQUEST_POST)?"POST":"GET"); log_verbose2 (" Path : '%s'", req->path); - log_verbose2 (" Spec : '%s'", req->spec); + log_verbose2 (" Spec : '%s'", (req->version==HTTP_1_0)?"HTTP/1.0":"HTTP/1.1"); log_verbose1 (" Parsed query string :"); pair = req->query; @@ -303,6 +330,30 @@ httpd_request_print (hrequest_t * req) } + +httpd_conn_t *httpd_new(hsocket_t sock) +{ + httpd_conn_t *conn = (httpd_conn_t *) malloc (sizeof (httpd_conn_t)); + conn->sock = sock; + conn->out = NULL; + conn->content_type[0] = '\0'; + conn->header = NULL; + + return conn; +} + + +void httpd_free(httpd_conn_t *conn) +{ + if (conn->out != NULL) + http_output_stream_free(conn->out); + + if (conn->header != NULL) + hpairnode_free_deep(conn->header); + + free(conn); +} + /* * ----------------------------------------------------- * FUNCTION: httpd_session_main @@ -335,64 +386,52 @@ httpd_session_main (void *data) log_verbose1 ("starting httpd_session_main()"); conn->atime = time ((time_t) 0); - while (len < 4064) - { - /* printf("receiving ...\n"); */ - total = recv (conn->sock, ch, 1, 0); - if (total == 0) - break; - header[len] = ch[0]; - len++; - if (len > 3) - { - if (!strncmp (&header[len - 4], "\r\n\r\n", 4)) - { - header[len] = '\0'; - break; - } - } - } - - /* log_verbose2("=== HEADER ===\n%s\n============\n", header); */ /* call the service */ - req = hrequest_new_from_buffer (header); - httpd_request_print (req); +/* req = hrequest_new_from_buffer (header);*/ + rconn = httpd_new(conn->sock); + req = hrequest_new_from_socket (conn->sock); + if (req == NULL) + { + httpd_send_internal_error (rconn, "Request parse error!"); + } + else + { + httpd_request_print (req); - rconn = (httpd_conn_t *) malloc (sizeof (httpd_conn_t)); - rconn->sock = conn->sock; - rconn->content_type[0] = '\0'; - service = httpd_find_service (req->path); - if (service != NULL) - { - log_verbose2 ("service '%s' found", req->path); - if (service->func != NULL) + service = httpd_find_service (req->path); + if (service != NULL) { - service->func (rconn, req); + log_verbose2 ("service '%s' found", req->path); + if (service->func != NULL) + { + service->func (rconn, req); + } + else + { + sprintf (buffer, + "service '%s' not registered properly (func == NULL)", + req->path); + log_verbose1 (buffer); + httpd_send_internal_error (rconn, buffer); + } } else { - sprintf (buffer, - "service '%s' not registered properly (func == NULL)", - req->path); + sprintf (buffer, "service '%s' not found", req->path); log_verbose1 (buffer); httpd_send_internal_error (rconn, buffer); } - } - else - { - sprintf (buffer, "service '%s' not found", req->path); - log_verbose1 (buffer); - httpd_send_internal_error (rconn, buffer); + + /* httpd_response_free(res); */ + /*hrequest_free (req);*/ } - close (conn->sock); + hsocket_close(conn->sock); conn->sock = 0; - - /* httpd_response_free(res); */ - hrequest_free (req); - + hrequest_free(req); + httpd_free(rconn); #ifdef WIN32 CloseHandle ((HANDLE) conn->tid); _endthread (); @@ -403,6 +442,44 @@ httpd_session_main (void *data) #endif } +int +httpd_set_header (httpd_conn_t * conn, const char *key, const char *value) +{ + hpair_t *p; + + if (conn == NULL) + { + log_warn1 ("Connection object is NULL"); + return 0; + } + p = conn->header; + while (p != NULL) + { + if (p->key != NULL) + { + if (!strcmp (p->key, key)) + { + free (p->value); + p->value = (char *) malloc (strlen (value) + 1); + strcpy (p->value, value); + return 1; + } + } + p = p->next; + } + + conn->header = hpairnode_new (key, value, conn->header); + return 0; +} + + +void httpd_set_headers(httpd_conn_t *conn, hpair_t *header) +{ + while (header) { + httpd_set_header(conn, header->key, header->value); + header = header->next; + } +} /* * ----------------------------------------------------- @@ -410,19 +487,21 @@ httpd_session_main (void *data) * ----------------------------------------------------- */ #ifdef WIN32 -BOOL WINAPI httpd_term(DWORD sig) +BOOL WINAPI +httpd_term (DWORD sig) { - log_debug2("Got signal %d", sig); + log_debug2 ("Got signal %d", sig); if (sig == _httpd_terminate_signal) - _httpd_run = 0; + _httpd_run = 0; return TRUE; } - + #else void -httpd_term (int sig) { - log_debug2("Got signal %d", sig); +httpd_term (int sig) +{ + log_debug2 ("Got signal %d", sig); if (sig == _httpd_terminate_signal) _httpd_run = 0; } @@ -435,20 +514,21 @@ httpd_term (int sig) { * FUNCTION: _httpd_register_signal_handler * ----------------------------------------------------- */ -static -void _httpd_register_signal_handler() +static void +_httpd_register_signal_handler () { log_verbose2 ("registering termination signal handler (SIGNAL:%d)", _httpd_terminate_signal); #ifdef WIN32 - if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)httpd_term, TRUE) == FALSE){ - log_error1 ("Unable to install console event handler!"); + if (SetConsoleCtrlHandler ((PHANDLER_ROUTINE) httpd_term, TRUE) == FALSE) + { + log_error1 ("Unable to install console event handler!"); } #else signal (_httpd_terminate_signal, httpd_term); #endif -} +} @@ -456,56 +536,55 @@ void _httpd_register_signal_handler() /*-------------------------------------------------- FUNCTION: _httpd_wait_for_empty_conn ----------------------------------------------------*/ -static -conndata_t *_httpd_wait_for_empty_conn() +static conndata_t * +_httpd_wait_for_empty_conn () { - int i; + int i; for (i = 0;; i++) { if (!_httpd_run) - return NULL; - - if (i >= _httpd_max_connections) + return NULL; + + if (i >= _httpd_max_connections) { - system_sleep(1); + system_sleep (1); i = 0; } else if (_httpd_connection[i].sock == 0) { break; - } - } - - return &_httpd_connection[i]; -} + } + } + + return &_httpd_connection[i]; +} /* * ----------------------------------------------------- * FUNCTION: _httpd_start_thread * ----------------------------------------------------- */ -static -void _httpd_start_thread(conndata_t* conn) +static void +_httpd_start_thread (conndata_t * conn) { int err; - + #ifdef WIN32 - conn->tid = - (HANDLE) _beginthreadex (NULL, 65535, httpd_session_main, conn, 0, &err); + conn->tid = + (HANDLE) _beginthreadex (NULL, 65535, httpd_session_main, conn, 0, &err); #else - pthread_attr_init (&(conn-> attr)); - #ifdef PTHREAD_CREATE_DETACHED - pthread_attr_setdetachstate (&(conn->attr), - PTHREAD_CREATE_DETACHED); - #endif - err = pthread_create (&(conn->tid), &(conn->attr),httpd_session_main, - conn); + pthread_attr_init (&(conn->attr)); +#ifdef PTHREAD_CREATE_DETACHED + pthread_attr_setdetachstate (&(conn->attr), PTHREAD_CREATE_DETACHED); +#endif + err = pthread_create (&(conn->tid), &(conn->attr), httpd_session_main, + conn); if (err) - { - log_error2 ("Error creating thread: ('%d')", err); - } + { + log_error2 ("Error creating thread: ('%d')", err); + } #endif -} +} @@ -529,8 +608,8 @@ httpd_run () timeout.tv_usec = 0; /* listen to port */ - err = hsocket_listen (_httpd_socket, 15); - if (err != HSOCKET_OK) + err = hsocket_listen (_httpd_socket); + if (err != H_OK) { log_error2 ("httpd_run(): '%d'", err); return err; @@ -538,69 +617,120 @@ httpd_run () log_verbose2 ("listening to port '%d'", _httpd_port); /* register signal handler */ - _httpd_register_signal_handler(); - + _httpd_register_signal_handler (); + /* make the socket non blocking */ - err = hsocket_makenonblock (_httpd_socket); - if (err != HSOCKET_OK) + err = hsocket_block (_httpd_socket, 0); + if (err != H_OK) { log_error2 ("httpd_run(): '%d'", err); return err; } - + while (_httpd_run) { /* Get an empty connection struct */ - conn = _httpd_wait_for_empty_conn(); - if (!_httpd_run) break; - + conn = _httpd_wait_for_empty_conn (); + if (!_httpd_run) + break; + /* Wait for a socket to accept */ - while (_httpd_run) - { - /* zero and set file descriptior */ - FD_ZERO (&fds); - FD_SET (_httpd_socket, &fds); - - /* select socket descriptor */ - switch (select(_httpd_socket+1, &fds, NULL, NULL, &timeout)) - { - case 0: - /* descriptor is not ready */ - continue; - case -1: - /* got a signal? */ - continue; - default: - /* no nothing */ - break; - } - if (FD_ISSET (_httpd_socket, &fds)) + while (_httpd_run) + { + /* zero and set file descriptior */ + FD_ZERO (&fds); + FD_SET (_httpd_socket, &fds); + + /* select socket descriptor */ + switch (select (_httpd_socket + 1, &fds, NULL, NULL, &timeout)) + { + case 0: + /* descriptor is not ready */ + continue; + case -1: + /* got a signal? */ + continue; + default: + /* no nothing */ + break; + } + if (FD_ISSET (_httpd_socket, &fds)) { - break; - } + break; + } } - - /* check signal status*/ + + /* check signal status */ if (!_httpd_run) - break; - + break; + /* Accept a socket */ - err = hsocket_accept(_httpd_socket, &(conn->sock)); - if (err != HSOCKET_OK) + err = hsocket_accept (_httpd_socket, &(conn->sock)); + if (err != H_OK) { - log_error2("Can not accept socket: %d", err); - return -1; /* this is hard core! */ - } - + log_error2 ("Can not accept socket: %d", err); + return -1; /* this is hard core! */ + } + /* Now start a thread */ - _httpd_start_thread(conn); + _httpd_start_thread (conn); } free (_httpd_connection); return 0; } +void httpd_destroy() +{ + hservice_t *tmp, *cur = _httpd_services_head; + + while (cur != NULL) + { + tmp = cur->next; + hservice_free(cur); + cur = tmp; + } + +} + +#ifdef WIN32 + +static +void WSAReaper(void *x) +{ + short int connections; + short int i; + char junk[10]; + int rc; + time_t ctime; + + for (;;) { + connections=0; + ctime=time((time_t)0); + for (i=0;i<_httpd_max_connections;i++) { + if (_httpd_connection[i].tid==0) continue; + GetExitCodeThread((HANDLE)_httpd_connection[i].tid,(PDWORD) &rc); + if (rc!=STILL_ACTIVE) continue; + connections++; + if ((ctime-_httpd_connection[i].atime<_httpd_max_idle)|| + (_httpd_connection[i].atime==0)) continue; + log_verbose3("Reaping socket %u from (runtime ~= %d seconds)", + _httpd_connection[i].sock, ctime-_httpd_connection[i].atime); + shutdown(_httpd_connection[i].sock, 2); + while (recv(_httpd_connection[i].sock, junk, sizeof(junk), 0)>0) { }; + closesocket(_httpd_connection[i].sock); + _httpd_connection[i].sock = 0; + TerminateThread(_httpd_connection[i].tid, (DWORD)&rc); + CloseHandle(_httpd_connection[i].tid); + memset((char *)&_httpd_connection[i], 0, sizeof(_httpd_connection[i])); + } + Sleep(100); + } + return; +} + +#endif unsigned char * httpd_get_postdata (httpd_conn_t * conn, hrequest_t * req, long *received, @@ -611,7 +741,7 @@ httpd_get_postdata (httpd_conn_t * conn, hrequest_t * req, long *received, long total = 0; unsigned char *postdata = NULL; - if (!strcmp (req->method, "POST")) + if (req->method == HTTP_REQUEST_POST) { content_length_str = @@ -643,8 +773,7 @@ httpd_get_postdata (httpd_conn_t * conn, hrequest_t * req, long *received, log_error1 ("Not enough memory"); return NULL; } - if (hsocket_read (conn->sock, postdata, - (int)content_length, 1)>0) + if (http_input_stream_read(req->in, postdata, (int) content_length) > 0) { *received = content_length; postdata[content_length] = '\0'; @@ -653,3 +782,185 @@ httpd_get_postdata (httpd_conn_t * conn, hrequest_t * req, long *received, free (postdata); return NULL; } + + + + +/* + MIME support httpd_mime_* function set +*/ + +static void +_httpd_mime_get_boundary (httpd_conn_t * conn, char *dest) +{ + sprintf (dest, "---=.Part_NH_%p", conn); + log_verbose2 ("boundary= \"%s\"", dest); +} + + +/** + Begin MIME multipart/related POST + Returns: H_OK or error flag +*/ +int +httpd_mime_send_header (httpd_conn_t * conn, + const char *related_start, + const char *related_start_info, + const char *related_type, int code, const char *text) +{ + int status; + char buffer[300]; + char temp[250]; + char boundary[250]; + hpair_t *header; + + /* + Set Content-type + Set multipart/related parameter + type=..; start=.. ; start-info= ..; boundary=... + + */ + sprintf (buffer, "multipart/related;"); + + if (related_type) + { + snprintf (temp, 75, " type=\"%s\";", related_type); + strcat (buffer, temp); + } + + if (related_start) + { + snprintf (temp, 250, " start=\"%s\";", related_start); + strcat (buffer, temp); + } + + if (related_start_info) + { + snprintf (temp, 250, " start-info=\"%s\";", related_start_info); + strcat (buffer, temp); + } + + _httpd_mime_get_boundary (conn, boundary); + snprintf (temp, 250, " boundary=\"%s\"", boundary); + strcat (buffer, temp); + + httpd_set_header (conn, HEADER_CONTENT_TYPE, buffer); + + return httpd_send_header (conn, code, text); +} + + +/** + Send boundary and part header and continue + with next part +*/ +int +httpd_mime_next (httpd_conn_t * conn, + const char *content_id, + const char *content_type, const char *transfer_encoding) +{ + int status; + char buffer[512]; + char boundary[75]; + + /* Get the boundary string */ + _httpd_mime_get_boundary (conn, boundary); + sprintf (buffer, "\r\n--%s\r\n", boundary); + + /* Send boundary */ + status = http_output_stream_write (conn->out, + (const byte_t *) buffer, + strlen (buffer)); + + if (status != H_OK) + return status; + + /* Send Content header */ + sprintf (buffer, "%s: %s\r\n%s: %s\r\n%s: %s\r\n\r\n", + HEADER_CONTENT_TYPE, content_type ? content_type : "text/plain", + HEADER_CONTENT_TRANSFER_ENCODING, + transfer_encoding ? transfer_encoding : "binary", + HEADER_CONTENT_ID, + content_id ? content_id : ""); + + status = http_output_stream_write (conn->out, + (const byte_t *) buffer, + strlen (buffer)); + + return status; +} + +/** + Send boundary and part header and continue + with next part +*/ +int +httpd_mime_send_file (httpd_conn_t * conn, + const char *content_id, + const char *content_type, + const char *transfer_encoding, const char *filename) +{ + int status; + FILE *fd = fopen (filename, "rb"); + byte_t buffer[MAX_FILE_BUFFER_SIZE]; + size_t size; + + if (fd == NULL) + return FILE_ERROR_OPEN; + + status = + httpd_mime_next (conn, content_id, content_type, transfer_encoding); + if (status != H_OK) + { + fclose (fd); + return status; + } + + while (!feof (fd)) + { + size = fread (buffer, 1, MAX_FILE_BUFFER_SIZE, fd); + if (size == -1) + { + fclose (fd); + return FILE_ERROR_READ; + } + + status = http_output_stream_write (conn->out, buffer, size); + if (status != H_OK) { + fclose (fd); + return status; + } + } + + fclose (fd); + return H_OK; +} + +/** + Finish MIME request + Returns: H_OK or error flag +*/ +int +httpd_mime_end (httpd_conn_t * conn) +{ + int status; + char buffer[512]; + char boundary[75]; + + /* Get the boundary string */ + _httpd_mime_get_boundary (conn, boundary); + sprintf (buffer, "\r\n--%s--\r\n\r\n", boundary); + + /* Send boundary */ + status = http_output_stream_write (conn->out, + (const byte_t *) buffer, + strlen (buffer)); + + if (status != H_OK) + return status; + + /* Flush put stream */ + status = http_output_stream_flush (conn->out); + + return status; +} diff --git a/nanohttp/nanohttp-server.h b/nanohttp/nanohttp-server.h index 3170c3d..7e5263e 100644 --- a/nanohttp/nanohttp-server.h +++ b/nanohttp/nanohttp-server.h @@ -1,5 +1,5 @@ /****************************************************************** - * $Id: nanohttp-server.h,v 1.5 2004/09/19 07:05:03 snowdrop Exp $ + * $Id: nanohttp-server.h,v 1.6 2004/10/15 13:29:37 snowdrop Exp $ * * CSOAP Project: A http client/server library in C * Copyright (C) 2003 Ferhat Ayaz @@ -19,7 +19,7 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * - * Email: ayaz@jprogrammer.net + * Email: ferhatayaz@yahoo.com ******************************************************************/ #ifndef NANO_HTTP_SERVER_H #define NANO_HTTP_SERVER_H @@ -27,16 +27,20 @@ #include #include -#include +#include +#include #define NHTTPD_ARG_PORT "-NHTTPport" #define NHTTPD_ARG_TERMSIG "-NHTTPtsig" #define NHTTPD_ARG_MAXCONN "-NHTTPmaxconn" + typedef struct httpd_conn { hsocket_t sock; char content_type[25]; + http_output_stream_t *out; + hpair_t *header; }httpd_conn_t; @@ -69,12 +73,55 @@ void httpd_destroy(); hservice_t *httpd_services(); int httpd_send_header(httpd_conn_t *res, - int code, const char* text, - hpair_t *pair); + int code, const char* text); +int httpd_set_header(httpd_conn_t *conn, const char *key, const char* value); +void httpd_set_headers(httpd_conn_t *conn, hpair_t *header); unsigned char *httpd_get_postdata(httpd_conn_t *conn, hrequest_t *req, long *received, long max); +/* -------------------------------------------------------------- + MIME RELATED FUNCTIONS + ---------------------------------------------------------------*/ +/* + MIME support httpd_mime_* function set +*/ + +/** + Begin MIME multipart/related POST + Returns: HSOCKET_OK or error flag +*/ +int httpd_mime_send_header(httpd_conn_t *conn, + const char* related_start, + const char* related_start_info, + const char* related_type, int code, const char* text); + +/** + Send boundary and part header and continue + with next part +*/ +int httpd_mime_next(httpd_conn_t *conn, + const char* content_id, + const char* content_type, + const char* transfer_encoding); + +/** + Send boundary and part header and continue + with next part +*/ +int httpd_mime_send_file(httpd_conn_t *conn, + const char* content_id, + const char* content_type, + const char* transfer_encoding, + const char* filename); + +/** + Finish MIME request + Returns: HSOCKET_OK or error flag +*/ +int httpd_mime_end(httpd_conn_t *conn); + + #endif diff --git a/nanohttp/nanohttp-socket.c b/nanohttp/nanohttp-socket.c index 718b96f..82d7280 100644 --- a/nanohttp/nanohttp-socket.c +++ b/nanohttp/nanohttp-socket.c @@ -1,5 +1,5 @@ /****************************************************************** -* $Id: nanohttp-socket.c,v 1.22 2004/09/19 07:05:03 snowdrop Exp $ +* $Id: nanohttp-socket.c,v 1.23 2004/10/15 13:29:37 snowdrop Exp $ * * CSOAP Project: A http client/server library in C * Copyright (C) 2003 Ferhat Ayaz @@ -25,16 +25,16 @@ #include #ifdef WIN32 -#include "wsockcompat.h" -#include -#include - -#ifndef __MINGW32__ -typedef int ssize_t; -#endif - + #include "wsockcompat.h" + #include + #include + + #ifndef __MINGW32__ + typedef int ssize_t; + #endif + #else -#include + #include #endif #ifdef HAVE_CONFIG_H @@ -64,12 +64,16 @@ typedef int ssize_t; #include #include +#ifdef MEM_DEBUG +#include +#endif + /*-------------------------------------------------- FUNCTION: hsocket_module_init NOTE: This will be called from httpd_init() for server and from httpc_init() for client ----------------------------------------------------*/ -int +hstatus_t hsocket_module_init () { #ifdef WIN32 @@ -79,7 +83,7 @@ hsocket_module_init () #else /* */ /* nothing to init for unix sockets */ #endif /* */ - return 0; + return H_OK; } /*-------------------------------------------------- @@ -99,12 +103,12 @@ hsocket_module_destroy () /*-------------------------------------------------- FUNCTION: hsocket_init ----------------------------------------------------*/ -int +hstatus_t hsocket_init (hsocket_t * sock) { /* just set the descriptor to -1 */ *sock = -1; - return 0; + return H_OK; } /*-------------------------------------------------- @@ -119,7 +123,7 @@ hsocket_free (hsocket_t sock) /*-------------------------------------------------- FUNCTION: hsocket_open ----------------------------------------------------*/ -int +hstatus_t hsocket_open (hsocket_t * dsock, const char *hostname, int port) { hsocket_t sock; @@ -129,12 +133,12 @@ hsocket_open (hsocket_t * dsock, const char *hostname, int port) sock = socket (AF_INET, SOCK_STREAM, 0); if (sock <= 0) - return HSOCKET_CAN_NOT_CREATE; + return HSOCKET_ERROR_CREATE; /* Get host data */ host = gethostbyname (hostname); if (host == NULL) - return HSOCKET_CAN_NOT_GET_HOSTNAME; + return HSOCKET_ERROR_GET_HOSTNAME; ip = inet_ntoa (*(struct in_addr *) *host->h_addr_list); address.sin_addr.s_addr = inet_addr (ip); @@ -145,16 +149,16 @@ hsocket_open (hsocket_t * dsock, const char *hostname, int port) /* connect to the server */ if (connect (sock, (struct sockaddr *) &address, sizeof (address)) != 0) - return HSOCKET_CAN_NOT_CONNECT; + return HSOCKET_ERROR_CONNECT; *dsock = sock; - return HSOCKET_OK; + return H_OK; } /*-------------------------------------------------- FUNCTION: hsocket_bind ----------------------------------------------------*/ -int +hstatus_t hsocket_bind (hsocket_t * dsock, int port) { hsocket_t sock; @@ -165,7 +169,7 @@ hsocket_bind (hsocket_t * dsock, int port) if (sock == -1) { log_error2 ("Can not create socket: '%s'", strerror (errno)); - return HSOCKET_CAN_NOT_CREATE; + return HSOCKET_ERROR_CREATE; } /* bind socket */ addr.sin_family = AF_INET; @@ -177,63 +181,69 @@ hsocket_bind (hsocket_t * dsock, int port) if (bind (sock, (struct sockaddr *) &addr, sizeof (struct sockaddr)) == -1) { log_error2 ("Can not bind: '%s'", strerror (errno)); - return HSOCKET_CAN_NOT_BIND; + return HSOCKET_ERROR_BIND; } *dsock = sock; - return HSOCKET_OK; + return H_OK; } /*---------------------------------------------------------- FUNCTION: hsocket_accept ----------------------------------------------------------*/ -int +hstatus_t hsocket_accept (hsocket_t sock, hsocket_t * dest) { socklen_t asize; hsocket_t sockfd; struct sockaddr_in addr; + if (sock <= 0) + return HSOCKET_ERROR_NOT_INITIALIZED; + asize = sizeof (struct sockaddr_in); #ifdef WIN32 while (1) { sockfd = accept (sock, (struct sockaddr *) &addr, &asize); - if (sockfd == INVALID_SOCKET) - { + if (sockfd == INVALID_SOCKET) { if (WSAGetLastError () != WSAEWOULDBLOCK) - return HSOCKET_CAN_NOT_ACCEPT; - } - else - { + return HSOCKET_ERROR_ACCEPT; + } else { break; } } -#else +#else +/* TODO (#1#): why not a loop like in win32? */ sockfd = accept (sock, (struct sockaddr *) &addr, &asize); - if (sockfd == -1) - { - return HSOCKET_CAN_NOT_ACCEPT; + if (sockfd == -1) { + return HSOCKET_ERROR_ACCEPT; } -#endif +#endif +/* TODO (#1#): Write to access.log file */ + log_verbose3 ("accept new socket (%d) from '%s'", sockfd, SAVE_STR (((char *) inet_ntoa (addr.sin_addr)))); *dest = sockfd; - return HSOCKET_OK; + return H_OK; } /*-------------------------------------------------- FUNCTION: hsocket_listen ----------------------------------------------------*/ -int -hsocket_listen (hsocket_t sock, int n) +hstatus_t +hsocket_listen (hsocket_t sock) { - if (listen (sock, n) == -1) + if (sock <= 0) + return HSOCKET_ERROR_NOT_INITIALIZED; + + if (listen (sock, 15) == -1) { log_error2 ("Can not listen: '%s'", strerror (errno)); - return HSOCKET_CAN_NOT_LISTEN; + return HSOCKET_ERROR_LISTEN; } - return HSOCKET_OK; + + return H_OK; } /*-------------------------------------------------- @@ -243,7 +253,18 @@ void hsocket_close (hsocket_t sock) { #ifdef WIN32 + /*shutdown(sock,SD_RECEIVE);*/ + +/* struct linger _linger; + + hsocket_block(sock,1); + _linger.l_onoff =1; + _linger.l_linger = 30000; + setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char*)&_linger, sizeof(struct linger)); + + closesocket (sock); +*/ #else close (sock); #endif @@ -252,22 +273,39 @@ hsocket_close (hsocket_t sock) /*-------------------------------------------------- FUNCTION: hsocket_send ----------------------------------------------------*/ -int -hsocket_nsend (hsocket_t sock, const unsigned char *bytes, int n) +hstatus_t +hsocket_nsend (hsocket_t sock, const byte_t *bytes, int n) { int size; - size = send ((int) sock, bytes, n, 0); - if (size == -1) - return HSOCKET_CAN_NOT_SEND; + if (sock <= 0) + return HSOCKET_ERROR_NOT_INITIALIZED; - return HSOCKET_OK; + /* TODO (#1#): check return value and send again until n bytes sent */ + + while (1) + { + size = send((int) sock, bytes, n, 0); +#ifdef WIN32 + if (size == INVALID_SOCKET) + if (WSAGetLastError () == WSAEWOULDBLOCK) + continue; + else + return HSOCKET_ERROR_SEND; +#else + if (size == -1) + return HSOCKET_ERROR_SEND; +#endif + n -= size; + if (n<=0) break; + } + return H_OK; } /*-------------------------------------------------- FUNCTION: hsocket_send ----------------------------------------------------*/ -int +hstatus_t hsocket_send (hsocket_t sock, const char *str) { return hsocket_nsend (sock, str, strlen (str)); @@ -277,214 +315,87 @@ hsocket_send (hsocket_t sock, const char *str) return: -1 is error. read bytes otherwise */ int -hsocket_read (hsocket_t sock, unsigned char *buffer, int total, int force) +hsocket_read (hsocket_t sock, byte_t *buffer, int total, int force) { int status; int totalRead; - + int wsa_error; totalRead = 0; do { - status = recv (sock, &buffer[totalRead], total - totalRead, 0); + status = recv(sock, &buffer[totalRead], total - totalRead, 0); #ifdef WIN32 - if (status == INVALID_SOCKET) - if (WSAGetLastError () == WSAEWOULDBLOCK) - continue; - else - return -1; + if (status == INVALID_SOCKET) + { + wsa_error = WSAGetLastError(); + switch (wsa_error) + { + case WSAEWOULDBLOCK: + case WSAEALREADY: + case WSAEINPROGRESS: + continue; + default: + log_error2("WSAGetLastError()=%d", wsa_error); + return -1; + } + } + #else +/* + switch (errno) { + case EWOULDBLOCK: + case EALREADY: + case EINPROGRESS: + return true; + } +*/ if (status == -1) return -1; #endif - if (!force) + if (!force) { + _log_str("socket.recv", buffer, status); return status; + } totalRead += status; - if (totalRead == total) + if (totalRead == total) { + _log_str("socket.recv", buffer, totalRead); return totalRead; + } } while (1); } -/*-------------------------------------------------- -FUNCTION: hsocket_recv -----------------------------------------------------*/ -int -hsocket_recv (hsocket_t sock, unsigned char **buffer, int *totalSize) -{ - ssize_t size; - int chunk = 1; - char tmp[HSOCKET_MAX_BUFSIZE + 1]; - int fsize; - int bufSize; - - if (*totalSize > 0) - bufSize = *totalSize; - else - bufSize = HSOCKET_MAX_BUFSIZE; - - *totalSize = 0; - - /* calculate first size for realloc */ - if (*buffer) - fsize = strlen (*buffer); - else - fsize = 0; - - do - { - size = recv (sock, tmp, bufSize, 0); - bufSize = HSOCKET_MAX_BUFSIZE; - if (size == -1) - { - log_error1 ("Error reading from socket"); - return HSOCKET_CAN_NOT_RECEIVE; - } - - if (size == 0) - { - break; - } - *totalSize += size; - if (*buffer) - { - log_verbose2 ("reallocation %d bytes", *totalSize + fsize + 1); - *buffer = (char *) realloc ((char *) *buffer, - (*totalSize) + fsize + HSOCKET_MAX_BUFSIZE); - strcat (*buffer, tmp); - } - else - { - *buffer = (char *) realloc (NULL, *totalSize + 1); - strcpy (*buffer, tmp); - } - - (*buffer)[*totalSize + fsize] = '\0'; - chunk++; - } while (size > 0); - - return HSOCKET_OK; -} - -/*-------------------------------------------------- -FUNCTION: hsocket_recv -----------------------------------------------------*/ -int -hsocket_recv_cb (hsocket_t sock, hsocket_recv_callback cb, void *userdata) -{ - ssize_t size; - char tmp[HSOCKET_MAX_BUFSIZE + 1]; - - do - { - - size = recv (sock, tmp, HSOCKET_MAX_BUFSIZE, 0); - - if (size == -1) - { - log_error1 ("Error reading from socket"); - return HSOCKET_CAN_NOT_RECEIVE; - } - - if (size == 0) - { - break; - } - tmp[size] = '\0'; - if (!cb (sock, tmp, size, userdata)) - { - break; - } - } - while (size > 0); - - return HSOCKET_OK; -} - -/*-------------------------------------------------- -FUNCTION: hbufsocket_read -----------------------------------------------------*/ -int -hbufsocket_read (hbufsocket_t * bufsock, char *buffer, int size) +hstatus_t +hsocket_block(hsocket_t sock, int block) { - int status; - int tmpsize; - - if (bufsock->bufsize - bufsock->cur >= size) - { - - /* no need to read from socket*/ - strncpy (buffer, &(bufsock->buffer[bufsock->cur]), size); - bufsock->cur += size; - return HSOCKET_OK; - - } - else - { - - tmpsize = bufsock->bufsize - bufsock->cur; - log_verbose5 ("tmpsize=%d, bufsock->bufsize=%d, bufsock->cur=%d, size=%d", - tmpsize, bufsock->bufsize, bufsock->cur, size); - - if (tmpsize > 0) - strncpy (buffer, &(bufsock->buffer[bufsock->cur]), tmpsize); - - size -= tmpsize; - - free (bufsock->buffer); - - status = hsocket_read (bufsock->sock, &buffer[tmpsize], size, 1); - log_verbose3("hsocket_read(): size=%d,status=%d", size, status); - if (status == size) - { - bufsock->buffer = (char *) malloc (size + 1); - strncpy (bufsock->buffer, &buffer[tmpsize], size); - bufsock->cur = size; - bufsock->bufsize = size; - } - else - { - return status; - } +#ifdef WIN32 + unsigned long iMode; +#endif - return HSOCKET_OK; - } -} + if (sock <= 0) + return HSOCKET_ERROR_NOT_INITIALIZED; -int -hsocket_makenonblock (hsocket_t sock) -{ #ifdef WIN32 - unsigned long iMode; - iMode = HSOCKET_NONBLOCKMODE; +/*#define HSOCKET_BLOCKMODE 0 +#define HSOCKET_NONBLOCKMODE 1 +*/ + iMode = (block==0)?1:0; /* Non block mode */ if (ioctlsocket (sock, FIONBIO, (u_long FAR *) & iMode) == INVALID_SOCKET) { log_error1 ("ioctlsocket error"); - return -1; + return HSOCKET_ERROR_IOCTL; } #else /* fcntl(sock, F_SETFL, O_NONBLOCK); */ +/* TODO (#1#): check for *nix the non blocking sockets */ + #endif - return HSOCKET_OK; + return H_OK; } -#ifdef WIN32 - -struct tm * -localtime_r (const time_t * const timep, struct tm *p_tm) -{ - static struct tm *tmp; - tmp = localtime (timep); - if (tmp) - { - memcpy (p_tm, tmp, sizeof (struct tm)); - tmp = p_tm; - } - return tmp; -} -#endif /* */ diff --git a/nanohttp/nanohttp-socket.h b/nanohttp/nanohttp-socket.h index 48d2a0b..8deb273 100644 --- a/nanohttp/nanohttp-socket.h +++ b/nanohttp/nanohttp-socket.h @@ -1,5 +1,5 @@ /****************************************************************** - * $Id: nanohttp-socket.h,v 1.12 2004/09/19 07:05:03 snowdrop Exp $ + * $Id: nanohttp-socket.h,v 1.13 2004/10/15 13:29:37 snowdrop Exp $ * * CSOAP Project: A http client/server library in C * Copyright (C) 2003 Ferhat Ayaz @@ -24,182 +24,184 @@ #ifndef NANO_HTTP_SOCKET_H #define NANO_HTTP_SOCKET_H -#define HSOCKET_OK 0 -#define HSOCKET_CAN_NOT_CREATE 1001 -#define HSOCKET_CAN_NOT_GET_HOSTNAME 1002 -#define HSOCKET_CAN_NOT_CONNECT 1003 -#define HSOCKET_CAN_NOT_SEND 1004 -#define HSOCKET_CAN_NOT_RECEIVE 1005 -#define HSOCKET_CAN_NOT_BIND 1006 -#define HSOCKET_CAN_NOT_LISTEN 1007 -#define HSOCKET_CAN_NOT_ACCEPT 1008 +#include -#define HSOCKET_MAX_BUFSIZE 1024 +#include + +#ifdef WIN32 + #include +#endif -#define HSOCKET_BLOCKMODE 0 -#define HSOCKET_NONBLOCKMODE 1 -#include #ifdef WIN32 -#include -#include -#include -typedef SOCKET hsocket_t; -typedef int socklen_t; -#define close(s) closesocket(s) + typedef SOCKET hsocket_t; + typedef int socklen_t; #else -#include -typedef int hsocket_t; + typedef int hsocket_t; #endif -/* - PROTOTYPE: - int my_recv_cb(hsocket_t sock, char *buffer, int size, void *userdata); - returns 1 to continue 0 to stop receiving. - */ -typedef int (*hsocket_recv_callback)(hsocket_t, char *, int, void*); -/* - hsocket_module_init - Returns 0 if success. - >0 if fail. + +/** + Initializes the socket modul. This should be called only + once for an application. + + @returns This function should always return H_OK. */ -int hsocket_module_init(); +hstatus_t hsocket_module_init(); + + +/** + Destroys the socket modul. This should be called after + finishing an application. +*/ void hsocket_module_destroy(); -/* - hsocket_init - Returns 0 if success. - >0 if fail. +/** + Initializes a given socket object. This function should + be called for every socket before using it. + + @param sock the destination socket to initialize. + + @returns This function should always return H_OK. */ -int hsocket_init(hsocket_t *sock); +hstatus_t hsocket_init(hsocket_t *sock); + + +/** + Destroys and releases a given socket. + + @param sock the socket to destroy +*/ void hsocket_free(hsocket_t sock); -/* - hsocket_open: create and connect a socket - Returns 0 if success - >0 if fail. +/** + Connects to a given host. The hostname can be an IP number + or a humen readable hostname. + + @param sock the destonation socket object to use + @param host hostname + @param port port number to connect to + + @returns H_OK if success. One of the followings if fails:

+
HSOCKET_ERROR_CREATE +
HSOCKET_ERROR_GET_HOSTNAME +
HSOCKET_ERROR_CONNECT */ -int hsocket_open(hsocket_t *sock, const char* host, int port); +hstatus_t hsocket_open(hsocket_t *sock, const char* host, int port); + + +/** + Close a socket connection. + + @param sock the socket to close +*/ void hsocket_close(hsocket_t sock); -/* - hsocket_bind: create and bind a socket - Returns 0 if success - >0 if fail. - */ -int hsocket_bind(hsocket_t *sock, int port); -/* - Listen to socket. Must be called after bind +/** + Binds a socket to a given port number. After bind you + can call hsocket_listen() to listen to the port. + + @param sock socket to use. + @param port port number to bind to + + @returns H_OK if success. One of the followings if fails:

+
HSOCKET_ERROR_CREATE +
HSOCKET_ERROR_BIND + + @see hsocket_listen */ -int hsocket_listen(hsocket_t sock, int n); +hstatus_t hsocket_bind(hsocket_t *sock, int port); -typedef struct tag_conndata -{ - hsocket_t sock; -#ifdef WIN32 - HANDLE tid; -#else - pthread_t tid; - pthread_attr_t attr; -#endif - time_t atime; -}conndata_t; -int hsocket_accept(hsocket_t sock, hsocket_t *dest); +/** + Set the socket to the listen mode. You must bind + the socket to a port with hsocket_bind() before + you can listen to the port. + @param sock the socket to use -/* - hsocket_nsend - sends n bytes of data - Returns 0 if success - >0 if fail - */ -int hsocket_nsend(hsocket_t sock, const unsigned char* buffer, int n); + @returns H_OK if success. One of the followings if fails:

+
HSOCKET_ERROR_NOT_INITIALIZED +
HSOCKET_ERROR_LISTEN +*/ +hstatus_t hsocket_listen(hsocket_t sock); -/* - hsocket_send - sends strlen(buffer) bytes of data - Returns 0 if success - >0 if fail - */ -int hsocket_send(hsocket_t sock, const char* str); - - -/* - hsocket_recv - receives everything quequed on the socket. - Sets *buffer to the received buffer. - Sets size to the received size. - You must free the buffer manually with free(). - If buffer is non zero, this functions assumes that - buffer is valid and just reallocates the given buffer. - If buffer is zero (like in the following example), - the buffer will be allocated first. - - Example: - - int size; - char *buffer; - hsocket_t sock; - - buffer = 0; - sock = ... - - if (!hsocket_recv(sock, &buffer, &size)) { - printf("Received total: %d\n", size); - printf("Received: '%s'\n", buffer); - - free(buffer); - } else { - printf("Error receiving data\n"); - } - - Returns 0 if success - >0 if fail - */ -int hsocket_recv(hsocket_t sock, unsigned char** buffer, int *size); -int hsocket_recv_limit(hsocket_t sock, char** buffer, - const char* delim, char **rest, - int *totalBuffer, int *totalRest); +/** + Accepts an incoming socket request. Note that this function + will not return until a socket connection is ready. -/* - returns 1 to continue, 0 to break; - */ -int hsocket_recv_cb(hsocket_t sock, - hsocket_recv_callback cb, void *userdata); + @param sock the socket which listents to a port + @param dest the destination socket which will be created + + @returns H_OK if success. One of the followings if fails:

+
HSOCKET_ERROR_NOT_INITIALIZED +
HSOCKET_ERROR_ACCEPT +*/ +hstatus_t hsocket_accept(hsocket_t sock, hsocket_t *dest); -int hsocket_read(hsocket_t sock, unsigned char* buffer, int total, int force); +/** + Sends data throught the socket. -/* ======================================== */ -/* Buffered socket */ -/* ======================================== */ -typedef struct _bufsocket -{ - hsocket_t sock; - char *buffer; - int bufsize; - int cur; -}hbufsocket_t; + @param sock the socket to use to send the data + @param bytes bytes to send + @param size size of memory to sent pointed by bytes. + @returns H_OK if success. One of the followings if fails:

+
HSOCKET_ERROR_NOT_INITIALIZED +
HSOCKET_ERROR_SEND +*/ +hstatus_t hsocket_nsend(hsocket_t sock, const byte_t* bytes, int size); -int hbufsocket_read(hbufsocket_t *bufsock, char *buffer, int size); -/*-------------------------------------------------- -FUNCTION: hsocket_makenonblock -----------------------------------------------------*/ -int hsocket_makenonblock(hsocket_t sock); +/** + Sends a string throught the socket -#ifdef WIN32 + @param sock the socket to use to send the data + @param str the null terminated string to sent + + @returns H_OK if success. One of the followings if fails:

+
HSOCKET_ERROR_NOT_INITIALIZED +
HSOCKET_ERROR_SEND +*/ +hstatus_t hsocket_send(hsocket_t sock, const char* str); + + +/** + Reads data from the socket. + + @param sock the socket to read data from + @param buffer the buffer to use to save the readed bytes + @param size the maximum size of bytes to read + @param force if force is 1 then hsocket_read() will wait until + maximum size of bytes (size parameter) was readed. Otherwise + this function will not wait and will return with the bytes + quequed on the socket. + + @returns This function will return -1 if an read error was occured. + Otherwise the return value is the size of bytes readed from + the socket. + +*/ +int hsocket_read(hsocket_t sock, byte_t* buffer, int size, int force); + +/** + Sets the goven socket to non-blocking socket mode. + + @param sock the socket to set into the non-blocking mode + + @returns H_OK if success. One of the followings if fails:

+
HSOCKET_ERROR_NOT_INITIALIZED +
HSOCKET_ERROR_IOCTL +*/ +hstatus_t hsocket_block(hsocket_t sock, int block); -struct tm *localtime_r(const time_t *const timep, struct tm *p_tm); -#endif #endif -- cgit v1.1-32-gdbae