| /* |
| * lws-minimal-secure-streams-server |
| * |
| * Written in 2010-2020 by Andy Green <andy@warmcat.com> |
| * |
| * This file is made available under the Creative Commons CC0 1.0 |
| * Universal Public Domain Dedication. |
| */ |
| |
| #include <libwebsockets.h> |
| #include <assert.h> |
| |
| extern int interrupted, bad, multipart; |
| |
| static const char *html = |
| /* normally we serve this... */ |
| "<head><meta content=\"text/html;charset=utf-8\" " |
| "http-equiv=\"Content-Type\"><script>" |
| " var ws = new WebSocket(\"wss://localhost:7681\", \"mywsprotocol\");" |
| "try { ws.onopen = function() { console.log(\"open\"); }; " |
| "ws.onmessage = function got_packet(msg) { " |
| "var s=\"\"; s += msg.data; " |
| "document.getElementById(\"wsd\").innerHTML = s; };" |
| "} catch(exception) {" |
| "alert(\"<p>Error\" + exception); }" |
| "</script></head><html><body>" |
| "Hello from the web server<br>" |
| "<div id=\"wsd\"></div>" |
| "</body></html>", |
| |
| *multipart_html = |
| /* |
| * If you use -m commandline switch we send this instead, as |
| * multipart/form-data |
| */ |
| "--aBoundaryString\r\n" |
| "Content-Disposition: form-data; name=\"myFile\"; filename=\"xxx.txt\"\r\n" |
| "Content-Type: text/plain\r\n" |
| "\r\n" |
| "The file contents\r\n" |
| "--aBoundaryString\r\n" |
| "Content-Disposition: form-data; name=\"myField\"\r\n" |
| "\r\n" |
| "(data)\r\n" |
| "--aBoundaryString--\r\n"; |
| |
| |
| typedef struct myss { |
| struct lws_ss_handle *ss; |
| void *opaque_data; |
| /* ... application specific state ... */ |
| |
| lws_sorted_usec_list_t sul; |
| int count; |
| char upgraded; |
| |
| } myss_srv_t; |
| |
| /* |
| * This is the Secure Streams Server RX and TX for HTTP(S) |
| */ |
| |
| static int |
| myss_srv_rx(void *userobj, const uint8_t *buf, size_t len, int flags) |
| { |
| // myss_srv_t *m = (myss_srv_t *)userobj; |
| |
| lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags); |
| lwsl_hexdump_info(buf, len); |
| |
| /* |
| * If we received the whole message, for our example it means |
| * we are done. |
| */ |
| if (flags & LWSSS_FLAG_EOM) { |
| bad = 0; |
| interrupted = 1; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| myss_srv_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, |
| int *flags) |
| { |
| myss_srv_t *m = (myss_srv_t *)userobj; |
| const char *send = html; |
| |
| if (m->upgraded) |
| return LWSSSSRET_TX_DONT_SEND; |
| |
| if (multipart) |
| send = multipart_html; |
| |
| *flags = LWSSS_FLAG_SOM | LWSSS_FLAG_EOM; |
| |
| lws_strncpy((char *)buf, send, *len); |
| *len = strlen(send); |
| |
| return 0; |
| } |
| |
| /* |
| * This is the Secure Streams Server RX and TX for WS(S)... when we get a |
| * state that the underlying connection upgraded protocol, we switch the stream |
| * rx and tx handlers to here. |
| */ |
| |
| static int |
| myss_ws_rx(void *userobj, const uint8_t *buf, size_t len, int flags) |
| { |
| // myss_srv_t *m = (myss_srv_t *)userobj; |
| |
| lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags); |
| lwsl_hexdump_info(buf, len); |
| |
| /* |
| * If we received the whole message, for our example it means |
| * we are done. |
| */ |
| if (flags & LWSSS_FLAG_EOM) { |
| bad = 0; |
| interrupted = 1; |
| } |
| |
| return 0; |
| } |
| |
| /* this is the callback that mediates sending the incrementing number */ |
| |
| static void |
| spam_sul_cb(struct lws_sorted_usec_list *sul) |
| { |
| myss_srv_t *m = lws_container_of(sul, myss_srv_t, sul); |
| |
| lws_ss_request_tx(m->ss); |
| |
| lws_sul_schedule(lws_ss_get_context(m->ss), 0, &m->sul, spam_sul_cb, |
| 100 * LWS_US_PER_MS); |
| } |
| |
| static int |
| myss_ws_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, |
| int *flags) |
| { |
| myss_srv_t *m = (myss_srv_t *)userobj; |
| |
| *flags = LWSSS_FLAG_SOM | LWSSS_FLAG_EOM; |
| |
| *len = lws_snprintf((char *)buf, *len, "hello from ws %d", m->count++); |
| |
| lws_sul_schedule(lws_ss_get_context(m->ss), 0, &m->sul, spam_sul_cb, |
| 100 * LWS_US_PER_MS); |
| |
| return 0; |
| } |
| |
| static int |
| myss_srv_state(void *userobj, void *sh, lws_ss_constate_t state, |
| lws_ss_tx_ordinal_t ack) |
| { |
| myss_srv_t *m = (myss_srv_t *)userobj; |
| |
| lwsl_user("%s: %p %s, ord 0x%x\n", __func__, m->ss, |
| lws_ss_state_name(state), (unsigned int)ack); |
| |
| switch (state) { |
| case LWSSSCS_DISCONNECTED: |
| lws_sul_cancel(&m->sul); |
| break; |
| case LWSSSCS_CREATING: |
| lws_ss_request_tx(m->ss); |
| break; |
| case LWSSSCS_ALL_RETRIES_FAILED: |
| /* if we're out of retries, we want to close the app and FAIL */ |
| interrupted = 1; |
| break; |
| |
| case LWSSSCS_SERVER_TXN: |
| /* |
| * The underlying protocol started a transaction, let's |
| * describe how we want to complete it. We can defer this until |
| * later, eg, after we have consumed any rx that's coming with |
| * the client's transaction initiation phase, but in this |
| * example we know what we want to do already. |
| * |
| * We do want to ack the transaction... |
| */ |
| lws_ss_server_ack(m->ss, 0); |
| /* |
| * ... it's going to be either text/html or multipart ... |
| */ |
| if (multipart) |
| lws_ss_set_metadata(m->ss, "mime", |
| "multipart/form-data; boundary=aBoundaryString", 45); |
| else |
| lws_ss_set_metadata(m->ss, "mime", "text/html", 9); |
| /* |
| * ...it's going to be whatever size it is (and request tx) |
| */ |
| lws_ss_request_tx_len(m->ss, multipart ? strlen(multipart_html) : |
| strlen(html)); |
| break; |
| |
| case LWSSSCS_SERVER_UPGRADE: |
| |
| /* |
| * This is sent when the underlying protocol has experienced |
| * an upgrade, eg, http->ws... it's a one-way upgrade on this |
| * stream, change the handlers to deal with the kind of |
| * messages we send on ws |
| */ |
| |
| m->upgraded = 1; |
| lws_ss_change_handlers(m->ss, myss_ws_rx, myss_ws_tx, NULL); |
| lws_ss_request_tx(m->ss); /* we want to start sending numbers */ |
| break; |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| const lws_ss_info_t ssi_server = { |
| .handle_offset = offsetof(myss_srv_t, ss), |
| .opaque_user_data_offset = offsetof(myss_srv_t, opaque_data), |
| .streamtype = "myserver", |
| .rx = myss_srv_rx, |
| .tx = myss_srv_tx, |
| .state = myss_srv_state, |
| .user_alloc = sizeof(myss_srv_t), |
| }; |