1/* 2 * lws-minimal-secure-streams-server 3 * 4 * Written in 2010-2020 by Andy Green <andy@warmcat.com> 5 * 6 * This file is made available under the Creative Commons CC0 1.0 7 * Universal Public Domain Dedication. 8 */ 9 10#include <libwebsockets.h> 11#include <assert.h> 12 13extern int interrupted, bad, multipart; 14 15static const char *html = 16 /* normally we serve this... */ 17 "<head><meta content=\"text/html;charset=utf-8\" " 18 "http-equiv=\"Content-Type\"><script>" 19 " var ws = new WebSocket(\"wss://localhost:7681\", \"mywsprotocol\");" 20 "try { ws.onopen = function() { console.log(\"open\"); }; " 21 "ws.onmessage = function got_packet(msg) { " 22 "var s=\"\"; s += msg.data; " 23 "document.getElementById(\"wsd\").innerHTML = s; };" 24 "} catch(exception) {" 25 "alert(\"<p>Error\" + exception); }" 26 "</script></head><html><body>" 27 "Hello from the web server<br>" 28 "<div id=\"wsd\"></div>" 29 "</body></html>", 30 31*multipart_html = 32 /* 33 * If you use -m commandline switch we send this instead, as 34 * multipart/form-data 35 */ 36 "--aBoundaryString\r\n" 37 "Content-Disposition: form-data; name=\"myFile\"; filename=\"xxx.txt\"\r\n" 38 "Content-Type: text/plain\r\n" 39 "\r\n" 40 "The file contents\r\n" 41 "--aBoundaryString\r\n" 42 "Content-Disposition: form-data; name=\"myField\"\r\n" 43 "\r\n" 44 "(data)\r\n" 45 "--aBoundaryString--\r\n"; 46 47 48typedef struct myss { 49 struct lws_ss_handle *ss; 50 void *opaque_data; 51 /* ... application specific state ... */ 52 53 lws_sorted_usec_list_t sul; 54 int count; 55 char upgraded; 56 57} myss_srv_t; 58 59/* 60 * This is the Secure Streams Server RX and TX for HTTP(S) 61 */ 62 63static lws_ss_state_return_t 64myss_srv_rx(void *userobj, const uint8_t *buf, size_t len, int flags) 65{ 66// myss_srv_t *m = (myss_srv_t *)userobj; 67 68 lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags); 69 lwsl_hexdump_info(buf, len); 70 71 /* 72 * If we received the whole message, for our example it means 73 * we are done. 74 */ 75 if (flags & LWSSS_FLAG_EOM) { 76 bad = 0; 77 interrupted = 1; 78 } 79 80 return 0; 81} 82 83static lws_ss_state_return_t 84myss_srv_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, 85 int *flags) 86{ 87 myss_srv_t *m = (myss_srv_t *)userobj; 88 const char *send = html; 89 90 if (m->upgraded) 91 return LWSSSSRET_TX_DONT_SEND; 92 93 if (multipart) 94 send = multipart_html; 95 96 *flags = LWSSS_FLAG_SOM | LWSSS_FLAG_EOM; 97 98 lws_strncpy((char *)buf, send, *len); 99 *len = strlen(send); 100 101 return 0; 102} 103 104/* 105 * This is the Secure Streams Server RX and TX for WS(S)... when we get a 106 * state that the underlying connection upgraded protocol, we switch the stream 107 * rx and tx handlers to here. 108 */ 109 110static lws_ss_state_return_t 111myss_ws_rx(void *userobj, const uint8_t *buf, size_t len, int flags) 112{ 113// myss_srv_t *m = (myss_srv_t *)userobj; 114 115 lwsl_user("%s: len %d, flags: %d\n", __func__, (int)len, flags); 116 lwsl_hexdump_info(buf, len); 117 118 /* 119 * If we received the whole message, for our example it means 120 * we are done. 121 */ 122 if (flags & LWSSS_FLAG_EOM) { 123 bad = 0; 124 interrupted = 1; 125 } 126 127 return 0; 128} 129 130/* this is the callback that mediates sending the incrementing number */ 131 132static void 133spam_sul_cb(struct lws_sorted_usec_list *sul) 134{ 135 myss_srv_t *m = lws_container_of(sul, myss_srv_t, sul); 136 137 if (!lws_ss_request_tx(m->ss)) 138 lws_sul_schedule(lws_ss_get_context(m->ss), 0, &m->sul, spam_sul_cb, 139 100 * LWS_US_PER_MS); 140} 141 142static lws_ss_state_return_t 143myss_ws_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, 144 int *flags) 145{ 146 myss_srv_t *m = (myss_srv_t *)userobj; 147 148 *flags = LWSSS_FLAG_SOM | LWSSS_FLAG_EOM; 149 150 *len = (unsigned int)lws_snprintf((char *)buf, *len, "hello from ws %d", m->count++); 151 152 lws_sul_schedule(lws_ss_get_context(m->ss), 0, &m->sul, spam_sul_cb, 153 100 * LWS_US_PER_MS); 154 155 return 0; 156} 157 158static lws_ss_state_return_t 159myss_srv_state(void *userobj, void *sh, lws_ss_constate_t state, 160 lws_ss_tx_ordinal_t ack) 161{ 162 myss_srv_t *m = (myss_srv_t *)userobj; 163 164 lwsl_user("%s: %p %s, ord 0x%x\n", __func__, m->ss, 165 lws_ss_state_name((int)state), (unsigned int)ack); 166 167 switch (state) { 168 case LWSSSCS_DISCONNECTED: 169 lws_sul_cancel(&m->sul); 170 break; 171 case LWSSSCS_CREATING: 172 return lws_ss_request_tx(m->ss); 173 174 case LWSSSCS_ALL_RETRIES_FAILED: 175 /* if we're out of retries, we want to close the app and FAIL */ 176 interrupted = 1; 177 break; 178 179 case LWSSSCS_SERVER_TXN: 180 /* 181 * The underlying protocol started a transaction, let's 182 * describe how we want to complete it. We can defer this until 183 * later, eg, after we have consumed any rx that's coming with 184 * the client's transaction initiation phase, but in this 185 * example we know what we want to do already. 186 * 187 * We do want to ack the transaction... 188 */ 189 lws_ss_server_ack(m->ss, 0); 190 /* 191 * ... it's going to be either text/html or multipart ... 192 */ 193 if (multipart) { 194 if (lws_ss_set_metadata(m->ss, "mime", 195 "multipart/form-data; boundary=aBoundaryString", 45)) 196 return LWSSSSRET_DISCONNECT_ME; 197 } else 198 if (lws_ss_set_metadata(m->ss, "mime", "text/html", 9)) 199 return LWSSSSRET_DISCONNECT_ME; 200 /* 201 * ...it's going to be whatever size it is (and request tx) 202 */ 203 return lws_ss_request_tx_len(m->ss, (unsigned long) 204 (multipart ? strlen(multipart_html) : 205 strlen(html))); 206 207 case LWSSSCS_SERVER_UPGRADE: 208 209 /* 210 * This is sent when the underlying protocol has experienced 211 * an upgrade, eg, http->ws... it's a one-way upgrade on this 212 * stream, change the handlers to deal with the kind of 213 * messages we send on ws 214 */ 215 216 m->upgraded = 1; 217 lws_ss_change_handlers(m->ss, myss_ws_rx, myss_ws_tx, NULL); 218 return lws_ss_request_tx(m->ss); /* we want to start sending numbers */ 219 220 default: 221 break; 222 } 223 224 return 0; 225} 226 227const lws_ss_info_t ssi_server = { 228 .handle_offset = offsetof(myss_srv_t, ss), 229 .opaque_user_data_offset = offsetof(myss_srv_t, opaque_data), 230 .streamtype = "myserver", 231 .rx = myss_srv_rx, 232 .tx = myss_srv_tx, 233 .state = myss_srv_state, 234 .user_alloc = sizeof(myss_srv_t), 235}; 236