1*6236dae4SAndroid Build Coastguard Worker<!-- 2*6236dae4SAndroid Build Coastguard WorkerCopyright (C) Daniel Stenberg, <[email protected]>, et al. 3*6236dae4SAndroid Build Coastguard Worker 4*6236dae4SAndroid Build Coastguard WorkerSPDX-License-Identifier: curl 5*6236dae4SAndroid Build Coastguard Worker--> 6*6236dae4SAndroid Build Coastguard Worker 7*6236dae4SAndroid Build Coastguard Worker# WebSocket in curl 8*6236dae4SAndroid Build Coastguard Worker 9*6236dae4SAndroid Build Coastguard Worker## URL 10*6236dae4SAndroid Build Coastguard Worker 11*6236dae4SAndroid Build Coastguard WorkerWebSocket communication with libcurl is done by setting up a transfer to a URL 12*6236dae4SAndroid Build Coastguard Workerusing the `ws://` or `wss://` URL schemes. The latter one being the secure 13*6236dae4SAndroid Build Coastguard Workerversion done over HTTPS. 14*6236dae4SAndroid Build Coastguard Worker 15*6236dae4SAndroid Build Coastguard WorkerWhen using `wss://` to do WebSocket over HTTPS, the standard TLS and HTTPS 16*6236dae4SAndroid Build Coastguard Workeroptions are acknowledged for the CA, verification of server certificate etc. 17*6236dae4SAndroid Build Coastguard Worker 18*6236dae4SAndroid Build Coastguard WorkerWebSocket communication is done by upgrading a connection from either HTTP or 19*6236dae4SAndroid Build Coastguard WorkerHTTPS. When given a WebSocket URL to work with, libcurl considers it a 20*6236dae4SAndroid Build Coastguard Workertransfer failure if the upgrade procedure fails. This means that a plain HTTP 21*6236dae4SAndroid Build Coastguard Worker200 response code is considered an error for this work. 22*6236dae4SAndroid Build Coastguard Worker 23*6236dae4SAndroid Build Coastguard Worker## API 24*6236dae4SAndroid Build Coastguard Worker 25*6236dae4SAndroid Build Coastguard WorkerThe WebSocket API is described in the individual man pages for the new API. 26*6236dae4SAndroid Build Coastguard Worker 27*6236dae4SAndroid Build Coastguard WorkerWebSocket with libcurl can be done two ways. 28*6236dae4SAndroid Build Coastguard Worker 29*6236dae4SAndroid Build Coastguard Worker1. Get the WebSocket frames from the server sent to the write callback. You 30*6236dae4SAndroid Build Coastguard Worker can then respond with `curl_ws_send()` from within the callback (or outside 31*6236dae4SAndroid Build Coastguard Worker of it). 32*6236dae4SAndroid Build Coastguard Worker 33*6236dae4SAndroid Build Coastguard Worker2. Set `CURLOPT_CONNECT_ONLY` to 2L (new for WebSocket), which makes libcurl 34*6236dae4SAndroid Build Coastguard Worker do an HTTP GET + `Upgrade:` request plus response in the 35*6236dae4SAndroid Build Coastguard Worker `curl_easy_perform()` call before it returns and then you can use 36*6236dae4SAndroid Build Coastguard Worker `curl_ws_recv()` and `curl_ws_send()` to receive and send WebSocket frames 37*6236dae4SAndroid Build Coastguard Worker from and to the server. 38*6236dae4SAndroid Build Coastguard Worker 39*6236dae4SAndroid Build Coastguard WorkerThe new options to `curl_easy_setopt()`: 40*6236dae4SAndroid Build Coastguard Worker 41*6236dae4SAndroid Build Coastguard Worker `CURLOPT_WS_OPTIONS` - to control specific behavior. `CURLWS_RAW_MODE` makes 42*6236dae4SAndroid Build Coastguard Worker libcurl provide all WebSocket traffic raw in the callback. 43*6236dae4SAndroid Build Coastguard Worker 44*6236dae4SAndroid Build Coastguard WorkerThe new function calls: 45*6236dae4SAndroid Build Coastguard Worker 46*6236dae4SAndroid Build Coastguard Worker `curl_ws_recv()` - receive a WebSocket frame 47*6236dae4SAndroid Build Coastguard Worker 48*6236dae4SAndroid Build Coastguard Worker `curl_ws_send()` - send a WebSocket frame 49*6236dae4SAndroid Build Coastguard Worker 50*6236dae4SAndroid Build Coastguard Worker `curl_ws_meta()` - return WebSocket metadata within a write callback 51*6236dae4SAndroid Build Coastguard Worker 52*6236dae4SAndroid Build Coastguard Worker## Max frame size 53*6236dae4SAndroid Build Coastguard Worker 54*6236dae4SAndroid Build Coastguard WorkerThe current implementation only supports frame sizes up to a max (64K right 55*6236dae4SAndroid Build Coastguard Workernow). This is because the API delivers full frames and it then cannot manage 56*6236dae4SAndroid Build Coastguard Workerthe full 2^63 bytes size. 57*6236dae4SAndroid Build Coastguard Worker 58*6236dae4SAndroid Build Coastguard WorkerIf we decide we need to support (much) larger frames than 64K, we need to 59*6236dae4SAndroid Build Coastguard Workeradjust the API accordingly to be able to deliver partial frames in both 60*6236dae4SAndroid Build Coastguard Workerdirections. 61*6236dae4SAndroid Build Coastguard Worker 62*6236dae4SAndroid Build Coastguard Worker## Errors 63*6236dae4SAndroid Build Coastguard Worker 64*6236dae4SAndroid Build Coastguard WorkerIf the given WebSocket URL (using `ws://` or `wss://`) fails to get upgraded 65*6236dae4SAndroid Build Coastguard Workervia a 101 response code and instead gets another response code back from the 66*6236dae4SAndroid Build Coastguard WorkerHTTP server - the transfer returns `CURLE_HTTP_RETURNED_ERROR` for that 67*6236dae4SAndroid Build Coastguard Workertransfer. Note then that even 2xx response codes are then considered error 68*6236dae4SAndroid Build Coastguard Workersince it failed to provide a WebSocket transfer. 69*6236dae4SAndroid Build Coastguard Worker 70*6236dae4SAndroid Build Coastguard Worker## Test suite 71*6236dae4SAndroid Build Coastguard Worker 72*6236dae4SAndroid Build Coastguard WorkerI looked for an existing small WebSocket server implementation with maximum 73*6236dae4SAndroid Build Coastguard Workerflexibility to dissect and cram into the test suite but I ended up deciding 74*6236dae4SAndroid Build Coastguard Workerthat extending the existing test suite server sws to deal with WebSocket 75*6236dae4SAndroid Build Coastguard Workermight be the better way. 76*6236dae4SAndroid Build Coastguard Worker 77*6236dae4SAndroid Build Coastguard Worker- This server is already integrated and working in the test suite 78*6236dae4SAndroid Build Coastguard Worker 79*6236dae4SAndroid Build Coastguard Worker- We want maximum control and ability to generate broken protocol and negative 80*6236dae4SAndroid Build Coastguard Worker tests as well. A dumber and simpler TCP server could then be easier to 81*6236dae4SAndroid Build Coastguard Worker massage into this than a "proper" WebSocket server. 82*6236dae4SAndroid Build Coastguard Worker 83*6236dae4SAndroid Build Coastguard Worker## Command line tool WebSocket 84*6236dae4SAndroid Build Coastguard Worker 85*6236dae4SAndroid Build Coastguard WorkerThe plan is to make curl do WebSocket similar to telnet/nc. That part of the 86*6236dae4SAndroid Build Coastguard Workerwork has not been started. 87*6236dae4SAndroid Build Coastguard Worker 88*6236dae4SAndroid Build Coastguard WorkerIdeas: 89*6236dae4SAndroid Build Coastguard Worker 90*6236dae4SAndroid Build Coastguard Worker - Read stdin and send off as messages. Consider newline as end of fragment. 91*6236dae4SAndroid Build Coastguard Worker (default to text? offer option to set binary) 92*6236dae4SAndroid Build Coastguard Worker - Respond to PINGs automatically 93*6236dae4SAndroid Build Coastguard Worker - Issue PINGs at some default interval (option to switch off/change interval?) 94*6236dae4SAndroid Build Coastguard Worker - Allow `-d` to specify (initial) data to send (should the format allow for 95*6236dae4SAndroid Build Coastguard Worker multiple separate frames?) 96*6236dae4SAndroid Build Coastguard Worker - Exit after N messages received, where N can be zero. 97*6236dae4SAndroid Build Coastguard Worker 98*6236dae4SAndroid Build Coastguard Worker## Future work 99*6236dae4SAndroid Build Coastguard Worker 100*6236dae4SAndroid Build Coastguard Worker- Verify the Sec-WebSocket-Accept response. It requires a sha-1 function. 101*6236dae4SAndroid Build Coastguard Worker- Verify Sec-WebSocket-Extensions and Sec-WebSocket-Protocol in the response 102*6236dae4SAndroid Build Coastguard Worker- Make WebSocket work with hyper 103*6236dae4SAndroid Build Coastguard Worker- Consider a `curl_ws_poll()` 104*6236dae4SAndroid Build Coastguard Worker- Make sure WebSocket code paths are fuzzed 105*6236dae4SAndroid Build Coastguard Worker- Add client-side PING interval 106*6236dae4SAndroid Build Coastguard Worker- Provide option to disable PING-PONG automation 107*6236dae4SAndroid Build Coastguard Worker- Support compression (`CURLWS_COMPRESS`) 108*6236dae4SAndroid Build Coastguard Worker 109*6236dae4SAndroid Build Coastguard Worker## Why not libWebSocket 110*6236dae4SAndroid Build Coastguard Worker 111*6236dae4SAndroid Build Coastguard WorkerlibWebSocket is said to be a solid, fast and efficient WebSocket library with 112*6236dae4SAndroid Build Coastguard Workera vast amount of users. My plan was originally to build upon it to skip having 113*6236dae4SAndroid Build Coastguard Workerto implement the low level parts of WebSocket myself. 114*6236dae4SAndroid Build Coastguard Worker 115*6236dae4SAndroid Build Coastguard WorkerHere are the reasons why I have decided to move forward with WebSocket in 116*6236dae4SAndroid Build Coastguard Workercurl **without using libWebSocket**: 117*6236dae4SAndroid Build Coastguard Worker 118*6236dae4SAndroid Build Coastguard Worker- doxygen generated docs only makes them hard to navigate. No tutorial, no 119*6236dae4SAndroid Build Coastguard Worker clearly written explanatory pages for specific functions. 120*6236dae4SAndroid Build Coastguard Worker 121*6236dae4SAndroid Build Coastguard Worker- seems (too) tightly integrated with a specific TLS library, while we want to 122*6236dae4SAndroid Build Coastguard Worker support WebSocket with whatever TLS library libcurl was already made to 123*6236dae4SAndroid Build Coastguard Worker work with. 124*6236dae4SAndroid Build Coastguard Worker 125*6236dae4SAndroid Build Coastguard Worker- seems (too) tightly integrated with event libraries 126*6236dae4SAndroid Build Coastguard Worker 127*6236dae4SAndroid Build Coastguard Worker- the references to threads and thread-pools in code and APIs indicate too 128*6236dae4SAndroid Build Coastguard Worker much logic for our purposes 129*6236dae4SAndroid Build Coastguard Worker 130*6236dae4SAndroid Build Coastguard Worker- "bloated" - it is a *huge* library that is actually more lines of code than 131*6236dae4SAndroid Build Coastguard Worker libcurl itself 132*6236dae4SAndroid Build Coastguard Worker 133*6236dae4SAndroid Build Coastguard Worker- WebSocket is a fairly simple protocol on the network/framing layer so 134*6236dae4SAndroid Build Coastguard Worker making a homegrown handling of it should be fine 135