commit 7d8c068e5ea3a574155fa6950f7d19fa67c49e62
parent 56713e94bbc7ca31a197db4103b76cef3a6c882c
Author: Christian Grothoff <christian@grothoff.org>
Date: Tue, 28 Jul 2026 19:46:07 +0200
add tutorials for #4944 and #5085
Diffstat:
24 files changed, 1905 insertions(+), 2829 deletions(-)
diff --git a/doc/Makefile.am b/doc/Makefile.am
@@ -24,6 +24,7 @@ microhttpd_TEXINFOS = \
microhttpd_tutorial_TEXINFOS = \
chapters/basicauthentication.inc \
chapters/bibliography.inc \
+ chapters/callbackresponse.inc \
chapters/exploringrequests.inc \
chapters/hellobrowser.inc \
chapters/introduction.inc \
@@ -32,7 +33,7 @@ microhttpd_tutorial_TEXINFOS = \
chapters/responseheaders.inc \
chapters/tlsauthentication.inc \
chapters/sessions.inc \
- chapters/websocket.inc
+ chapters/upgrade.inc
EXTRA_DIST = \
$(microhttpd_TEXINFOS) $(microhttpd_tutorial_TEXINFOS) \
diff --git a/doc/chapters/basicauthentication.inc b/doc/chapters/basicauthentication.inc
@@ -30,7 +30,7 @@ start there. It will, however, turn out to have still severe weaknesses in
terms of security which need consideration.
Before we will start implementing @emph{Basic Authentication} as described in
-@emph{RFC 2617}, we will also abandon the simplistic and generally
+@emph{RFC 7617}, we will also abandon the simplistic and generally
problematic practice of responding every request the first time our callback
is called for a given connection. Queuing a response upon the first request
is akin to generating an error response (even if it is a "200 OK" reply!).
@@ -64,8 +64,9 @@ application should suspend the connection).
@end enumerate
But how can we tell whether the callback has been called before for the
-particular request? Initially, the pointer this parameter references is
-set by @emph{MHD} in the callback. But it will also be "remembered" on the
+particular request? This is what the @code{req_cls} parameter of the callback
+is for: initially, the pointer it references is set to NULL by @emph{MHD}.
+But whatever we store there will be "remembered" on the
next call (for the same request). Thus, we can use the @code{req_cls}
location to keep track of the request state. For now, we will simply
generate no response until the parameter is non-null---implying the callback
@@ -77,7 +78,7 @@ will be pointing to a legal address, so we take this.
The first time @code{answer_to_connection} is called, we will not even look at the headers.
@verbatim
-static int
+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,
@@ -104,11 +105,13 @@ correct username/password, so every "GET" request will be challenged.
@emph{RFC 7617} describes how the server shall ask for authentication by
adding a @emph{WWW-Authenticate} response header with the name of the
@emph{realm} protected. MHD can generate and queue such a failure response
-for you using the @code{MHD_queue_basic_auth_fail_response} API. The only
+for you using the @code{MHD_queue_basic_auth_required_response3} API. The only
thing you need to do is construct a response with the error page to be shown
to the user if he aborts basic authentication. But first, you should check if
the proper credentials were already supplied using the
-@code{MHD_basic_auth_get_username_password} call.
+@code{MHD_basic_auth_get_username_password3} call. (The older
+@code{MHD_queue_basic_auth_fail_response} and
+@code{MHD_basic_auth_get_username_password} calls are deprecated.)
Your code would then look like this:
@verbatim
@@ -135,10 +138,10 @@ answer_to_connection (void *cls, struct MHD_Connection *connection,
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_fail_response3 (connection,
- "admins",
- MHD_YES,
- response);
+ 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",
@@ -153,10 +156,10 @@ answer_to_connection (void *cls, struct MHD_Connection *connection,
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_fail_response3 (connection,
- "admins",
- MHD_YES,
- response);
+ ret = MHD_queue_basic_auth_required_response3 (connection,
+ "admins",
+ MHD_YES,
+ response);
}
else
{
diff --git a/doc/chapters/bibliography.inc b/doc/chapters/bibliography.inc
@@ -1,16 +1,16 @@
@heading API reference
@itemize @bullet
@item
-The @emph{GNU libmicrohttpd} manual by Marco Maggi and Christian Grothoff 2008
-@uref{http://gnunet.org/libmicrohttpd/microhttpd.html}
+The @emph{GNU libmicrohttpd} manual by Marco Maggi and Christian Grothoff
+@uref{https://www.gnu.org/software/libmicrohttpd/manual/libmicrohttpd.html}
@item
All referenced RFCs can be found on the website of @emph{The Internet Engineering Task Force}
-@uref{http://www.ietf.org/}
+@uref{https://www.ietf.org/}
@item
-@emph{RFC 2616}: Fielding, R., Gettys, J., Mogul, J., Frystyk, H., and T. Berners-Lee,
-"Hypertext Transfer Protocol -- HTTP/1.1", RFC 2016, January 1997.
+@emph{RFC 2616}: Fielding, R., Gettys, J., Mogul, J., Frystyk, H., Masinter, L., Leach, P.,
+and T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
@item
@emph{RFC 2617}: Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P.,
@@ -20,13 +20,14 @@ Luotonen, A., and L. Stewart, "HTTP Authentication: Basic and Digest Access Auth
@emph{RFC 6455}: Fette, I., Melnikov, A., "The WebSocket Protocol", RFC 6455, December 2011.
-@item
+@item
A well--structured @emph{HTML} reference can be found on
@uref{http://www.echoecho.com/html.htm}
-For those readers understanding German or French, there is an excellent document both for learning
-@emph{HTML} and for reference, whose English version unfortunately has been discontinued.
-@uref{http://de.selfhtml.org/} and @uref{http://fr.selfhtml.org/}
+For those readers understanding German, there is an excellent document both for learning
+@emph{HTML} and for reference, whose English and French versions unfortunately have been
+discontinued.
+@uref{https://wiki.selfhtml.org/}
@end itemize
diff --git a/doc/chapters/callbackresponse.inc b/doc/chapters/callbackresponse.inc
@@ -0,0 +1,467 @@
+All responses we have built so far were complete before they were
+queued. @code{MHD_create_response_from_buffer()} needs the whole
+answer to be sitting in memory already, and
+@code{MHD_create_response_from_fd()} needs it to be a file that the
+operating system can read for us. Neither fits an answer that is
+computed while it is being sent: the output of a long-running report,
+a log file that is filtered on the fly, the result of a database
+cursor, or simply a document that is far too large to be worth
+assembling in a buffer first.
+
+For those cases @emph{libmicrohttpd} lets the application hand over a
+callback instead of the data. Whenever @emph{MHD} has room on the
+socket, it calls that function and asks for the next few bytes. The
+application only ever needs one modest buffer, and the client starts
+receiving the answer long before the last byte has been produced.
+
+
+@heading Creating the response
+
+The response object is created with
+
+@verbatim
+struct MHD_Response *
+MHD_create_response_from_callback (uint64_t size,
+ size_t block_size,
+ MHD_ContentReaderCallback crc,
+ void *crc_cls,
+ MHD_ContentReaderFreeCallback crfc);
+@end verbatim
+@noindent
+
+The five arguments are:
+
+@table @code
+@item size
+The total length of the body in bytes, or the constant
+@code{MHD_SIZE_UNKNOWN} if the application does not know it in
+advance. If a concrete number is given, @emph{MHD} announces it as
+@code{Content-Length} and expects the callback to deliver exactly that
+many bytes.
+
+@item block_size
+The buffer size @emph{MHD} should use when it queries the callback.
+The value must not be zero---@code{MHD_create_response_from_callback()}
+returns @code{NULL} otherwise. @emph{MHD} allocates a buffer of that
+size together with the response object. Note that this buffer is only
+used for responses of known size; when chunked transfer encoding is in
+use (see below) the data is collected in the connection's own memory
+pool instead, and @code{block_size} has no influence on how much
+@emph{MHD} asks for. In either case the value is advisory: the
+callback must always look at the @code{max} argument it is passed and
+never write more than that.
+
+@item crc
+The content reader callback itself, described in the next section.
+
+@item crc_cls
+An arbitrary pointer that is passed back to @code{crc} on every
+invocation. This is where the per-request state goes.
+
+@item crfc
+A callback that is invoked once, when the response object is
+destroyed, so that @code{crc_cls} can be released. May be
+@code{NULL} if there is nothing to clean up.
+@end table
+
+
+@heading The content reader callback
+
+The callback has the following signature:
+
+@verbatim
+typedef ssize_t
+(*MHD_ContentReaderCallback) (void *cls,
+ uint64_t pos,
+ char *buf,
+ size_t max);
+@end verbatim
+@noindent
+
+@code{cls} is the @code{crc_cls} pointer given at creation time.
+@code{buf} points to the memory @emph{MHD} wants filled and
+@code{max} is the number of bytes that may be written there.
+
+The @code{pos} argument deserves a closer look, because its meaning
+depends on how the response is used. For a response created from a
+file descriptor, @emph{MHD} uses it as a file offset and reads with
+@code{pread()}; that is what makes such a response object reusable for
+many connections. For a response that is created fresh for each
+request---which is what we do here---@emph{MHD} guarantees that
+@code{pos} is simply the sum of all non-negative values the callback
+has returned so far. In other words: for a stream, @code{pos} is a
+monotonically increasing count of the bytes already produced, and the
+callback will never be asked to go back. A generator that has no use
+for random access can ignore @code{pos} completely. Do note that the
+guarantee only holds as long as the response object is used for one
+request; if the same object is queued for several connections, the
+same content reader may be asked for the same data more than once, and
+sharing one mutable buffer between them would be wrong.
+
+The return value tells @emph{MHD} what happened:
+
+@table @asis
+@item a positive number
+That many bytes were written to @code{buf}. Returning more than
+@code{max} is a fatal application error and makes @emph{MHD} close the
+connection.
+
+@item @code{MHD_CONTENT_READER_END_OF_STREAM}
+The body is complete. With chunked encoding @emph{MHD} terminates the
+chunk sequence properly and appends any footers; without chunked
+encoding and an unknown size it simply closes the connection. If a
+concrete @code{size} was announced, ending the stream early is not
+legal, and @emph{MHD} treats it essentially like an error---the client
+receives fewer bytes than the @code{Content-Length} promised.
+
+@item @code{MHD_CONTENT_READER_END_WITH_ERROR}
+Something went wrong while generating the data; see below.
+
+@item @code{0}
+No data is available @emph{right now}, but the stream is not over.
+@end table
+
+@noindent
+That last case is the dangerous one. The header file states that
+returning zero is legal only when @emph{MHD} is @emph{not} using an
+internal sockets polling mode, and this is not a formality:
+@emph{MHD} does not diagnose the situation, it simply arranges for the
+next poll to time out immediately and calls the callback again. A
+callback that returns zero while a request is stalled for a third of a
+second is called several million times in that period and pins a CPU
+core at 100%. In external polling mode the effect is milder---the
+callback is retried whenever the application calls one of the
+@code{MHD_run*()} functions---but it is still a spin. The proper
+answer for "no data yet" is to suspend the connection, which is
+covered further down.
+
+
+@heading Keeping state per request
+
+Because the callback is a plain C function, everything it needs to
+know has to travel through @code{crc_cls}. The natural shape of a
+streaming handler is therefore: allocate a context on the heap,
+allocate its data buffer, create the response with that context as
+@code{crc_cls}, and let the free callback dispose of both.
+
+@verbatim
+struct ResponseContext
+{
+ char *buf; /* heap buffer, refilled again and again */
+ size_t fill; /* number of valid bytes in buf */
+ size_t off; /* number of bytes of buf given to MHD */
+ unsigned int block; /* number of blocks produced so far */
+};
+@end verbatim
+@noindent
+
+The two offsets are what make the buffer independent of @emph{MHD}'s
+appetite. A block is generated once and may then be handed out over
+several invocations, because @code{max} can be smaller than what we
+have ready:
+
+@verbatim
+static ssize_t
+content_reader (void *cls, uint64_t pos, char *buf, size_t max)
+{
+ struct ResponseContext *ctx = cls;
+ size_t ready;
+
+ (void) pos;
+
+ if (ctx->off == ctx->fill)
+ {
+ /* Everything we had was passed on, produce the next block. */
+ if (BLOCK_COUNT == ctx->block)
+ return MHD_CONTENT_READER_END_OF_STREAM;
+ generate_block (ctx);
+ }
+ ready = ctx->fill - ctx->off;
+ if (ready > max)
+ ready = max;
+ memcpy (buf, &ctx->buf[ctx->off], ready);
+ ctx->off += ready;
+ return (ssize_t) ready;
+}
+@end verbatim
+@noindent
+
+@code{generate_block()} is the place where a real application would
+run its expensive computation, query, or network fetch. The important
+property is that the same buffer is reused every time, so the memory
+the server needs does not grow with the size of the document.
+
+The handler assembles all of this and, from that moment on, hands
+ownership of the context to the response object:
+
+@verbatim
+ ctx = malloc (sizeof (struct ResponseContext));
+ if (NULL == ctx)
+ return MHD_NO;
+ ctx->buf = malloc (BUFFER_SIZE);
+ if (NULL == ctx->buf)
+ {
+ free (ctx);
+ return MHD_NO;
+ }
+ ctx->fill = 0;
+ ctx->off = 0;
+ ctx->block = 0;
+
+ response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
+ IO_BLOCK_SIZE,
+ &content_reader,
+ ctx,
+ &free_context);
+ if (NULL == response)
+ {
+ /* No response object exists, so nobody will call the free
+ callback for us. */
+ free_context (ctx);
+ return MHD_NO;
+ }
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+@end verbatim
+@noindent
+
+Note the error path: as long as
+@code{MHD_create_response_from_callback()} has not returned a response,
+the free callback will never run and the application must clean up
+itself.
+
+
+@heading Releasing the state
+
+The free callback is trivial:
+
+@verbatim
+static void
+free_context (void *cls)
+{
+ struct ResponseContext *ctx = cls;
+
+ free (ctx->buf);
+ free (ctx);
+}
+@end verbatim
+@noindent
+
+@emph{MHD} invokes it when the last reference to the response object
+goes away. The call to @code{MHD_destroy_response()} in the handler
+does not trigger it, because queuing the response took a reference;
+the callback runs when the connection is finished with the response.
+That happens after a completed body, but equally after a client that
+closed the socket in the middle of the download, after a timeout, and
+when the daemon is stopped. This is precisely what makes the free
+callback the right place for the cleanup: there is no other hook that
+covers all of these cases.
+
+
+@heading Unknown size and chunked encoding
+
+Passing @code{MHD_SIZE_UNKNOWN} as the size leaves @emph{MHD} with the
+problem of telling the client where the body ends. For an HTTP/1.1
+client it solves it by switching to chunked transfer encoding: it adds
+a @code{Transfer-Encoding: chunked} header and frames every piece the
+callback returns as one chunk, terminating the body with a zero-length
+chunk when the callback reports the end of the stream. Nothing has to
+be done for this; it is a consequence of the unknown size.
+
+@verbatim
+< HTTP/1.1 200 OK
+< Content-Type: text/plain
+< Transfer-Encoding: chunked
+@end verbatim
+@noindent
+
+An HTTP/1.0 client cannot be sent chunks. In that case @emph{MHD}
+falls back to the only other framing the protocol offers and marks the
+connection as one that must be closed: the body is sent raw, without
+@code{Content-Length}, and its end is the end of the connection. The
+data arrives intact, but the client has no way to distinguish a
+complete answer from one that was cut short.
+
+If the size @emph{is} known---and in the example program the URL
+@code{/sized} shows this variant---it should be announced, simply by
+passing the number instead of @code{MHD_SIZE_UNKNOWN}. @emph{MHD}
+then emits a normal @code{Content-Length} header, no chunk framing is
+added, and the callback is queried with @code{max} limited by both
+@code{block_size} and the number of bytes still outstanding. The
+callback must produce exactly the announced number of bytes.
+
+
+@heading Aborting a response
+
+Generating data can fail halfway through---the database connection
+drops, a file disappears, an allocation fails. Since the response
+headers, and probably a good part of the body, have already been sent,
+there is no way to turn the answer into an error page any more.
+Returning @code{MHD_CONTENT_READER_END_WITH_ERROR} is the way to give
+up: @emph{MHD} logs the condition and closes the connection
+immediately, without writing the terminating chunk.
+
+What the client sees depends on the framing. With chunked encoding it
+is an abnormally terminated chunk stream, and a well-behaved client
+reports it as such---@code{curl} for instance fails with "transfer
+closed with outstanding read data remaining" and a non-zero exit
+status. With an announced @code{Content-Length} the client notices
+that the body is shorter than promised. Only in the HTTP/1.0 case
+discussed above, where the end of the body is the end of the
+connection, is the failure indistinguishable from a normal ending;
+that is a property of the protocol and not something @emph{MHD} can
+repair. The URL @code{/error} of the example program aborts the
+stream after a few blocks so that this can be observed.
+
+
+@heading When the data is not ready yet
+
+So far the generator could always produce the next block on the spot.
+If it cannot---because the data is coming from another thread, another
+process, or the network---then, as explained above, returning
+@code{0} in an internal polling mode turns the daemon into a busy
+loop. The supported answer is to take the connection out of the event
+loop until the data has arrived, with the pair
+
+@verbatim
+void MHD_suspend_connection (struct MHD_Connection *connection);
+void MHD_resume_connection (struct MHD_Connection *connection);
+@end verbatim
+@noindent
+
+The daemon has to be started with the @code{MHD_ALLOW_SUSPEND_RESUME}
+flag; both functions call @code{MHD_PANIC()} and abort the process
+otherwise. The flag implies @code{MHD_USE_ITC}, so that a resumed
+connection wakes the polling thread up instead of waiting for the next
+timeout. Suspending is not available for daemons running with
+@code{MHD_USE_THREAD_PER_CONNECTION}, and the daemon must not be
+stopped while connections are still suspended.
+
+@code{MHD_suspend_connection()} may only be called from the access
+handler or from the content reader callback, which means the
+connection handle has to be stored in the context struct so that the
+callback can reach it. The pattern is then:
+
+@verbatim
+ if (! ctx->data_available)
+ {
+ MHD_suspend_connection (ctx->connection);
+ return 0;
+ }
+@end verbatim
+@noindent
+
+@code{MHD_resume_connection()}, in contrast, may be called from any
+thread and at any time. In particular it is explicitly allowed to
+call it before the suspension has taken effect: the producer may
+already be done by the time the callback gets around to suspending.
+@emph{MHD} handles this by remembering the resume request and, when the
+suspension is finally processed, cancelling it instead of parking the
+connection, so the wakeup can never be lost. This is what makes the
+"check, suspend, return 0" sequence above safe without a lock of its
+own.
+
+The difference is dramatic in practice. A callback that stalls for
+three hundred milliseconds and returns @code{0} is entered several
+million times and burns a full core; the same callback suspending the
+connection is entered exactly once more and the daemon uses no
+measurable CPU time at all.
+
+
+@heading Trailers
+
+Chunked responses may carry trailers, that is header fields that are
+transmitted after the body. They are added with
+
+@verbatim
+MHD_add_response_footer (response, "X-Checksum", "deadbeef");
+@end verbatim
+@noindent
+
+@emph{MHD} writes them out after the terminating zero-length chunk:
+
+@verbatim
+0
+X-Checksum: deadbeef
+
+@end verbatim
+@noindent
+
+Footers may still be added while the body is being generated---even
+from inside the content reader callback, immediately before returning
+@code{MHD_CONTENT_READER_END_OF_STREAM}---which is what makes them
+useful in the first place: a checksum or a row count over data that
+has only just been produced cannot be put into the headers, because
+those are long gone. This is safe as long as the response object
+belongs to a single request, as it does in the pattern used here.
+@emph{MHD} does not generate a @code{Trailer} response header by
+itself; if the client should be told in advance which fields to
+expect, add it with @code{MHD_add_response_header()}. Be aware that
+many clients discard trailers silently.
+
+
+@heading Example code
+
+The complete program is available as @code{callbackresponse.c}. It
+serves a document of sixty-four blocks that are produced one at a time
+into a single heap buffer. @code{GET /} streams it with
+@code{MHD_SIZE_UNKNOWN} and thus with chunked encoding,
+@code{GET /sized} streams the same bytes with a @code{Content-Length}
+announced up front, and @code{GET /error} aborts in the middle.
+
+
+@heading Remarks
+
+Calls into the content reader of one response object are serialized by
+@emph{MHD}, so a per-request context needs no locking of its own as
+long as no other thread touches it. As soon as a producer thread is
+involved---the suspend/resume case---the shared fields do need
+protection.
+
+The chunk boundaries are not under the application's control and carry
+no meaning: a client may receive the bytes of one callback invocation
+spread over several chunks or, in other configurations, several
+invocations combined. Chunked encoding is a transport detail, and any
+record structure the application needs must be part of the body
+itself.
+
+Finally, returning very small amounts of data per invocation is
+correct but wasteful, since every invocation may become its own chunk
+with its own framing overhead. If the producer has more data ready, it
+should fill as much of @code{buf} as @code{max} permits.
+
+
+@heading Exercises
+
+@itemize @bullet
+
+@item
+Add a URL that reports how far the transfer has progressed by writing
+the value of @code{pos} into every line, and confirm with a slow
+client (@code{curl --limit-rate}) that it really is the number of
+bytes already sent, and not a block counter.
+
+@item
+Register a request completion callback with
+@code{MHD_OPTION_NOTIFY_COMPLETED} and print the termination code.
+Then download the document, interrupt the client in the middle, and
+watch in which order the completion callback and the content reader's
+free callback are invoked, and that the free callback runs in both
+cases.
+
+@item
+Turn the generator into an asynchronous one. Start a thread that
+appends a line to a shared buffer once per second, keep the connection
+handle in the context struct, and suspend the connection whenever the
+buffer is empty. Do not forget @code{MHD_ALLOW_SUSPEND_RESUME} when
+starting the daemon. Then remove the call to
+@code{MHD_suspend_connection()}, leaving only the @code{return 0}, and
+compare the CPU usage of the server process.
+
+@item
+Compute a checksum over the generated document while it is being sent
+and append it as a trailer. Watch it on the wire---most command line
+clients will not show it to you, so a raw @code{nc} or a small script
+speaking HTTP by hand is the easier tool here.
+
+@end itemize
diff --git a/doc/chapters/exploringrequests.inc b/doc/chapters/exploringrequests.inc
@@ -1,5 +1,5 @@
This chapter will deal with the information which the client sends to the
-server at every request. We are going to examine the most useful fields of such an request
+server at every request. We are going to examine the most useful fields of such a request
and print them out in a readable manner. This could be useful for logging facilities.
The starting point is the @emph{hellobrowser} program with the former response removed.
@@ -9,9 +9,9 @@ just return MHD_NO after we have probed the request. This way, the connection is
without much ado by the server.
@verbatim
-static int
-answer_to_connection (void *cls, struct MHD_Connection *connection,
- const char *url,
+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)
@@ -34,15 +34,16 @@ printf ("New %s request for %s using version %s\n", method, url, version);
@end verbatim
@noindent
-The rest of the information is a bit more hidden. Nevertheless, there is lot of it sent from common
+The rest of the information is a bit more hidden. Nevertheless, there is a lot of it sent from common
Internet browsers. It is stored in "key-value" pairs and we want to list what we find in the header.
As there is no mandatory set of keys a client has to send, each key-value pair is printed out one by
one until there are no more left. We do this by writing a separate function which will be called for
each pair just like the above function is called for each HTTP request.
It can then print out the content of this pair.
@verbatim
-int print_out_key (void *cls, enum MHD_ValueKind kind,
- const char *key, const char *value)
+static enum MHD_Result
+print_out_key (void *cls, enum MHD_ValueKind kind,
+ const char *key, const char *value)
{
printf ("%s: %s\n", key, value);
return MHD_YES;
@@ -63,8 +64,8 @@ so the last parameter can be NULL.
All in all, this constitutes the complete @code{logging.c} program for this chapter which can be
found in the @code{examples} section.
-Connecting with any modern Internet browser should yield a handful of keys. You should try to
-interpret them with the aid of @emph{RFC 2616}.
+Connecting with any modern Internet browser should yield a handful of keys. You should try to
+interpret them with the aid of @emph{RFC 9110}.
Especially worth mentioning is the "Host" key which is often used to serve several different websites
hosted under one single IP address but reachable by different domain names (this is called virtual hosting).
@@ -93,9 +94,10 @@ returned accompanied by an informative message.
A very interesting information has still been ignored by our logger---the client's IP address.
Implement a callback function
@verbatim
-static int on_client_connect (void *cls,
- const struct sockaddr *addr,
- socklen_t addrlen)
+static enum MHD_Result
+on_client_connect (void *cls,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
@end verbatim
@noindent
that prints out the IP address in an appropriate format. You might want to use the POSIX function
diff --git a/doc/chapters/hellobrowser.inc b/doc/chapters/hellobrowser.inc
@@ -14,7 +14,9 @@ After the necessary includes and the definition of the port which our server sho
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
+#include <string.h>
#include <microhttpd.h>
+#include <stdio.h>
#define PORT 8888
@@ -39,11 +41,12 @@ daemon to sent the reply.
Talking about the reply, it is defined as a string right after the function header
@verbatim
-int 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)
+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>";
@@ -54,21 +57,22 @@ HTTP is a rather strict protocol and the client would certainly consider it "ina
just sent the answer string "as is". Instead, it has to be wrapped with additional information stored in so-called headers and footers. Most of the work in this area is done by the library for us---we
just have to ask. Our reply string packed in the necessary layers will be called a "response".
To obtain such a response we hand our data (the reply--string) and its size over to the
-@code{MHD_create_response_from_buffer} function. The last two parameters basically tell @emph{MHD}
-that we do not want it to dispose the message data for us when it has been sent and there also needs
-no internal copy to be done because the @emph{constant} string won't change anyway.
+@code{MHD_create_response_from_buffer_static} function. By using the @code{_static}
+variant we tell @emph{MHD} that the buffer will stay valid and unchanged for at least
+as long as the response lives, so that @emph{MHD} neither has to free the message data
+for us when it has been sent nor has to make an internal copy of it---which is exactly
+the case for our @emph{constant} string.
@verbatim
struct MHD_Response *response;
- int ret;
+ enum MHD_Result ret;
- response = MHD_create_response_from_buffer (strlen (page),
- (void*) page, MHD_RESPMEM_PERSISTENT);
+ response = MHD_create_response_from_buffer_static (strlen (page), page);
@end verbatim
@noindent
-Now that the the response has been laced up, it is ready for delivery and can be queued for sending.
+Now that the response has been laced up, it is ready for delivery and can be queued for sending.
This is done by passing it to another @emph{GNU libmicrohttpd} function. As all our work was done in
the scope of one function, the recipient is without doubt the one associated with the
local variable @code{connection} and consequently this variable is given to the queue function.
@@ -91,27 +95,31 @@ already being set at this point to either MHD_YES or MHD_NO in case of success o
With the primary task of our server implemented, we can start the actual server daemon which will listen
on @code{PORT} for connections. This is done in the main function.
@verbatim
-int main ()
+int main (void)
{
struct MHD_Daemon *daemon;
- daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL,
+ 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;
@end verbatim
@noindent
-The first parameter is one of three possible modes of operation. Here we want the daemon to run in
+The first parameter is a bit-wise OR of flags selecting, among other things, the mode of
+operation. With @code{MHD_USE_INTERNAL_POLLING_THREAD} we want the daemon to run in
a separate thread and to manage all incoming connections in the same thread. This means that while
producing the response for one connection, the other connections will be put on hold. In this
example, where the reply is already known and therefore the request is served quickly, this poses no problem.
+The additional @code{MHD_USE_AUTO} flag simply lets @emph{MHD} pick the best polling function
+(@code{epoll}, @code{poll} or @code{select}) available on the platform.
We will allow all clients to connect regardless of their name or location, therefore we do not check
them on connection and set the third and fourth parameter to NULL.
-Parameter five is the address of the function we want to be called whenever a new connection has been
-established. Our @code{answer_to_connection} knows best what the client wants and needs no additional
+Parameter five is the address of the function we want to be called whenever a new request has been
+received. Our @code{answer_to_connection} knows best what the client wants and needs no additional
information (which could be passed via the next parameter) so the next (sixth) parameter is NULL. Likewise,
we do not need to pass extra options to the daemon so we just write the MHD_OPTION_END as the last parameter.
@@ -168,16 +176,19 @@ Both of these issues you will find addressed in the official @code{minimal_examp
the @code{src/examples} directory of the @emph{MHD} package. The source code of this
program should look very familiar to you by now and easy to understand.
-For our example, we create the response from a static (persistent) buffer in memory and thus pass @code{MHD_RESPMEM_PERSISTENT} to the response construction
-function. In the usual case, responses are not transmitted immediately
+For our example, we create the response from a static (persistent) buffer in memory and thus use
+@code{MHD_create_response_from_buffer_static}.
+In the usual case, responses are not transmitted immediately
after being queued. For example, there might be other data on the system that needs to be sent with
a higher priority. Nevertheless, the queue function will return successfully---raising the problem
that the data we have pointed to may be invalid by the time it is about being sent. This is not an
issue here because we can expect the @code{page} string, which is a constant @emph{string literal}
here, to be static. That means it will be present and unchanged for as long as the program runs.
For dynamic data, one could choose to either have @emph{MHD} free the memory @code{page} points
-to itself when it is not longer needed (by passing @code{MHD_RESPMEM_MUST_FREE}) or, alternatively, have the library to make and manage
-its own copy of it (by passing @code{MHD_RESPMEM_MUST_COPY}). Naturally, this last option is the most expensive.
+to itself when it is no longer needed (by using
+@code{MHD_create_response_from_buffer_with_free_callback} with @code{&free}) or, alternatively,
+have the library make and manage its own copy of it (by using
+@code{MHD_create_response_from_buffer_copy}). Naturally, this last option is the most expensive.
@heading Exercises
@itemize @bullet
@@ -200,9 +211,11 @@ and an altered response is sent? Make sure you read about the status codes in th
@item
-Add the option @code{MHD_USE_PEDANTIC_CHECKS} to the start function of the daemon in @code{main}.
-Mind the special format of the parameter list here which is described in the manual. How indulgent
-is the server now to your input?
+Add the option @code{MHD_OPTION_CLIENT_DISCIPLINE_LVL} with a value of @code{1} to the start
+function of the daemon in @code{main} (this option supersedes the deprecated
+@code{MHD_USE_PEDANTIC_CHECKS} flag). Mind the special format of the parameter list here which is
+described in the manual: the option is followed by its @code{int} value, and @code{MHD_OPTION_END}
+still terminates the list. How indulgent is the server now to your input?
@item
diff --git a/doc/chapters/largerpost.inc b/doc/chapters/largerpost.inc
@@ -6,7 +6,7 @@ clients concurrently uploading, responding with a proper busy message if necessa
@heading Prepared answers
-We choose to operate the server with the @code{SELECT_INTERNALLY} method. This makes it easier to
+We choose to operate the server with the @code{MHD_USE_INTERNAL_POLLING_THREAD} method. This makes it easier to
synchronize the global states at the cost of possible delays for other connections if the processing
of a request is too slow. One of these variables that needs to be shared for all connections is the
total number of clients that are uploading.
@@ -45,13 +45,17 @@ const char* completepage = "<html><body>The upload has been completed.</body></h
@end verbatim
@noindent
-We want the server to report internal errors, such as memory shortage or file access problems,
-adequately.
+We want the server to report malformed requests and internal errors, such as file access
+problems, adequately.
@verbatim
const char* servererrorpage
- = "<html><body>An internal server error has occurred.</body></html>";
+ = "<html><body>Invalid request.</body></html>";
const char* fileexistspage
= "<html><body>This file already exists.</body></html>";
+const char* fileioerror
+ = "<html><body>IO error writing to disk.</body></html>";
+const char* postprocerror
+ = "<html><body>Error processing POST data.</body></html>";
@end verbatim
@noindent
@@ -62,13 +66,12 @@ status code but in order to improve the @code{HTTP} conformance of our server a
@verbatim
static enum MHD_Result
send_page (struct MHD_Connection *connection,
- const char* page, int status_code)
+ const char* page, unsigned int status_code)
{
enum MHD_Result ret;
struct MHD_Response *response;
- response = MHD_create_response_from_buffer (strlen (page), (void*) page,
- MHD_RESPMEM_MUST_COPY);
+ response = MHD_create_response_from_buffer_copy (strlen (page), page);
if (!response) return MHD_NO;
ret = MHD_queue_response (connection, status_code, response);
@@ -79,14 +82,14 @@ send_page (struct MHD_Connection *connection,
@end verbatim
@noindent
-Note how we ask @emph{MHD} to make its own copy of the message data. The reason behind this will
-become clear later.
+Note how we ask @emph{MHD} to make its own copy of the message data---that is what the
+@code{_copy} suffix stands for. The reason behind this will become clear later.
@heading Connection cycle
The decision whether the server is busy or not is made right at the beginning of the connection. To
do that at this stage is especially important for @emph{POST} requests because if no response is
-queued at this point, and @code{MHD_YES} returned, @emph{MHD} will not sent any queued messages until
+queued at this point, and @code{MHD_YES} returned, @emph{MHD} will not send any queued messages until
a postprocessor has been created and the post iterator is called at least once.
@verbatim
@@ -107,12 +110,14 @@ answer_to_connection (void *cls, struct MHD_Connection *connection,
@noindent
If the server is not busy, the @code{connection_info} structure is initialized as usual, with
-the addition of a filepointer for each connection.
+the addition of a filepointer for each connection. The status code is set to zero, which
+we use as the marker for "no answer decided yet".
@verbatim
con_info = malloc (sizeof (struct connection_info_struct));
if (NULL == con_info) return MHD_NO;
- con_info->fp = 0;
+ con_info->answercode = 0;
+ con_info->fp = NULL;
if (0 == strcmp (method, "POST"))
{
@@ -131,8 +136,7 @@ For @emph{POST} requests, the postprocessor is created and we register a new upl
this point on, there are many possible places for errors to occur that make it necessary to interrupt
the uploading process. We need a means of having the proper response message ready at all times.
Therefore, the @code{connection_info} structure is extended to hold the most current response
-message so that whenever a response is sent, the client will get the most informative message. Here,
-the structure is initialized to "no error".
+message so that whenever a response is sent, the client will get the most informative message.
@verbatim
if (0 == strcmp (method, "POST"))
{
@@ -149,8 +153,6 @@ the structure is initialized to "no error".
nr_of_uploading_clients++;
con_info->connectiontype = POST;
- con_info->answercode = MHD_HTTP_OK;
- con_info->answerstring = completepage;
}
else con_info->connectiontype = GET;
@end verbatim
@@ -164,7 +166,7 @@ own copy of it for as long as it is needed.
{
char buffer[1024];
- sprintf (buffer, askpage, nr_of_uploading_clients);
+ snprintf (buffer, sizeof (buffer), askpage, nr_of_uploading_clients);
return send_page (connection, buffer, MHD_HTTP_OK);
}
@end verbatim
@@ -174,7 +176,9 @@ own copy of it for as long as it is needed.
The rest of the @code{answer_to_connection} function is very similar to the @code{simplepost.c}
example, except the more flexible content of the responses. The @emph{POST} data is processed until
there is none left and the execution falls through to return an error page if the connection
-constituted no expected request method.
+constituted no expected request method. Note that once we know the answer---because something
+went wrong---we no longer feed the data to the post processor, but we still have to consume it,
+or the client would never get to read our response.
@verbatim
if (0 == strcmp (method, "POST"))
{
@@ -182,15 +186,33 @@ constituted no expected request method.
if (0 != *upload_data_size)
{
- MHD_post_process (con_info->postprocessor,
- upload_data, *upload_data_size);
+ if (0 == con_info->answercode)
+ {
+ 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;
}
- else
- return send_page (connection, con_info->answerstring,
- con_info->answercode);
+
+ if (NULL != con_info->fp)
+ {
+ fclose (con_info->fp);
+ con_info->fp = NULL;
+ }
+ if (0 == con_info->answercode)
+ {
+ con_info->answerstring = completepage;
+ con_info->answercode = MHD_HTTP_OK;
+ }
+ return send_page (connection, con_info->answerstring,
+ con_info->answercode);
}
return send_page(connection, errorpage, MHD_HTTP_BAD_REQUEST);
@@ -199,11 +221,11 @@ constituted no expected request method.
@noindent
-@heading Storing to data
+@heading Storing the data
Unlike the @code{simplepost.c} example, here it is to be expected that post iterator will be called
several times now. This means that for any given connection (there might be several concurrent of them)
the posted data has to be written to the correct file. That is why we store a file handle in every
-@code{connection_info}, so that the it is preserved between successive iterations.
+@code{connection_info}, so that it is preserved between successive iterations.
@verbatim
static enum MHD_Result
iterate_post (void *coninfo_cls, enum MHD_ValueKind kind,
@@ -213,23 +235,25 @@ iterate_post (void *coninfo_cls, enum MHD_ValueKind kind,
uint64_t off, size_t size)
{
struct connection_info_struct *con_info = coninfo_cls;
+ FILE *fp;
@end verbatim
@noindent
Because the following actions depend heavily on correct file processing, which might be error prone,
-we default to reporting internal errors in case anything will go wrong.
-
-@verbatim
-con_info->answerstring = servererrorpage;
-con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
-@end verbatim
-@noindent
+we record the page to answer with as soon as anything goes wrong. Note that the iterator always
+returns @code{MHD_YES}: returning @code{MHD_NO} would abort the post processor and thus make it
+impossible to send our carefully chosen error page to the client.
In the "askpage" @emph{form}, we told the client to label its post data with the "file" key. Anything else
would be an error.
@verbatim
- if (0 != strcmp (key, "file")) return MHD_NO;
+ if (0 != strcmp (key, "file"))
+ {
+ con_info->answerstring = servererrorpage;
+ con_info->answercode = MHD_HTTP_BAD_REQUEST;
+ return MHD_YES;
+ }
@end verbatim
@noindent
@@ -241,16 +265,24 @@ race between the two "fopen" calls, but we will overlook this for portability sa
@verbatim
if (!con_info->fp)
{
+ if (0 != con_info->answercode) /* something went wrong before */
+ return MHD_YES;
+
if (NULL != (fp = fopen (filename, "rb")) )
{
fclose (fp);
con_info->answerstring = fileexistspage;
con_info->answercode = MHD_HTTP_FORBIDDEN;
- return MHD_NO;
+ return MHD_YES;
}
con_info->fp = fopen (filename, "ab");
- if (!con_info->fp) return MHD_NO;
+ if (!con_info->fp)
+ {
+ con_info->answerstring = fileioerror;
+ con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ return MHD_YES;
+ }
}
@end verbatim
@noindent
@@ -259,20 +291,21 @@ race between the two "fopen" calls, but we will overlook this for portability sa
Occasionally, the iterator function will be called even when there are 0 new bytes to process. The
server only needs to write data to the file if there is some.
@verbatim
-if (size > 0)
+ if (size > 0)
{
- if (!fwrite (data, size, sizeof(char), con_info->fp))
- return MHD_NO;
+ if (!fwrite (data, sizeof (char), size, con_info->fp))
+ {
+ con_info->answerstring = fileioerror;
+ con_info->answercode = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ }
}
@end verbatim
@noindent
-If this point has been reached, everything worked well for this iteration and the response can
-be set to success again. If the upload has finished, this iterator function will not be called again.
+If this point has been reached without setting an answer code, everything worked well for this
+iteration. If the upload has finished, this iterator function will not be called again and
+@code{answer_to_connection} will declare success.
@verbatim
- con_info->answerstring = completepage;
- con_info->answercode = MHD_HTTP_OK;
-
return MHD_YES;
}
@end verbatim
@@ -282,7 +315,7 @@ be set to success again. If the upload has finished, this iterator function will
The new client was registered when the postprocessor was created. Likewise, we unregister the client
on destroying the postprocessor when the request is completed.
@verbatim
-void
+static void
request_completed (void *cls, struct MHD_Connection *connection,
void **req_cls,
enum MHD_RequestTerminationCode toe)
diff --git a/doc/chapters/processingpost.inc b/doc/chapters/processingpost.inc
@@ -17,7 +17,7 @@ edit field for the name.
const char* askpage = "<html><body>\
What's your name, Sir?<br>\
<form action=\"/namepost\" method=\"post\">\
- <input name=\"name\" type=\"text\"\
+ <input name=\"name\" type=\"text\">\
<input type=\"submit\" value=\" Send \"></form>\
</body></html>";
@end verbatim
@@ -30,7 +30,7 @@ We also prepare the answer page, where the name is to be filled in later, and an
as the response for anything but proper @emph{GET} and @emph{POST} requests:
@verbatim
-const char* greatingpage="<html><body><h1>Welcome, %s!</center></h1></body></html>";
+const char* greetingpage="<html><body><h1>Welcome, %s!</h1></body></html>";
const char* errorpage="<html><body>This doesn't seem to be right.</body></html>";
@end verbatim
@@ -67,11 +67,13 @@ struct connection_info_struct
@noindent
With these information available to the iterator function, it is able to fulfill its task.
-Once it has composed the greeting string, it returns @code{MHD_NO} to inform the post processor
-that it does not need to be called again. Note that this function does not handle processing
-of data for the same @code{key}. If we were to expect that the name will be posted in several
-chunks, we had to expand the namestring dynamically as additional parts of it with the same @code{key}
-came in. But in this example, the name is assumed to fit entirely inside one single packet.
+Once it has composed the greeting string, it returns @code{MHD_YES} so that the post processor
+keeps parsing the remainder of the upload. Returning @code{MHD_NO} would instead abort the
+parsing and make the next call to @code{MHD_post_process} fail. Note that this function does not
+handle processing of data for the same @code{key}. If we were to expect that the name will be
+posted in several chunks, we had to expand the namestring dynamically as additional parts of it
+with the same @code{key} came in. But in this example, the name is assumed to fit entirely inside
+one single packet.
@verbatim
static enum MHD_Result
@@ -90,12 +92,10 @@ iterate_post (void *coninfo_cls, enum MHD_ValueKind kind, const char *key,
answerstring = malloc (MAXANSWERSIZE);
if (!answerstring) return MHD_NO;
- snprintf (answerstring, MAXANSWERSIZE, greatingpage, data);
+ snprintf (answerstring, MAXANSWERSIZE, greetingpage, data);
con_info->answerstring = answerstring;
}
else con_info->answerstring = NULL;
-
- return MHD_NO;
}
return MHD_YES;
@@ -110,7 +110,7 @@ string. This cleanup function must take into account that it will also be called
requests other than @emph{POST} requests.
@verbatim
-void
+static void
request_completed (void *cls, struct MHD_Connection *connection,
void **req_cls,
enum MHD_RequestTerminationCode toe)
@@ -136,7 +136,8 @@ in the main function.
@verbatim
...
-daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD, PORT, NULL, NULL,
+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);
@@ -218,8 +219,10 @@ considered---all of it.
if (*upload_data_size != 0)
{
- MHD_post_process (con_info->postprocessor, upload_data,
- *upload_data_size);
+ if (MHD_YES !=
+ MHD_post_process (con_info->postprocessor, upload_data,
+ *upload_data_size))
+ return MHD_NO;
*upload_data_size = 0;
return MHD_YES;
diff --git a/doc/chapters/responseheaders.inc b/doc/chapters/responseheaders.inc
@@ -30,7 +30,6 @@ answer_to_connection (void *cls, struct MHD_Connection *connection,
const char *upload_data,
size_t *upload_data_size, void **req_cls)
{
- unsigned char *buffer = NULL;
struct MHD_Response *response;
@end verbatim
@noindent
@@ -58,46 +57,24 @@ server side and if so, the client should be informed with @code{MHD_HTTP_INTERNA
@verbatim
/* error accessing file */
- if (fd != -1) close (fd);
+ if (fd != -1) close (fd);
const char *errorstr =
"<html><body>An internal server error has occurred!\
</body></html>";
response =
- MHD_create_response_from_buffer (strlen (errorstr),
- (void *) errorstr,
- MHD_RESPMEM_PERSISTENT);
- if (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 MHD_YES;
+ return ret;
}
else
return MHD_NO;
- if (!ret)
- {
- const char *errorstr = "<html><body>An internal server error has occurred!\
- </body></html>";
-
- if (buffer) free(buffer);
-
- response = MHD_create_response_from_buffer (strlen(errorstr), (void*) errorstr,
- MHD_RESPMEM_PERSISTENT);
-
- if (response)
- {
- ret = MHD_queue_response (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- response);
- MHD_destroy_response (response);
-
- return MHD_YES;
- }
- else return MHD_NO;
- }
@end verbatim
@noindent
@@ -113,10 +90,8 @@ But in the case of success a response will be constructed directly from the file
}
response =
- MHD_create_response_from_fd_at_offset (sbuf.st_size, fd, 0);
+ MHD_create_response_from_fd_at_offset64 ((uint64_t) sbuf.st_size, fd, 0);
MHD_add_response_header (response, "Content-Type", MIMETYPE);
- ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
- MHD_destroy_response (response);
@end verbatim
@noindent
@@ -129,7 +104,7 @@ MHD_add_response_header(response, "Content-Type", MIMETYPE);
@end verbatim
@noindent
We do not have to append a colon expected by the protocol behind the first
-field---@emph{GNU libhttpdmicro} will take care of this.
+field---@emph{GNU libmicrohttpd} will take care of this.
The function finishes with the well-known lines
@verbatim
@@ -145,7 +120,7 @@ Find a @emph{PNG} file you like and save it to the directory the example is run
@code{picture.png}. You should find the image displayed on your browser if everything worked well.
@heading Remarks
-The include file of the @emph{MHD} library comes with the header types mentioned in @emph{RFC 2616}
+The include file of the @emph{MHD} library comes with the header types mentioned in @emph{RFC 9110}
already defined as macros. Thus, we could have written @code{MHD_HTTP_HEADER_CONTENT_TYPE} instead
of @code{"Content-Type"} as well. However, one is not limited to these standard headers and could
add custom response headers without violating the protocol. Whether, and how, the client would react
@@ -169,8 +144,8 @@ instantly, postpone the queuing in the callback with the @code{sleep} function f
Now start two instances of your browser (or even use two machines) and see how the second client
is put on hold while the first waits for his request on the slow file to be fulfilled.
-Finally, change the sourcecode to use @code{MHD_USE_THREAD_PER_CONNECTION} when the daemon is
-started and try again.
+Finally, change the sourcecode to add @code{MHD_USE_THREAD_PER_CONNECTION} to the daemon's flags
+(this flag must be combined with @code{MHD_USE_INTERNAL_POLLING_THREAD}) and try again.
@item
diff --git a/doc/chapters/sessions.inc b/doc/chapters/sessions.inc
@@ -30,15 +30,15 @@ Here, "KEY" is the name we chose for our session cookie.
MHD requires the user to provide the full cookie format string in order to set
cookies. In order to generate a unique cookie, our example creates a random
-64-character text string to be used as the value of the cookie:
+32-character text string to be used as the value of the cookie:
@verbatim
char value[128];
-char raw_value[65];
+char raw_value[33];
-for (unsigned int i=0;i<sizeof (raw_value);i++)
- raw_value = 'A' + (rand () % 26); /* bad PRNG! */
-raw_value[64] = '\0';
+for (unsigned int i=0;i<sizeof (raw_value) - 1;i++)
+ raw_value[i] = 'A' + (rand () % 26); /* bad PRNG! */
+raw_value[sizeof (raw_value) - 1] = '\0';
snprintf (value, sizeof (value),
"%s=%s",
"KEY",
@@ -49,13 +49,17 @@ Given this cookie value, we can then set the cookie header in our HTTP response
as follows:
@verbatim
-assert (MHD_YES ==
- MHD_set_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_SET_COOKIE,
- value));
+if (MHD_NO ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_SET_COOKIE,
+ value))
+ fprintf (stderr, "Failed to set session cookie header!\n");
@end verbatim
+Note that the header has to be added @emph{before} the response is queued with
+@code{MHD_queue_response}; MHD refuses to modify a response that is already
+queued.
+
@heading Remark: Session expiration
@@ -75,7 +79,7 @@ forms (which are dynamically created using values from previous
POST requests from the same session) is available
as the example @code{sessions.c}.
-Note that the example uses a simple, $O(n)$ linked list traversal to
+Note that the example uses a simple, @math{O(n)} linked list traversal to
look up sessions and to expire old sessions. Using a hash table and a
heap would be more appropriate if a large number of concurrent
sessions is expected.
diff --git a/doc/chapters/tlsauthentication.inc b/doc/chapters/tlsauthentication.inc
@@ -3,9 +3,9 @@ any traffic, including the credentials, could be intercepted by anyone between
the browser client and the server. Protecting the data while it is sent over
unsecured lines will be the goal of this chapter.
-Since version 0.4, the @emph{MHD} library includes support for encrypting the
-traffic by employing SSL/TSL. If @emph{GNU libmicrohttpd} has been configured to
-support these, encryption and decryption can be applied transparently on the
+The @emph{MHD} library includes support for encrypting the traffic by employing
+TLS (the protocol formerly known as SSL). If @emph{GNU libmicrohttpd} has been configured to
+support this, encryption and decryption can be applied transparently on the
data being sent, with only minimal changes to the actual source code of the example.
@@ -14,10 +14,10 @@ data being sent, with only minimal changes to the actual source code of the exam
First, a private key for the server will be generated. With this key, the server
will later be able to authenticate itself to the client---preventing anyone else
from stealing the password by faking its identity. The @emph{OpenSSL} suite, which
-is available on many operating systems, can generate such a key. For the scope of
-this tutorial, we will be content with a 1024 bit key:
+is available on many operating systems, can generate such a key. 1024 bit keys are
+no longer considered secure, so we use a 2048 bit key:
@verbatim
-> openssl genrsa -out server.key 1024
+> openssl genrsa -out server.key 2048
@end verbatim
@noindent
@@ -25,13 +25,16 @@ In addition to the key, a certificate describing the server in human readable to
is also needed. This certificate will be attested with our aforementioned key. In this way,
we obtain a self-signed certificate, valid for one year.
+To avoid unnecessary error messages in the browser, the certificate needs to carry a
+@emph{subjectAltName} that matches the @emph{URI}, for example, "localhost" or the domain
+(current browsers ignore the common name and look at the @emph{subjectAltName} only).
+
@verbatim
-> openssl req -days 365 -out server.pem -new -x509 -key server.key
+> openssl req -days 365 -out server.pem -new -x509 -key server.key \
+ -addext "subjectAltName=DNS:localhost"
@end verbatim
@noindent
-To avoid unnecessary error messages in the browser, the certificate needs to
-have a name that matches the @emph{URI}, for example, "localhost" or the domain.
If you plan to have a publicly reachable server, you will need to ask a trusted third party,
called @emph{Certificate Authority}, or @emph{CA}, to attest the certificate for you. This way,
any visitor can make sure the server's identity is real.
@@ -48,7 +51,7 @@ We merely have to extend the server program so that it loads the two files into
@verbatim
int
-main ()
+main (void)
{
struct MHD_Daemon *daemon;
char *key_pem;
@@ -68,7 +71,7 @@ main ()
and then we point the @emph{MHD} daemon to it upon initialization.
@verbatim
- daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_SSL,
+ 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,
@@ -138,7 +141,7 @@ To do this, you will need to link your application against @emph{gnutls}.
Next, when you start the MHD daemon, you must specify the root CA that you're
willing to trust:
@verbatim
- daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_SSL,
+ 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,
@@ -157,7 +160,7 @@ obtain the raw GnuTLS session handle from @emph{MHD} using
#include <gnutls/x509.h>
gnutls_session_t tls_session;
-union MHD_ConnectionInfo *ci;
+const union MHD_ConnectionInfo *ci;
ci = MHD_get_connection_info (connection,
MHD_CONNECTION_INFO_GNUTLS_SESSION);
@@ -333,7 +336,7 @@ of one domain is that you need to provide a callback instead of the key
and certificate. For example, when you start the MHD daemon, you could
do this:
@verbatim
- daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_SSL,
+ daemon = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS,
PORT, NULL, NULL,
&answer_to_connection, NULL,
MHD_OPTION_HTTPS_CERT_CALLBACK, &sni_callback,
@@ -348,11 +351,11 @@ as follows:
@deftypefn {Function Pointer} int {*gnutls_certificate_retrieve_function2} (gnutls_session_t, const gnutls_datum_t* req_ca_dn, int nreqs, const gnutls_pk_algorithm_t* pk_algos, int pk_algos_length, gnutls_pcert_st** pcert, unsigned int *pcert_length, gnutls_privkey_t * pkey)
@table @var
-@item req_ca_cert
+@item req_ca_dn
is only used in X.509 certificates. Contains a list with the CA names that the server considers trusted. Normally we should send a certificate that is signed by one of these CAs. These names are DER encoded. To get a more meaningful value use the function @code{gnutls_x509_rdn_get()}.
@item pk_algos
-contains a list with server’s acceptable signature algorithms. The certificate returned should support the server’s given algorithms.
+contains a list with server's acceptable signature algorithms. The certificate returned should support the server's given algorithms.
@item pcert
should contain a single certificate and public or a list of them.
diff --git a/doc/chapters/upgrade.inc b/doc/chapters/upgrade.inc
@@ -0,0 +1,422 @@
+HTTP is a request/response protocol: the client asks, the server
+answers, and the connection is either closed or reused for the next
+request. Some applications need something else entirely --- a
+bidirectional byte stream that neither side has to poll for. Rather
+than opening a second connection on another port, @emph{RFC 9110} lets
+a client ask the server to switch the already established connection
+over to a different protocol. The client sends an ordinary request
+carrying an @code{Upgrade:} header field, and if the server agrees it
+answers with the status code @code{101 Switching Protocols}. Once
+that response has gone out, HTTP is over: the two sides simply talk
+whatever protocol they agreed on over the same TCP connection.
+
+Since version 0.9.52 @emph{libmicrohttpd} supports this protocol
+switch. The API is deliberately protocol-agnostic --- @emph{MHD}
+performs the HTTP part of the handshake and then hands you the socket.
+What you send over that socket afterwards is entirely your business;
+@emph{MHD} neither parses nor generates a single byte of it any more.
+
+The best known user of this mechanism is the Websocket protocol
+(@emph{RFC 6455}), which uses the upgrade API as a means to an end and
+then needs a good deal of machinery of its own for framing, masking
+and the closing handshake. This chapter takes the opposite approach:
+it shows the upgrade API on its own, with a protocol so simple that no
+helper library is needed. Our server switches to a line-based echo
+protocol: every line the client sends is sent straight back, until the
+client sends the line @code{QUIT}.
+
+@heading Enabling upgrades
+
+Upgrading is not allowed by default, because handing raw sockets to
+application code has consequences the library cannot control. You opt
+in with the @code{MHD_ALLOW_UPGRADE} flag:
+
+@verbatim
+daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION |
+ MHD_USE_INTERNAL_POLLING_THREAD |
+ MHD_ALLOW_UPGRADE |
+ MHD_USE_ERROR_LOG,
+ PORT, NULL, NULL,
+ &access_handler, NULL,
+ MHD_OPTION_END);
+@end verbatim
+@noindent
+
+The choice of threading mode matters more here than anywhere else in
+this tutorial, because it decides whether your code is allowed to
+block.
+
+With @code{MHD_USE_THREAD_PER_CONNECTION} every connection already has
+a thread of its own, and that thread is the one that will run your
+upgrade callback. Blocking in it stalls exactly one client, so you
+may write the straightforward loop of @code{recv()} and @code{send()}
+calls that everybody has in their head. This is what makes the
+example in this chapter short enough to read in one go.
+
+Without it --- that is, with a single internal polling thread, a
+thread pool, epoll, or an external event loop --- your callback runs
+@emph{inside} @emph{MHD}'s event loop. Blocking there freezes every
+other connection the same loop is serving. In that case the callback
+must return immediately, and you have to register the socket in your
+own event loop (or hand it to a thread you started yourself) and drive
+the conversation from there.
+
+@heading Deciding whether to upgrade
+
+A client that wants to switch protocols says so twice: it names the
+protocol in the @code{Upgrade:} header field, and it lists the token
+@code{Upgrade} in the @code{Connection:} header field. Both fields
+are comma-separated lists, so a browser may well send
+@code{Connection: keep-alive, Upgrade}. Comparing the whole field
+value against the string @code{"Upgrade"} would reject such a request;
+the example therefore contains a small helper @code{has_token()} that
+walks the list. Note also that a protocol switch is a HTTP/1.1
+feature; @code{MHD_queue_response()} will refuse an upgrade response
+on a HTTP/1.0 connection.
+
+@verbatim
+ if ( (0 != strcmp (method, MHD_HTTP_METHOD_GET)) ||
+ (0 != strcmp (version, MHD_HTTP_VERSION_1_1)) ||
+ (! has_token (MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONNECTION),
+ "Upgrade")) ||
+ (! has_token (MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_UPGRADE),
+ PROTOCOL)) )
+ return answer_plain (connection,
+ MHD_HTTP_UPGRADE_REQUIRED,
+ PAGE_UPGRADE_REQUIRED,
+ 1);
+@end verbatim
+@noindent
+
+Refusing is not an error condition: the client asked a question and
+gets an ordinary HTTP answer. Which answer depends on who is at
+fault. If the request is malformed --- for instance an
+@code{Upgrade:} field without the matching @code{Connection:} token
+--- @code{MHD_HTTP_BAD_REQUEST} is appropriate. If the request is
+perfectly well-formed but simply does not speak the protocol this
+resource requires, @code{MHD_HTTP_UPGRADE_REQUIRED} (426) is the
+better answer, and it should carry an @code{Upgrade:} header field
+naming the protocol the server would have accepted. Our example
+folds both cases into a 426 for brevity. Either way you queue a
+perfectly normal response and the connection stays a HTTP connection.
+
+@heading Creating the upgrade response
+
+If we do want to switch, we create a special response object with
+@code{MHD_create_response_for_upgrade()}. It takes the callback that
+will receive the socket and a closure pointer for it, and returns
+@code{NULL} on error. The response has no body --- it is a header and
+nothing else.
+
+@verbatim
+ response = MHD_create_response_for_upgrade (&upgrade_handler,
+ NULL);
+ if (NULL == response)
+ return MHD_NO;
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_UPGRADE,
+ PROTOCOL);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_SWITCHING_PROTOCOLS,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+@end verbatim
+@noindent
+
+Two details are easy to get wrong here.
+
+The first is the @code{Connection: Upgrade} response header field, and
+the surprise is that you must @emph{not} add it.
+@code{MHD_create_response_for_upgrade()} puts it into the response
+itself, and @code{MHD_queue_response()} then verifies that it is still
+there and still contains the @code{upgrade} token before it accepts
+the response. @emph{MHD} does merge repeated @code{Connection:}
+header fields into a single field rather than emitting two of them,
+but it only de-duplicates the @code{close} token, so adding
+@code{Upgrade} a second time puts @code{Connection: Upgrade, Upgrade}
+on the wire. Clients tolerate that, but there is no reason to send
+it.
+
+What @emph{MHD} does @emph{not} invent for you is the @code{Upgrade:}
+field naming the new protocol --- that one is yours to add, since only
+you know what you are switching to. Do not forget it:
+@code{MHD_queue_response()} does not check for it, so in a normal
+build the client simply receives a 101 that fails to say what it
+switched to, while a library built with assertions enabled aborts when
+it performs the switch.
+
+The second is the status code. @code{MHD_queue_response()} rejects an
+upgrade response with any status other than
+@code{MHD_HTTP_SWITCHING_PROTOCOLS}, and conversely it rejects status
+101 combined with a response that is not an upgrade response. On any
+of these mistakes it returns @code{MHD_NO} and, with
+@code{MHD_USE_ERROR_LOG} enabled, explains itself on @code{stderr};
+this is by far the quickest way to debug a handshake that does not
+happen.
+
+The response object itself follows the usual rules.
+@code{MHD_queue_response()} takes its own reference, so calling
+@code{MHD_destroy_response()} immediately afterwards is correct and is
+what all the other examples in this tutorial do. The object is only
+really freed once the last connection using it is done with it, which
+for an upgrade means after the callback has returned. As with any
+other response you may build one upgrade response and queue it for
+many connections; the callback is then simply invoked once per
+connection.
+
+@heading The upgrade callback
+
+After the 101 response has been written to the socket, @emph{MHD}
+calls the @code{MHD_UpgradeHandler} you registered:
+
+@verbatim
+static void
+upgrade_handler (void *cls,
+ struct MHD_Connection *connection,
+ void *req_cls,
+ const char *extra_in,
+ size_t extra_in_size,
+ MHD_socket sock,
+ struct MHD_UpgradeResponseHandle *urh)
+@end verbatim
+@noindent
+
+@table @code
+@item cls
+The closure that was passed to
+@code{MHD_create_response_for_upgrade()}. Since the same response
+object may serve many connections, this is per-@emph{response} state,
+not per-connection state.
+
+@item connection
+The original HTTP connection. It is handed over so that you can have
+a last look at the request --- @code{MHD_lookup_connection_value()}
+still works here, which is how a real protocol picks up, say, a
+subprotocol name or an authentication token. Do not queue anything on
+it any more.
+
+@item req_cls
+Whatever your access handler left in @code{*req_cls}. This is the
+natural place to put per-connection state that the access handler
+already computed.
+
+@item extra_in / extra_in_size
+Bytes of your new protocol that the client sent immediately behind the
+request header, and that @emph{MHD} therefore already read off the
+socket while parsing the request. They are @emph{gone} from the
+socket: if you go straight to @code{recv()} you silently lose them.
+Process this buffer first, exactly as if you had read it yourself.
+The buffer belongs to @emph{MHD} and is only valid for the duration of
+the call, so copy anything you want to keep. A client that waits for
+the 101 before it starts talking will produce
+@code{extra_in_size == 0}, which is why this argument is so easy to
+overlook and so annoying when it finally bites.
+
+@item sock
+The socket. For a plain HTTP connection this is the connection to the
+client; for HTTPS it is one end of a socketpair with @emph{MHD}
+relaying the TLS traffic, so socket options and @code{getpeername()}
+will not give you what you expect. Read and write it as you please,
+and you may call @code{shutdown()} on it, but you must never call
+@code{close()}.
+
+@item urh
+The handle for @code{MHD_upgrade_action()}, described below.
+@end table
+
+The loop itself is then unremarkable, apart from consuming
+@code{extra_in} before the first @code{recv()}:
+
+@verbatim
+ make_blocking (sock);
+ es.line_len = 0;
+ done = echo_feed (sock, &es, extra_in, extra_in_size);
+ while (0 == done)
+ {
+ got = recv (sock, buf, sizeof (buf), 0);
+ if (0 > got)
+ {
+ if (EINTR == errno)
+ continue;
+ break; /* read error */
+ }
+ if (0 == got)
+ break; /* client closed the connection */
+ done = echo_feed (sock, &es, buf, (size_t) got);
+ }
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+@end verbatim
+@noindent
+
+@heading Blocking or not blocking
+
+The call to @code{make_blocking()} deserves a warning, because the
+current behaviour is the opposite of what the threading modes suggest.
+@emph{MHD} puts every accepted client socket into non-blocking mode,
+and it does so regardless of the threading mode --- in fact the
+library refuses to perform the switch at all, logging @code{Cannot
+execute "upgrade" as the socket is in the blocking mode}, if it finds
+the socket blocking. So even under
+@code{MHD_USE_THREAD_PER_CONNECTION}, where you are perfectly entitled
+to block, the socket you are handed is non-blocking and a
+@code{recv()} on an idle connection returns @code{-1} with
+@code{EAGAIN} instead of waiting.
+
+If you want the simple blocking loop shown above, you therefore have
+to ask for it. The example does so with a small
+@code{make_blocking()} helper --- @code{fcntl()} clearing
+@code{O_NONBLOCK} on POSIX systems, @code{ioctlsocket()} on W32 ---
+which is the only operating-system-dependent code in the whole
+program.
+
+Under any other threading mode you leave the socket alone, keep it
+non-blocking, and add it to your own event loop instead.
+
+@heading Closing the connection
+
+The last obligation of the callback is to hand the socket back:
+
+@verbatim
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+@end verbatim
+@noindent
+
+This is not a formality. An upgraded connection is put into an
+internal ``suspended'' state, and it stays there --- socket open,
+connection structure allocated, slot counted against the connection
+limit --- until the application says it is done. Only
+@code{MHD_UPGRADE_ACTION_CLOSE} says that. Calling @code{close()} on
+the socket yourself does not, and additionally leaves @emph{MHD}
+holding a file descriptor number that the operating system may already
+have handed to somebody else. Call the action exactly once, on every
+path out of the callback, including the error paths.
+
+@code{MHD_upgrade_action()} takes two further actions in the current
+release, @code{MHD_UPGRADE_ACTION_CORK_ON} and
+@code{MHD_UPGRADE_ACTION_CORK_OFF}. They switch @code{TCP_CORK} (or
+@code{TCP_NOPUSH}) on the underlying socket so that several small
+writes can be coalesced into a single segment. They are a hint, not a
+guarantee: the call returns @code{MHD_NO} if the platform has no such
+option or if the socket is not a TCP socket, which is the normal case
+for HTTPS connections. The header file marks this part of the API as
+not yet finalised, so do not build anything load-bearing on it.
+
+@heading Remarks
+
+@code{MHD_stop_daemon()} does @emph{not} interrupt an upgraded
+connection. It marks the connection as closed internally and logs
+@code{Initiated daemon shutdown while "upgraded" connection was not
+closed}, but it neither shuts down nor closes the socket --- and then
+it waits for the connection's thread, which means it blocks until your
+callback has returned. A callback parked in a blocking @code{recv()}
+therefore hangs the shutdown for as long as the client keeps the
+connection open. Any server that wants to terminate on demand needs
+its own way out of that loop: a global shutdown flag combined with a
+@code{shutdown()} on the sockets you are holding, a self-pipe added to
+a @code{select()} in the loop, or a receive timeout on the socket.
+Our example sidesteps the problem by only stopping the daemon when the
+user presses return at a moment when nothing is connected.
+
+Error handling in the callback is entirely yours. @emph{MHD} will not
+tell you that a client vanished and will not time an upgraded
+connection out --- @code{MHD_OPTION_CONNECTION_TIMEOUT} stops applying
+the moment the connection is upgraded. Treat @code{0} from
+@code{recv()} as a clean disconnect and a negative return as a broken
+one, and remember that a peer which disappears without a FIN produces
+neither until the TCP stack gives up, which can take a very long time.
+Whatever happens, end by calling @code{MHD_upgrade_action()} with
+@code{MHD_UPGRADE_ACTION_CLOSE}.
+
+Finally, keep in mind that an upgraded connection is a connection that
+@emph{MHD} can no longer recycle, and that intermediaries are entitled
+to be unhelpful: @code{Upgrade:} is listed in @code{Connection:} and
+is therefore a hop-by-hop header field. A proxy that does not
+understand your protocol is allowed to drop it, in which case your
+server never sees an upgrade request at all and the client gets an
+ordinary HTTP response where it expected a 101. This is the main
+reason why deployed protocols on top of this mechanism --- Websockets
+above all --- make the handshake verifiable by both ends instead of
+trusting that the request arrived intact.
+
+The complete program is available as @code{upgrade.c} in the
+@code{examples} section. Start it and talk to it with any tool that
+lets you type raw bytes at a socket --- @code{telnet localhost 8888}
+will do --- by sending
+
+@verbatim
+GET / HTTP/1.1
+Host: localhost
+Connection: Upgrade
+Upgrade: echo
+
+@end verbatim
+@noindent
+
+followed by an empty line. The server answers with
+
+@verbatim
+HTTP/1.1 101 Switching Protocols
+Date: ...
+Connection: Upgrade
+Upgrade: echo
+
+@end verbatim
+@noindent
+
+and from then on echoes every line back until you type @code{QUIT}.
+Note the absence of a @code{Content-Length:} field: a 101 response has
+no body, and everything after the empty line already belongs to the
+new protocol.
+
+For a real-world protocol built on exactly this API, read
+@emph{RFC 6455} and note how much of it exists only to make the
+handshake and the framing verifiable --- none of which the upgrade API
+itself has an opinion about.
+
+@heading Exercises
+
+@itemize @bullet
+
+@item
+Point a browser at the server. The browser sends a plain @code{GET /}
+and receives the @code{426} page. Now add a second resource that
+serves a small HTML page and switch the upgrade to a different URL, so
+that the same server can be both browsed and upgraded.
+
+@item
+Make the echo server useful: keep a list of the sockets of all
+upgraded connections and forward every line one client sends to all
+the others, turning the example into a minimal chat server. You will
+need a mutex around the list, and you will discover why the closing
+rules matter --- a socket must leave the list before its callback
+returns.
+
+@item
+Fix the shutdown behaviour discussed in the remarks. Add a global
+``quitting'' flag and a list of active sockets, have @code{main()} set
+the flag and call @code{shutdown (sock, SHUT_RDWR)} on every entry
+before it calls @code{MHD_stop_daemon()}, and verify that the daemon
+now stops even while a client is connected and silent.
+
+@item
+Change the daemon flags to plain
+@code{MHD_USE_INTERNAL_POLLING_THREAD} without
+@code{MHD_USE_THREAD_PER_CONNECTION} and observe that the server now
+serves exactly one upgraded client and stops answering everybody else.
+Then repair it: leave the socket non-blocking, start a thread from
+within the callback, and return immediately.
+
+@item
+Have the client send a line longer than @code{LINE_SIZE}. The example
+silently drops the excess. What should a real protocol do instead,
+and which status code would you have used if the problem had been
+detectable during the handshake?
+
+@end itemize
diff --git a/doc/chapters/websocket.inc b/doc/chapters/websocket.inc
@@ -1,886 +0,0 @@
-Websockets are a genuine way to implement push notifications,
-where the server initiates the communication while the client can be idle.
-Usually a HTTP communication is half-duplex and always requested by the client,
-but websockets are full-duplex and only initialized by the client.
-In the further communication both sites can use the websocket at any time
-to send data to the other site.
-
-To initialize a websocket connection the client sends a special HTTP request
-to the server and initializes
-a handshake between client and server which switches from the HTTP protocol
-to the websocket protocol.
-Thus both the server as well as the client must support websockets.
-If proxys are used, they must support websockets too.
-In this chapter we take a look on server and client, but with a focus on
-the server with @emph{libmicrohttpd}.
-
-Since version 0.9.52 @emph{libmicrohttpd} supports upgrading requests,
-which is required for switching from the HTTP protocol.
-Since version 0.9.74 the library @emph{libmicrohttpd_ws} has been added
-to support the websocket protocol.
-
-@heading Upgrading connections with libmicrohttpd
-
-To support websockets we need to enable upgrading of HTTP connections first.
-This is done by passing the flag @code{MHD_ALLOW_UPGRADE} to
-@code{MHD_start_daemon()}.
-
-
-@verbatim
-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);
-@end verbatim
-@noindent
-
-
-The next step is to turn a specific request into an upgraded connection.
-This done in our @code{access_handler} by calling
-@code{MHD_create_response_for_upgrade()}.
-An @code{upgrade_handler} will be passed to perform the low-level actions
-on the socket.
-
-@emph{Please note that the socket here is just a regular socket as provided
-by the operating system.
-To use it as a websocket, some more steps from the following
-chapters are required.}
-
-
-@verbatim
-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 **ptr)
-{
- /* ... */
- /* some code to decide whether to upgrade or not */
- /* ... */
-
- /* create the response for upgrade */
- response = MHD_create_response_for_upgrade (&upgrade_handler,
- NULL);
-
- /* ... */
- /* additional headers, etc. */
- /* ... */
-
- ret = MHD_queue_response (connection,
- MHD_HTTP_SWITCHING_PROTOCOLS,
- response);
- MHD_destroy_response (response);
-
- return ret;
-}
-@end verbatim
-@noindent
-
-
-In the @code{upgrade_handler} we receive the low-level socket,
-which is used for the communication with the specific client.
-In addition to the low-level socket we get:
-@itemize @bullet
-@item
-Some data, which has been read too much while @emph{libmicrohttpd} was
-switching the protocols.
-This value is usually empty, because it would mean that the client
-has sent data before the handshake was complete.
-
-@item
-A @code{struct MHD_UpgradeResponseHandle} which is used to perform
-special actions like closing, corking or uncorking the socket.
-These commands are executed by passing the handle
-to @code{MHD_upgrade_action()}.
-
-
-@end itemize
-
-Depending of the flags specified while calling @code{MHD_start_deamon()}
-our @code{upgrade_handler} is either executed in the same thread
-as our daemon or in a thread specific for each connection.
-If it is executed in the same thread then @code{upgrade_handler} is
-a blocking call for our webserver and
-we should finish it as fast as possible (i. e. by creating a thread and
-passing the information there).
-If @code{MHD_USE_THREAD_PER_CONNECTION} was passed to
-@code{MHD_start_daemon()} then a separate thread is used and
-thus our @code{upgrade_handler} needs not to start a separate thread.
-
-An @code{upgrade_handler}, which is called with a separate thread
-per connection, could look like this:
-
-
-@verbatim
-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)
-{
- /* ... */
- /* do something with the socket `fd` like `recv()` or `send()` */
- /* ... */
-
- /* close the socket when it is not needed anymore */
- MHD_upgrade_action (urh,
- MHD_UPGRADE_ACTION_CLOSE);
-}
-@end verbatim
-@noindent
-
-
-This is all you need to know for upgrading connections
-with @emph{libmicrohttpd}.
-The next chapters focus on using the websocket protocol
-with @emph{libmicrohttpd_ws}.
-
-
-@heading Websocket handshake with libmicrohttpd_ws
-
-To request a websocket connection the client must send
-the following information with the HTTP request:
-
-@itemize @bullet
-@item
-A @code{GET} request must be sent.
-
-@item
-The version of the HTTP protocol must be 1.1 or higher.
-
-@item
-A @code{Host} header field must be sent
-
-@item
-A @code{Upgrade} header field containing the keyword "websocket"
-(case-insensitive).
-Please note that the client could pass multiple protocols separated by comma.
-
-@item
-A @code{Connection} header field that includes the token "Upgrade"
-(case-insensitive).
-Please note that the client could pass multiple tokens separated by comma.
-
-@item
-A @code{Sec-WebSocket-Key} header field with a base64-encoded value.
-The decoded the value is 16 bytes long
-and has been generated randomly by the client.
-
-@item
-A @code{Sec-WebSocket-Version} header field with the value "13".
-
-@end itemize
-
-
-Optionally the client can also send the following information:
-
-
-@itemize @bullet
-@item
-A @code{Origin} header field can be used to determine the source
-of the client (i. e. the website).
-
-@item
-A @code{Sec-WebSocket-Protocol} header field can contain a list
-of supported protocols by the client, which can be sent over the websocket.
-
-@item
-A @code{Sec-WebSocket-Extensions} header field which may contain extensions
-to the websocket protocol. The extensions must be registered by IANA.
-
-@end itemize
-
-
-A valid example request from the client could look like this:
-
-
-@verbatim
-GET /chat HTTP/1.1
-Host: server.example.com
-Upgrade: websocket
-Connection: Upgrade
-Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-Sec-WebSocket-Version: 13
-@end verbatim
-@noindent
-
-
-To complete the handshake the server must respond with
-some specific response headers:
-
-@itemize @bullet
-@item
-The HTTP response code @code{101 Switching Protocols} must be answered.
-
-@item
-An @code{Upgrade} header field containing the value "websocket" must be sent.
-
-@item
-A @code{Connection} header field containing the value "Upgrade" must be sent.
-
-@item
-A @code{Sec-WebSocket-Accept} header field containing a value, which
-has been calculated from the @code{Sec-WebSocket-Key} request header field,
-must be sent.
-
-@end itemize
-
-
-Optionally the server may send following headers:
-
-
-@itemize @bullet
-@item
-A @code{Sec-WebSocket-Protocol} header field containing a protocol
-of the list specified in the corresponding request header field.
-
-@item
-A @code{Sec-WebSocket-Extension} header field containing all used extensions
-of the list specified in the corresponding request header field.
-
-@end itemize
-
-
-A valid websocket HTTP response could look like this:
-
-@verbatim
-HTTP/1.1 101 Switching Protocols
-Upgrade: websocket
-Connection: Upgrade
-Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-@end verbatim
-@noindent
-
-
-To upgrade a connection to a websocket the @emph{libmicrohttpd_ws} provides
-some helper functions for the @code{access_handler} callback function:
-
-@itemize @bullet
-@item
-@code{MHD_websocket_check_http_version()} checks whether the HTTP version
-is 1.1 or above.
-
-@item
-@code{MHD_websocket_check_connection_header()} checks whether the value
-of the @code{Connection} request header field contains
-an "Upgrade" token (case-insensitive).
-
-@item
-@code{MHD_websocket_check_upgrade_header()} checks whether the value
-of the @code{Upgrade} request header field contains
-the "websocket" keyword (case-insensitive).
-
-@item
-@code{MHD_websocket_check_version_header()} checks whether the value
-of the @code{Sec-WebSocket-Version} request header field is "13".
-
-@item
-@code{MHD_websocket_create_accept_header()} takes the value from
-the @code{Sec-WebSocket-Key} request header and calculates the value
-for the @code{Sec-WebSocket-Accept} response header field.
-
-@end itemize
-
-
-The @code{access_handler} example of the previous chapter can now be
-extended with these helper functions to perform the websocket handshake:
-
-@verbatim
-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 **ptr)
-{
- static int aptr;
- struct MHD_Response *response;
- enum MHD_Result 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 != *ptr)
- {
- /* do never respond on first call */
- *ptr = &aptr;
- return MHD_YES;
- }
- *ptr = NULL; /* reset when done */
-
- if (0 == strcmp (url, "/"))
- {
- /* Default page for visiting the server */
- struct MHD_Response *response = MHD_create_response_from_buffer (
- strlen (PAGE),
- PAGE,
- MHD_RESPMEM_PERSISTENT);
- 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_CONNECTION,
- "Upgrade");
- 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 = MHD_create_response_from_buffer (
- strlen (PAGE_INVALID_WEBSOCKET_REQUEST),
- PAGE_INVALID_WEBSOCKET_REQUEST,
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_BAD_REQUEST,
- response);
- MHD_destroy_response (response);
- }
- }
- else
- {
- struct MHD_Response*response = MHD_create_response_from_buffer (
- strlen (PAGE_NOT_FOUND),
- PAGE_NOT_FOUND,
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_NOT_FOUND,
- response);
- MHD_destroy_response (response);
- }
-
- return ret;
-}
-@end verbatim
-@noindent
-
-Please note that we skipped the check of the Host header field here,
-because we don't know the host for this example.
-
-@heading Decoding/encoding the websocket protocol with libmicrohttpd_ws
-
-Once the websocket connection is established you can receive/send frame data
-with the low-level socket functions @code{recv()} and @code{send()}.
-The frame data which goes over the low-level socket is encoded according
-to the websocket protocol.
-To use received payload data, you need to decode the frame data first.
-To send payload data, you need to encode it into frame data first.
-
-@emph{libmicrohttpd_ws} provides several functions for encoding of
-payload data and decoding of frame data:
-
-@itemize @bullet
-@item
-@code{MHD_websocket_decode()} decodes received frame data.
-The payload data may be of any kind, depending upon what the client has sent.
-So this decode function is used for all kind of frames and returns
-the frame type along with the payload data.
-
-@item
-@code{MHD_websocket_encode_text()} encodes text.
-The text must be encoded with UTF-8.
-
-@item
-@code{MHD_websocket_encode_binary()} encodes binary data.
-
-@item
-@code{MHD_websocket_encode_ping()} encodes a ping request to
-check whether the websocket is still valid and to test latency.
-
-@item
-@code{MHD_websocket_encode_ping()} encodes a pong response to
-answer a received ping request.
-
-@item
-@code{MHD_websocket_encode_close()} encodes a close request.
-
-@item
-@code{MHD_websocket_free()} frees data returned by the encode/decode functions.
-
-@end itemize
-
-Since you could receive or send fragmented data (i. e. due to a too
-small buffer passed to @code{recv}) all of these encode/decode
-functions require a pointer to a @code{struct MHD_WebSocketStream} passed
-as argument.
-In this structure @emph{libmicrohttpd_ws} stores information
-about encoding/decoding of the particular websocket.
-For each websocket you need a unique @code{struct MHD_WebSocketStream}
-to encode/decode with this library.
-
-To create or destroy @code{struct MHD_WebSocketStream}
-we have additional functions:
-
-@itemize @bullet
-@item
-@code{MHD_websocket_stream_init()} allocates and initializes
-a new @code{struct MHD_WebSocketStream}.
-You can specify some options here to alter the behavior of the websocket stream.
-
-@item
-@code{MHD_websocket_stream_free()} frees a previously allocated
-@code{struct MHD_WebSocketStream}.
-
-@end itemize
-
-With these encode/decode functions we can improve our @code{upgrade_handler}
-callback function from an earlier example to a working websocket:
-
-
-@verbatim
-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)
-{
-#if defined(MHD_POSIX_SOCKETS)
- int flags;
-
- flags = fcntl (fd, F_GETFL);
- if (-1 == flags)
- return;
- if ((flags & ~O_NONBLOCK) != flags)
- if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
- abort ();
-#elif defined(MHD_WINSOCK_SOCKETS)
- unsigned long flags = 0;
-
- ioctlsocket (fd, FIONBIO, &flags);
-#endif /* MHD_WINSOCK_SOCKETS */
-}
-
-@end verbatim
-@noindent
-
-
-Please note that the websocket in this example is only half-duplex.
-It waits until the blocking @code{recv()} call returns and
-only does then something.
-In this example all frame types are decoded by @emph{libmicrohttpd_ws},
-but we only do something when a text, ping or close frame is received.
-Binary and pong frames are ignored in our code.
-This is legit, because the server is only required to implement at
-least support for ping frame or close frame (the other frame types
-could be skipped in theory, because they don't require an answer).
-The pong frame doesn't require an answer and whether text frames or
-binary frames get an answer simply belongs to your server application.
-So this is a valid minimal example.
-
-Until this point you've learned everything you need to basically
-use websockets with @emph{libmicrohttpd} and @emph{libmicrohttpd_ws}.
-These libraries offer much more functions for some specific cases.
-
-
-The further chapters of this tutorial focus on some specific problems
-and the client site programming.
-
-
-@heading Using full-duplex websockets
-
-To use full-duplex websockets you can simply create two threads
-per websocket connection.
-One of these threads is used for receiving data with
-a blocking @code{recv()} call and the other thread is triggered
-by the application internal codes and sends the data.
-
-A full-duplex websocket example is implemented in the example file
-@code{websocket_chatserver_example.c}.
-
-@heading Error handling
-
-The most functions of @emph{libmicrohttpd_ws} return a value
-of @code{enum MHD_WEBSOCKET_STATUS}.
-The values of this enumeration can be converted into an integer
-and have an easy interpretation:
-
-@itemize @bullet
-@item
-If the value is less than zero an error occurred and the call has failed.
-Check the enumeration values for more specific information.
-
-@item
-If the value is equal to zero, the call succeeded.
-
-@item
-If the value is greater than zero, the call succeeded and the value
-specifies the decoded frame type.
-Currently positive values are only returned by @code{MHD_websocket_decode()}
-(of the functions with this return enumeration type).
-
-@end itemize
-
-A websocket stream can also get broken when invalid frame data is received.
-Also the other site could send a close frame which puts the stream into
-a state where it may not be used for regular communication.
-Whether a stream has become broken, can be checked with
-@code{MHD_websocket_stream_is_valid()}.
-
-
-@heading Fragmentation
-
-In addition to the regular TCP/IP fragmentation the websocket protocol also
-supports fragmentation.
-Fragmentation could be used for continuous payload data such as video data
-from a webcam.
-Whether or not you want to receive fragmentation is specified upon
-initialization of the websocket stream.
-If you pass @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} in the flags parameter
-of @code{MHD_websocket_stream_init()} then you can receive fragments.
-If you don't pass this flag (in the most cases you just pass zero as flags)
-then you don't want to handle fragments on your own.
-@emph{libmicrohttpd_ws} removes then the fragmentation for you
-in the background.
-You only get the completely assembled frames.
-
-Upon encoding you specify whether or not you want to create a fragmented frame
-by passing a flag to the corresponding encode function.
-Only @code{MHD_websocket_encode_text()} and @code{MHD_websocket_encode_binary()}
-can be used for fragmentation, because the other frame types may
-not be fragmented.
-Encoding fragmented frames is independent of
-the @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} flag upon initialization.
-
-@heading Quick guide to websockets in JavaScript
-
-Websockets are supported in all modern web browsers.
-You initialize a websocket connection by creating an instance of
-the @code{WebSocket} class provided by the web browser.
-
-There are some simple rules for using websockets in the browser:
-
-@itemize @bullet
-@item
-When you initialize the instance of the websocket class you must pass an URL.
-The URL must either start with @code{ws://}
-(for not encrypted websocket protocol) or @code{wss://}
-(for TLS-encrypted websocket protocol).
-
-@strong{IMPORTANT:} If your website is accessed via @code{https://}
-then you are in a security context, which means that you are only allowed to
-access other secure protocols.
-So you can only use @code{wss://} for websocket connections then.
-If you try to @code{ws://} instead then your websocket connection will
-automatically fail.
-
-@item
-The WebSocket class uses events to handle the receiving of data.
-JavaScript is per definition a single-threaded language so
-the receiving events will never overlap.
-Sending is done directly by calling a method of the instance of
-the WebSocket class.
-
-@end itemize
-
-
-Here is a short example for receiving/sending data to the same host
-as the website is running on:
-
-@verbatim
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="UTF-8">
-<title>Websocket Demo</title>
-<script>
-
-let url = 'ws' + (window.location.protocol === 'https:' ? 's' : '') + '://' +
- window.location.host + '/chat';
-let socket = null;
-
-window.onload = function(event) {
- socket = new WebSocket(url);
- socket.onopen = function(event) {
- document.write('The websocket connection has been established.<br>');
-
- // Send some text
- socket.send('Hello from JavaScript!');
- }
-
- socket.onclose = function(event) {
- document.write('The websocket connection has been closed.<br>');
- }
-
- socket.onerror = function(event) {
- document.write('An error occurred during the websocket communication.<br>');
- }
-
- socket.onmessage = function(event) {
- document.write('Websocket message received: ' + event.data + '<br>');
- }
-}
-
-</script>
-</head>
-<body>
-</body>
-</html>
-
-@end verbatim
-@noindent
diff --git a/doc/examples/.gitignore b/doc/examples/.gitignore
@@ -1,3 +1,4 @@
+/upgrade
/tlsauthentication
/simplepost
/sessions
@@ -5,6 +6,7 @@
/logging
/largepost
/hellobrowser
+/callbackresponse
/basicauthentication
*.exe
*.o
diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am
@@ -21,10 +21,16 @@ $(top_builddir)/src/microhttpd/libmicrohttpd.la: $(top_builddir)/src/microhttpd/
# example programs
noinst_PROGRAMS = \
+ callbackresponse \
hellobrowser \
logging \
responseheaders
-
+
+if ENABLE_UPGRADE
+noinst_PROGRAMS += \
+ upgrade
+endif
+
if ENABLE_BAUTH
noinst_PROGRAMS += \
basicauthentication
@@ -42,19 +48,20 @@ 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
+callbackresponse_SOURCES = \
+ callbackresponse.c
+callbackresponse_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
+
hellobrowser_SOURCES = \
- hellobrowser.c
+ hellobrowser.c
hellobrowser_LDADD = \
- $(top_builddir)/src/microhttpd/libmicrohttpd.la
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
logging_SOURCES = \
logging.c
@@ -86,8 +93,7 @@ largepost_SOURCES = \
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
+upgrade_SOURCES = \
+ upgrade.c
+upgrade_LDADD = \
+ $(top_builddir)/src/microhttpd/libmicrohttpd.la
diff --git a/doc/examples/callbackresponse.c b/doc/examples/callbackresponse.c
@@ -0,0 +1,281 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+/**
+ * @file callbackresponse.c
+ * @brief Example for generating a response on the fly with
+ * MHD_create_response_from_callback(). The body is not a
+ * file and is never assembled in memory as a whole; it is
+ * produced block by block into a single heap buffer that is
+ * refilled every time libmicrohttpd asks for more data.
+ * "GET /" streams the document with an unknown size (and thus
+ * with chunked transfer encoding), "GET /sized" streams the
+ * very same document with a "Content-Length" announced up
+ * front, and "GET /error" aborts the stream in the middle to
+ * show MHD_CONTENT_READER_END_WITH_ERROR.
+ * @author Christian Grothoff
+ */
+
+#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 <stdlib.h>
+#include <string.h>
+
+#define PORT 8888
+
+/**
+ * Length of one generated line, including the newline character.
+ */
+#define LINE_SIZE 64
+
+/**
+ * Size of the heap buffer that is refilled over and over again. This
+ * is the amount of data we produce in one go; it has nothing to do
+ * with the block size we pass to MHD.
+ */
+#define BUFFER_SIZE (64 * LINE_SIZE)
+
+/**
+ * Number of times the buffer is refilled before the stream ends. The
+ * complete document is BLOCK_COUNT * BUFFER_SIZE bytes long.
+ */
+#define BLOCK_COUNT 64
+
+/**
+ * Block size we ask MHD to use when it queries our callback. MHD
+ * allocates a buffer of this size together with the response; it is an
+ * advisory value and MHD may ask for less.
+ */
+#define IO_BLOCK_SIZE 1024
+
+/**
+ * For the "/error" URL: number of blocks to deliver before the stream
+ * is aborted with MHD_CONTENT_READER_END_WITH_ERROR.
+ */
+#define ABORT_AFTER 3
+
+
+/**
+ * State we keep for one response that is being streamed. One such
+ * structure is allocated per request and released by the free callback
+ * that we hand to MHD_create_response_from_callback().
+ */
+struct ResponseContext
+{
+ /**
+ * Heap buffer holding the block that is currently being delivered.
+ * Refilled by generate_block() whenever it has been drained.
+ */
+ char *buf;
+
+ /**
+ * Number of valid bytes in @e buf.
+ */
+ size_t fill;
+
+ /**
+ * Number of bytes of @e buf that were already handed to MHD.
+ */
+ size_t off;
+
+ /**
+ * Number of blocks generated so far, and thus the number of the
+ * block that generate_block() will produce next.
+ */
+ unsigned int block;
+
+ /**
+ * Non-zero if this stream is supposed to fail in the middle.
+ */
+ int fail;
+};
+
+
+/**
+ * Produce the next block of the document into @a ctx->buf. This
+ * stands in for whatever expensive computation, database query or
+ * network fetch a real application would perform here. Note that the
+ * same buffer is reused for every block, so the memory needed by the
+ * server does not grow with the size of the document.
+ *
+ * Every line is exactly LINE_SIZE bytes long, which is what allows us
+ * to announce an exact "Content-Length" for the "/sized" URL.
+ *
+ * @param ctx the per-response state to refill
+ */
+static void
+generate_block (struct ResponseContext *ctx)
+{
+ unsigned int i;
+ char *line;
+ char label[32];
+ int len;
+
+ for (i = 0; i < BUFFER_SIZE / LINE_SIZE; i++)
+ {
+ line = &ctx->buf[i * LINE_SIZE];
+ memset (line, '.', LINE_SIZE);
+ len = snprintf (label,
+ sizeof (label),
+ "block %3u line %3u ",
+ ctx->block,
+ i);
+ if ((0 < len) && (LINE_SIZE > (size_t) len))
+ memcpy (line, label, (size_t) len);
+ line[LINE_SIZE - 1] = '\n';
+ }
+ ctx->fill = BUFFER_SIZE;
+ ctx->off = 0;
+ ctx->block++;
+}
+
+
+/**
+ * Callback used by MHD to obtain the next piece of the response body.
+ *
+ * @param cls our `struct ResponseContext`
+ * @param pos number of bytes already returned for this response
+ * @param buf where to copy the data
+ * @param max maximum number of bytes to copy to @a buf
+ * @return number of bytes written to @a buf,
+ * MHD_CONTENT_READER_END_OF_STREAM for the regular end,
+ * MHD_CONTENT_READER_END_WITH_ERROR to abort the transfer
+ */
+static ssize_t
+content_reader (void *cls,
+ uint64_t pos,
+ char *buf,
+ size_t max)
+{
+ struct ResponseContext *ctx = cls;
+ size_t ready;
+
+ /* We generate a pure stream and never look back, so the position is
+ of no interest to us. MHD guarantees that it is the sum of all
+ non-negative values we returned so far. */
+ (void) pos; /* Unused. Silent compiler warning. */
+
+ if (ctx->off == ctx->fill)
+ {
+ /* Everything we had was passed on, produce the next block. */
+ if (BLOCK_COUNT == ctx->block)
+ return MHD_CONTENT_READER_END_OF_STREAM;
+ if ((0 != ctx->fail) && (ABORT_AFTER == ctx->block))
+ return MHD_CONTENT_READER_END_WITH_ERROR;
+ generate_block (ctx);
+ }
+ /* Hand over as much of the current block as MHD is willing to take;
+ the rest follows on the next invocation. */
+ ready = ctx->fill - ctx->off;
+ if (ready > max)
+ ready = max;
+ memcpy (buf, &ctx->buf[ctx->off], ready);
+ ctx->off += ready;
+ return (ssize_t) ready;
+}
+
+
+/**
+ * Release the resources of a `struct ResponseContext`. MHD calls this
+ * once the response is destroyed, which happens no matter whether the
+ * body was transmitted completely or the client went away early.
+ *
+ * @param cls our `struct ResponseContext`
+ */
+static void
+free_context (void *cls)
+{
+ struct ResponseContext *ctx = cls;
+
+ free (ctx->buf);
+ free (ctx);
+}
+
+
+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 ResponseContext *ctx;
+ struct MHD_Response *response;
+ enum MHD_Result ret;
+ uint64_t size;
+ (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. */
+
+ if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
+ return MHD_NO;
+
+ /* Allocate the state of this particular response before the response
+ object exists; from now on the free callback owns it. */
+ ctx = malloc (sizeof (struct ResponseContext));
+ if (NULL == ctx)
+ return MHD_NO;
+ ctx->buf = malloc (BUFFER_SIZE);
+ if (NULL == ctx->buf)
+ {
+ free (ctx);
+ return MHD_NO;
+ }
+ ctx->fill = 0;
+ ctx->off = 0;
+ ctx->block = 0;
+ ctx->fail = (0 == strcmp (url, "/error"));
+
+ /* "/sized" announces the length in advance, everything else is sent
+ with an unknown size and thus with chunked transfer encoding. */
+ if (0 == strcmp (url, "/sized"))
+ size = (uint64_t) BLOCK_COUNT * BUFFER_SIZE;
+ else
+ size = MHD_SIZE_UNKNOWN;
+
+ response = MHD_create_response_from_callback (size,
+ IO_BLOCK_SIZE,
+ &content_reader,
+ ctx,
+ &free_context);
+ if (NULL == response)
+ {
+ /* The response was never created, so nobody will call the free
+ callback for us. */
+ free_context (ctx);
+ return MHD_NO;
+ }
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ "text/plain");
+ 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
@@ -92,7 +92,9 @@ send_page (struct MHD_Connection *connection,
enum MHD_Result ret;
struct MHD_Response *response;
- response = MHD_create_response_from_buffer_static (strlen (page), page);
+ /* NOTE: we let MHD make its own copy of the page, as some of the
+ pages we serve live in a buffer on the stack of the caller. */
+ response = MHD_create_response_from_buffer_copy (strlen (page), page);
if (! response)
return MHD_NO;
if (MHD_YES !=
@@ -319,7 +321,7 @@ answer_to_connection (void *cls,
con_info->answercode);
}
- /* Note a GET or a POST, generate error */
+ /* Not a GET or a POST, generate error */
return send_page (connection,
errorpage,
MHD_HTTP_BAD_REQUEST);
diff --git a/doc/examples/responseheaders.c b/doc/examples/responseheaders.c
@@ -63,7 +63,7 @@ answer_to_connection (void *cls, struct MHD_Connection *connection,
return MHD_NO;
}
response =
- MHD_create_response_from_fd_at_offset64 ((size_t) sbuf.st_size,
+ MHD_create_response_from_fd_at_offset64 ((uint64_t) sbuf.st_size,
fd,
0);
if (MHD_YES !=
diff --git a/doc/examples/sessions.c b/doc/examples/sessions.c
@@ -449,9 +449,8 @@ not_found_page (const void *cls,
/* 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);
+ /* NOTE: headers must be added _before_ the response is queued,
+ MHD refuses to modify a response that was already queued. */
if (MHD_YES !=
MHD_add_response_header (response,
MHD_HTTP_HEADER_CONTENT_TYPE,
@@ -461,6 +460,9 @@ not_found_page (const void *cls,
"Failed to set content type header!\n");
/* return response without content type anyway ... */
}
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_FOUND,
+ response);
MHD_destroy_response (response);
return ret;
}
@@ -474,7 +476,7 @@ static const struct Page pages[] = {
{ "/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 */
+ { NULL, "text/html", ¬_found_page, NULL } /* 404 */
};
diff --git a/doc/examples/simplepost.c b/doc/examples/simplepost.c
@@ -39,7 +39,7 @@ static const char *askpage =
"</body></html>";
#define GREETINGPAGE \
- "<html><body><h1>Welcome, %s!</center></h1></body></html>"
+ "<html><body><h1>Welcome, %s!</h1></body></html>"
static const char *errorpage =
"<html><body>This doesn't seem to be right.</body></html>";
@@ -174,12 +174,12 @@ answer_to_connection (void *cls,
}
return MHD_YES;
}
- if (GET == con_info->connectiontype)
+ if (0 == strcmp (method, "GET"))
{
return send_page (connection,
askpage);
}
- if (POST == con_info->connectiontype)
+ if (0 == strcmp (method, "POST"))
{
if (0 != *upload_data_size)
{
diff --git a/doc/examples/upgrade.c b/doc/examples/upgrade.c
@@ -0,0 +1,458 @@
+/* Feel free to use this example code in any way
+ you see fit (Public Domain) */
+
+/**
+ * @file upgrade.c
+ * @brief Minimal example for the HTTP "Upgrade" API of libmicrohttpd.
+ * A "GET /" carrying "Connection: Upgrade" and "Upgrade: echo"
+ * is answered with "101 Switching Protocols"; afterwards the
+ * raw socket speaks a trivial line-based echo protocol until
+ * the client sends the line "QUIT" or closes the connection.
+ * @author Christian Grothoff
+ */
+
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#define PORT 8888
+
+/**
+ * Name of the protocol we are willing to switch to. Any name would
+ * do; "echo" is not registered with IANA and is only used here to keep
+ * the example self-contained.
+ */
+#define PROTOCOL "echo"
+
+/**
+ * Maximum length of a line of the echo protocol. Longer lines are
+ * truncated.
+ */
+#define LINE_SIZE 1024
+
+#define PAGE_UPGRADE_REQUIRED \
+ "This resource speaks the \"" PROTOCOL "\" protocol only.\n"
+
+#define PAGE_NOT_FOUND \
+ "Not found.\n"
+
+
+/**
+ * State of the line-based echo protocol for one connection.
+ */
+struct EchoState
+{
+ /**
+ * Bytes of the line received so far (without the line terminator).
+ */
+ char line[LINE_SIZE];
+
+ /**
+ * Number of valid bytes in @e line.
+ */
+ size_t line_len;
+};
+
+
+/**
+ * Compare the first @a len bytes of @a a and @a b, ignoring case.
+ * Only ASCII letters are folded, which is all HTTP tokens may contain.
+ *
+ * @param a first string
+ * @param b second string
+ * @param len number of bytes to compare
+ * @return 1 if the strings match, 0 if they do not
+ */
+static int
+equal_caseless (const char *a,
+ const char *b,
+ size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ {
+ char ca = a[i];
+ char cb = b[i];
+
+ if (('A' <= ca) && ('Z' >= ca))
+ ca = (char) (ca - 'A' + 'a');
+ if (('A' <= cb) && ('Z' >= cb))
+ cb = (char) (cb - 'A' + 'a');
+ if (ca != cb)
+ return 0;
+ }
+ return 1;
+}
+
+
+/**
+ * Check whether the comma-separated list @a value contains the token
+ * @a token. Both the "Connection:" and the "Upgrade:" request header
+ * fields are lists, so a client may legitimately send
+ * "Connection: keep-alive, Upgrade"; a plain string comparison against
+ * the whole field value would reject such a request.
+ *
+ * @param value the header field value, may be NULL
+ * @param token the token to look for, compared case-insensitively
+ * @return 1 if @a token is one of the tokens in @a value, 0 if not
+ */
+static int
+has_token (const char *value,
+ const char *token)
+{
+ const size_t token_len = strlen (token);
+ const char *pos = value;
+
+ if (NULL == value)
+ return 0;
+ while ('\0' != *pos)
+ {
+ const char *end;
+ size_t len;
+
+ /* skip separators and optional whitespace before the token */
+ while ((',' == *pos) || (' ' == *pos) || ('\t' == *pos))
+ pos++;
+ end = pos;
+ while (('\0' != *end) && (',' != *end))
+ end++;
+ len = (size_t) (end - pos);
+ /* strip optional whitespace after the token */
+ while ((0 != len) &&
+ ((' ' == pos[len - 1]) || ('\t' == pos[len - 1])))
+ len--;
+ if ((len == token_len) &&
+ (equal_caseless (pos, token, token_len)))
+ return 1;
+ pos = end;
+ if ('\0' != *pos)
+ pos++; /* skip the comma */
+ }
+ return 0;
+}
+
+
+/**
+ * Send @a len bytes from @a buf over @a sock, retrying until either
+ * everything was sent or the connection broke.
+ *
+ * @param sock the socket to write to
+ * @param buf the data to send
+ * @param len number of bytes in @a buf
+ * @return 1 on success, 0 if the connection broke
+ */
+static int
+send_all (MHD_socket sock,
+ const char *buf,
+ size_t len)
+{
+ ssize_t ret;
+ size_t off;
+
+ for (off = 0; off < len; off += (size_t) ret)
+ {
+ ret = send (sock,
+ buf + off,
+ (int) (len - off),
+ 0);
+ if (0 > ret)
+ {
+ if (EINTR == errno)
+ {
+ ret = 0;
+ continue;
+ }
+ return 0;
+ }
+ if (0 == ret)
+ return 0;
+ }
+ return 1;
+}
+
+
+/**
+ * Make @a sock blocking. MHD hands out the socket in non-blocking
+ * mode, see the tutorial chapter for the reason why. This function
+ * contains the only operating-system-dependent code of this example.
+ *
+ * @param sock the socket to modify
+ */
+static void
+make_blocking (MHD_socket sock)
+{
+#ifndef _WIN32
+ int flags;
+
+ flags = fcntl (sock, F_GETFL);
+ if (-1 == flags)
+ abort ();
+ if ((flags & ~O_NONBLOCK) != flags)
+ if (-1 == fcntl (sock, F_SETFL, flags & ~O_NONBLOCK))
+ abort ();
+#else /* _WIN32 */
+ unsigned long flags = 0;
+
+ if (0 != ioctlsocket (sock, (int) FIONBIO, &flags))
+ abort ();
+#endif /* _WIN32 */
+}
+
+
+/**
+ * Feed @a size bytes of received data into the echo protocol state
+ * machine, echoing back every complete line.
+ *
+ * @param sock the socket to echo to
+ * @param es the protocol state of this connection
+ * @param data the received bytes
+ * @param size number of bytes in @a data
+ * @return 1 if the client asked us to stop or the connection broke,
+ * 0 if we should keep going
+ */
+static int
+echo_feed (MHD_socket sock,
+ struct EchoState *es,
+ const char *data,
+ size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ {
+ if ('\n' != data[i])
+ {
+ if (es->line_len < sizeof (es->line))
+ es->line[es->line_len++] = data[i];
+ /* else: line too long, drop the excess bytes */
+ continue;
+ }
+ /* A complete line was received; drop the CR of a CRLF terminator */
+ if ((0 != es->line_len) &&
+ ('\r' == es->line[es->line_len - 1]))
+ es->line_len--;
+ if ((4 == es->line_len) &&
+ (equal_caseless (es->line, "QUIT", 4)))
+ {
+ (void) send_all (sock, "BYE\r\n", 5);
+ es->line_len = 0;
+ return 1;
+ }
+ if ((! send_all (sock, es->line, es->line_len)) ||
+ (! send_all (sock, "\r\n", 2)))
+ return 1;
+ es->line_len = 0;
+ }
+ return 0;
+}
+
+
+/**
+ * Called by MHD once the "101 Switching Protocols" response was sent.
+ * From here on MHD is out of the picture: @a sock is an ordinary
+ * socket and we are free to speak whatever protocol we like on it.
+ *
+ * As the daemon runs with #MHD_USE_THREAD_PER_CONNECTION, this
+ * function has a thread of its own and may block for as long as it
+ * wants.
+ *
+ * @param cls closure given to #MHD_create_response_for_upgrade()
+ * @param connection the original HTTP connection
+ * @param req_cls last value of @a req_cls of the access handler
+ * @param extra_in bytes the client sent after the request header and
+ * which MHD read together with the header
+ * @param extra_in_size number of bytes in @a extra_in
+ * @param sock the socket to talk to the client on
+ * @param urh handle to pass to #MHD_upgrade_action()
+ */
+static void
+upgrade_handler (void *cls,
+ struct MHD_Connection *connection,
+ void *req_cls,
+ const char *extra_in,
+ size_t extra_in_size,
+ MHD_socket sock,
+ struct MHD_UpgradeResponseHandle *urh)
+{
+ struct EchoState es;
+ char buf[256];
+ ssize_t got;
+ int done;
+
+ (void) cls; /* Unused. Silent compiler warning. */
+ (void) connection; /* Unused. Silent compiler warning. */
+ (void) req_cls; /* Unused. Silent compiler warning. */
+
+ /* MHD hands out a non-blocking socket; this example wants to use
+ plain blocking recv()/send() calls on it. */
+ make_blocking (sock);
+
+ es.line_len = 0;
+
+ /* The client may have pipelined payload directly behind the request
+ header, in which case MHD has read it already. Those bytes are
+ gone from the socket, so they must be processed first. */
+ done = echo_feed (sock,
+ &es,
+ extra_in,
+ extra_in_size);
+
+ while (0 == done)
+ {
+ got = recv (sock,
+ buf,
+ sizeof (buf),
+ 0);
+ if (0 > got)
+ {
+ if (EINTR == errno)
+ continue;
+ break; /* read error */
+ }
+ if (0 == got)
+ break; /* client closed the connection */
+ done = echo_feed (sock,
+ &es,
+ buf,
+ (size_t) got);
+ }
+
+ /* Hand the socket back to MHD. This is the only legal way to get
+ rid of it; never call close() on it ourselves, and note that the
+ connection is not cleaned up before this call happens. */
+ MHD_upgrade_action (urh,
+ MHD_UPGRADE_ACTION_CLOSE);
+}
+
+
+/**
+ * Answer an ordinary HTTP request from a static string.
+ *
+ * @param connection the connection to answer
+ * @param status_code the HTTP status code to use
+ * @param page the response body
+ * @param offer_upgrade if true, advertise our protocol in an
+ * "Upgrade:" header field
+ * @return #MHD_YES on success, #MHD_NO on error
+ */
+static enum MHD_Result
+answer_plain (struct MHD_Connection *connection,
+ unsigned int status_code,
+ const char *page,
+ int offer_upgrade)
+{
+ struct MHD_Response *response;
+ enum MHD_Result ret;
+
+ response = MHD_create_response_from_buffer_static (strlen (page),
+ page);
+ if (NULL == response)
+ return MHD_NO;
+ if (offer_upgrade)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_UPGRADE,
+ PROTOCOL);
+ ret = MHD_queue_response (connection,
+ status_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+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)
+{
+ struct MHD_Response *response;
+ enum MHD_Result ret;
+
+ (void) cls; /* 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 (url, "/"))
+ return answer_plain (connection,
+ MHD_HTTP_NOT_FOUND,
+ PAGE_NOT_FOUND,
+ 0);
+ /* Upgrading requires HTTP/1.1 and a request that actually asks for
+ our protocol. A client that does not ask gets a plain 426. */
+ if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) ||
+ (0 != strcmp (version, MHD_HTTP_VERSION_1_1)) ||
+ (! has_token (MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_CONNECTION),
+ "Upgrade")) ||
+ (! has_token (MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_UPGRADE),
+ PROTOCOL)))
+ return answer_plain (connection,
+ MHD_HTTP_UPGRADE_REQUIRED,
+ PAGE_UPGRADE_REQUIRED,
+ 1);
+
+ /* The request is fine, switch protocols. MHD already put
+ "Connection: Upgrade" into the response for us, we only have to
+ name the protocol we switch to. */
+ response = MHD_create_response_for_upgrade (&upgrade_handler,
+ NULL);
+ if (NULL == response)
+ return MHD_NO;
+ if (MHD_YES !=
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_UPGRADE,
+ PROTOCOL))
+ {
+ MHD_destroy_response (response);
+ return MHD_NO;
+ }
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_SWITCHING_PROTOCOLS,
+ response);
+ /* The response may be destroyed right away: MHD keeps its own
+ reference until the upgrade handler has been called. */
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+int
+main (void)
+{
+ struct MHD_Daemon *daemon;
+
+ daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
+ | MHD_USE_INTERNAL_POLLING_THREAD
+ | MHD_ALLOW_UPGRADE
+ | MHD_USE_ERROR_LOG,
+ PORT, NULL, NULL,
+ &access_handler, NULL,
+ MHD_OPTION_END);
+ if (NULL == daemon)
+ return 1;
+
+ (void) getchar ();
+
+ MHD_stop_daemon (daemon);
+ return 0;
+}
diff --git a/doc/examples/websocket.c b/doc/examples/websocket.c
@@ -1,449 +0,0 @@
-/* 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;
-}
diff --git a/doc/libmicrohttpd-tutorial.texi b/doc/libmicrohttpd-tutorial.texi
@@ -1,10 +1,10 @@
\input texinfo @c -*-texinfo-*-
@finalout
@setfilename libmicrohttpd-tutorial.info
-@set UPDATED 2 April 2016
-@set UPDATED-MONTH April 2016
-@set EDITION 0.9.48
-@set VERSION 0.9.48
+@set UPDATED 28 July 2026
+@set UPDATED-MONTH July 2026
+@set EDITION 1.0.8
+@set VERSION 1.0.8
@settitle A tutorial for GNU libmicrohttpd
@c Unify all the indices into concept index.
@syncodeindex fn cp
@@ -24,7 +24,7 @@ updated @value{UPDATED}.
Copyright (c) 2008 Sebastian Gerhardt.
-Copyright (c) 2010, 2011, 2012, 2013, 2016, 2021 Christian Grothoff.
+Copyright (c) 2010, 2011, 2012, 2013, 2016, 2021, 2026 Christian Grothoff.
@quotation
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
@@ -67,8 +67,9 @@ Free Documentation License".
* Processing POST data::
* Improved processing of POST data::
* Session management::
+* Generating responses on the fly::
* Adding a layer of security::
-* Websockets::
+* Upgrading connections::
* Bibliography::
* License text::
* Example programs::
@@ -106,13 +107,17 @@ Free Documentation License".
@chapter Session management
@include chapters/sessions.inc
+@node Generating responses on the fly
+@chapter Generating responses on the fly
+@include chapters/callbackresponse.inc
+
@node Adding a layer of security
@chapter Adding a layer of security
@include chapters/tlsauthentication.inc
-@node Websockets
-@chapter Websockets
-@include chapters/websocket.inc
+@node Upgrading connections
+@chapter Upgrading connections
+@include chapters/upgrade.inc
@node Bibliography
@appendix Bibliography
@@ -132,8 +137,9 @@ Free Documentation License".
* simplepost.c::
* largepost.c::
* sessions.c::
+* callbackresponse.c::
* tlsauthentication.c::
-* websocket.c::
+* upgrade.c::
@end menu
@node hellobrowser.c
@@ -178,16 +184,22 @@ Free Documentation License".
@verbatiminclude examples/sessions.c
@end smalldisplay
+@node callbackresponse.c
+@section callbackresponse.c
+@smalldisplay
+@verbatiminclude examples/callbackresponse.c
+@end smalldisplay
+
@node tlsauthentication.c
@section tlsauthentication.c
@smalldisplay
@verbatiminclude examples/tlsauthentication.c
@end smalldisplay
-@node websocket.c
-@section websocket.c
+@node upgrade.c
+@section upgrade.c
@smalldisplay
-@verbatiminclude examples/websocket.c
+@verbatiminclude examples/upgrade.c
@end smalldisplay
@bye
diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi
@@ -67,7 +67,6 @@ Free Documentation License".
* microhttpd-post:: Adding a @code{POST} processor.
* microhttpd-info:: Obtaining and modifying status information.
* microhttpd-util:: Utilities.
-* microhttpd-websocket:: Websockets.
Appendices
@@ -1246,493 +1245,6 @@ list.
@end deftp
-@deftp {Enumeration} MHD_WEBSOCKET_FLAG
-@cindex websocket
-Options for the MHD websocket stream.
-
-This is used for initialization of a websocket stream when calling
-@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2} and
-alters the behavior of the websocket stream.
-
-Note that websocket streams are only available if you include the header file
-@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
-
-@table @code
-@item MHD_WEBSOCKET_FLAG_SERVER
-The websocket stream is initialized in server mode (default).
-Thus all outgoing payload will not be masked.
-All incoming payload must be masked.
-
-This flag cannot be used together with @code{MHD_WEBSOCKET_FLAG_CLIENT}.
-
-@item MHD_WEBSOCKET_FLAG_CLIENT
-The websocket stream is initialized in client mode.
-You will usually never use that mode in combination with @emph{libmicrohttpd},
-because @emph{libmicrohttpd} provides a server and not a client.
-In client mode all outgoing payload will be masked
-(XOR-ed with random values).
-All incoming payload must be unmasked.
-If you use this mode, you must always call @code{MHD_websocket_stream_init2}
-instead of @code{MHD_websocket_stream_init}, because you need
-to pass a random number generator callback function for masking.
-
-This flag cannot be used together with @code{MHD_WEBSOCKET_FLAG_SERVER}.
-
-@item MHD_WEBSOCKET_FLAG_NO_FRAGMENTS
-You don't want to get fragmented data while decoding (default).
-Fragmented frames will be internally put together until
-they are complete.
-Whether or not data is fragmented is decided
-by the sender of the data during encoding.
-
-This cannot be used together with @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}.
-
-@item MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS
-You want fragmented data, if it appears while decoding.
-You will receive the content of the fragmented frame,
-but if you are decoding text, you will never get an unfinished
-UTF-8 sequence (if the sequence appears between two fragments).
-Instead the text will end before the unfinished UTF-8 sequence.
-With the next fragment, which finishes the UTF-8 sequence,
-you will get the complete UTF-8 sequence.
-
-This cannot be used together with @code{MHD_WEBSOCKET_FLAG_NO_FRAGMENTS}.
-
-@item MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR
-If the websocket stream becomes invalid during decoding due to
-protocol errors, a matching close frame will automatically
-be generated.
-The close frame will be returned via the parameters
-@code{payload} and @code{payload_len} of @code{MHD_websocket_decode} and
-the return value is negative (a value of @code{enum MHD_WEBSOCKET_STATUS}).
-
-The generated close frame must be freed by the caller
-with @code{MHD_websocket_free}.
-
-@end table
-@end deftp
-
-
-@deftp {Enumeration} MHD_WEBSOCKET_FRAGMENTATION
-@cindex websocket
-This enumeration is used to specify the fragmentation behavior
-when encoding of data (text/binary) for a websocket stream.
-This is used with @code{MHD_websocket_encode_text} or
-@code{MHD_websocket_encode_binary}.
-
-Note that websocket streams are only available if you include the header file
-@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
-
-@table @code
-@item MHD_WEBSOCKET_FRAGMENTATION_NONE
-You don't want to use fragmentation.
-The encoded frame consists of only one frame.
-
-@item MHD_WEBSOCKET_FRAGMENTATION_FIRST
-You want to use fragmentation.
-The encoded frame is the first frame of
-a series of data frames of the same type
-(text or binary).
-You may send control frames (ping, pong or close)
-between these data frames.
-
-@item MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING
-You want to use fragmentation.
-The encoded frame is not the first frame of
-the series of data frames, but also not the last one.
-You may send control frames (ping, pong or close)
-between these data frames.
-
-@item MHD_WEBSOCKET_FRAGMENTATION_LAST
-You want to use fragmentation.
-The encoded frame is the last frame of
-the series of data frames, but also not the first one.
-After this frame, you may send all types of frames again.
-
-@end table
-@end deftp
-
-
-@deftp {Enumeration} MHD_WEBSOCKET_STATUS
-@cindex websocket
-This enumeration is used for the return value of almost
-every websocket stream function.
-Errors are negative and values equal to or above zero mean a success.
-Positive values are only used by @code{MHD_websocket_decode}.
-
-Note that websocket streams are only available if you include the header file
-@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
-
-@table @code
-@item MHD_WEBSOCKET_STATUS_OK
-The call succeeded.
-Especially for @code{MHD_websocket_decode} this means that no error occurred,
-but also no frame has been completed yet.
-For other functions this means simply a success.
-
-@item MHD_WEBSOCKET_STATUS_TEXT_FRAME
-@code{MHD_websocket_decode} has decoded a text frame.
-The parameters @code{payload} and @code{payload_len} are filled with
-the decoded text (if any).
-You must free the returned @code{payload} after use with
-@code{MHD_websocket_free}.
-
-@item MHD_WEBSOCKET_STATUS_BINARY_FRAME
-@code{MHD_websocket_decode} has decoded a binary frame.
-The parameters @code{payload} and @code{payload_len} are filled with
-the decoded binary data (if any).
-You must free the returned @code{payload} after use with
-@code{MHD_websocket_free}.
-
-@item MHD_WEBSOCKET_STATUS_CLOSE_FRAME
-@code{MHD_websocket_decode} has decoded a close frame.
-This means you must close the socket using @code{MHD_upgrade_action}
-with @code{MHD_UPGRADE_ACTION_CLOSE}.
-You may respond with a close frame before closing.
-The parameters @code{payload} and @code{payload_len} are filled with
-the close reason (if any).
-The close reason starts with a two byte sequence of close code
-in network byte order (see @code{enum MHD_WEBSOCKET_CLOSEREASON}).
-After these two bytes a UTF-8 encoded close reason may follow.
-You can call @code{MHD_websocket_split_close_reason} to split that
-close reason.
-You must free the returned @code{payload} after use with
-@code{MHD_websocket_free}.
-
-@item MHD_WEBSOCKET_STATUS_PING_FRAME
-@code{MHD_websocket_decode} has decoded a ping frame.
-You should respond to this with a pong frame.
-The pong frame must contain the same binary data as
-the corresponding ping frame (if it had any).
-The parameters @code{payload} and @code{payload_len} are filled with
-the binary ping data (if any).
-You must free the returned @code{payload} after use with
-@code{MHD_websocket_free}.
-
-@item MHD_WEBSOCKET_STATUS_PONG_FRAME
-@code{MHD_websocket_decode} has decoded a pong frame.
-You should usually only receive pong frames if you sent
-a ping frame before.
-The binary data should be equal to your ping frame and can be
-used to distinguish the response if you sent multiple ping frames.
-The parameters @code{payload} and @code{payload_len} are filled with
-the binary pong data (if any).
-You must free the returned @code{payload} after use with
-@code{MHD_websocket_free}.
-
-@item MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT
-@code{MHD_websocket_decode} has decoded a text frame fragment.
-The parameters @code{payload} and @code{payload_len} are filled with
-the decoded text (if any).
-This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FRAME}, but it can only
-appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} during
-the call of @code{MHD_websocket_stream_init} or
-@code{MHD_websocket_stream_init2}.
-You must free the returned @code{payload} after use with
-@code{MHD_websocket_free}.
-
-@item MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT
-@code{MHD_websocket_decode} has decoded a binary frame fragment.
-The parameters @code{payload} and @code{payload_len} are filled with
-the decoded binary data (if any).
-This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FRAME}, but it can only
-appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS} during
-the call of @code{MHD_websocket_stream_init} or
-@code{MHD_websocket_stream_init2}.
-You must free the returned @code{payload} after use with
-@code{MHD_websocket_free}.
-
-@item MHD_WEBSOCKET_STATUS_TEXT_NEXT_FRAGMENT
-@code{MHD_websocket_decode} has decoded the next text frame fragment.
-The parameters @code{payload} and @code{payload_len} are filled with
-the decoded text (if any).
-This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT}, but it appears
-only after the first and before the last fragment of a series of fragments.
-It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
-during the call of @code{MHD_websocket_stream_init} or
-@code{MHD_websocket_stream_init2}.
-You must free the returned @code{payload} after use with
-@code{MHD_websocket_free}.
-
-@item MHD_WEBSOCKET_STATUS_BINARY_NEXT_FRAGMENT
-@code{MHD_websocket_decode} has decoded the next binary frame fragment.
-The parameters @code{payload} and @code{payload_len} are filled with
-the decoded binary data (if any).
-This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT}, but it appears
-only after the first and before the last fragment of a series of fragments.
-It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
-during the call of @code{MHD_websocket_stream_init} or
-@code{MHD_websocket_stream_init2}.
-You must free the returned @code{payload} after use with
-@code{MHD_websocket_free}.
-
-@item MHD_WEBSOCKET_STATUS_TEXT_LAST_FRAGMENT
-@code{MHD_websocket_decode} has decoded the last text frame fragment.
-The parameters @code{payload} and @code{payload_len} are filled with
-the decoded text (if any).
-This is like @code{MHD_WEBSOCKET_STATUS_TEXT_FIRST_FRAGMENT}, but it appears
-only for the last fragment of a series of fragments.
-It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
-during the call of @code{MHD_websocket_stream_init} or
-@code{MHD_websocket_stream_init2}.
-You must free the returned @code{payload} after use with
-@code{MHD_websocket_free}.
-
-@item MHD_WEBSOCKET_STATUS_BINARY_LAST_FRAGMENT
-@code{MHD_websocket_decode} has decoded the last binary frame fragment.
-The parameters @code{payload} and @code{payload_len} are filled with
-the decoded binary data (if any).
-This is like @code{MHD_WEBSOCKET_STATUS_BINARY_FIRST_FRAGMENT}, but it appears
-only for the last fragment of a series of fragments.
-It can only appear if you specified @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
-during the call of @code{MHD_websocket_stream_init} or
-@code{MHD_websocket_stream_init2}.
-You must free the returned @code{payload} after use with
-@code{MHD_websocket_free}.
-
-@item MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR
-The call failed and the stream is invalid now for decoding.
-You must close the websocket now using @code{MHD_upgrade_action}
-with @code{MHD_UPGRADE_ACTION_CLOSE}.
-You may send a close frame before closing.
-This is only used by @code{MHD_websocket_decode} and happens
-if the stream contains errors (i. e. invalid byte data).
-
-@item MHD_WEBSOCKET_STATUS_STREAM_BROKEN
-You tried to decode something, but the stream has already
-been marked invalid.
-You must close the websocket now using @code{MHD_upgrade_action}
-with @code{MHD_UPGRADE_ACTION_CLOSE}.
-You may send a close frame before closing.
-This is only used by @code{MHD_websocket_decode} and happens
-if you call @code{MDM_websocket_decode} again after
-has been invalidated.
-You can call @code{MHD_websocket_stream_is_valid} at any time
-to check whether a stream is invalid or not.
-
-@item MHD_WEBSOCKET_STATUS_MEMORY_ERROR
-A memory allocation failed. The stream remains valid.
-If this occurred while decoding, the decoding could be
-possible later if enough memory is available.
-This could happen while decoding if you received a too big data frame.
-You could try to specify max_payload_size during the call of
-@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2} to
-avoid this and close the websocket instead.
-
-@item MHD_WEBSOCKET_STATUS_PARAMETER_ERROR
-You passed invalid parameters during the function call
-(i. e. a NULL pointer for a required parameter).
-The stream remains valid.
-
-@item MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED
-The maximum payload size has been exceeded.
-If you got this return code from @code{MHD_websocket_decode} then
-the stream becomes invalid and the websocket must be closed
-using @code{MHD_upgrade_action} with @code{MHD_UPGRADE_ACTION_CLOSE}.
-You may send a close frame before closing.
-The maximum payload size is specified during the call of
-@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2}.
-This can also appear if you specified 0 as maximum payload size
-when the message is greater than the maximum allocatable memory size
-(i. e. more than 4 GiB on 32 bit systems).
-If you got this return code from @code{MHD_websocket_encode_close},
-@code{MHD_websocket_encode_ping} or @code{MHD_websocket_encode_pong} then
-you passed to much payload data. The stream remains valid then.
-
-@item MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR
-An UTF-8 sequence is invalid.
-If you got this return code from @code{MHD_websocket_decode} then
-the stream becomes invalid and you must close the websocket
-using @code{MHD_upgrade_action} with @code{MHD_UPGRADE_ACTION_CLOSE}.
-You may send a close frame before closing.
-If you got this from @code{MHD_websocket_encode_text} or
-@code{MHD_websocket_encode_close} then you passed invalid UTF-8 text.
-The stream remains valid then.
-
-@item MHD_WEBSOCKET_STATUS_NO_WEBSOCKET_HANDSHAKE_HEADER
-A check routine for the HTTP headers came to the conclusion that
-the header value isn't valid for a websocket handshake request.
-This value can only be returned from the following functions:
-@code{MHD_websocket_check_http_version},
-@code{MHD_websocket_check_connection_header},
-@code{MHD_websocket_check_upgrade_header},
-@code{MHD_websocket_check_version_header},
-@code{MHD_websocket_create_accept_header}
-
-@end table
-@end deftp
-
-
-@deftp {Enumeration} MHD_WEBSOCKET_CLOSEREASON
-@cindex websocket
-Enumeration of possible close reasons for websocket close frames.
-
-The possible values are specified in RFC 6455 7.4.1
-These close reasons here are the default set specified by RFC 6455,
-but also other close reasons could be used.
-
-The definition is for short:
-@itemize @bullet
-@item 0-999 are never used (if you pass 0 in
-@code{MHD_websocket_encode_close} then no close reason is used).
-@item 1000-2999 are specified by RFC 6455.
-@item 3000-3999 are specified by libraries, etc. but must be registered by IANA.
-@item 4000-4999 are reserved for private use.
-@end itemize
-
-Note that websocket streams are only available if you include the header file
-@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
-
-@table @code
-@item MHD_WEBSOCKET_CLOSEREASON_NO_REASON
-This value is used as placeholder for @code{MHD_websocket_encode_close}
-to tell that you don't want to specify any reason.
-If you use this value then no reason text may be used.
-This value cannot be a result of decoding, because this value
-is not a valid close reason for the websocket protocol.
-
-@item MHD_WEBSOCKET_CLOSEREASON_REGULAR
-You close the websocket because it fulfilled its purpose and shall
-now be closed in a normal, planned way.
-
-@item MHD_WEBSOCKET_CLOSEREASON_GOING_AWAY
-You close the websocket because you are shutting down the server or
-something similar.
-
-@item MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR
-You close the websocket because a protocol error occurred
-during decoding (i. e. invalid byte data).
-
-@item MHD_WEBSOCKET_CLOSEREASON_UNSUPPORTED_DATATYPE
-You close the websocket because you received data which you don't accept.
-For example if you received a binary frame,
-but your application only expects text frames.
-
-@item MHD_WEBSOCKET_CLOSEREASON_MALFORMED_UTF8
-You close the websocket because it contains malformed UTF-8.
-The UTF-8 validity is automatically checked by @code{MHD_websocket_decode},
-so you don't need to check it on your own.
-UTF-8 is specified in RFC 3629.
-
-@item MHD_WEBSOCKET_CLOSEREASON_POLICY_VIOLATED
-You close the websocket because you received a frame which is too big
-to process.
-You can specify the maximum allowed payload size during the call of
-@code{MHD_websocket_stream_init} or @code{MHD_websocket_stream_init2}.
-
-@item MHD_WEBSOCKET_CLOSEREASON_MISSING_EXTENSION
-This status code can be sent by the client if it
-expected a specific extension, but this extension hasn't been negotiated.
-
-@item MHD_WEBSOCKET_CLOSEREASON_UNEXPECTED_CONDITION
-The server closes the websocket because it encountered
-an unexpected condition that prevented it from fulfilling the request.
-
-@end table
-@end deftp
-
-
-@deftp {Enumeration} MHD_WEBSOCKET_UTF8STEP
-@cindex websocket
-Enumeration of possible UTF-8 check steps for websocket functions
-
-These values are used during the encoding of fragmented text frames
-or for error analysis while encoding text frames.
-Its values specify the next step of the UTF-8 check.
-UTF-8 sequences consist of one to four bytes.
-This enumeration just says how long the current UTF-8 sequence is
-and what is the next expected byte.
-
-Note that websocket streams are only available if you include the header file
-@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
-
-@table @code
-@item MHD_WEBSOCKET_UTF8STEP_NORMAL
-There is no open UTF-8 sequence.
-The next byte must be 0x00-0x7F or 0xC2-0xF4.
-
-@item MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1
-The second byte of a two byte UTF-8 sequence.
-The first byte was 0xC2-0xDF.
-The next byte must be 0x80-0xBF.
-
-@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2
-The second byte of a three byte UTF-8 sequence.
-The first byte was 0xE0.
-The next byte must be 0xA0-0xBF.
-
-@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2
-The second byte of a three byte UTF-8 sequence.
-The first byte was 0xED.
-The next byte must by 0x80-0x9F.
-
-@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2
-The second byte of a three byte UTF-8 sequence.
-The first byte was 0xE1-0xEC or 0xEE-0xEF.
-The next byte must be 0x80-0xBF.
-
-@item MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2
-The third byte of a three byte UTF-8 sequence.
-The next byte must be 0x80-0xBF.
-
-@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3
-The second byte of a four byte UTF-8 sequence.
-The first byte was 0xF0.
-The next byte must be 0x90-0xBF.
-
-@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3
-The second byte of a four byte UTF-8 sequence.
-The first byte was 0xF4.
-The next byte must be 0x80-0x8F.
-
-@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3
-The second byte of a four byte UTF-8 sequence.
-The first byte was 0xF1-0xF3.
-The next byte must be 0x80-0xBF.
-
-@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3
-The third byte of a four byte UTF-8 sequence.
-The next byte must be 0x80-0xBF.
-
-@item MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3
-The fourth byte of a four byte UTF-8 sequence.
-The next byte must be 0x80-0xBF.
-
-@end table
-@end deftp
-
-
-@deftp {Enumeration} MHD_WEBSOCKET_VALIDITY
-@cindex websocket
-Enumeration of validity values of a websocket stream
-
-These values are used for @code{MHD_websocket_stream_is_valid}
-and specify the validity status.
-
-Note that websocket streams are only available if you include the header file
-@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
-
-@table @code
-@item MHD_WEBSOCKET_VALIDITY_INVALID
-The stream is invalid.
-It cannot be used for decoding anymore.
-
-@item MHD_WEBSOCKET_VALIDITY_VALID
-The stream is valid.
-Decoding works as expected.
-
-@item MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES
-The stream has received a close frame and
-is partly invalid.
-You can still use the stream for decoding,
-but if a data frame is received an error will be reported.
-After a close frame has been sent, no data frames
-may follow from the sender of the close frame.
-
-@end table
-@end deftp
-
-
@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@c ------------------------------------------------------------
@@ -1778,11 +1290,6 @@ Information about an MHD daemon.
@end deftp
-@deftp {C Struct} MHD_WebSocketStream
-@cindex websocket
-Information about a MHD websocket stream.
-@end deftp
-
@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@@ -2042,95 +1549,6 @@ iteration.
@end deftypefn
-@deftypefn {Function Pointer} void* {*MHD_WebSocketMallocCallback} (size_t buf_len)
-@cindex websocket
-This callback function is used internally by many websocket functions
-for allocating data.
-By default @code{malloc} is used.
-You can use your own allocation function with @code{MHD_websocket_stream_init2}
-if you wish to.
-This can be useful for operating systems like Windows
-where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
-You can call the associated @code{malloc} callback of
-a websocket stream with @code{MHD_websocket_malloc}.
-
-@table @var
-@item buf_len
-size of the buffer to allocate in bytes.
-@end table
-
-Return the pointer of the allocated buffer or @code{NULL} on failure.
-@end deftypefn
-
-
-@deftypefn {Function Pointer} void* {*MHD_WebSocketReallocCallback} (void *buf, size_t new_buf_len)
-@cindex websocket
-This callback function is used internally by many websocket
-functions for reallocating data.
-By default @code{realloc} is used.
-You can use your own reallocation function with
-@code{MHD_websocket_stream_init2} if you wish to.
-This can be useful for operating systems like Windows
-where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
-You can call the associated @code{realloc} callback of
-a websocket stream with @code{MHD_websocket_realloc}.
-
-@table @var
-@item buf
-current buffer, may be @code{NULL};
-
-@item new_buf_len
-new size of the buffer in bytes.
-@end table
-
-Return the pointer of the reallocated buffer or @code{NULL} on failure.
-On failure the old pointer must remain valid.
-@end deftypefn
-
-
-@deftypefn {Function Pointer} void {*MHD_WebSocketFreeCallback} (void *buf)
-@cindex websocket
-This callback function is used internally by many websocket
-functions for freeing data.
-By default @code{free} is used.
-You can use your own free function with
-@code{MHD_websocket_stream_init2} if you wish to.
-This can be useful for operating systems like Windows
-where @code{malloc}, @code{realloc} and @code{free} are compiler-dependent.
-You can call the associated @code{free} callback of
-a websocket stream with @code{MHD_websocket_free}.
-
-@table @var
-@item cls
-current buffer to free, this may be @code{NULL} then nothing happens.
-@end table
-@end deftypefn
-
-
-@deftypefn {Function Pointer} size_t {*MHD_WebSocketRandomNumberGenerator} (void *cls, void* buf, size_t buf_len)
-@cindex websocket
-This callback function is used for generating random numbers
-for masking payload data in client mode.
-If you use websockets in server mode with @emph{libmicrohttpd} then
-you don't need a random number generator, because
-the server doesn't mask its outgoing messages.
-However if you wish to use a websocket stream in client mode,
-you must pass this callback function to @code{MHD_websocket_stream_init2}.
-
-@table @var
-@item cls
-closure specified in @code{MHD_websocket_stream_init2};
-@item buf
-buffer to fill with random values;
-@item buf_len
-size of buffer in bytes.
-@end table
-
-Return the number of generated random bytes.
-The return value should usually equal to buf_len.
-@end deftypefn
-
-
@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
@c ------------------------------------------------------------
@@ -4020,703 +3438,6 @@ shorter afterwards due to elimination of escape sequences).
@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-@c ------------------------------------------------------------
-@node microhttpd-websocket
-@chapter Websocket functions.
-
-@noindent
-Websocket functions provide what you need to use an upgraded connection
-as a websocket.
-These functions are only available if you include the header file
-@code{microhttpd_ws.h} and compiled @emph{libmicrohttpd} with websockets.
-
-@menu
-* microhttpd-websocket handshake:: Websocket handshake functions
-* microhttpd-websocket stream:: Websocket stream functions
-* microhttpd-websocket decode:: Websocket decode functions
-* microhttpd-websocket encode:: Websocket encode functions
-* microhttpd-websocket memory:: Websocket memory functions
-@end menu
-
-@c ------------------------------------------------------------
-@node microhttpd-websocket handshake
-@section Websocket handshake functions
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_http_version (const char* http_version)
-@cindex websocket
-Checks the HTTP version of the incoming request.
-Websocket requests are only allowed for HTTP/1.1 or above.
-
-@table @var
-@item http_version
-The value of the @code{version} parameter of your
-@code{access_handler} callback.
-If you pass @code{NULL} then this is handled like a not
-matching HTTP version.
-@end table
-
-Returns 0 when the HTTP version is
-valid for a websocket request and
-a value less than zero when the HTTP version isn't
-valid for a websocket request.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_connection_header (const char* connection_header)
-@cindex websocket
-Checks the value of the @code{Connection} HTTP request header.
-Websocket requests require the token @code{Upgrade} in
-the @code{Connection} HTTP request header.
-
-@table @var
-@item connection_header
-Value of the @code{Connection} request header.
-You can get this request header value by passing
-@code{MHD_HTTP_HEADER_CONNECTION} to
-@code{MHD_lookup_connection_value()}.
-If you pass @code{NULL} then this is handled like a not
-matching @code{Connection} header value.
-@end table
-
-Returns 0 when the @code{Connection} header is
-valid for a websocket request and
-a value less than zero when the @code{Connection} header isn't
-valid for a websocket request.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_upgrade_header (const char* upgrade_header)
-@cindex websocket
-Checks the value of the @code{Upgrade} HTTP request header.
-Websocket requests require the value @code{websocket} in
-the @code{Upgrade} HTTP request header.
-
-@table @var
-@item upgrade_header
-Value of the @code{Upgrade} request header.
-You can get this request header value by passing
-@code{MHD_HTTP_HEADER_UPGRADE} to
-@code{MHD_lookup_connection_value()}.
-If you pass @code{NULL} then this is handled like a not
-matching @code{Upgrade} header value.
-@end table
-
-Returns 0 when the @code{Upgrade} header is
-valid for a websocket request and
-a value less than zero when the @code{Upgrade} header isn't
-valid for a websocket request.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_check_version_header (const char* version_header)
-@cindex websocket
-Checks the value of the @code{Sec-WebSocket-Version} HTTP request header.
-Websocket requests require the value @code{13} in
-the @code{Sec-WebSocket-Version} HTTP request header.
-
-@table @var
-@item version_header
-Value of the @code{Sec-WebSocket-Version} request header.
-You can get this request header value by passing
-@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_VERSION} to
-@code{MHD_lookup_connection_value()}.
-If you pass @code{NULL} then this is handled like a not
-matching @code{Sec-WebSocket-Version} header value.
-@end table
-
-Returns 0 when the @code{Sec-WebSocket-Version} header is
-valid for a websocket request and
-a value less than zero when the @code{Sec-WebSocket-Version} header isn't
-valid for a websocket request.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_create_accept_header (const char* sec_websocket_key, char* sec_websocket_accept)
-@cindex websocket
-Checks the value of the @code{Sec-WebSocket-Key}
-HTTP request header and generates the value for
-the @code{Sec-WebSocket-Accept} HTTP response header.
-The generated value must be sent to the client.
-
-@table @var
-@item sec_websocket_key
-Value of the @code{Sec-WebSocket-Key} request header.
-You can get this request header value by passing
-@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_KEY} to
-@code{MHD_lookup_connection_value()}.
-If you pass @code{NULL} then this is handled like a not
-matching @code{Sec-WebSocket-Key} header value.
-
-@item sec_websocket_accept
-Response buffer, which will receive
-the generated value for the @code{Sec-WebSocket-Accept}
-HTTP response header.
-This buffer must be at least 29 bytes long and
-will contain the response value plus a terminating @code{NUL}
-character on success.
-Must not be @code{NULL}.
-You can add this HTTP header to your response by passing
-@code{MHD_HTTP_HEADER_SEC_WEBSOCKET_ACCEPT} to
-@code{MHD_add_response_header()}.
-@end table
-
-Returns 0 when the @code{Sec-WebSocket-Key} header was
-not empty and a result value for the @code{Sec-WebSocket-Accept}
-was calculated.
-A value less than zero is returned when the @code{Sec-WebSocket-Key}
-header isn't valid for a websocket request or when any
-error occurred.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@c ------------------------------------------------------------
-@node microhttpd-websocket stream
-@section Websocket stream functions
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_init (struct MHD_WebSocketStream **ws, int flags, size_t max_payload_size)
-@cindex websocket
-Creates a new websocket stream, used for decoding/encoding.
-
-@table @var
-@item ws
-pointer a variable to fill with the newly created
-@code{struct MHD_WebSocketStream},
-receives @code{NULL} on error. May not be @code{NULL}.
-
-If not required anymore, free the created websocket stream with
-@code{MHD_websocket_stream_free()}.
-
-@item flags
-combination of @code{enum MHD_WEBSOCKET_FLAG} values to
-modify the behavior of the websocket stream.
-
-@item max_payload_size
-maximum size for incoming payload data in bytes. Use 0 to allow each size.
-@end table
-
-Returns 0 on success, negative values on error.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_init2 (struct MHD_WebSocketStream **ws, int flags, size_t max_payload_size, MHD_WebSocketMallocCallback callback_malloc, MHD_WebSocketReallocCallback callback_realloc, MHD_WebSocketFreeCallback callback_free, void* cls_rng, MHD_WebSocketRandomNumberGenerator callback_rng)
-@cindex websocket
-Creates a new websocket stream, used for decoding/encoding,
-but with custom memory functions for malloc, realloc and free.
-Also a random number generator can be specified for client mode.
-
-@table @var
-@item ws
-pointer a variable to fill with the newly created
-@code{struct MHD_WebSocketStream},
-receives @code{NULL} on error. Must not be @code{NULL}.
-
-If not required anymore, free the created websocket stream with
-@code{MHD_websocket_stream_free}.
-
-@item flags
-combination of @code{enum MHD_WEBSOCKET_FLAG} values to
-modify the behavior of the websocket stream.
-
-@item max_payload_size
-maximum size for incoming payload data in bytes. Use 0 to allow each size.
-
-@item callback_malloc
-callback function for allocating memory. Must not be @code{NULL}.
-The shorter @code{MHD_websocket_stream_init()} passes a reference to @code{malloc} here.
-
-@item callback_realloc
-callback function for reallocating memory. Must not be @code{NULL}.
-The shorter @code{MHD_websocket_stream_init()} passes a reference to @code{realloc} here.
-
-@item callback_free
-callback function for freeing memory. Must not be @code{NULL}.
-The shorter @code{MHD_websocket_stream_init()} passes a reference to @code{free} here.
-
-@item cls_rng
-closure for the random number generator.
-This is only required when
-@code{MHD_WEBSOCKET_FLAG_CLIENT} is passed in @code{flags}.
-The given value is passed to the random number generator callback.
-May be @code{NULL} if not needed.
-Should be @code{NULL} when you are not using @code{MHD_WEBSOCKET_FLAG_CLIENT}.
-The shorter @code{MHD_websocket_stream_init} passes @code{NULL} here.
-
-@item callback_rng
-callback function for a secure random number generator.
-This is only required when @code{MHD_WEBSOCKET_FLAG_CLIENT} is
-passed in @code{flags} and must not be @code{NULL} then.
-Should be @code{NULL} otherwise.
-The shorter @code{MHD_websocket_stream_init()} passes @code{NULL} here.
-@end table
-
-Returns 0 on success, negative values on error.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_free (struct MHD_WebSocketStream *ws)
-@cindex websocket
-Frees a previously allocated websocket stream
-
-@table @var
-@item ws
-websocket stream to free, this value may be @code{NULL}.
-@end table
-
-Returns 0 on success, negative values on error.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_stream_invalidate (struct MHD_WebSocketStream *ws)
-@cindex websocket
-Invalidates a websocket stream.
-After invalidation a websocket stream cannot be used for decoding anymore.
-Encoding is still possible.
-
-@table @var
-@item ws
-websocket stream to invalidate.
-@end table
-
-Returns 0 on success, negative values on error.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_VALIDITY} MHD_websocket_stream_is_valid (struct MHD_WebSocketStream *ws)
-@cindex websocket
-Queries whether a websocket stream is valid.
-Invalidated websocket streams cannot be used for decoding anymore.
-Encoding is still possible.
-
-@table @var
-@item ws
-websocket stream to invalidate.
-@end table
-
-Returns 0 if invalid, 1 if valid for all types or
-2 if valid only for control frames.
-Can be compared with @code{enum MHD_WEBSOCKET_VALIDITY}.
-@end deftypefun
-
-
-@c ------------------------------------------------------------
-@node microhttpd-websocket decode
-@section Websocket decode functions
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_decode (struct MHD_WebSocketStream* ws, const char* streambuf, size_t streambuf_len, size_t* streambuf_read_len, char** payload, size_t* payload_len)
-@cindex websocket
-Decodes a byte sequence for a websocket stream.
-Decoding is done until either a frame is complete or
-the end of the byte sequence is reached.
-
-@table @var
-@item ws
-websocket stream for decoding.
-
-@item streambuf
-byte sequence for decoding.
-This is what you typically received via @code{recv()}.
-
-@item streambuf_len
-length of the byte sequence in parameter @code{streambuf}.
-
-@item streambuf_read_len
-pointer to a variable, which receives the number of bytes,
-that has been processed by this call.
-This value may be less than the value of @code{streambuf_len} when
-a frame is decoded before the end of the buffer is reached.
-The remaining bytes of @code{buf} must be passed to
-the next call of this function.
-
-@item payload
-pointer to a variable, which receives the allocated buffer with the payload
-data of the decoded frame. Must not be @code{NULL}.
-If no decoded data is available or an error occurred @code{NULL} is returned.
-When the returned value is not @code{NULL} then the buffer contains always
-@code{payload_len} bytes plus one terminating @code{NUL} character
-(regardless of the frame type).
-
-The caller must free this buffer using @code{MHD_websocket_free()}.
-
-If you passed the flag @code{MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR}
-upon creation of the websocket stream and a decoding error occurred
-(function return value less than 0), then this buffer contains
-a generated close frame, which must be sent via the socket to the recipient.
-
-If you passed the flag @code{MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS}
-upon creation of the websocket stream then
-this payload may only be a part of the complete message.
-Only complete UTF-8 sequences are returned for fragmented text frames.
-If necessary the UTF-8 sequence will be completed with the next text fragment.
-
-@item payload_len
-pointer to a variable, which receives length of the result
-@code{payload} buffer in bytes.
-Must not be @code{NULL}.
-This receives 0 when no data is available, when the decoded payload
-has a length of zero or when an error occurred.
-@end table
-
-Returns a value greater than zero when a frame is complete.
-Compare with @code{enum MHD_WEBSOCKET_STATUS} to distinguish the frame type.
-Returns 0 when the call succeeded, but no frame is available.
-Returns a value less than zero on errors.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_split_close_reason (const char* payload, size_t payload_len, unsigned short* reason_code, const char** reason_utf8, size_t* reason_utf8_len)
-@cindex websocket
-Splits the payload of a decoded close frame.
-
-@table @var
-@item payload
-payload of the close frame.
-This parameter may only be @code{NULL} if @code{payload_len} is 0.
-
-@item payload_len
-length of @code{payload}.
-
-@item reason_code
-pointer to a variable, which receives the numeric close reason.
-If there was no close reason, this is 0.
-This value can be compared with @code{enum MHD_WEBSOCKET_CLOSEREASON}.
-May be @code{NULL}.
-
-@item reason_utf8
-pointer to a variable, which receives the literal close reason.
-If there was no literal close reason, this will be @code{NULL}.
-May be @code{NULL}.
-
-Please note that no memory is allocated in this function.
-If not @code{NULL} the returned value of this parameter
-points to a position in the specified @code{payload}.
-
-@item reason_utf8_len
-pointer to a variable, which receives the length of the literal close reason.
-If there was no literal close reason, this is 0.
-May be @code{NULL}.
-@end table
-
-Returns 0 on success or a value less than zero on errors.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@c ------------------------------------------------------------
-@node microhttpd-websocket encode
-@section Websocket encode functions
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_text (struct MHD_WebSocketStream* ws, const char* payload_utf8, size_t payload_utf8_len, int fragmentation, char** frame, size_t* frame_len, int* utf8_step)
-@cindex websocket
-Encodes an UTF-8 encoded text into websocket text frame
-
-@table @var
-@item ws
-websocket stream;
-
-@item payload_utf8
-text to send. This must be UTF-8 encoded.
-If you don't want UTF-8 then send a binary frame
-with @code{MHD_websocket_encode_binary()} instead.
-May be be @code{NULL} if @code{payload_utf8_len} is 0,
-must not be @code{NULL} otherwise.
-
-@item payload_utf8_len
-length of @code{payload_utf8} in bytes.
-
-@item fragmentation
-A value of @code{enum MHD_WEBSOCKET_FRAGMENTATION}
-to specify the fragmentation behavior.
-Specify @code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or just 0
-if you don't want to use fragmentation (default).
-
-@item frame
-pointer to a variable, which receives a buffer with the encoded text frame.
-Must not be @code{NULL}.
-The buffer contains what you typically send via @code{send()} to the recipient.
-If no encoded data is available the variable receives @code{NULL}.
-
-If the variable is not @code{NULL} then the buffer contains always
-@code{frame_len} bytes plus one terminating @code{NUL} character.
-The caller must free this buffer using @code{MHD_websocket_free()}.
-
-@item frame_len
-pointer to a variable, which receives the length of the encoded frame in bytes.
-Must not be @code{NULL}.
-
-@item utf8_step
-If fragmentation is used (the parameter @code{fragmentation} is not 0)
-then is parameter is required and must not be @code{NULL}.
-If no fragmentation is used, this parameter is optional and
-should be @code{NULL}.
-
-This parameter is a pointer to a variable which contains the last check status
-of the UTF-8 sequence. It is required to continue a previous
-UTF-8 sequence check when fragmentation is used, because a UTF-8 sequence
-could be split upon fragments.
-
-@code{enum MHD_WEBSOCKET_UTF8STEP} is used for this value.
-If you start a new fragment using
-@code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or
-@code{MHD_WEBSOCKET_FRAGMENTATION_FIRST} the old value of this variable
-will be discarded and the value of this variable will be initialized
-to @code{MHD_WEBSOCKET_UTF8STEP_NORMAL}.
-On all other fragmentation modes the previous value of the pointed variable
-will be used to continue the UTF-8 sequence check.
-@end table
-
-Returns 0 on success or a value less than zero on errors.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_binary (struct MHD_WebSocketStream* ws, const char* payload, size_t payload_len, int fragmentation, char** frame, size_t* frame_len)
-@cindex websocket
-Encodes binary data into websocket binary frame
-
-@table @var
-@item ws
-websocket stream;
-
-@item payload
-binary data to send.
-May be be @code{NULL} if @code{payload_len} is 0,
-must not be @code{NULL} otherwise.
-
-@item payload_len
-length of @code{payload} in bytes.
-
-@item fragmentation
-A value of @code{enum MHD_WEBSOCKET_FRAGMENTATION}
-to specify the fragmentation behavior.
-Specify @code{MHD_WEBSOCKET_FRAGMENTATION_NONE} or just 0
-if you don't want to use fragmentation (default).
-
-@item frame
-pointer to a variable, which receives a buffer with the encoded binary frame.
-Must not be @code{NULL}.
-The buffer contains what you typically send via @code{send()} to the recipient.
-If no encoded data is available the variable receives @code{NULL}.
-
-If the variable is not @code{NULL} then the buffer contains always
-@code{frame_len} bytes plus one terminating @code{NUL} character.
-The caller must free this buffer using @code{MHD_websocket_free()}.
-
-@item frame_len
-pointer to a variable, which receives the length of the encoded frame in bytes.
-Must not be @code{NULL}.
-@end table
-
-Returns 0 on success or a value less than zero on errors.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_ping (struct MHD_WebSocketStream* ws, const char* payload, size_t payload_len, char** frame, size_t* frame_len)
-@cindex websocket
-Encodes a websocket ping frame.
-Ping frames are used to check whether a recipient is still available
-and what latency the websocket connection has.
-
-@table @var
-@item ws
-websocket stream;
-
-@item payload
-binary ping data to send.
-May be @code{NULL} if @code{payload_len} is 0.
-
-@item payload_len
-length of @code{payload} in bytes.
-This may not exceed 125 bytes.
-
-@item frame
-pointer to a variable, which receives a buffer with the encoded ping frame.
-Must not be @code{NULL}.
-The buffer contains what you typically send via @code{send()} to the recipient.
-If no encoded data is available the variable receives @code{NULL}.
-
-If the variable is not @code{NULL} then the buffer contains always
-@code{frame_len} bytes plus one terminating @code{NUL} character.
-The caller must free this buffer using @code{MHD_websocket_free()}.
-
-@item frame_len
-pointer to a variable, which receives the length of the encoded frame in bytes.
-Must not be @code{NULL}.
-@end table
-
-Returns 0 on success or a value less than zero on errors.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_pong (struct MHD_WebSocketStream* ws, const char* payload, size_t payload_len, char** frame, size_t* frame_len)
-@cindex websocket
-Encodes a websocket pong frame.
-Pong frames are used to answer a previously received websocket ping frame.
-
-@table @var
-@item ws
-websocket stream;
-
-@item payload
-binary pong data to send, which should be
-the decoded payload from the received ping frame.
-May be @code{NULL} if @code{payload_len} is 0.
-
-@item payload_len
-length of @code{payload} in bytes.
-This may not exceed 125 bytes.
-
-@item frame
-pointer to a variable, which receives a buffer with the encoded pong frame.
-Must not be @code{NULL}.
-The buffer contains what you typically send via @code{send()} to the recipient.
-If no encoded data is available the variable receives @code{NULL}.
-
-If the variable is not @code{NULL} then the buffer contains always
-@code{frame_len} bytes plus one terminating @code{NUL} character.
-The caller must free this buffer using @code{MHD_websocket_free()}.
-
-@item frame_len
-pointer to a variable, which receives the length of the encoded frame in bytes.
-Must not be @code{NULL}.
-@end table
-
-Returns 0 on success or a value less than zero on errors.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@deftypefun {enum MHD_WEBSOCKET_STATUS} MHD_websocket_encode_close (struct MHD_WebSocketStream* ws, unsigned short reason_code, const char* reason_utf8, size_t reason_utf8_len, char** frame, size_t* frame_len)
-@cindex websocket
-Encodes a websocket close frame.
-Close frames are used to close a websocket connection in a formal way.
-
-@table @var
-@item ws
-websocket stream;
-
-@item reason_code
-reason for close.
-You can use @code{enum MHD_WEBSOCKET_CLOSEREASON} for typical reasons,
-but you are not limited to these values.
-The allowed values are specified in RFC 6455 7.4.
-If you don't want to enter a reason, you can specify
-@code{MHD_WEBSOCKET_CLOSEREASON_NO_REASON} (or just 0) then
-no reason is encoded.
-
-@item reason_utf8
-An UTF-8 encoded text reason why the connection is closed.
-This may be @code{NULL} if @code{reason_utf8_len} is 0.
-This must be @code{NULL} if @code{reason_code} equals to zero
-(@code{MHD_WEBSOCKET_CLOSEREASON_NO_REASON}).
-
-@item reason_utf8_len
-length of the UTF-8 encoded text reason in bytes.
-This may not exceed 123 bytes.
-
-@item frame
-pointer to a variable, which receives a buffer with the encoded close frame.
-Must not be @code{NULL}.
-The buffer contains what you typically send via @code{send()} to the recipient.
-If no encoded data is available the variable receives @code{NULL}.
-
-If the variable is not @code{NULL} then the buffer contains always
-@code{frame_len} bytes plus one terminating @code{NUL} character.
-The caller must free this buffer using @code{MHD_websocket_free()}.
-
-@item frame_len
-pointer to a variable, which receives the length of the encoded frame in bytes.
-Must not be @code{NULL}.
-@end table
-
-Returns 0 on success or a value less than zero on errors.
-Can be compared with @code{enum MHD_WEBSOCKET_STATUS}.
-@end deftypefun
-
-
-@c ------------------------------------------------------------
-@node microhttpd-websocket memory
-@section Websocket memory functions
-
-
-@deftypefun {void*} MHD_websocket_malloc (struct MHD_WebSocketStream* ws, size_t buf_len)
-@cindex websocket
-Allocates memory with the associated @code{malloc()} function
-of the websocket stream.
-The memory allocation function could be different for a websocket stream if
-@code{MHD_websocket_stream_init2()} has been used for initialization.
-
-@table @var
-@item ws
-websocket stream;
-
-@item buf_len
-size of the buffer to allocate in bytes.
-@end table
-
-Returns the pointer of the allocated buffer or @code{NULL} on failure.
-@end deftypefun
-
-
-@deftypefun {void*} MHD_websocket_realloc (struct MHD_WebSocketStream* ws, void* buf, size_t new_buf_len)
-@cindex websocket
-Reallocates memory with the associated @code{realloc()} function
-of the websocket stream.
-The memory reallocation function could be different for a websocket stream if
-@code{MHD_websocket_stream_init2()} has been used for initialization.
-
-@table @var
-@item ws
-websocket stream;
-
-@item buf
-current buffer, may be @code{NULL};
-
-@item new_buf_len
-new size of the buffer in bytes.
-@end table
-
-Return the pointer of the reallocated buffer or @code{NULL} on failure.
-On failure the old pointer remains valid.
-@end deftypefun
-
-
-@deftypefun {void} MHD_websocket_free (struct MHD_WebSocketStream* ws, void* buf)
-@cindex websocket
-Frees memory with the associated @code{free()} function
-of the websocket stream.
-The memory free function could be different for a websocket stream if
-@code{MHD_websocket_stream_init2()} has been used for initialization.
-
-@table @var
-@item ws
-websocket stream;
-
-@item buf
-buffer to free, this may be @code{NULL} then nothing happens.
-@end table
-
-@end deftypefun
-
-
-
-
-
-@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-
@c **********************************************************
@c ******************* Appendices *************************