commit 64e88643d14e66ff6fc241bc2de433100078efc1
parent 7971df739ffdf324abc87a7886b901375644807a
Author: Christian Grothoff <grothoff@gnunet.org>
Date: Tue, 13 May 2025 12:31:46 +0200
re-add example code
Diffstat:
11 files changed, 2482 insertions(+), 0 deletions(-)
diff --git a/doc/examples/.gitignore b/doc/examples/.gitignore
@@ -0,0 +1,12 @@
+/tlsauthentication
+/simplepost
+/sessions
+/responseheaders
+/logging
+/largepost
+/hellobrowser
+/basicauthentication
+*.exe
+*.o
+*.lo
+*.la
diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am
@@ -0,0 +1,93 @@
+# This Makefile.am is in the public domain
+SUBDIRS = .
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/include \
+ $(CPPFLAGS_ac)
+
+AM_CFLAGS = $(CFLAGS_ac) @LIBGCRYPT_CFLAGS@
+
+AM_LDFLAGS = $(LDFLAGS_ac)
+
+AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac)
+
+if USE_COVERAGE
+ AM_CFLAGS += --coverage
+endif
+
+$(top_builddir)/src/microhttpd/libmicrohttpd.la: $(top_builddir)/src/microhttpd/Makefile
+ @echo ' cd $(top_builddir)/src/microhttpd && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd.la'; \
+ $(am__cd) $(top_builddir)/src/microhttpd && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd.la
+
+# example programs
+noinst_PROGRAMS = \
+ hellobrowser \
+ logging \
+ responseheaders
+
+if MHD_SUPPORT_AUTH_BASIC
+noinst_PROGRAMS += \
+ basicauthentication
+if MHD_SUPPORT_HTTPS
+noinst_PROGRAMS += \
+ tlsauthentication
+endif
+endif
+
+if MHD_SUPPORT_POST_PARSER
+noinst_PROGRAMS += simplepost largepost sessions
+endif
+
+if HAVE_W32
+AM_CPPFLAGS += -DWINDOWS
+endif
+
+if HAVE_EXPERIMENTAL
+noinst_PROGRAMS += websocket
+endif
+
+basicauthentication_SOURCES = \
+ basicauthentication.c
+basicauthentication_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+hellobrowser_SOURCES = \
+ hellobrowser.c
+hellobrowser_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+logging_SOURCES = \
+ logging.c
+logging_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+responseheaders_SOURCES = \
+ responseheaders.c
+responseheaders_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+sessions_SOURCES = \
+ sessions.c
+sessions_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+tlsauthentication_SOURCES = \
+ tlsauthentication.c
+tlsauthentication_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+simplepost_SOURCES = \
+ simplepost.c
+simplepost_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+largepost_SOURCES = \
+ largepost.c
+largepost_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
+websocket_SOURCES = \
+ websocket.c
+websocket_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la \
+ $(top_builddir)/src/microhttpd_ws/libmicrohttpd_ws.la
diff --git a/doc/examples/basicauthentication.c b/doc/examples/basicauthentication.c
@@ -0,0 +1,98 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#endif
+#include <microhttpd.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define PORT 8888
+
+
+static enum MHD_Result
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **req_cls)
+{
+ struct MHD_BasicAuthInfo *auth_info;
+ enum MHD_Result ret;
+ struct MHD_Response *response;
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) url; /* Unused. Silent compiler warning. */
+ (void) version; /* Unused. Silent compiler warning. */
+ (void) upload_data; /* Unused. Silent compiler warning. */
+ (void) upload_data_size; /* Unused. Silent compiler warning. */
+
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO;
+ if (NULL == *req_cls)
+ {
+ *req_cls = connection;
+ return MHD_YES;
+ }
+ auth_info = MHD_basic_auth_get_username_password3 (connection);
+ if (NULL == auth_info)
+ {
+ static const char *page =
+ "<html><body>Authorization required</body></html>";
+ response = MHD_create_response_from_buffer_static (strlen (page), page);
+ ret = MHD_queue_basic_auth_required_response3 (connection,
+ "admins",
+ MHD_YES,
+ response);
+ }
+ else if ((strlen ("root") != auth_info->username_len) ||
+ (0 != memcmp (auth_info->username, "root",
+ auth_info->username_len)) ||
+ /* The next check against NULL is optional,
+ * if 'password' is NULL then 'password_len' is always zero. */
+ (NULL == auth_info->password) ||
+ (strlen ("pa$$w0rd") != auth_info->password_len) ||
+ (0 != memcmp (auth_info->password, "pa$$w0rd",
+ auth_info->password_len)))
+ {
+ static const char *page =
+ "<html><body>Wrong username or password</body></html>";
+ response = MHD_create_response_from_buffer_static (strlen (page), page);
+ ret = MHD_queue_basic_auth_required_response3 (connection,
+ "admins",
+ MHD_YES,
+ response);
+ }
+ else
+ {
+ static const char *page = "<html><body>A secret.</body></html>";
+ response = MHD_create_response_from_buffer_static (strlen (page), page);
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ }
+ if (NULL != auth_info)
+ MHD_free (auth_info);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+int
+main (void)
+{
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL,
+ &answer_to_connection, NULL, MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+ return 0;
+}
diff --git a/doc/examples/hellobrowser.c b/doc/examples/hellobrowser.c
@@ -0,0 +1,57 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#endif
+#include <string.h>
+#include <microhttpd.h>
+#include <stdio.h>
+
+#define PORT 8888
+
+static enum MHD_Result
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **req_cls)
+{
+ const char *page = "<html><body>Hello, browser!</body></html>";
+ struct MHD_Response *response;
+ enum MHD_Result ret;
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) url; /* Unused. Silent compiler warning. */
+ (void) method; /* Unused. Silent compiler warning. */
+ (void) version; /* Unused. Silent compiler warning. */
+ (void) upload_data; /* Unused. Silent compiler warning. */
+ (void) upload_data_size; /* Unused. Silent compiler warning. */
+ (void) req_cls; /* Unused. Silent compiler warning. */
+
+ response = MHD_create_response_from_buffer_static (strlen (page), page);
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+
+ return ret;
+}
+
+
+int
+main (void)
+{
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
+ PORT, NULL, NULL,
+ &answer_to_connection, NULL, MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+ return 0;
+}
diff --git a/doc/examples/largepost.c b/doc/examples/largepost.c
@@ -0,0 +1,349 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <microhttpd.h>
+
+#if defined(_MSC_VER) && _MSC_VER + 0 <= 1800
+/* Substitution is OK while return value is not used */
+#define snprintf _snprintf
+#endif
+
+#define PORT 8888
+#define POSTBUFFERSIZE 512
+#define MAXCLIENTS 2
+
+enum ConnectionType
+{
+ GET = 0,
+ POST = 1
+};
+
+static unsigned int nr_of_uploading_clients = 0;
+
+
+/**
+ * Information we keep per connection.
+ */
+struct connection_info_struct
+{
+ enum ConnectionType connectiontype;
+
+ /**
+ * Handle to the POST processing state.
+ */
+ struct MHD_PostProcessor *postprocessor;
+
+ /**
+ * File handle where we write uploaded data.
+ */
+ FILE *fp;
+
+ /**
+ * HTTP response body we will return, NULL if not yet known.
+ */
+ const char *answerstring;
+
+ /**
+ * HTTP status code we will return, 0 for undecided.
+ */
+ unsigned int answercode;
+};
+
+
+#define ASKPAGE \
+ "<html><body>\n" \
+ "Upload a file, please!<br>\n" \
+ "There are %u clients uploading at the moment.<br>\n" \
+ "<form action=\"/filepost\" method=\"post\" enctype=\"multipart/form-data\">\n" \
+ "<input name=\"file\" type=\"file\">\n" \
+ "<input type=\"submit\" value=\" Send \"></form>\n" \
+ "</body></html>"
+static const char *busypage =
+ "<html><body>This server is busy, please try again later.</body></html>";
+static const char *completepage =
+ "<html><body>The upload has been completed.</body></html>";
+static const char *errorpage =
+ "<html><body>This doesn't seem to be right.</body></html>";
+static const char *servererrorpage =
+ "<html><body>Invalid request.</body></html>";
+static const char *fileexistspage =
+ "<html><body>This file already exists.</body></html>";
+static const char *fileioerror =
+ "<html><body>IO error writing to disk.</body></html>";
+static const char *const postprocerror =
+ "<html><head><title>Error</title></head><body>Error processing POST data</body></html>";
+
+
+static enum MHD_Result
+send_page (struct MHD_Connection *connection,
+ const char *page,
+ unsigned int status_code)
+{
+ enum MHD_Result ret;
+ struct MHD_Response *response;
+
+ response = MHD_create_response_from_buffer_static (strlen (page), page);
+ if (! response)
+ return MHD_NO;
+ if (MHD_YES !=
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ "text/html"))
+ {
+ fprintf (stderr,
+ "Failed to set content type header!\n");
+ }
+ ret = MHD_queue_response (connection,
+ status_code,
+ response);
+ MHD_destroy_response (response);
+
+ return ret;
+}
+
+
+static enum MHD_Result
+iterate_post (void *coninfo_cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *filename,
+ const char *content_type,
+ const char *transfer_encoding,
+ const char *data,
+ uint64_t off,
+ size_t size)
+{
+ struct connection_info_struct *con_info = coninfo_cls;
+ FILE *fp;
+ (void) kind; /* Unused. Silent compiler warning. */
+ (void) content_type; /* Unused. Silent compiler warning. */
+ (void) transfer_encoding; /* Unused. Silent compiler warning. */
+ (void) off; /* Unused. Silent compiler warning. */
+
+ if (0 != strcmp (key, "file"))
+ {
+ con_info->answerstring = servererrorpage;
+ con_info->answercode = MHD_HTTP_BAD_REQUEST;
+ return MHD_YES;
+ }
+
+ if (! con_info->fp)
+ {
+ if (0 != con_info->answercode) /* something went wrong */
+ return MHD_YES;
+ if (NULL != (fp = fopen (filename, "rb")))
+ {
+ fclose (fp);
+ con_info->answerstring = fileexistspage;
+ con_info->answercode = MHD_HTTP_FORBIDDEN;
+ return MHD_YES;
+ }
+ /* NOTE: This is technically a race with the 'fopen()' above,
+ but there is no easy fix, short of moving to open(O_EXCL)
+ instead of using fopen(). For the example, we do not care. */
+ con_info->fp = fopen (filename, "ab");
+ if (! con_info->fp)
+ {
+ con_info->answerstring = fileioerror;
+ con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ return MHD_YES;
+ }
+ }
+
+ if (size > 0)
+ {
+ if (! fwrite (data, sizeof (char), size, con_info->fp))
+ {
+ con_info->answerstring = fileioerror;
+ con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ return MHD_YES;
+ }
+ }
+
+ return MHD_YES;
+}
+
+
+static void
+request_completed (void *cls,
+ struct MHD_Connection *connection,
+ void **req_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ struct connection_info_struct *con_info = *req_cls;
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) connection; /* Unused. Silent compiler warning. */
+ (void) toe; /* Unused. Silent compiler warning. */
+
+ if (NULL == con_info)
+ return;
+
+ if (con_info->connectiontype == POST)
+ {
+ if (NULL != con_info->postprocessor)
+ {
+ MHD_destroy_post_processor (con_info->postprocessor);
+ nr_of_uploading_clients--;
+ }
+
+ if (con_info->fp)
+ fclose (con_info->fp);
+ }
+
+ free (con_info);
+ *req_cls = NULL;
+}
+
+
+static enum MHD_Result
+answer_to_connection (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **req_cls)
+{
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) url; /* Unused. Silent compiler warning. */
+ (void) version; /* Unused. Silent compiler warning. */
+
+ if (NULL == *req_cls)
+ {
+ /* First call, setup data structures */
+ struct connection_info_struct *con_info;
+
+ if (nr_of_uploading_clients >= MAXCLIENTS)
+ return send_page (connection,
+ busypage,
+ MHD_HTTP_SERVICE_UNAVAILABLE);
+
+ con_info = malloc (sizeof (struct connection_info_struct));
+ if (NULL == con_info)
+ return MHD_NO;
+ con_info->answercode = 0; /* none yet */
+ con_info->fp = NULL;
+
+ if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+ {
+ con_info->postprocessor =
+ MHD_create_post_processor (connection,
+ POSTBUFFERSIZE,
+ &iterate_post,
+ (void *) con_info);
+
+ if (NULL == con_info->postprocessor)
+ {
+ free (con_info);
+ return MHD_NO;
+ }
+
+ nr_of_uploading_clients++;
+
+ con_info->connectiontype = POST;
+ }
+ else
+ {
+ con_info->connectiontype = GET;
+ }
+
+ *req_cls = (void *) con_info;
+
+ return MHD_YES;
+ }
+
+ if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
+ {
+ /* We just return the standard form for uploads on all GET requests */
+ char buffer[1024];
+
+ snprintf (buffer,
+ sizeof (buffer),
+ ASKPAGE,
+ nr_of_uploading_clients);
+ return send_page (connection,
+ buffer,
+ MHD_HTTP_OK);
+ }
+
+ if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+ {
+ struct connection_info_struct *con_info = *req_cls;
+
+ if (0 != *upload_data_size)
+ {
+ /* Upload not yet done */
+ if (0 != con_info->answercode)
+ {
+ /* we already know the answer, skip rest of upload */
+ *upload_data_size = 0;
+ return MHD_YES;
+ }
+ if (MHD_YES !=
+ MHD_post_process (con_info->postprocessor,
+ upload_data,
+ *upload_data_size))
+ {
+ con_info->answerstring = postprocerror;
+ con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ *upload_data_size = 0;
+
+ return MHD_YES;
+ }
+ /* Upload finished */
+ if (NULL != con_info->fp)
+ {
+ fclose (con_info->fp);
+ con_info->fp = NULL;
+ }
+ if (0 == con_info->answercode)
+ {
+ /* No errors encountered, declare success */
+ con_info->answerstring = completepage;
+ con_info->answercode = MHD_HTTP_OK;
+ }
+ return send_page (connection,
+ con_info->answerstring,
+ con_info->answercode);
+ }
+
+ /* Note a GET or a POST, generate error */
+ return send_page (connection,
+ errorpage,
+ MHD_HTTP_BAD_REQUEST);
+}
+
+
+int
+main (void)
+{
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD,
+ PORT, NULL, NULL,
+ &answer_to_connection, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED, &request_completed,
+ NULL,
+ MHD_OPTION_END);
+ if (NULL == daemon)
+ {
+ fprintf (stderr,
+ "Failed to start daemon.\n");
+ return 1;
+ }
+ (void) getchar ();
+ MHD_stop_daemon (daemon);
+ return 0;
+}
diff --git a/doc/examples/logging.c b/doc/examples/logging.c
@@ -0,0 +1,62 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#endif
+#include <microhttpd.h>
+#include <stdio.h>
+
+#define PORT 8888
+
+
+static enum MHD_Result
+print_out_key (void *cls, enum MHD_ValueKind kind, const char *key,
+ const char *value)
+{
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) kind; /* Unused. Silent compiler warning. */
+ printf ("%s: %s\n", key, value);
+ return MHD_YES;
+}
+
+
+static enum MHD_Result
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **req_cls)
+{
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) version; /* Unused. Silent compiler warning. */
+ (void) upload_data; /* Unused. Silent compiler warning. */
+ (void) upload_data_size; /* Unused. Silent compiler warning. */
+ (void) req_cls; /* Unused. Silent compiler warning. */
+ printf ("New %s request for %s using version %s\n", method, url, version);
+
+ MHD_get_connection_values (connection, MHD_HEADER_KIND, print_out_key,
+ NULL);
+
+ return MHD_NO;
+}
+
+
+int
+main (void)
+{
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL,
+ &answer_to_connection, NULL, MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+ return 0;
+}
diff --git a/doc/examples/responseheaders.c b/doc/examples/responseheaders.c
@@ -0,0 +1,102 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#endif
+#include <microhttpd.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+
+#define PORT 8888
+#define FILENAME "picture.png"
+#define MIMETYPE "image/png"
+
+static enum MHD_Result
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **req_cls)
+{
+ struct MHD_Response *response;
+ int fd;
+ enum MHD_Result ret;
+ struct stat sbuf;
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) url; /* Unused. Silent compiler warning. */
+ (void) version; /* Unused. Silent compiler warning. */
+ (void) upload_data; /* Unused. Silent compiler warning. */
+ (void) upload_data_size; /* Unused. Silent compiler warning. */
+ (void) req_cls; /* Unused. Silent compiler warning. */
+
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO;
+
+ if ( (-1 == (fd = open (FILENAME, O_RDONLY))) ||
+ (0 != fstat (fd, &sbuf)) )
+ {
+ const char *errorstr =
+ "<html><body>An internal server error has occurred!\
+ </body></html>";
+ /* error accessing file */
+ if (fd != -1)
+ (void) close (fd);
+ response =
+ MHD_create_response_from_buffer_static (strlen (errorstr), errorstr);
+ if (NULL != response)
+ {
+ ret =
+ MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
+ response);
+ MHD_destroy_response (response);
+
+ return ret;
+ }
+ else
+ return MHD_NO;
+ }
+ response =
+ MHD_create_response_from_fd_at_offset64 ((size_t) sbuf.st_size,
+ fd,
+ 0);
+ if (MHD_YES !=
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ MIMETYPE))
+ {
+ fprintf (stderr,
+ "Failed to set content type header!\n");
+ /* return response without content encoding anyway ... */
+ }
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+
+ return ret;
+}
+
+
+int
+main (void)
+{
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL,
+ &answer_to_connection, NULL, MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+
+ return 0;
+}
diff --git a/doc/examples/sessions.c b/doc/examples/sessions.c
@@ -0,0 +1,831 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <time.h>
+#include <microhttpd.h>
+
+/**
+ * Invalid method page.
+ */
+#define METHOD_ERROR \
+ "<html><head><title>Illegal request</title></head><body>Go away.</body></html>"
+
+/**
+ * Invalid URL page.
+ */
+#define NOT_FOUND_ERROR \
+ "<html><head><title>Not found</title></head><body>Go away.</body></html>"
+
+/**
+ * Front page. (/)
+ */
+#define MAIN_PAGE \
+ "<html><head><title>Welcome</title></head><body><form action=\"/2\" method=\"post\">What is your name? <input type=\"text\" name=\"v1\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></body></html>"
+
+#define FORM_V1 MAIN_PAGE
+
+/**
+ * Second page. (/2)
+ */
+#define SECOND_PAGE \
+ "<html><head><title>Tell me more</title></head><body><a href=\"/\">previous</a> <form action=\"/S\" method=\"post\">%s, what is your job? <input type=\"text\" name=\"v2\" value=\"%s\" /><input type=\"submit\" value=\"Next\" /></body></html>"
+
+#define FORM_V1_V2 SECOND_PAGE
+
+/**
+ * Second page (/S)
+ */
+#define SUBMIT_PAGE \
+ "<html><head><title>Ready to submit?</title></head><body><form action=\"/F\" method=\"post\"><a href=\"/2\">previous </a> <input type=\"hidden\" name=\"DONE\" value=\"yes\" /><input type=\"submit\" value=\"Submit\" /></body></html>"
+
+/**
+ * Last page.
+ */
+#define LAST_PAGE \
+ "<html><head><title>Thank you</title></head><body>Thank you.</body></html>"
+
+/**
+ * Name of our cookie.
+ */
+#define COOKIE_NAME "session"
+
+
+/**
+ * State we keep for each user/session/browser.
+ */
+struct Session
+{
+ /**
+ * We keep all sessions in a linked list.
+ */
+ struct Session *next;
+
+ /**
+ * Unique ID for this session.
+ */
+ char sid[33];
+
+ /**
+ * Reference counter giving the number of connections
+ * currently using this session.
+ */
+ unsigned int rc;
+
+ /**
+ * Time when this session was last active.
+ */
+ time_t start;
+
+ /**
+ * String submitted via form.
+ */
+ char value_1[64];
+
+ /**
+ * Another value submitted via form.
+ */
+ char value_2[64];
+
+};
+
+
+/**
+ * Data kept per request.
+ */
+struct Request
+{
+
+ /**
+ * Associated session.
+ */
+ struct Session *session;
+
+ /**
+ * Post processor handling form data (IF this is
+ * a POST request).
+ */
+ struct MHD_PostProcessor *pp;
+
+ /**
+ * URL to serve in response to this POST (if this request
+ * was a 'POST')
+ */
+ const char *post_url;
+
+};
+
+
+/**
+ * Linked list of all active sessions. Yes, O(n) but a
+ * hash table would be overkill for a simple example...
+ */
+static struct Session *sessions;
+
+
+/**
+ * Return the session handle for this connection, or
+ * create one if this is a new user.
+ */
+static struct Session *
+get_session (struct MHD_Connection *connection)
+{
+ struct Session *ret;
+ const char *cookie;
+
+ cookie = MHD_lookup_connection_value (connection,
+ MHD_COOKIE_KIND,
+ COOKIE_NAME);
+ if (cookie != NULL)
+ {
+ /* find existing session */
+ ret = sessions;
+ while (NULL != ret)
+ {
+ if (0 == strcmp (cookie, ret->sid))
+ break;
+ ret = ret->next;
+ }
+ if (NULL != ret)
+ {
+ ret->rc++;
+ return ret;
+ }
+ }
+ /* create fresh session */
+ ret = calloc (1, sizeof (struct Session));
+ if (NULL == ret)
+ {
+ fprintf (stderr, "calloc error: %s\n", strerror (errno));
+ return NULL;
+ }
+ /* not a super-secure way to generate a random session ID,
+ but should do for a simple example... */
+ snprintf (ret->sid,
+ sizeof (ret->sid),
+ "%X%X%X%X",
+ (unsigned int) rand (),
+ (unsigned int) rand (),
+ (unsigned int) rand (),
+ (unsigned int) rand ());
+ ret->rc++;
+ ret->start = time (NULL);
+ ret->next = sessions;
+ sessions = ret;
+ return ret;
+}
+
+
+/**
+ * Type of handler that generates a reply.
+ *
+ * @param cls content for the page (handler-specific)
+ * @param mime mime type to use
+ * @param session session information
+ * @param connection connection to process
+ * @param #MHD_YES on success, #MHD_NO on failure
+ */
+typedef enum MHD_Result (*PageHandler)(const void *cls,
+ const char *mime,
+ struct Session *session,
+ struct MHD_Connection *connection);
+
+
+/**
+ * Entry we generate for each page served.
+ */
+struct Page
+{
+ /**
+ * Acceptable URL for this page.
+ */
+ const char *url;
+
+ /**
+ * Mime type to set for the page.
+ */
+ const char *mime;
+
+ /**
+ * Handler to call to generate response.
+ */
+ PageHandler handler;
+
+ /**
+ * Extra argument to handler.
+ */
+ const void *handler_cls;
+};
+
+
+/**
+ * Add header to response to set a session cookie.
+ *
+ * @param session session to use
+ * @param response response to modify
+ */
+static void
+add_session_cookie (struct Session *session,
+ struct MHD_Response *response)
+{
+ char cstr[256];
+ snprintf (cstr,
+ sizeof (cstr),
+ "%s=%s",
+ COOKIE_NAME,
+ session->sid);
+ if (MHD_NO ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_SET_COOKIE,
+ cstr))
+ {
+ fprintf (stderr,
+ "Failed to set session cookie header!\n");
+ }
+}
+
+
+/**
+ * Handler that returns a simple static HTTP page that
+ * is passed in via 'cls'.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+static enum MHD_Result
+serve_simple_form (const void *cls,
+ const char *mime,
+ struct Session *session,
+ struct MHD_Connection *connection)
+{
+ enum MHD_Result ret;
+ const char *form = cls;
+ struct MHD_Response *response;
+
+ /* return static form */
+ response = MHD_create_response_from_buffer_static (strlen (form), form);
+ add_session_cookie (session, response);
+ if (MHD_YES !=
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ mime))
+ {
+ fprintf (stderr,
+ "Failed to set content type header!\n");
+ /* return response without content type anyway ... */
+ }
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Handler that adds the 'v1' value to the given HTML code.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+static enum MHD_Result
+fill_v1_form (const void *cls,
+ const char *mime,
+ struct Session *session,
+ struct MHD_Connection *connection)
+{
+ enum MHD_Result ret;
+ char *reply;
+ struct MHD_Response *response;
+ int reply_len;
+ (void) cls; /* Unused */
+
+ /* Emulate 'asprintf' */
+ reply_len = snprintf (NULL, 0, FORM_V1, session->value_1);
+ if (0 > reply_len)
+ return MHD_NO; /* Internal error */
+
+ reply = (char *) malloc ((size_t) ((size_t) reply_len + 1));
+ if (NULL == reply)
+ return MHD_NO; /* Out-of-memory error */
+
+ if (reply_len != snprintf (reply,
+ (size_t) (((size_t) reply_len) + 1),
+ FORM_V1,
+ session->value_1))
+ {
+ free (reply);
+ return MHD_NO; /* printf error */
+ }
+
+ /* return static form */
+ response =
+ MHD_create_response_from_buffer_with_free_callback ((size_t) reply_len,
+ (void *) reply,
+ &free);
+ if (NULL != response)
+ {
+ add_session_cookie (session, response);
+ if (MHD_YES !=
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ mime))
+ {
+ fprintf (stderr,
+ "Failed to set content type header!\n");
+ /* return response without content type anyway ... */
+ }
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ }
+ else
+ {
+ free (reply);
+ ret = MHD_NO;
+ }
+ return ret;
+}
+
+
+/**
+ * Handler that adds the 'v1' and 'v2' values to the given HTML code.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+static enum MHD_Result
+fill_v1_v2_form (const void *cls,
+ const char *mime,
+ struct Session *session,
+ struct MHD_Connection *connection)
+{
+ enum MHD_Result ret;
+ char *reply;
+ struct MHD_Response *response;
+ int reply_len;
+ (void) cls; /* Unused */
+
+ /* Emulate 'asprintf' */
+ reply_len = snprintf (NULL, 0, FORM_V1_V2, session->value_1,
+ session->value_2);
+ if (0 > reply_len)
+ return MHD_NO; /* Internal error */
+
+ reply = (char *) malloc ((size_t) ((size_t) reply_len + 1));
+ if (NULL == reply)
+ return MHD_NO; /* Out-of-memory error */
+
+ if (reply_len != snprintf (reply,
+ (size_t) ((size_t) reply_len + 1),
+ FORM_V1_V2,
+ session->value_1,
+ session->value_2))
+ {
+ free (reply);
+ return MHD_NO; /* printf error */
+ }
+
+ /* return static form */
+ response =
+ MHD_create_response_from_buffer_with_free_callback ((size_t) reply_len,
+ (void *) reply,
+ &free);
+ if (NULL != response)
+ {
+ add_session_cookie (session, response);
+ if (MHD_YES !=
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ mime))
+ {
+ fprintf (stderr,
+ "Failed to set content type header!\n");
+ /* return response without content type anyway ... */
+ }
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ }
+ else
+ {
+ free (reply);
+ ret = MHD_NO;
+ }
+ return ret;
+}
+
+
+/**
+ * Handler used to generate a 404 reply.
+ *
+ * @param cls a 'const char *' with the HTML webpage to return
+ * @param mime mime type to use
+ * @param session session handle
+ * @param connection connection to use
+ */
+static enum MHD_Result
+not_found_page (const void *cls,
+ const char *mime,
+ struct Session *session,
+ struct MHD_Connection *connection)
+{
+ enum MHD_Result ret;
+ struct MHD_Response *response;
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) session; /* Unused. Silent compiler warning. */
+
+ /* unsupported HTTP method */
+ response = MHD_create_response_from_buffer_static (strlen (NOT_FOUND_ERROR),
+ NOT_FOUND_ERROR);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_FOUND,
+ response);
+ if (MHD_YES !=
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ mime))
+ {
+ fprintf (stderr,
+ "Failed to set content type header!\n");
+ /* return response without content type anyway ... */
+ }
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * List of all pages served by this HTTP server.
+ */
+static const struct Page pages[] = {
+ { "/", "text/html", &fill_v1_form, NULL },
+ { "/2", "text/html", &fill_v1_v2_form, NULL },
+ { "/S", "text/html", &serve_simple_form, SUBMIT_PAGE },
+ { "/F", "text/html", &serve_simple_form, LAST_PAGE },
+ { NULL, NULL, ¬_found_page, NULL } /* 404 */
+};
+
+
+/**
+ * Iterator over key-value pairs where the value
+ * maybe made available in increments and/or may
+ * not be zero-terminated. Used for processing
+ * POST data.
+ *
+ * @param cls user-specified closure
+ * @param kind type of the value
+ * @param key 0-terminated key for the value
+ * @param filename name of the uploaded file, NULL if not known
+ * @param content_type mime-type of the data, NULL if not known
+ * @param transfer_encoding encoding of the data, NULL if not known
+ * @param data pointer to size bytes of data at the
+ * specified offset
+ * @param off offset of data in the overall value
+ * @param size number of bytes in data available
+ * @return #MHD_YES to continue iterating,
+ * #MHD_NO to abort the iteration
+ */
+static enum MHD_Result
+post_iterator (void *cls,
+ enum MHD_ValueKind kind,
+ const char *key,
+ const char *filename,
+ const char *content_type,
+ const char *transfer_encoding,
+ const char *data, uint64_t off, size_t size)
+{
+ struct Request *request = cls;
+ struct Session *session = request->session;
+ (void) kind; /* Unused. Silent compiler warning. */
+ (void) filename; /* Unused. Silent compiler warning. */
+ (void) content_type; /* Unused. Silent compiler warning. */
+ (void) transfer_encoding; /* Unused. Silent compiler warning. */
+
+ if (0 == strcmp ("DONE", key))
+ {
+ fprintf (stdout,
+ "Session `%s' submitted `%s', `%s'\n",
+ session->sid,
+ session->value_1,
+ session->value_2);
+ return MHD_YES;
+ }
+ if (0 == strcmp ("v1", key))
+ {
+ if (off >= sizeof(session->value_1) - 1)
+ return MHD_YES; /* Discard extra data */
+ if (size + off >= sizeof(session->value_1))
+ size = (size_t) (sizeof (session->value_1) - off - 1); /* crop extra data */
+ memcpy (&session->value_1[off],
+ data,
+ size);
+ if (size + off < sizeof (session->value_1))
+ session->value_1[size + off] = '\0';
+ return MHD_YES;
+ }
+ if (0 == strcmp ("v2", key))
+ {
+ if (off >= sizeof(session->value_2) - 1)
+ return MHD_YES; /* Discard extra data */
+ if (size + off >= sizeof(session->value_2))
+ size = (size_t) (sizeof (session->value_2) - off - 1); /* crop extra data */
+ memcpy (&session->value_2[off],
+ data,
+ size);
+ if (size + off < sizeof (session->value_2))
+ session->value_2[size + off] = '\0';
+ return MHD_YES;
+ }
+ fprintf (stderr, "Unsupported form value `%s'\n", key);
+ return MHD_YES;
+}
+
+
+/**
+ * Main MHD callback for handling requests.
+ *
+ *
+ * @param cls argument given together with the function
+ * pointer when the handler was registered with MHD
+ * @param connection handle to connection which is being processed
+ * @param url the requested url
+ * @param method the HTTP method used ("GET", "PUT", etc.)
+ * @param version the HTTP version string (i.e. "HTTP/1.1")
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ * for a POST that fits into memory and that is encoded
+ * with a supported encoding, the POST data will NOT be
+ * given in upload_data and is instead available as
+ * part of MHD_get_connection_values; very large POST
+ * data *will* be made available incrementally in
+ * upload_data)
+ * @param upload_data_size set initially to the size of the
+ * upload_data provided; the method must update this
+ * value to the number of bytes NOT processed;
+ * @param req_cls pointer that the callback can set to some
+ * address and that will be preserved by MHD for future
+ * calls for this request; since the access handler may
+ * be called many times (i.e., for a PUT/POST operation
+ * with plenty of upload data) this allows the application
+ * to easily associate some request-specific state.
+ * If necessary, this state can be cleaned up in the
+ * global "MHD_RequestCompleted" callback (which
+ * can be set with the MHD_OPTION_NOTIFY_COMPLETED).
+ * Initially, <tt>*req_cls</tt> will be NULL.
+ * @return MHS_YES if the connection was handled successfully,
+ * MHS_NO if the socket must be closed due to a serious
+ * error while handling the request
+ */
+static enum MHD_Result
+create_response (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **req_cls)
+{
+ struct MHD_Response *response;
+ struct Request *request;
+ struct Session *session;
+ enum MHD_Result ret;
+ unsigned int i;
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) version; /* Unused. Silent compiler warning. */
+
+ request = *req_cls;
+ if (NULL == request)
+ {
+ request = calloc (1, sizeof (struct Request));
+ if (NULL == request)
+ {
+ fprintf (stderr, "calloc error: %s\n", strerror (errno));
+ return MHD_NO;
+ }
+ *req_cls = request;
+ if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+ {
+ request->pp = MHD_create_post_processor (connection, 1024,
+ &post_iterator, request);
+ if (NULL == request->pp)
+ {
+ fprintf (stderr, "Failed to setup post processor for `%s'\n",
+ url);
+ return MHD_NO; /* internal error */
+ }
+ }
+ return MHD_YES;
+ }
+ if (NULL == request->session)
+ {
+ request->session = get_session (connection);
+ if (NULL == request->session)
+ {
+ fprintf (stderr, "Failed to setup session for `%s'\n",
+ url);
+ return MHD_NO; /* internal error */
+ }
+ }
+ session = request->session;
+ session->start = time (NULL);
+ if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+ {
+ /* evaluate POST data */
+ if (MHD_YES !=
+ MHD_post_process (request->pp,
+ upload_data,
+ *upload_data_size))
+ return MHD_NO; /* internal error */
+ if (0 != *upload_data_size)
+ {
+ *upload_data_size = 0;
+ return MHD_YES;
+ }
+ /* done with POST data, serve response */
+ MHD_destroy_post_processor (request->pp);
+ request->pp = NULL;
+ method = MHD_HTTP_METHOD_GET; /* fake 'GET' */
+ if (NULL != request->post_url)
+ url = request->post_url;
+ }
+
+ if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
+ (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
+ {
+ /* find out which page to serve */
+ i = 0;
+ while ( (pages[i].url != NULL) &&
+ (0 != strcmp (pages[i].url, url)) )
+ i++;
+ ret = pages[i].handler (pages[i].handler_cls,
+ pages[i].mime,
+ session, connection);
+ if (ret != MHD_YES)
+ fprintf (stderr, "Failed to create page for `%s'\n",
+ url);
+ return ret;
+ }
+ /* unsupported HTTP method */
+ response = MHD_create_response_from_buffer_static (strlen (METHOD_ERROR),
+ METHOD_ERROR);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_ACCEPTABLE,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Callback called upon completion of a request.
+ * Decrements session reference counter.
+ *
+ * @param cls not used
+ * @param connection connection that completed
+ * @param req_cls session handle
+ * @param toe status code
+ */
+static void
+request_completed_callback (void *cls,
+ struct MHD_Connection *connection,
+ void **req_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ struct Request *request = *req_cls;
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) connection; /* Unused. Silent compiler warning. */
+ (void) toe; /* Unused. Silent compiler warning. */
+
+ if (NULL == request)
+ return;
+ if (NULL != request->session)
+ request->session->rc--;
+ if (NULL != request->pp)
+ MHD_destroy_post_processor (request->pp);
+ free (request);
+}
+
+
+/**
+ * Clean up handles of sessions that have been idle for
+ * too long.
+ */
+static void
+expire_sessions (void)
+{
+ struct Session *pos;
+ struct Session *prev;
+ struct Session *next;
+ time_t now;
+
+ now = time (NULL);
+ prev = NULL;
+ pos = sessions;
+ while (NULL != pos)
+ {
+ next = pos->next;
+ if (now - pos->start > 60 * 60)
+ {
+ /* expire sessions after 1h */
+ if (NULL == prev)
+ sessions = pos->next;
+ else
+ prev->next = next;
+ free (pos);
+ }
+ else
+ prev = pos;
+ pos = next;
+ }
+}
+
+
+/**
+ * Call with the port number as the only argument.
+ * Never terminates (other than by signals, such as CTRL-C).
+ */
+int
+main (int argc, char *const *argv)
+{
+ struct MHD_Daemon *d;
+ struct timeval tv;
+ struct timeval *tvp;
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ MHD_socket max;
+ uint64_t mhd_timeout;
+ unsigned int port;
+
+ if (argc != 2)
+ {
+ printf ("%s PORT\n", argv[0]);
+ return 1;
+ }
+ if ( (1 != sscanf (argv[1], "%u", &port)) ||
+ (0 == port) || (65535 < port) )
+ {
+ fprintf (stderr,
+ "Port must be a number between 1 and 65535.\n");
+ return 1;
+ }
+
+ /* initialize PRNG */
+ srand ((unsigned int) time (NULL));
+ d = MHD_start_daemon (MHD_USE_ERROR_LOG,
+ (uint16_t) port,
+ NULL, NULL,
+ &create_response, NULL,
+ MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 15,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &request_completed_callback, NULL,
+ MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
+ MHD_OPTION_END);
+ if (NULL == d)
+ return 1;
+ while (1)
+ {
+ expire_sessions ();
+ max = 0;
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
+ break; /* fatal internal error */
+ if (MHD_get_timeout64 (d, &mhd_timeout) == MHD_YES)
+ {
+#if ! defined(_WIN32) || defined(__CYGWIN__)
+ tv.tv_sec = (time_t) (mhd_timeout / 1000);
+#else /* Native W32 */
+ tv.tv_sec = (long) (mhd_timeout / 1000);
+#endif /* Native W32 */
+ tv.tv_usec = ((long) (mhd_timeout % 1000)) * 1000;
+ tvp = &tv;
+ }
+ else
+ tvp = NULL;
+ if (-1 == select ((int) max + 1, &rs, &ws, &es, tvp))
+ {
+ if (EINTR != errno)
+ fprintf (stderr,
+ "Aborting due to error during select: %s\n",
+ strerror (errno));
+ break;
+ }
+ MHD_run (d);
+ }
+ MHD_stop_daemon (d);
+ return 0;
+}
diff --git a/doc/examples/simplepost.c b/doc/examples/simplepost.c
@@ -0,0 +1,215 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#endif
+#include <microhttpd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if defined(_MSC_VER) && _MSC_VER + 0 <= 1800
+/* Substitution is OK while return value is not used */
+#define snprintf _snprintf
+#endif
+
+#define PORT 8888
+#define POSTBUFFERSIZE 512
+#define MAXNAMESIZE 20
+#define MAXANSWERSIZE 512
+
+#define GET 0
+#define POST 1
+
+struct connection_info_struct
+{
+ int connectiontype;
+ char *answerstring;
+ struct MHD_PostProcessor *postprocessor;
+};
+
+static const char *askpage =
+ "<html><body>\n"
+ "What's your name, Sir?<br>\n"
+ "<form action=\"/namepost\" method=\"post\">\n"
+ "<input name=\"name\" type=\"text\">\n"
+ "<input type=\"submit\" value=\" Send \"></form>\n"
+ "</body></html>";
+
+#define GREETINGPAGE \
+ "<html><body><h1>Welcome, %s!</center></h1></body></html>"
+
+static const char *errorpage =
+ "<html><body>This doesn't seem to be right.</body></html>";
+
+
+static enum MHD_Result
+send_page (struct MHD_Connection *connection, const char *page)
+{
+ enum MHD_Result ret;
+ struct MHD_Response *response;
+
+
+ response = MHD_create_response_from_buffer_static (strlen (page), page);
+ if (! response)
+ return MHD_NO;
+
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+
+ return ret;
+}
+
+
+static enum MHD_Result
+iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
+ const char *filename, const char *content_type,
+ const char *transfer_encoding, const char *data, uint64_t off,
+ size_t size)
+{
+ struct connection_info_struct *con_info = coninfo_cls;
+ (void) kind; /* Unused. Silent compiler warning. */
+ (void) filename; /* Unused. Silent compiler warning. */
+ (void) content_type; /* Unused. Silent compiler warning. */
+ (void) transfer_encoding; /* Unused. Silent compiler warning. */
+ (void) off; /* Unused. Silent compiler warning. */
+
+ if (0 == strcmp (key, "name"))
+ {
+ if ((size > 0) && (size <= MAXNAMESIZE))
+ {
+ char *answerstring;
+ answerstring = malloc (MAXANSWERSIZE);
+ if (! answerstring)
+ return MHD_NO;
+
+ snprintf (answerstring, MAXANSWERSIZE, GREETINGPAGE, data);
+ con_info->answerstring = answerstring;
+ }
+ else
+ con_info->answerstring = NULL;
+
+ return MHD_NO;
+ }
+
+ return MHD_YES;
+}
+
+
+static void
+request_completed (void *cls, struct MHD_Connection *connection,
+ void **req_cls, enum MHD_RequestTerminationCode toe)
+{
+ struct connection_info_struct *con_info = *req_cls;
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) connection; /* Unused. Silent compiler warning. */
+ (void) toe; /* Unused. Silent compiler warning. */
+
+ if (NULL == con_info)
+ return;
+
+ if (con_info->connectiontype == POST)
+ {
+ MHD_destroy_post_processor (con_info->postprocessor);
+ if (con_info->answerstring)
+ free (con_info->answerstring);
+ }
+
+ free (con_info);
+ *req_cls = NULL;
+}
+
+
+static enum MHD_Result
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **req_cls)
+{
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) url; /* Unused. Silent compiler warning. */
+ (void) version; /* Unused. Silent compiler warning. */
+
+ if (NULL == *req_cls)
+ {
+ struct connection_info_struct *con_info;
+
+ con_info = malloc (sizeof (struct connection_info_struct));
+ if (NULL == con_info)
+ return MHD_NO;
+ con_info->answerstring = NULL;
+
+ if (0 == strcmp (method, "POST"))
+ {
+ con_info->postprocessor =
+ MHD_create_post_processor (connection, POSTBUFFERSIZE,
+ iterate_post, (void *) con_info);
+
+ if (NULL == con_info->postprocessor)
+ {
+ free (con_info);
+ return MHD_NO;
+ }
+
+ con_info->connectiontype = POST;
+ }
+ else
+ con_info->connectiontype = GET;
+
+ *req_cls = (void *) con_info;
+
+ return MHD_YES;
+ }
+
+ if (0 == strcmp (method, "GET"))
+ {
+ return send_page (connection, askpage);
+ }
+
+ if (0 == strcmp (method, "POST"))
+ {
+ struct connection_info_struct *con_info = *req_cls;
+
+ if (*upload_data_size != 0)
+ {
+ if (MHD_YES !=
+ MHD_post_process (con_info->postprocessor,
+ upload_data,
+ *upload_data_size))
+ return MHD_NO;
+ *upload_data_size = 0;
+
+ return MHD_YES;
+ }
+ else if (NULL != con_info->answerstring)
+ return send_page (connection, con_info->answerstring);
+ }
+
+ return send_page (connection, errorpage);
+}
+
+
+int
+main (void)
+{
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
+ PORT, NULL, NULL,
+ &answer_to_connection, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED, request_completed,
+ NULL, MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+
+ return 0;
+}
diff --git a/doc/examples/tlsauthentication.c b/doc/examples/tlsauthentication.c
@@ -0,0 +1,214 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#else
+#include <winsock2.h>
+#endif
+#include <microhttpd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define PORT 8888
+
+#define REALM "Maintenance"
+#define USER "a legitimate user"
+#define PASSWORD "and his password"
+
+#define SERVERKEYFILE "server.key"
+#define SERVERCERTFILE "server.pem"
+
+
+static size_t
+get_file_size (const char *filename)
+{
+ FILE *fp;
+
+ fp = fopen (filename, "rb");
+ if (fp)
+ {
+ long size;
+
+ if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp))))
+ size = 0;
+
+ fclose (fp);
+
+ return (size_t) size;
+ }
+ else
+ return 0;
+}
+
+
+static char *
+load_file (const char *filename)
+{
+ FILE *fp;
+ char *buffer;
+ size_t size;
+
+ size = get_file_size (filename);
+ if (0 == size)
+ return NULL;
+
+ fp = fopen (filename, "rb");
+ if (! fp)
+ return NULL;
+
+ buffer = malloc (size + 1);
+ if (! buffer)
+ {
+ fclose (fp);
+ return NULL;
+ }
+ buffer[size] = '\0';
+
+ if (size != fread (buffer, 1, size, fp))
+ {
+ free (buffer);
+ buffer = NULL;
+ }
+
+ fclose (fp);
+ return buffer;
+}
+
+
+static enum MHD_Result
+ask_for_authentication (struct MHD_Connection *connection, const char *realm)
+{
+ enum MHD_Result ret;
+ struct MHD_Response *response;
+
+ response = MHD_create_response_empty (MHD_RF_NONE);
+ if (! response)
+ return MHD_NO;
+
+ ret = MHD_queue_basic_auth_required_response3 (connection,
+ realm,
+ MHD_YES,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+static int
+is_authenticated (struct MHD_Connection *connection,
+ const char *username,
+ const char *password)
+{
+ struct MHD_BasicAuthInfo *auth_info;
+ int authenticated;
+
+ auth_info = MHD_basic_auth_get_username_password3 (connection);
+ if (NULL == auth_info)
+ return 0;
+ authenticated =
+ ( (strlen (username) == auth_info->username_len) &&
+ (0 == memcmp (auth_info->username, username, auth_info->username_len)) &&
+ /* The next check against NULL is optional,
+ * if 'password' is NULL then 'password_len' is always zero. */
+ (NULL != auth_info->password) &&
+ (strlen (password) == auth_info->password_len) &&
+ (0 == memcmp (auth_info->password, password, auth_info->password_len)) );
+
+ MHD_free (auth_info);
+
+ return authenticated;
+}
+
+
+static enum MHD_Result
+secret_page (struct MHD_Connection *connection)
+{
+ enum MHD_Result ret;
+ struct MHD_Response *response;
+ const char *page = "<html><body>A secret.</body></html>";
+
+ response = MHD_create_response_from_buffer_static (strlen (page), page);
+ if (! response)
+ return MHD_NO;
+
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+
+ return ret;
+}
+
+
+static enum MHD_Result
+answer_to_connection (void *cls, struct MHD_Connection *connection,
+ const char *url, const char *method,
+ const char *version, const char *upload_data,
+ size_t *upload_data_size, void **req_cls)
+{
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) url; /* Unused. Silent compiler warning. */
+ (void) version; /* Unused. Silent compiler warning. */
+ (void) upload_data; /* Unused. Silent compiler warning. */
+ (void) upload_data_size; /* Unused. Silent compiler warning. */
+
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO;
+ if (NULL == *req_cls)
+ {
+ *req_cls = connection;
+ return MHD_YES;
+ }
+
+ if (! is_authenticated (connection, USER, PASSWORD))
+ return ask_for_authentication (connection, REALM);
+
+ return secret_page (connection);
+}
+
+
+int
+main (void)
+{
+ struct MHD_Daemon *daemon;
+ char *key_pem;
+ char *cert_pem;
+
+ key_pem = load_file (SERVERKEYFILE);
+ cert_pem = load_file (SERVERCERTFILE);
+
+ if ((key_pem == NULL) || (cert_pem == NULL))
+ {
+ printf ("The key/certificate files could not be read.\n");
+ if (NULL != key_pem)
+ free (key_pem);
+ if (NULL != cert_pem)
+ free (cert_pem);
+ return 1;
+ }
+
+ daemon =
+ MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS, PORT, NULL,
+ NULL, &answer_to_connection, NULL,
+ MHD_OPTION_HTTPS_MEM_KEY, key_pem,
+ MHD_OPTION_HTTPS_MEM_CERT, cert_pem, MHD_OPTION_END);
+ if (NULL == daemon)
+ {
+ printf ("%s\n", cert_pem);
+
+ free (key_pem);
+ free (cert_pem);
+
+ return 1;
+ }
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+ free (key_pem);
+ free (cert_pem);
+
+ return 0;
+}
diff --git a/doc/examples/websocket.c b/doc/examples/websocket.c
@@ -0,0 +1,449 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#else
+#include <winsock2.h>
+#endif
+#include <microhttpd.h>
+#include <microhttpd_ws.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define PORT 80
+
+#define PAGE \
+ "<!DOCTYPE html>\n" \
+ "<html>\n" \
+ "<head>\n" \
+ "<meta charset=\"UTF-8\">\n" \
+ "<title>Websocket Demo</title>\n" \
+ "<script>\n" \
+ "\n" \
+ "let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '')" \
+ " + ':/" "/' +\n" \
+ " window.location.host + '/chat';\n" \
+ "let socket = null;\n" \
+ "\n" \
+ "window.onload = function(event) {\n" \
+ " socket = new WebSocket(url);\n" \
+ " socket.onopen = function(event) {\n" \
+ " document.write('The websocket connection has been " \
+ "established.<br>');\n" \
+ "\n" \
+ " /" "/ Send some text\n" \
+ " socket.send('Hello from JavaScript!');\n" \
+ " }\n" \
+ "\n" \
+ " socket.onclose = function(event) {\n" \
+ " document.write('The websocket connection has been closed.<br>');\n" \
+ " }\n" \
+ "\n" \
+ " socket.onerror = function(event) {\n" \
+ " document.write('An error occurred during the websocket " \
+ "communication.<br>');\n" \
+ " }\n" \
+ "\n" \
+ " socket.onmessage = function(event) {\n" \
+ " document.write('Websocket message received: ' + " \
+ "event.data + '<br>');\n" \
+ " }\n" \
+ "}\n" \
+ "\n" \
+ "</script>\n" \
+ "</head>\n" \
+ "<body>\n" \
+ "</body>\n" \
+ "</html>"
+
+#define PAGE_NOT_FOUND \
+ "404 Not Found"
+
+#define PAGE_INVALID_WEBSOCKET_REQUEST \
+ "Invalid WebSocket request!"
+
+static void
+send_all (MHD_socket fd,
+ const char *buf,
+ size_t len);
+
+static void
+make_blocking (MHD_socket fd);
+
+static void
+upgrade_handler (void *cls,
+ struct MHD_Connection *connection,
+ void *req_cls,
+ const char *extra_in,
+ size_t extra_in_size,
+ MHD_socket fd,
+ struct MHD_UpgradeResponseHandle *urh)
+{
+ /* make the socket blocking (operating-system-dependent code) */
+ make_blocking (fd);
+
+ /* create a websocket stream for this connection */
+ struct MHD_WebSocketStream *ws;
+ int result = MHD_websocket_stream_init (&ws,
+ 0,
+ 0);
+ if (0 != result)
+ {
+ /* Couldn't create the websocket stream.
+ * So we close the socket and leave
+ */
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+ return;
+ }
+
+ /* Let's wait for incoming data */
+ const size_t buf_len = 256;
+ char buf[buf_len];
+ ssize_t got;
+ while (MHD_WEBSOCKET_VALIDITY_VALID == MHD_websocket_stream_is_valid (ws))
+ {
+ got = recv (fd,
+ buf,
+ buf_len,
+ 0);
+ if (0 >= got)
+ {
+ /* the TCP/IP socket has been closed */
+ break;
+ }
+
+ /* parse the entire received data */
+ size_t buf_offset = 0;
+ while (buf_offset < (size_t) got)
+ {
+ size_t new_offset = 0;
+ char *frame_data = NULL;
+ size_t frame_len = 0;
+ int status = MHD_websocket_decode (ws,
+ buf + buf_offset,
+ ((size_t) got) - buf_offset,
+ &new_offset,
+ &frame_data,
+ &frame_len);
+ if (0 > status)
+ {
+ /* an error occurred and the connection must be closed */
+ if (NULL != frame_data)
+ {
+ MHD_websocket_free (ws, frame_data);
+ }
+ break;
+ }
+ else
+ {
+ buf_offset += new_offset;
+ if (0 < status)
+ {
+ /* the frame is complete */
+ switch (status)
+ {
+ case MHD_WEBSOCKET_STATUS_TEXT_FRAME:
+ /* The client has sent some text.
+ * We will display it and answer with a text frame.
+ */
+ if (NULL != frame_data)
+ {
+ printf ("Received message: %s\n", frame_data);
+ MHD_websocket_free (ws, frame_data);
+ frame_data = NULL;
+ }
+ result = MHD_websocket_encode_text (ws,
+ "Hello",
+ 5, /* length of "Hello" */
+ 0,
+ &frame_data,
+ &frame_len,
+ NULL);
+ if (0 == result)
+ {
+ send_all (fd,
+ frame_data,
+ frame_len);
+ }
+ break;
+
+ case MHD_WEBSOCKET_STATUS_CLOSE_FRAME:
+ /* if we receive a close frame, we will respond with one */
+ MHD_websocket_free (ws,
+ frame_data);
+ frame_data = NULL;
+
+ result = MHD_websocket_encode_close (ws,
+ 0,
+ NULL,
+ 0,
+ &frame_data,
+ &frame_len);
+ if (0 == result)
+ {
+ send_all (fd,
+ frame_data,
+ frame_len);
+ }
+ break;
+
+ case MHD_WEBSOCKET_STATUS_PING_FRAME:
+ /* if we receive a ping frame, we will respond */
+ /* with the corresponding pong frame */
+ {
+ char *pong = NULL;
+ size_t pong_len = 0;
+ result = MHD_websocket_encode_pong (ws,
+ frame_data,
+ frame_len,
+ &pong,
+ &pong_len);
+ if (0 == result)
+ {
+ send_all (fd,
+ pong,
+ pong_len);
+ }
+ MHD_websocket_free (ws,
+ pong);
+ }
+ break;
+
+ default:
+ /* Other frame types are ignored
+ * in this minimal example.
+ * This is valid, because they become
+ * automatically skipped if we receive them unexpectedly
+ */
+ break;
+ }
+ }
+ if (NULL != frame_data)
+ {
+ MHD_websocket_free (ws, frame_data);
+ }
+ }
+ }
+ }
+
+ /* free the websocket stream */
+ MHD_websocket_stream_free (ws);
+
+ /* close the socket when it is not needed anymore */
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+}
+
+
+/* This helper function is used for the case that
+ * we need to resend some data
+ */
+static void
+send_all (MHD_socket fd,
+ const char *buf,
+ size_t len)
+{
+ ssize_t ret;
+ size_t off;
+
+ for (off = 0; off < len; off += ret)
+ {
+ ret = send (fd,
+ &buf[off],
+ (int) (len - off),
+ 0);
+ if (0 > ret)
+ {
+ if (EAGAIN == errno)
+ {
+ ret = 0;
+ continue;
+ }
+ break;
+ }
+ if (0 == ret)
+ break;
+ }
+}
+
+
+/* This helper function contains operating-system-dependent code and
+ * is used to make a socket blocking.
+ */
+static void
+make_blocking (MHD_socket fd)
+{
+#ifndef _WIN32
+ int flags;
+
+ flags = fcntl (fd, F_GETFL);
+ if (-1 == flags)
+ abort ();
+ if ((flags & ~O_NONBLOCK) != flags)
+ if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
+ abort ();
+#else /* _WIN32 */
+ unsigned long flags = 0;
+
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+ abort ();
+#endif /* _WIN32 */
+}
+
+
+static enum MHD_Result
+access_handler (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **req_cls)
+{
+ static int aptr;
+ struct MHD_Response *response;
+ int ret;
+
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) upload_data; /* Unused. Silent compiler warning. */
+ (void) upload_data_size; /* Unused. Silent compiler warning. */
+
+ if (0 != strcmp (method, "GET"))
+ return MHD_NO; /* unexpected method */
+ if (&aptr != *req_cls)
+ {
+ /* do never respond on first call */
+ *req_cls = &aptr;
+ return MHD_YES;
+ }
+ *req_cls = NULL; /* reset when done */
+
+ if (0 == strcmp (url, "/"))
+ {
+ /* Default page for visiting the server */
+ struct MHD_Response *response;
+ response = MHD_create_response_from_buffer_static (strlen (PAGE),
+ PAGE);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+ MHD_destroy_response (response);
+ }
+ else if (0 == strcmp (url, "/chat"))
+ {
+ char is_valid = 1;
+ const char *value = NULL;
+ char sec_websocket_accept[29];
+
+ if (0 != MHD_websocket_check_http_version (version))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONNECTION);
+ if (0 != MHD_websocket_check_connection_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_UPGRADE);
+ if (0 != MHD_websocket_check_upgrade_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION);
+ if (0 != MHD_websocket_check_version_header (value))
+ {
+ is_valid = 0;
+ }
+ value = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY);
+ if (0 != MHD_websocket_create_accept_header (value, sec_websocket_accept))
+ {
+ is_valid = 0;
+ }
+
+ if (1 == is_valid)
+ {
+ /* upgrade the connection */
+ response = MHD_create_response_for_upgrade (&upgrade_handler,
+ NULL);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_UPGRADE,
+ "websocket");
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT,
+ sec_websocket_accept);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_SWITCHING_PROTOCOLS,
+ response);
+ MHD_destroy_response (response);
+ }
+ else
+ {
+ /* return error page */
+ struct MHD_Response *response;
+ response =
+ MHD_create_response_from_buffer_static (strlen (
+ PAGE_INVALID_WEBSOCKET_REQUEST),
+ PAGE_INVALID_WEBSOCKET_REQUEST);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_BAD_REQUEST,
+ response);
+ MHD_destroy_response (response);
+ }
+ }
+ else
+ {
+ struct MHD_Response *response;
+ response =
+ MHD_create_response_from_buffer_static (strlen (PAGE_NOT_FOUND),
+ PAGE_NOT_FOUND);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_FOUND,
+ response);
+ MHD_destroy_response (response);
+ }
+
+ return ret;
+}
+
+
+int
+main (int argc,
+ char *const *argv)
+{
+ (void) argc; /* Unused. Silent compiler warning. */
+ (void) argv; /* Unused. Silent compiler warning. */
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
+ | MHD_USE_THREAD_PER_CONNECTION
+ | MHD_ALLOW_UPGRADE
+ | MHD_USE_ERROR_LOG,
+ PORT, NULL, NULL,
+ &access_handler, NULL,
+ MHD_OPTION_END);
+
+ if (NULL == daemon)
+ return 1;
+ (void) getc (stdin);
+
+ MHD_stop_daemon (daemon);
+
+ return 0;
+}