/* Feel free to use this example code in any way you see fit (Public Domain) */ #include #ifndef _WIN32 #include #include #include #else #include #endif #include #include #include #include #include #include #include #define PORT 80 #define PAGE \ "\n" \ "\n" \ "\n" \ "\n" \ "Websocket Demo\n" \ "\n" \ "\n" \ "\n" \ "\n" \ "" #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; }