add tutorials for #4944 and #5085

This commit is contained in:
Christian Grothoff
2026-07-28 19:46:21 +02:00
parent 56713e94bb
commit 7d8c068e5e
24 changed files with 1905 additions and 2829 deletions
+2 -1
View File
@@ -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) \
+17 -14
View File
@@ -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
{
+10 -9
View File
@@ -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
+467
View File
@@ -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
+14 -12
View File
@@ -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
+37 -24
View File
@@ -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
+77 -44
View File
@@ -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)
+17 -14
View File
@@ -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;
+10 -35
View File
@@ -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
+15 -11
View File
@@ -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.
+19 -16
View File
@@ -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.
+422
View File
@@ -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
-886
View File
@@ -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
+2
View File
@@ -1,3 +1,4 @@
/upgrade
/tlsauthentication
/simplepost
/sessions
@@ -5,6 +6,7 @@
/logging
/largepost
/hellobrowser
/callbackresponse
/basicauthentication
*.exe
*.o
+18 -12
View File
@@ -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
+281
View File
@@ -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;
}
+4 -2
View File
@@ -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);
+1 -1
View File
@@ -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 !=
+6 -4
View File
@@ -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, &not_found_page, NULL } /* 404 */
{ NULL, "text/html", &not_found_page, NULL } /* 404 */
};
+3 -3
View File
@@ -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)
{
+458
View File
@@ -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;
}
-449
View File
@@ -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;
}
+25 -13
View File
@@ -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
-1279
View File
@@ -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 ------------------------------------------------------------
@@ -4018,703 +3436,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 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++