123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- /* 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;
- }
|