mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
454 lines
13 KiB
C
454 lines
13 KiB
C
/* 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_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;
|
|
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;
|
|
}
|