-
-#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
-char *resource_path = LOCAL_RESOURCE_PATH;
-
-/*
- * We take a strict whitelist approach to stop ../ attacks
- */
-
-struct serveable {
- const char *urlpath;
- const char *mimetype;
-};
-
-struct per_session_data__http {
- int fd;
-};
-
-/*
- * this is just an example of parsing handshake headers, you don't need this
- * in your code unless you will filter allowing connections by the header
- * content
- */
-
-static void
-dump_handshake_info(struct libwebsocket *wsi)
-{
- int n;
- static const char *token_names[] = {
- /*[WSI_TOKEN_GET_URI] =*/ "GET URI",
- /*[WSI_TOKEN_HOST] =*/ "Host",
- /*[WSI_TOKEN_CONNECTION] =*/ "Connection",
- /*[WSI_TOKEN_KEY1] =*/ "key 1",
- /*[WSI_TOKEN_KEY2] =*/ "key 2",
- /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol",
- /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade",
- /*[WSI_TOKEN_ORIGIN] =*/ "Origin",
- /*[WSI_TOKEN_DRAFT] =*/ "Draft",
- /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge",
-
- /* new for 04 */
- /*[WSI_TOKEN_KEY] =*/ "Key",
- /*[WSI_TOKEN_VERSION] =*/ "Version",
- /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin",
-
- /* new for 05 */
- /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions",
-
- /* client receives these */
- /*[WSI_TOKEN_ACCEPT] =*/ "Accept",
- /*[WSI_TOKEN_NONCE] =*/ "Nonce",
- /*[WSI_TOKEN_HTTP] =*/ "Http",
-
- "Accept:",
- "If-Modified-Since:",
- "Accept-Encoding:",
- "Accept-Language:",
- "Pragma:",
- "Cache-Control:",
- "Authorization:",
- "Cookie:",
- "Content-Type:",
- "Date:",
- "Range:",
- "Referer:",
- "Uri-Args:",
-
- /*[WSI_TOKEN_MUXURL] =*/ "MuxURL",
- };
- char buf[256];
-
- for (n = 0; n < sizeof(token_names) / sizeof(token_names[0]); n++) {
- if (!lws_hdr_total_length(wsi, n))
- continue;
-
- lws_hdr_copy(wsi, buf, sizeof buf, n);
-
- fprintf(stderr, " %s = %s\n", token_names[n], buf);
- }
-}
-
-const char * get_mimetype(const char *file)
-{
- int n = strlen(file);
-
- if (n < 5)
- return NULL;
-
- if (!strcmp(&file[n - 4], ".ico"))
- return "image/x-icon";
-
- if (!strcmp(&file[n - 4], ".png"))
- return "image/png";
-
- if (!strcmp(&file[n - 5], ".html"))
- return "text/html";
-
- return NULL;
-}
-
-/* this protocol server (always the first one) just knows how to do HTTP */
-
-static int callback_http(struct libwebsocket_context *context,
- struct libwebsocket *wsi,
- enum libwebsocket_callback_reasons reason, void *user,
- void *in, size_t len)
-{
-#if 0
- char client_name[128];
- char client_ip[128];
-#endif
- char buf[256];
- char leaf_path[1024];
- char b64[64];
- struct timeval tv;
- int n, m;
- unsigned char *p;
- char *other_headers;
- static unsigned char buffer[4096];
- struct stat stat_buf;
- struct per_session_data__http *pss =
- (struct per_session_data__http *)user;
- const char *mimetype;
-#ifdef EXTERNAL_POLL
- int fd = (int)(long)in;
-#endif
-
- switch (reason) {
- case LWS_CALLBACK_HTTP:
-
- dump_handshake_info(wsi);
-
- if (len < 1) {
- libwebsockets_return_http_status(context, wsi,
- HTTP_STATUS_BAD_REQUEST, NULL);
- return -1;
- }
-
- /* this server has no concept of directories */
- if (strchr((const char *)in + 1, '/')) {
- libwebsockets_return_http_status(context, wsi,
- HTTP_STATUS_FORBIDDEN, NULL);
- return -1;
- }
-
- /* check for the "send a big file by hand" example case */
-
- if (!strcmp((const char *)in, "/leaf.jpg")) {
- if (strlen(resource_path) > sizeof(leaf_path) - 10)
- return -1;
- sprintf(leaf_path, "%s/leaf.jpg", resource_path);
-
- /* well, let's demonstrate how to send the hard way */
-
- p = buffer;
-
-#ifdef WIN32
- pss->fd = open(leaf_path, O_RDONLY | _O_BINARY);
-#else
- pss->fd = open(leaf_path, O_RDONLY);
-#endif
-
- if (pss->fd < 0)
- return -1;
-
- fstat(pss->fd, &stat_buf);
-
- /*
- * we will send a big jpeg file, but it could be
- * anything. Set the Content-Type: appropriately
- * so the browser knows what to do with it.
- */
-
- p += sprintf((char *)p,
- "HTTP/1.0 200 OK\x0d\x0a"
- "Server: libwebsockets\x0d\x0a"
- "Content-Type: image/jpeg\x0d\x0a"
- "Content-Length: %u\x0d\x0a\x0d\x0a",
- (unsigned int)stat_buf.st_size);
-
- /*
- * send the http headers...
- * this won't block since it's the first payload sent
- * on the connection since it was established
- * (too small for partial)
- */
-
- n = libwebsocket_write(wsi, buffer,
- p - buffer, LWS_WRITE_HTTP);
-
- if (n < 0) {
- close(pss->fd);
- return -1;
- }
- /*
- * book us a LWS_CALLBACK_HTTP_WRITEABLE callback
- */
- libwebsocket_callback_on_writable(context, wsi);
- break;
- }
-
- /* if not, send a file the easy way */
- strcpy(buf, resource_path);
- if (strcmp(in, "/")) {
- if (*((const char *)in) != '/')
- strcat(buf, "/");
- strncat(buf, in, sizeof(buf) - strlen(resource_path));
- } else /* default file to serve */
- strcat(buf, "/test.html");
- buf[sizeof(buf) - 1] = '\0';
-
- /* refuse to serve files we don't understand */
- mimetype = get_mimetype(buf);
- if (!mimetype) {
- lwsl_err("Unknown mimetype for %s\n", buf);
- libwebsockets_return_http_status(context, wsi,
- HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
- return -1;
- }
-
- /* demostrates how to set a cookie on / */
-
- other_headers = NULL;
- if (!strcmp((const char *)in, "/") &&
- !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
- /* this isn't very unguessable but it'll do for us */
- gettimeofday(&tv, NULL);
- sprintf(b64, "LWS_%u_%u_COOKIE",
- (unsigned int)tv.tv_sec,
- (unsigned int)tv.tv_usec);
-
- sprintf(leaf_path,
- "Set-Cookie: test=LWS_%u_%u_COOKIE;Max-Age=360000\x0d\x0a",
- (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec);
- other_headers = leaf_path;
- lwsl_err(other_headers);
- }
-
- if (libwebsockets_serve_http_file(context, wsi, buf,
- mimetype, other_headers))
- return -1; /* through completion or error, close the socket */
-
- /*
- * notice that the sending of the file completes asynchronously,
- * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
- * it's done
- */
-
- break;
-
- case LWS_CALLBACK_HTTP_FILE_COMPLETION:
-// lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
- /* kill the connection after we sent one file */
- return -1;
-
- case LWS_CALLBACK_HTTP_WRITEABLE:
- /*
- * we can send more of whatever it is we were sending
- */
-
- do {
- n = read(pss->fd, buffer, sizeof buffer);
- /* problem reading, close conn */
- if (n < 0)
- goto bail;
- /* sent it all, close conn */
- if (n == 0)
- goto bail;
- /*
- * because it's HTTP and not websocket, don't need to take
- * care about pre and postamble
- */
- m = libwebsocket_write(wsi, buffer, n, LWS_WRITE_HTTP);
- if (m < 0)
- /* write failed, close conn */
- goto bail;
- if (m != n)
- /* partial write, adjust */
- lseek(pss->fd, m - n, SEEK_CUR);
-
- } while (!lws_send_pipe_choked(wsi));
- libwebsocket_callback_on_writable(context, wsi);
- break;
-
-bail:
- close(pss->fd);
- return -1;
-
- /*
- * callback for confirming to continue with client IP appear in
- * protocol 0 callback since no websocket protocol has been agreed
- * yet. You can just ignore this if you won't filter on client IP
- * since the default uhandled callback return is 0 meaning let the
- * connection continue.
- */
-
- case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
-#if 0
- libwebsockets_get_peer_addresses(context, wsi, (int)(long)in, client_name,
- sizeof(client_name), client_ip, sizeof(client_ip));
-
- fprintf(stderr, "Received network connect from %s (%s)\n",
- client_name, client_ip);
-#endif
- /* if we returned non-zero from here, we kill the connection */
- break;
-
-#ifdef EXTERNAL_POLL
- /*
- * callbacks for managing the external poll() array appear in
- * protocol 0 callback
- */
-
- case LWS_CALLBACK_ADD_POLL_FD:
-
- if (count_pollfds >= max_poll_elements) {
- lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
- return 1;
- }
-
- fd_lookup[fd] = count_pollfds;
- pollfds[count_pollfds].fd = fd;
- pollfds[count_pollfds].events = (int)(long)len;
- pollfds[count_pollfds++].revents = 0;
- break;
-
- case LWS_CALLBACK_DEL_POLL_FD:
- if (!--count_pollfds)
- break;
- m = fd_lookup[fd];
- /* have the last guy take up the vacant slot */
- pollfds[m] = pollfds[count_pollfds];
- fd_lookup[pollfds[count_pollfds].fd] = m;
- break;
-
- case LWS_CALLBACK_SET_MODE_POLL_FD:
- pollfds[fd_lookup[fd]].events |= (int)(long)len;
- break;
-
- case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
- pollfds[fd_lookup[fd]].events &= ~(int)(long)len;
- break;
-#endif
-
- default:
- break;
- }
-
- return 0;
-}
-
-
-/* dumb_increment protocol */
-
-/*
- * one of these is auto-created for each connection and a pointer to the
- * appropriate instance is passed to the callback in the user parameter
- *
- * for this example protocol we use it to individualize the count for each
- * connection.
- */
-
-struct per_session_data__dumb_increment {
- int number;
-};
-
-static int
-callback_dumb_increment(struct libwebsocket_context *context,
- struct libwebsocket *wsi,
- enum libwebsocket_callback_reasons reason,
- void *user, void *in, size_t len)
-{
- int n, m;
- unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 512 +
- LWS_SEND_BUFFER_POST_PADDING];
- unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
- struct per_session_data__dumb_increment *pss = (struct per_session_data__dumb_increment *)user;
-
- switch (reason) {
-
- case LWS_CALLBACK_ESTABLISHED:
- lwsl_info("callback_dumb_increment: "
- "LWS_CALLBACK_ESTABLISHED\n");
- pss->number = 0;
- break;
-
- case LWS_CALLBACK_SERVER_WRITEABLE:
- n = sprintf((char *)p, "%d", pss->number++);
- m = libwebsocket_write(wsi, p, n, LWS_WRITE_TEXT);
- if (m < n) {
- lwsl_err("ERROR %d writing to di socket\n", n);
- return -1;
- }
- if (close_testing && pss->number == 50) {
- lwsl_info("close tesing limit, closing\n");
- return -1;
- }
- break;
-
- case LWS_CALLBACK_RECEIVE:
-// fprintf(stderr, "rx %d\n", (int)len);
- if (len < 6)
- break;
- if (strcmp((const char *)in, "reset\n") == 0)
- pss->number = 0;
- break;
- /*
- * this just demonstrates how to use the protocol filter. If you won't
- * study and reject connections based on header content, you don't need
- * to handle this callback
- */
-
- case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
- dump_handshake_info(wsi);
- /* you could return non-zero here and kill the connection */
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-
-/* lws-mirror_protocol */
-
-#define MAX_MESSAGE_QUEUE 32
-
-struct per_session_data__lws_mirror {
- struct libwebsocket *wsi;
- int ringbuffer_tail;
-};
-
-struct a_message {
- void *payload;
- size_t len;
-};
-
-static struct a_message ringbuffer[MAX_MESSAGE_QUEUE];
-static int ringbuffer_head;
-
-static int
-callback_lws_mirror(struct libwebsocket_context *context,
- struct libwebsocket *wsi,
- enum libwebsocket_callback_reasons reason,
- void *user, void *in, size_t len)
-{
- int n;
- struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user;
-
- switch (reason) {
-
- case LWS_CALLBACK_ESTABLISHED:
- lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n");
- pss->ringbuffer_tail = ringbuffer_head;
- pss->wsi = wsi;
- break;
-
- case LWS_CALLBACK_PROTOCOL_DESTROY:
- lwsl_notice("mirror protocol cleaning up\n");
- for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++)
- if (ringbuffer[n].payload)
- free(ringbuffer[n].payload);
- break;
-
- case LWS_CALLBACK_SERVER_WRITEABLE:
- if (close_testing)
- break;
- while (pss->ringbuffer_tail != ringbuffer_head) {
-
- n = libwebsocket_write(wsi, (unsigned char *)
- ringbuffer[pss->ringbuffer_tail].payload +
- LWS_SEND_BUFFER_PRE_PADDING,
- ringbuffer[pss->ringbuffer_tail].len,
- LWS_WRITE_TEXT);
- if (n < ringbuffer[pss->ringbuffer_tail].len) {
- lwsl_err("ERROR %d writing to mirror socket\n", n);
- return -1;
- }
- if (n < ringbuffer[pss->ringbuffer_tail].len)
- lwsl_err("mirror partial write %d vs %d\n",
- n, ringbuffer[pss->ringbuffer_tail].len);
-
- if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1))
- pss->ringbuffer_tail = 0;
- else
- pss->ringbuffer_tail++;
-
- if (((ringbuffer_head - pss->ringbuffer_tail) &
- (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15))
- libwebsocket_rx_flow_allow_all_protocol(
- libwebsockets_get_protocol(wsi));
-
- // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
-
- if (lws_send_pipe_choked(wsi)) {
- libwebsocket_callback_on_writable(context, wsi);
- break;
- }
- /*
- * for tests with chrome on same machine as client and
- * server, this is needed to stop chrome choking
- */
- usleep(1);
- }
- break;
-
- case LWS_CALLBACK_RECEIVE:
-
- if (((ringbuffer_head - pss->ringbuffer_tail) &
- (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) {
- lwsl_err("dropping!\n");
- goto choke;
- }
-
- if (ringbuffer[ringbuffer_head].payload)
- free(ringbuffer[ringbuffer_head].payload);
-
- ringbuffer[ringbuffer_head].payload =
- malloc(LWS_SEND_BUFFER_PRE_PADDING + len +
- LWS_SEND_BUFFER_POST_PADDING);
- ringbuffer[ringbuffer_head].len = len;
- memcpy((char *)ringbuffer[ringbuffer_head].payload +
- LWS_SEND_BUFFER_PRE_PADDING, in, len);
- if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1))
- ringbuffer_head = 0;
- else
- ringbuffer_head++;
-
- if (((ringbuffer_head - pss->ringbuffer_tail) &
- (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2))
- goto done;
-
-choke:
- lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi);
- libwebsocket_rx_flow_control(wsi, 0);
-
-// lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1));
-done:
- libwebsocket_callback_on_writable_all_protocol(
- libwebsockets_get_protocol(wsi));
- break;
-
- /*
- * this just demonstrates how to use the protocol filter. If you won't
- * study and reject connections based on header content, you don't need
- * to handle this callback
- */
-
- case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
- dump_handshake_info(wsi);
- /* you could return non-zero here and kill the connection */
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-