From: Pekka Riikonen Date: Sat, 16 Sep 2006 08:17:02 +0000 (+0000) Subject: Added SILC HTTP, very simple HTTP server. X-Git-Tag: silc.client.1.1.beta1~277 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=c33005f2f5ead342fd0417931e38013aacfb98b8 Added SILC HTTP, very simple HTTP server. --- diff --git a/lib/silchttp/Makefile.ad b/lib/silchttp/Makefile.ad new file mode 100644 index 00000000..0a8d9578 --- /dev/null +++ b/lib/silchttp/Makefile.ad @@ -0,0 +1,34 @@ +# +# Makefile.ad +# +# Author: Pekka Riikonen +# +# Copyright (C) 2006 Pekka Riikonen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign + +noinst_LTLIBRARIES = libsilchttp.la + +libsilchttp_la_SOURCES = \ + silchttpserver.c + +#ifdef SILC_DIST_TOOLKIT +include_HEADERS = \ + silchttpserver.h + +SILC_EXTRA_DIST = tests +#endif SILC_DIST_TOOLKIT + +EXTRA_DIST = *.h $(SILC_EXTRA_DIST) + +include $(top_srcdir)/Makefile.defines.in diff --git a/lib/silchttp/silchttpserver.c b/lib/silchttp/silchttpserver.c new file mode 100644 index 00000000..fb5dae10 --- /dev/null +++ b/lib/silchttp/silchttpserver.c @@ -0,0 +1,590 @@ +/* + + silchttpserver.c + + Author: Pekka Riikonen + + Copyright (C) 2006 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#include "silc.h" +#include "silchttpserver.h" + +/************************** Types and definitions ***************************/ + +#define SILC_HTTP_SERVER_TIMEOUT 120 /* Connection timeout */ +#define SILC_HTTP_SERVER_CONNS 2 /* Default number of connections */ +#define SILC_HTTP_SERVER_BUFLEN 1024 /* Default data buffer length */ +#define SILC_HTTP_SERVER_HEADER "HTTP/1.1 200 OK\r\nServer: SILCHTTP/1.0\r\n" + +/* HTTP server context */ +struct SilcHttpServerStruct { + SilcNetListener listener; /* Server listener */ + SilcSchedule schedule; /* Scheduler */ + SilcList allconns; /* All connections */ + SilcList conns; /* Connection free list */ + SilcHttpServerCallback callback; /* Requset callback */ + void *context; /* Request callback context */ +}; + +/* HTTP connection context */ +struct SilcHttpConnectionStruct { + struct SilcHttpConnectionStruct *next; + SilcHttpServer httpd; /* Server */ + SilcStream stream; /* Connection stream */ + SilcBuffer inbuf; /* Read data buffer */ + SilcBuffer outbuf; /* Write data buffer */ + SilcInt64 touched; /* Time last connection was touched */ + SilcMime curheaders; /* HTTP request headers */ + SilcMime headers; /* HTTP reply headers */ + unsigned int keepalive : 1; /* Keep alive */ +}; + +/************************ Static utility functions **************************/ + +/* Close HTTP connection */ + +static void silc_http_server_close_connection(SilcHttpConnection conn) +{ + if (conn->headers) { + silc_mime_free(conn->headers); + conn->headers = NULL; + } + if (conn->curheaders) { + silc_mime_free(conn->curheaders); + conn->curheaders = NULL; + } + silc_buffer_clear(conn->inbuf); + silc_buffer_clear(conn->outbuf); + silc_buffer_reset(conn->inbuf); + silc_buffer_reset(conn->outbuf); + + if (conn->keepalive) + return; + + SILC_LOG_DEBUG(("Closing HTTP connection")); + + silc_schedule_task_del_by_context(conn->httpd->schedule, conn); + silc_stream_destroy(conn->stream); + + /* Add to free list */ + silc_list_add(conn->httpd->conns, conn); +} + +/* Parse HTTP data */ + +static SilcBool silc_http_server_parse(SilcHttpServer httpd, + SilcHttpConnection conn) +{ + SilcUInt32 data_len; + unsigned char *data, *tmp; + char *method, *uri; + const char *value; + SilcBufferStruct postdata; + + SILC_LOG_DEBUG(("Parsing HTTP data")); + SILC_LOG_HEXDUMP(("HTTP data"), silc_buffer_data(conn->inbuf), + silc_buffer_len(conn->inbuf)); + + data = silc_buffer_data(conn->inbuf); + data_len = silc_buffer_len(conn->inbuf); + + if (data_len < 3) + return TRUE; + + tmp = memchr(data, '\n', data_len); + if (!tmp || tmp[-1] != '\r') { + if (data_len < SILC_HTTP_SERVER_BUFLEN) + return TRUE; + return FALSE; + } + *tmp = 0; + + /* Get method */ + if (strchr(data, ' ')) + *strchr(data, ' ') = 0; + method = data; + SILC_LOG_DEBUG(("Method: '%s'", method)); + + /* Get URI */ + tmp = memchr(data, '\0', data_len); + if (!tmp) { + if (data_len < SILC_HTTP_SERVER_BUFLEN) + return TRUE; + return FALSE; + } + tmp++; + if (strchr(tmp, ' ')) + *strchr(tmp, ' ') = 0; + uri = tmp; + SILC_LOG_DEBUG(("URI: '%s'", uri)); + + /* Get HTTP headers */ + tmp++; + tmp = memchr(tmp, '\n', data_len - (tmp - data)) + 1; + if (!tmp) { + if (data_len < SILC_HTTP_SERVER_BUFLEN) + return TRUE; + return FALSE; + } + conn->curheaders = silc_mime_decode(NULL, tmp, data_len - (tmp - data)); + if (!conn->curheaders) { + if (data_len < SILC_HTTP_SERVER_BUFLEN) + return TRUE; + return FALSE; + } + + /* Check for persistent connection */ + value = silc_mime_get_field(conn->curheaders, "Keep-alive"); + if (value) + conn->keepalive = TRUE; + value = silc_mime_get_field(conn->curheaders, "Connection"); + if (value && !strcasecmp(value, "keep-alive")) + conn->keepalive = TRUE; + if (value && !strcasecmp(value, "close")) + conn->keepalive = FALSE; + + /* Deliver request to caller */ + if (!strcasecmp(method, "GET") || !strcasecmp(method, "HEAD")) { + /* Send request to caller */ + httpd->callback(httpd, conn, uri, method, NULL, httpd->context); + + } else if (!strcasecmp(method, "POST")) { + /* Get POST data */ + tmp = (unsigned char *)silc_mime_get_data(conn->curheaders, &data_len); + if (!tmp) { + silc_mime_free(conn->curheaders); + conn->curheaders = NULL; + if (data_len < SILC_HTTP_SERVER_BUFLEN) + return TRUE; + return FALSE; + } + silc_buffer_set(&postdata, tmp, data_len); + SILC_LOG_HEXDUMP(("HTTP POST data"), tmp, data_len); + + /* Send request to caller */ + httpd->callback(httpd, conn, uri, method, &postdata, httpd->context); + + } else { + /* XXX Send bad request error */ + } + + return TRUE; +} + +/* Send HTTP data to connection */ + +static SilcBool silc_http_server_send_internal(SilcHttpServer httpd, + SilcHttpConnection conn, + SilcBuffer data, + SilcBool headers) +{ + int ret; + + SILC_LOG_HEXDUMP(("HTTP data"), silc_buffer_data(data), + silc_buffer_len(data)); + + /* Write the packet to the stream */ + while (silc_buffer_len(data) > 0) { + ret = silc_stream_write(conn->stream, silc_buffer_data(data), + silc_buffer_len(data)); + if (ret == 0 || ret == - 2) + return FALSE; + + if (ret == -1) { + /* Cannot write now, write later. */ + if (silc_buffer_len(data) - ret >= silc_buffer_taillen(conn->outbuf)) + if (!silc_buffer_realloc(conn->outbuf, + silc_buffer_truelen(conn->outbuf) + + silc_buffer_len(data) - ret)) { + conn->keepalive = FALSE; + silc_http_server_close_connection(conn); + return FALSE; + } + silc_buffer_pull_tail(conn->outbuf, silc_buffer_len(data) - ret); + silc_buffer_put(conn->outbuf, silc_buffer_data(data) + ret, + silc_buffer_len(data) - ret); + return TRUE; + } + + /* Wrote data */ + silc_buffer_pull(data, ret); + } + + if (!headers) { + /* Data sent, close connection */ + SILC_LOG_DEBUG(("Data sent")); + silc_http_server_close_connection(conn); + } + + return TRUE; +} + +/* Allocate connection context */ + +static SilcHttpConnection silc_http_server_alloc_connection(void) +{ + SilcHttpConnection conn; + + conn = silc_calloc(1, sizeof(*conn)); + if (!conn) + return NULL; + + conn->inbuf = silc_buffer_alloc(SILC_HTTP_SERVER_BUFLEN); + if (!conn->inbuf) { + silc_free(conn); + return NULL; + } + + conn->outbuf = silc_buffer_alloc(SILC_HTTP_SERVER_BUFLEN); + if (!conn->outbuf) { + silc_buffer_free(conn->inbuf); + silc_free(conn); + return NULL; + } + + silc_buffer_reset(conn->inbuf); + silc_buffer_reset(conn->outbuf); + + return conn; +} + +/* Check if connection has timedout */ + +SILC_TASK_CALLBACK(silc_http_server_connection_timeout) +{ + SilcHttpConnection conn = context; + SilcInt64 curtime = silc_time(); + + if (curtime - conn->touched > SILC_HTTP_SERVER_TIMEOUT) { + SILC_LOG_DEBUG(("Connection timeout")); + conn->keepalive = FALSE; + silc_http_server_close_connection(conn); + return; + } + + silc_schedule_task_add_timeout(conn->httpd->schedule, + silc_http_server_connection_timeout, conn, + SILC_HTTP_SERVER_TIMEOUT, 0); +} + +/* Data I/O callback */ + +static void silc_http_server_io(SilcStream stream, SilcStreamStatus status, + void *context) +{ + SilcHttpConnection conn = context; + SilcHttpServer httpd = conn->httpd; + int ret; + + switch (status) { + case SILC_STREAM_CAN_READ: + SILC_LOG_DEBUG(("Read HTTP data")); + + conn->touched = silc_time(); + + /* Make sure we have fair amount of free space in inbuf */ + if (silc_buffer_taillen(conn->inbuf) < SILC_HTTP_SERVER_BUFLEN) + if (!silc_buffer_realloc(conn->inbuf, silc_buffer_truelen(conn->inbuf) + + SILC_HTTP_SERVER_BUFLEN * 2)) { + conn->keepalive = FALSE; + silc_http_server_close_connection(conn); + return; + } + + /* Read data from stream */ + ret = silc_stream_read(conn->stream, conn->inbuf->tail, + silc_buffer_taillen(conn->inbuf)); + + if (ret == 0 || ret == -2) { + silc_http_server_close_connection(conn); + return; + } + + if (ret == -1) { + /* Cannot read now, do it later. */ + silc_buffer_pull(conn->inbuf, silc_buffer_len(conn->inbuf)); + return; + } + + SILC_LOG_DEBUG(("Read %d bytes data", ret)); + + /* Parse the data */ + silc_buffer_pull_tail(conn->inbuf, ret); + if (!silc_http_server_parse(httpd, conn)) + silc_buffer_reset(conn->outbuf); + + break; + + case SILC_STREAM_CAN_WRITE: + SILC_LOG_DEBUG(("Write HTTP data")); + + conn->touched = silc_time(); + + /* Write pending data to stream */ + while (silc_buffer_len(conn->outbuf) > 0) { + ret = silc_stream_write(conn->stream, silc_buffer_data(conn->outbuf), + silc_buffer_len(conn->outbuf)); + + if (ret == 0 || ret == -2) { + conn->keepalive = FALSE; + silc_http_server_close_connection(conn); + return; + } + + if (ret == -1) + /* Cannot write now, write later. */ + return; + + /* Wrote data */ + silc_buffer_pull(conn->outbuf, ret); + } + + /* Data sent, close connection */ + SILC_LOG_DEBUG(("Data sent")); + silc_http_server_close_connection(conn); + break; + + default: + conn->keepalive = FALSE; + silc_http_server_close_connection(conn); + break; + } +} + +/* Accepts new connection */ + +static void silc_http_server_new_connection(SilcNetStatus status, + SilcStream stream, + void *context) +{ + SilcHttpServer httpd = context; + SilcHttpConnection conn; + const char *hostname = NULL, *ip = NULL; + + /* Get free connection */ + silc_list_start(httpd->conns); + conn = silc_list_get(httpd->conns); + if (!conn) { + conn = silc_http_server_alloc_connection(); + if (!conn) { + silc_stream_destroy(stream); + return; + } + } + + conn->httpd = httpd; + conn->stream = stream; + + silc_socket_stream_get_info(stream, NULL, &hostname, &ip, NULL); + SILC_LOG_INFO(("HTTPD: New connection %s (%s)", hostname, ip)); + + /* Schedule the connection for data I/O */ + silc_stream_set_notifier(stream, httpd->schedule, silc_http_server_io, conn); + + /* Add connection timeout check */ + silc_schedule_task_add_timeout(httpd->schedule, + silc_http_server_connection_timeout, conn, + SILC_HTTP_SERVER_TIMEOUT, 0); +} + + +/******************************* Public API *********************************/ + +/* Allocate HTTP server */ + +SilcHttpServer silc_http_server_alloc(const char *ip, SilcUInt16 port, + SilcUInt32 max_connections, + SilcSchedule schedule, + SilcHttpServerCallback callback, + void *context) +{ + SilcHttpServer httpd; + SilcHttpConnection conn; + int i; + + SILC_LOG_DEBUG(("Start HTTP server at %s:%d", ip, port)); + + if (!ip || !schedule || !callback) + return FALSE; + + httpd = silc_calloc(1, sizeof(*httpd)); + if (!httpd) + return NULL; + + /* Create server listener */ + httpd->listener = + silc_net_tcp_create_listener(&ip, 1, port, TRUE, FALSE, schedule, + silc_http_server_new_connection, httpd); + if (!httpd->listener) { + SILC_LOG_ERROR(("Could not bind HTTP server at %s:%d", ip, port)); + silc_http_server_free(httpd); + return NULL; + } + + httpd->schedule = schedule; + httpd->callback = callback; + httpd->context = context; + + /* Allocate connections list */ + if (!max_connections) + max_connections = SILC_HTTP_SERVER_CONNS; + for (i = 0; i < max_connections; i++) { + conn = silc_http_server_alloc_connection(); + if (!conn) + break; + silc_list_add(httpd->conns, conn); + silc_list_add(httpd->allconns, conn); + } + + SILC_LOG_DEBUG(("HTTP Server started")); + + return httpd; +} + +/* Free HTTP server */ + +void silc_http_server_free(SilcHttpServer httpd) +{ + SilcHttpConnection conn; + + silc_list_start(httpd->allconns); + while ((conn = silc_list_get(httpd->allconns))) { + conn->keepalive = FALSE; + silc_http_server_close_connection(conn); + silc_buffer_free(conn->inbuf); + silc_buffer_free(conn->outbuf); + silc_free(conn); + } + + if (httpd->listener) + silc_net_close_listener(httpd->listener); + + silc_free(httpd); +} + +/* Send HTTP data to connection */ + +SilcBool silc_http_server_send(SilcHttpServer httpd, + SilcHttpConnection conn, + SilcBuffer data) +{ + SilcBufferStruct h; + unsigned char *headers, tmp[16]; + SilcUInt32 headers_len; + SilcBool ret; + + SILC_LOG_DEBUG(("Sending HTTP data")); + + conn->touched = silc_time(); + + /* Write headers */ + silc_buffer_set(&h, SILC_HTTP_SERVER_HEADER, + strlen(SILC_HTTP_SERVER_HEADER)); + ret = silc_http_server_send_internal(httpd, conn, &h, TRUE); + if (!ret) { + conn->keepalive = FALSE; + silc_http_server_close_connection(conn); + return FALSE; + } + + if (!conn->headers) { + conn->headers = silc_mime_alloc(); + if (!conn->headers) { + conn->keepalive = FALSE; + silc_http_server_close_connection(conn); + return FALSE; + } + } + + snprintf(tmp, sizeof(tmp), "%d", (int)silc_buffer_len(data)); + silc_mime_add_field(conn->headers, "Content-Length", tmp); + silc_mime_add_field(conn->headers, "Connection", "keep-alive"); + snprintf(tmp, sizeof(tmp), "%d", (int)SILC_HTTP_SERVER_TIMEOUT); + silc_mime_add_field(conn->headers, "Keep-alive", tmp); + silc_mime_add_field(conn->headers, "Last-Modified", + silc_time_string(conn->touched)); + + headers = silc_mime_encode(conn->headers, &headers_len); + if (headers) { + silc_buffer_set(&h, headers, headers_len); + if (!silc_http_server_send_internal(httpd, conn, &h, TRUE)) { + conn->keepalive = FALSE; + silc_http_server_close_connection(conn); + return FALSE; + } + silc_free(headers); + } + + /* Write the page data */ + return silc_http_server_send_internal(httpd, conn, data, FALSE); +} + +/* Send error reply */ + +SilcBool silc_http_server_send_error(SilcHttpServer httpd, + SilcHttpConnection conn, + const char *error, + const char *error_message) +{ + SilcBool ret; + SilcBufferStruct data; + + memset(&data, 0, sizeof(data)); + silc_buffer_strformat(&data, + "HTTP/1.1 ", error, "\r\n\r\n", error_message, + SILC_STRFMT_END); + + /* Send the message */ + ret = silc_http_server_send_internal(httpd, conn, &data, FALSE); + + silc_buffer_purge(&data); + + /* Close connection */ + conn->keepalive = FALSE; + silc_http_server_close_connection(conn); + + return ret; +} + +/* Get field */ + +const char *silc_http_server_get_header(SilcHttpServer httpd, + SilcHttpConnection conn, + const char *field) +{ + if (!conn->curheaders) + return NULL; + return silc_mime_get_field(conn->curheaders, field); +} + +/* Add field */ + +SilcBool silc_http_server_add_header(SilcHttpServer httpd, + SilcHttpConnection conn, + const char *field, + const char *value) +{ + SILC_LOG_DEBUG(("Adding header %s:%s", field, value)); + + if (!conn->headers) { + conn->headers = silc_mime_alloc(); + if (!conn->headers) { + silc_http_server_close_connection(conn); + return FALSE; + } + } + + silc_mime_add_field(conn->headers, field, value); + return TRUE; +} diff --git a/lib/silchttp/silchttpserver.h b/lib/silchttp/silchttpserver.h new file mode 100644 index 00000000..4bc9a624 --- /dev/null +++ b/lib/silchttp/silchttpserver.h @@ -0,0 +1,205 @@ +/* + + silchttpserver.h + + Author: Pekka Riikonen + + Copyright (C) 2006 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +/****h* silchttp/SILC HTTP Server Interface + * + * DESCRIPTION + * + * Very simple HTTP server interface. This HTTP server supports basic HTTP + * features. All pages on the server are dynamically created by the caller + * of this interface. The server does not support plugins, modules, cgi-bin, + * server-side includes or any other special features. + * + ***/ + +#ifndef SILCHTTPSERVER_H +#define SILCHTTPSERVER_H + +typedef struct SilcHttpServerStruct *SilcHttpServer; +typedef struct SilcHttpConnectionStruct *SilcHttpConnection; + +/****f* silchttp/SilcHTTPServer/SilcHttpServerCallback + * + * SYNOPSIS + * + * typedef void (*SilcHttpServerCallback)(SilcHttpServer httpd, + * SilcHttpConnection conn, + * const char *uri, + * const char *method, + * SilcBuffer data, + * void *context); + * + * DESCRIPTION + * + * The HTTP request callback, that is called everytime a new HTTP request + * comes from a HTTP client. The `uri' is the requested URI (web page), + * and the `method' is the HTTP request method (GET, POST, etc.). The + * `data' is non-NULL only if the `method' is POST, and it includes the + * the POST data. + * + * The requested web page must be returned to the HTTP client from this + * callback by calling silc_http_server_send or error is returned by + * calling silc_http_server_send_error. + * + * The silc_http_server_get_header may be called to find a specific + * HTTP header from this request. New headers may be added to the + * reply by calling silc_http_server_add_header. + * + ***/ +typedef void (*SilcHttpServerCallback)(SilcHttpServer httpd, + SilcHttpConnection conn, + const char *uri, + const char *method, + SilcBuffer data, + void *context); + +/****f* silchttp/SilcHTTPServer/silc_http_server_alloc + * + * SYNOPSIS + * + * SilcHttpServer + * silc_http_server_alloc(const char *ip, SilcUInt16 port, + * SilcUInt32 max_connections, + * SilcSchedule schedule, + * SilcHttpServerCallback callback, void *context); + * + * DESCRIPTION + * + * Allocates HTTP server and binds it to the IP address `ip' on the + * `port'. If `max_connections' is non-zero, that many connections + * are allowed to the HTTP server. The `callback' with `context' will + * be called everytime a new HTTP request comes to the server from + * a HTTP client. In that callback the caller must then reply with + * the requested Web page or with an error. + * + ***/ +SilcHttpServer silc_http_server_alloc(const char *ip, SilcUInt16 port, + SilcUInt32 max_connections, + SilcSchedule schedule, + SilcHttpServerCallback callback, + void *context); + +/****f* silchttp/SilcHTTPServer/silc_http_server_free + * + * SYNOPSIS + * + * void silc_http_server_free(SilcHttpServer httpd); + * + * DESCRIPTION + * + * Close HTTP server and free all resources. + * + ***/ +void silc_http_server_free(SilcHttpServer httpd); + +/****f* silchttp/SilcHTTPServer/silc_http_server_free + * + * SYNOPSIS + * + * SilcBool silc_http_server_send(SilcHttpServer httpd, + * SilcHttpConnection conn, + * SilcBuffer data); + * + * DESCRIPTION + * + * Send the HTTP data indicated by `data' buffer into the connection + * indicated by `conn'. Returns TRUE after the data is sent, and FALSE + * if error occurred. Usually the `data' would be the requested web page. + * + ***/ +SilcBool silc_http_server_send(SilcHttpServer httpd, + SilcHttpConnection conn, + SilcBuffer data); + +/****f* silchttp/SilcHTTPServer/silc_http_server_free + * + * SYNOPSIS + * + * SilcBool silc_http_server_send_error(SilcHttpServer httpd, + * SilcHttpConnection conn, + * const char *error, + * const char *error_message); + * + * DESCRIPTION + * + * Send HTTP error back to the connection indicated by `conn'. The + * `error' is one of the 4xx or 5xx errors defined by the HTTP protocol. + * The `error_message' is the optional error message sent to the + * connection. Returns FALSE if the error could not be sent. + * + * Typical errors are: 400 Bad Request + * 403 Forbidden + * 404 Not Found + * + * EXAMPLE + * + * silc_http_server_send_error(httpd, conn, "400 Bad Request", + * "

400 Bad Request!!

"); + * + ***/ +SilcBool silc_http_server_send_error(SilcHttpServer httpd, + SilcHttpConnection conn, + const char *error, + const char *error_message); + +/****f* silchttp/SilcHTTPServer/silc_http_server_get_header + * + * SYNOPSIS + * + * const char *silc_http_server_get_header(SilcHttpServer httpd, + * SilcHttpConnection conn, + * const char *field); + * + * DESCRIPTION + * + * Finds a header field indicated by `field' from the current HTTP + * request sent by the HTTP client. Returns the field value or NULL + * if suchs header field does not exist. + * + ***/ +const char *silc_http_server_get_header(SilcHttpServer httpd, + SilcHttpConnection conn, + const char *field); + +/****f* silchttp/SilcHTTPServer/silc_http_server_add_header + * + * SYNOPSIS + * + * SilcBool silc_http_server_add_header(SilcHttpServer httpd, + * SilcHttpConnection conn, + * const char *field, + * const char *value); + * + * DESCRIPTION + * + * Adds a new header to the HTTP headers to be sent back to the + * HTTP client. This may be called to add needed headers to the + * HTTP reply. + * + * EXAMPLE + * + * silc_http_server_add_header(httpd, conn, "Content-Type", "image/jpeg"); + * + ***/ +SilcBool silc_http_server_add_header(SilcHttpServer httpd, + SilcHttpConnection conn, + const char *field, + const char *value); + +#endif /* SILCHTTPSERVER_H */ diff --git a/lib/silchttp/tests/Makefile.am b/lib/silchttp/tests/Makefile.am new file mode 100644 index 00000000..e3ce7c23 --- /dev/null +++ b/lib/silchttp/tests/Makefile.am @@ -0,0 +1,27 @@ +# +# Makefile.am +# +# Author: Pekka Riikonen +# +# Copyright (C) 2006 Pekka Riikonen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +AUTOMAKE_OPTIONS = 1.0 no-dependencies foreign + +bin_PROGRAMS = test_silchttpserver + +test_silchttpserver_SOURCES = test_silchttpserver.c + +LIBS = $(SILC_COMMON_LIBS) +LDADD = -L.. -L../.. -lsilc + +include $(top_srcdir)/Makefile.defines.in diff --git a/lib/silchttp/tests/test_silchttpserver.c b/lib/silchttp/tests/test_silchttpserver.c new file mode 100644 index 00000000..e4fc526a --- /dev/null +++ b/lib/silchttp/tests/test_silchttpserver.c @@ -0,0 +1,98 @@ +/* SilcHttpServer tests */ + +#include "silc.h" +#include "../silchttpserver.h" + +static void http_callback(SilcHttpServer httpd, SilcHttpConnection conn, + const char *uri, const char *method, + SilcBuffer data, void *context) +{ + SilcBufferStruct page; + + SILC_LOG_DEBUG(("HTTP data received, URI:%s, method:%s", uri, method)); + + if (!strcasecmp(method, "GET")) { + /* Send our default page */ + if (!strcmp(uri, "/") || !strcmp(uri, "/index.html")) { + memset(&page, 0, sizeof(page)); + silc_buffer_strformat(&page, + "", + silc_http_server_get_header(httpd, conn, + "User-Agent"), + "

OUR DEFAULT PAGE IS THIS: ", + silc_time_string(silc_time()), + "

" + "" + "
" + " Male
" + " Female
" + " " + "

" + "", + SILC_STRFMT_END); + silc_http_server_add_header(httpd, conn, "X-Date", + silc_time_string(silc_time())); + silc_http_server_send(httpd, conn, &page); + silc_buffer_purge(&page); + return; + } + } + + if (!strcasecmp(method, "POST")) { + if (strcmp(uri, "/posttest")) + return; + memset(&page, 0, sizeof(page)); + silc_buffer_strformat(&page, + "", + "POST PROCESSED:", + silc_buffer_data(data), + "", + SILC_STRFMT_END); + silc_http_server_add_header(httpd, conn, "X-Date", + silc_time_string(silc_time())); + silc_http_server_send(httpd, conn, &page); + silc_buffer_purge(&page); + return; + } + + silc_http_server_send_error(httpd, conn, "404 Not Found", + "

404 Not Found: The page you are looking for cannot be located

"); +} + +int main(int argc, char **argv) +{ + SilcBool success = FALSE; + SilcSchedule schedule; + SilcHttpServer httpd; + + if (argc > 1 && !strcmp(argv[1], "-d")) { + silc_log_debug(TRUE); + silc_log_debug_hexdump(TRUE); + silc_log_set_debug_string("*http*,*mime*"); + } + + signal(SIGPIPE, SIG_IGN); + + SILC_LOG_DEBUG(("Allocating scheduler")); + schedule = silc_schedule_init(0, NULL); + if (!schedule) + goto err; + + SILC_LOG_DEBUG(("Allocating HTTP server at 127.0.0.1:5000")); + httpd = silc_http_server_alloc("127.0.0.1", 5000, 0, schedule, + http_callback, NULL); + if (!httpd) + goto err; + + silc_schedule(schedule); + + silc_schedule_uninit(schedule); + + success = TRUE; + + err: + SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE")); + fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE"); + + return success; +}