--- /dev/null
+/*
+
+ silchttpserver.c
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2006 - 2007 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 "silcruntime.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;
+ struct SilcHttpConnectionStruct *next2;
+ 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 char *hptr; /* Pointer to start of headers */
+ char *method; /* Method */
+ char *uri; /* URI */
+ 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);
+ conn->hptr = conn->method = conn->uri = NULL;
+
+ if (conn->keepalive)
+ return;
+
+ SILC_LOG_DEBUG(("Closing HTTP connection %p", conn));
+
+ silc_schedule_task_del_by_context(conn->httpd->schedule, conn);
+ silc_stream_set_notifier(conn->stream, conn->httpd->schedule, NULL, NULL);
+ silc_stream_destroy(conn->stream);
+ conn->stream = NULL;
+
+ /* 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 long cll;
+ unsigned char *data, *tmp;
+ const char *value, *cl;
+ SilcBufferStruct postdata;
+ int i;
+
+ SILC_LOG_DEBUG(("Parsing HTTP data"));
+
+ data = silc_buffer_data(conn->inbuf);
+ data_len = silc_buffer_len(conn->inbuf);
+
+ /* Check for end of headers */
+ for (i = 0; i < data_len ; i++) {
+ if (data_len - i >= 4 &&
+ data[i ] == '\r' && data[i + 1] == '\n' &&
+ data[i + 2] == '\r' && data[i + 3] == '\n')
+ break;
+ }
+ if (i == data_len)
+ return TRUE;
+
+ SILC_LOG_HEXDUMP(("HTTP data"), silc_buffer_data(conn->inbuf),
+ silc_buffer_len(conn->inbuf));
+
+ if (!conn->method && !conn->uri) {
+ 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;
+ conn->method = data;
+ SILC_LOG_DEBUG(("Method: '%s'", conn->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;
+ conn->uri = tmp;
+ SILC_LOG_DEBUG(("URI: '%s'", conn->uri));
+
+ /* Protocol version compatibility */
+ tmp = ((unsigned char *)memchr(tmp, '\0', data_len - (tmp - data))) + 1;
+ SILC_LOG_DEBUG(("Protocol: %s", tmp));
+ if (strstr(tmp, "HTTP/1.0"))
+ conn->keepalive = FALSE;
+ if (strstr(tmp, "HTTP/1.1"))
+ conn->keepalive = TRUE;
+ if (strstr(tmp, "HTTP/1.2"))
+ conn->keepalive = TRUE;
+
+ /* Get HTTP headers */
+ tmp = memchr(tmp, '\0', data_len - (tmp - data));
+ if (!tmp) {
+ if (data_len < SILC_HTTP_SERVER_BUFLEN)
+ return TRUE;
+ return FALSE;
+ }
+ if (data_len - (tmp - data) < 2) {
+ if (data_len < SILC_HTTP_SERVER_BUFLEN)
+ return TRUE;
+ return FALSE;
+ }
+ conn->hptr = ++tmp;
+ }
+
+ /* Parse headers and data area */
+ conn->curheaders = silc_mime_decode(NULL, conn->hptr,
+ data_len - (conn->hptr - data));
+ if (!conn->curheaders)
+ return FALSE;
+
+ /* Check for persistent connection */
+ value = silc_mime_get_field(conn->curheaders, "Connection");
+ if (value && !strcasecmp(value, "close"))
+ conn->keepalive = FALSE;
+
+ /* Deliver request to caller */
+ if (!strcasecmp(conn->method, "GET") || !strcasecmp(conn->method, "HEAD")) {
+ httpd->callback(httpd, conn, conn->uri, conn->method,
+ NULL, httpd->context);
+
+ } else if (!strcasecmp(conn->method, "POST")) {
+ /* Get POST data */
+ tmp = (unsigned char *)silc_mime_get_data(conn->curheaders, &data_len);
+ if (!tmp)
+ return FALSE;
+
+ /* Check we have received all data */
+ cl = silc_mime_get_field(conn->curheaders, "Content-Length");
+ if (cl && sscanf(cl, "%lu", &cll) == 1) {
+ if (data_len < cll) {
+ /* More data to come */
+ silc_mime_free(conn->curheaders);
+ conn->curheaders = NULL;
+ return TRUE;
+ }
+ }
+
+ silc_buffer_set(&postdata, tmp, data_len);
+ SILC_LOG_HEXDUMP(("HTTP POST data"), tmp, data_len);
+
+ httpd->callback(httpd, conn, conn->uri, conn->method,
+ &postdata, httpd->context);
+ } else {
+ /* Send bad request */
+ silc_http_server_send_error(httpd, conn, "400 Bad Request",
+ "<body><h1>400 Bad Request</h1><body>");
+ return TRUE;
+ }
+
+ 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 %p", conn));
+ 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 %p", conn));
+ 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 %p", conn));
+
+ 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) {
+ conn->keepalive = FALSE;
+ 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)) {
+ conn->keepalive = FALSE;
+ silc_http_server_close_connection(conn);
+ }
+
+ break;
+
+ case SILC_STREAM_CAN_WRITE:
+ SILC_LOG_DEBUG(("Write HTTP data %p", conn));
+
+ 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(SilcResult 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) {
+ /* Add new connection */
+ conn = silc_http_server_alloc_connection();
+ if (!conn) {
+ silc_stream_destroy(stream);
+ return;
+ }
+ silc_list_add(httpd->allconns, conn);
+ }
+ silc_list_del(httpd->conns, conn);
+
+ 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));
+ SILC_LOG_DEBUG(("New connection %p", conn));
+
+ /* 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,
+ SilcSchedule schedule,
+ SilcHttpServerCallback callback,
+ void *context)
+{
+ SilcHttpServer httpd;
+ SilcHttpConnection conn;
+ int i;
+
+ SILC_LOG_DEBUG(("Start HTTP server at %s:%d", ip, port));
+
+ if (!schedule)
+ schedule = silc_schedule_get_global();
+
+ 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;
+
+ silc_list_init(httpd->conns, struct SilcHttpConnectionStruct, next);
+ silc_list_init(httpd->allconns, struct SilcHttpConnectionStruct, next2);
+
+ /* Allocate connections list */
+ for (i = 0; i < SILC_HTTP_SERVER_CONNS; i++) {
+ conn = silc_http_server_alloc_connection();
+ if (!conn)
+ break;
+ silc_list_add(httpd->conns, conn);
+ silc_list_add(httpd->allconns, conn);
+ conn->httpd = httpd;
+ }
+
+ 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;
+ if (conn->httpd && conn->stream)
+ 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;
+ }
+ }
+
+ silc_mime_add_field(conn->headers, "Last-Modified",
+ silc_time_string(conn->touched));
+ silc_snprintf(tmp, sizeof(tmp), "%d", (int)silc_buffer_len(data));
+ silc_mime_add_field(conn->headers, "Content-Length", tmp);
+ if (conn->keepalive) {
+ silc_mime_add_field(conn->headers, "Connection", "keep-alive");
+ silc_snprintf(tmp, sizeof(tmp), "%d", (int)SILC_HTTP_SERVER_TIMEOUT);
+ silc_mime_add_field(conn->headers, "Keep-alive", tmp);
+ }
+
+ 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;
+}
--- /dev/null
+/*
+
+ silchttpserver.h
+
+ Author: Pekka Riikonen <priikone@silcnet.org>
+
+ Copyright (C) 2006 - 2008 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/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. Naturally, the caller
+ * of this interface may itself implement such features.
+ *
+ ***/
+
+#ifndef SILCHTTPSERVER_H
+#define SILCHTTPSERVER_H
+
+/****s* silchttp/SilcHttpServer
+ *
+ * NAME
+ *
+ * typedef struct SilcHttpServerStruct *SilcHttpServer;
+ *
+ * DESCRIPTION
+ *
+ * The actual HTTP server allocated with silc_http_server_alloc and
+ * freed with silc_http_server_free.
+ *
+ ***/
+typedef struct SilcHttpServerStruct *SilcHttpServer;
+
+/****s* silchttp/SilcHttpConnection
+ *
+ * NAME
+ *
+ * typedef struct SilcHttpConnectionStruct *SilcHttpConnection;
+ *
+ * DESCRIPTION
+ *
+ * HTTP connection context. This is allocated by the library and
+ * delivered to application in SilcHttpServerCallback callbcak function.
+ * It is given as argument to many silc_http_server_* functions.
+ * It is freed automatically by the library.
+ *
+ ***/
+typedef struct SilcHttpConnectionStruct *SilcHttpConnection;
+
+/****f* silchttp/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/silc_http_server_alloc
+ *
+ * SYNOPSIS
+ *
+ * SilcHttpServer
+ * silc_http_server_alloc(const char *ip, SilcUInt16 port,
+ * SilcSchedule schedule,
+ * SilcHttpServerCallback callback, void *context);
+ *
+ * DESCRIPTION
+ *
+ * Allocates HTTP server and binds it to the IP address `ip' on the
+ * `port'. 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 error.
+ * If the `schedule' is NULL this will call silc_schedule_get_global to
+ * try to get global scheduler.
+ *
+ ***/
+SilcHttpServer silc_http_server_alloc(const char *ip, SilcUInt16 port,
+ SilcSchedule schedule,
+ SilcHttpServerCallback callback,
+ void *context);
+
+/****f* silchttp/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/silc_http_server_send
+ *
+ * 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/silc_http_server_send_error
+ *
+ * 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",
+ * "<body><h1>400 Bad Request!!</h1></body>");
+ *
+ ***/
+SilcBool silc_http_server_send_error(SilcHttpServer httpd,
+ SilcHttpConnection conn,
+ const char *error,
+ const char *error_message);
+
+/****f* silchttp/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 such header field does not exist.
+ *
+ ***/
+const char *silc_http_server_get_header(SilcHttpServer httpd,
+ SilcHttpConnection conn,
+ const char *field);
+
+/****f* silchttp/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");
+ * silc_http_server_send(httpd, conn, image_data);
+ *
+ ***/
+SilcBool silc_http_server_add_header(SilcHttpServer httpd,
+ SilcHttpConnection conn,
+ const char *field,
+ const char *value);
+
+#endif /* SILCHTTPSERVER_H */
--- /dev/null
+/* SilcHttpServer tests */
+/* Actually this is almost a full-fledged HTTP server. It can serve HTML
+ and PHP pages pretty well. In PHP the variables passed in URI with '?'
+ work in PHP script, with this HTTPD of ours, only if $_REQUEST variable
+ is used to fetch them (limitation in PHP command line version). In other
+ ways '?' in URI is not supported. */
+/* Usage: ./test_silchttpserver [-d] [<htdocsdir>] */
+
+#include "silcruntime.h"
+#include "../silchttpserver.h"
+#include "../silchttpphp.h"
+
+char *htdocs = ".";
+
+/* Add proper content type to reply per URI */
+
+static void http_content_type(SilcHttpServer httpd, SilcHttpConnection conn,
+ const char *uri)
+{
+ const char *type;
+
+ type = silc_http_server_get_header(httpd, conn, "Content-Type");
+ if (type)
+ silc_http_server_add_header(httpd, conn, "Content-Type", type);
+ else if (strstr(uri, ".jpg"))
+ silc_http_server_add_header(httpd, conn, "Content-Type", "image/jpeg");
+ else if (strstr(uri, ".gif"))
+ silc_http_server_add_header(httpd, conn, "Content-Type", "image/gif");
+ else if (strstr(uri, ".png"))
+ silc_http_server_add_header(httpd, conn, "Content-Type", "image/png");
+ else if (strstr(uri, ".css"))
+ silc_http_server_add_header(httpd, conn, "Content-Type", "text/css");
+ else if (strstr(uri, ".htm"))
+ silc_http_server_add_header(httpd, conn, "Content-Type", "text/html");
+ else if (strstr(uri, ".php"))
+ silc_http_server_add_header(httpd, conn, "Content-Type", "text/html");
+}
+
+/* Serve pages */
+
+static void http_callback_file(SilcHttpServer httpd, SilcHttpConnection conn,
+ const char *uri, const char *method,
+ SilcBuffer data, void *context)
+{
+ SilcBufferStruct page;
+ SilcBuffer php;
+ char *filedata, filename[256];
+ SilcUInt32 data_len;
+ SilcBool usephp = FALSE;
+
+ if (!strcasecmp(method, "GET")) {
+ if (strstr(uri, ".php"))
+ usephp = TRUE;
+
+ if (!strcmp(uri, "/"))
+ snprintf(filename, sizeof(filename), "%s/index.html", htdocs);
+ else
+ snprintf(filename, sizeof(filename), "%s%s", htdocs, uri);
+
+ if (strchr(filename, '?'))
+ *strchr(filename, '?') = ' ';
+ while (strchr(filename, '&'))
+ *strchr(filename, '&') = ' ';
+
+ SILC_LOG_DEBUG(("Filename: '%s'", filename));
+
+ if (!usephp) {
+ filedata = silc_file_readfile(filename, &data_len, NULL);
+ if (!filedata) {
+ silc_http_server_send_error(httpd, conn, "404 Not Found",
+ "<body><h1>404 Not Found</h1><p>The page you are looking for cannot be located</body>");
+ return;
+ }
+
+ http_content_type(httpd, conn, uri);
+
+ /* Send page */
+ silc_buffer_set(&page, filedata, data_len);
+ silc_http_server_send(httpd, conn, &page);
+ silc_buffer_purge(&page);
+ } else {
+ php = silc_http_php_file(filename);
+ if (!php) {
+ silc_http_server_send_error(httpd, conn, "404 Not Found",
+ "<body><h1>404 Not Found</h1><p>The page you are looking for cannot be located</body>");
+ return;
+ }
+
+ http_content_type(httpd, conn, uri);
+
+ /* Send page */
+ silc_http_server_send(httpd, conn, php);
+ silc_buffer_free(php);
+ }
+
+ return;
+ }
+
+ silc_http_server_send_error(httpd, conn, "404 Not Found",
+ "<body><h1>404 Not Found</h1><p>The page you are looking for cannot be located</body>");
+}
+
+int main(int argc, char **argv)
+{
+ SilcBool success = FALSE;
+ SilcSchedule schedule;
+ SilcHttpServer httpd;
+
+ if (argc > 1) {
+ if (!strcmp(argv[1], "-d")) {
+ silc_log_debug(TRUE);
+ silc_log_debug_hexdump(TRUE);
+ silc_log_set_debug_string("*http*,*mime*");
+ if (argc > 2)
+ htdocs = argv[2];
+ } else {
+ htdocs = argv[1];
+ }
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ SILC_LOG_DEBUG(("Allocating scheduler"));
+ schedule = silc_schedule_init(0, NULL, NULL, 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, schedule,
+ http_callback_file, 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;
+}