/* * Copyright (C) NGINX, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #define CONTENT_TYPE "Content-Type" #define CONTENT_LENGTH "Content-Length" #define TEXT_HTML "text/html" typedef struct { nxt_queue_link_t link; int id; } ws_chat_request_data_t; static int ws_chat_root(nxt_unit_request_info_t *req); static void ws_chat_broadcast(const char *buf, size_t size); static const char ws_chat_index_html[]; static const int ws_chat_index_html_size; static char ws_chat_index_content_length[34]; static int ws_chat_index_content_length_size; static nxt_queue_t ws_chat_sessions; static int ws_chat_next_id = 0; static void ws_chat_request_handler(nxt_unit_request_info_t *req) { static char buf[1024]; int buf_size; int rc = NXT_UNIT_OK; nxt_unit_request_t *r; ws_chat_request_data_t *data; r = req->request; const char* target = nxt_unit_sptr_get(&r->target); if (strcmp(target, "/") == 0) { rc = ws_chat_root(req); goto fail; } if (strcmp(target, "/chat") == 0) { if (!nxt_unit_request_is_websocket_handshake(req)) { goto notfound; } rc = nxt_unit_response_init(req, 101, 0, 0); if (nxt_slow_path(rc != NXT_UNIT_OK)) { goto fail; } data = req->data; nxt_queue_insert_tail(&ws_chat_sessions, &data->link); data->id = ws_chat_next_id++; nxt_unit_response_upgrade(req); nxt_unit_response_send(req); buf_size = snprintf(buf, sizeof(buf), "Guest #%d has joined.", data->id); ws_chat_broadcast(buf, buf_size); return; } notfound: rc = nxt_unit_response_init(req, 404, 0, 0); fail: nxt_unit_request_done(req, rc); } static int ws_chat_root(nxt_unit_request_info_t *req) { int rc; rc = nxt_unit_response_init(req, 200 /* Status code. */, 2 /* Number of response headers. */, nxt_length(CONTENT_TYPE) + nxt_length(TEXT_HTML) + nxt_length(CONTENT_LENGTH) + ws_chat_index_content_length_size + ws_chat_index_html_size); if (nxt_slow_path(rc != NXT_UNIT_OK)) { return rc; } rc = nxt_unit_response_add_field(req, CONTENT_TYPE, nxt_length(CONTENT_TYPE), TEXT_HTML, nxt_length(TEXT_HTML)); if (nxt_slow_path(rc != NXT_UNIT_OK)) { return rc; } rc = nxt_unit_response_add_field(req, CONTENT_LENGTH, nxt_length(CONTENT_LENGTH), ws_chat_index_content_length, ws_chat_index_content_length_size); if (nxt_slow_path(rc != NXT_UNIT_OK)) { return rc; } rc = nxt_unit_response_add_content(req, ws_chat_index_html, ws_chat_index_html_size); if (nxt_slow_path(rc != NXT_UNIT_OK)) { return rc; } return nxt_unit_response_send(req); } static void ws_chat_broadcast(const char *buf, size_t size) { ws_chat_request_data_t *data; nxt_unit_request_info_t *req; nxt_unit_debug(NULL, "broadcast: %*.s", (int) size, buf); nxt_queue_each(data, &ws_chat_sessions, ws_chat_request_data_t, link) { req = nxt_unit_get_request_info_from_data(data); nxt_unit_req_debug(req, "send: %*.s", (int) size, buf); nxt_unit_websocket_send(req, NXT_WEBSOCKET_OP_TEXT, 1, buf, size); } nxt_queue_loop; } static void ws_chat_websocket_handler(nxt_unit_websocket_frame_t *ws) { int buf_size; static char buf[1024]; ws_chat_request_data_t *data; if (ws->header->opcode != NXT_WEBSOCKET_OP_TEXT) { return; } data = ws->req->data; buf_size = snprintf(buf, sizeof(buf), "Guest #%d: ", data->id); buf_size += nxt_unit_websocket_read(ws, buf + buf_size, nxt_min(sizeof(buf), ws->content_length)); ws_chat_broadcast(buf, buf_size); nxt_unit_websocket_done(ws); } static void ws_chat_close_handler(nxt_unit_request_info_t *req) { int buf_size; static char buf[1024]; ws_chat_request_data_t *data; data = req->data; buf_size = snprintf(buf, sizeof(buf), "Guest #%d has disconnected.", data->id); nxt_queue_remove(&data->link); nxt_unit_request_done(req, NXT_UNIT_OK); ws_chat_broadcast(buf, buf_size); } int main(void) { nxt_unit_ctx_t *ctx; nxt_unit_init_t init; ws_chat_index_content_length_size = snprintf(ws_chat_index_content_length, sizeof(ws_chat_index_content_length), "%d", ws_chat_index_html_size); nxt_queue_init(&ws_chat_sessions); memset(&init, 0, sizeof(nxt_unit_init_t)); init.callbacks.request_handler = ws_chat_request_handler; init.callbacks.websocket_handler = ws_chat_websocket_handler; init.callbacks.close_handler = ws_chat_close_handler; init.request_data_size = sizeof(ws_chat_request_data_t); ctx = nxt_unit_init(&init); if (ctx == NULL) { return 1; } nxt_unit_run(ctx); nxt_unit_done(ctx); return 0; } static const char ws_chat_index_html[] = "\n" "\n" " WebSocket Chat Examples\n" " \n" " \n" "\n" "\n" "\n" "
\n" "

\n" "
\n" "
\n" "
\n" "
\n" "\n" "\n" ; static const int ws_chat_index_html_size = nxt_length(ws_chat_index_html);