summaryrefslogtreecommitdiff
path: root/include/qntan
diff options
context:
space:
mode:
Diffstat (limited to 'include/qntan')
-rw-r--r--include/qntan/Qntan.h199
1 files changed, 199 insertions, 0 deletions
diff --git a/include/qntan/Qntan.h b/include/qntan/Qntan.h
index a7d2c85..558125c 100644
--- a/include/qntan/Qntan.h
+++ b/include/qntan/Qntan.h
@@ -98,6 +98,8 @@ struct Qntan_JsonDec_Mentor;
struct Qntan_JsonTreeDec_JsonNode;
struct Qntan_JsonTreeDec;
struct Qntan_Websocket;
+struct Qntan_WebsocketClient_Connect_Opts;
+struct Qntan_WebsocketClient;
struct Qntan_XmlDec;
struct Qntan_XmlDec_Mentor;
struct Qntan_HttpClient_Exchg_Opts;
@@ -1917,6 +1919,203 @@ struct Qntan_Websocket {
+#ifndef Qntan_WebsocketClient_Connect_Opts_onRspHdr
+# define Qntan_WebsocketClient_Connect_Opts_onRspHdr(A, B, C, D, E) (*A)->onRspHdr(A, B, C, D, E)
+#endif
+#ifndef Qntan_WebsocketClient_Connect_Opts_onWebsocketMsg
+# define Qntan_WebsocketClient_Connect_Opts_onWebsocketMsg(A, B, C, D, E, F, G) (*A)->onWebsocketMsg(A, B, C, D, E, F, G)
+#endif
+
+struct Qntan_WebsocketClient_Connect_Opts {
+ /* WARN: DRAFT! Not established yet. Subject to change!
+ * Describes where and how 'Qntan_WebsocketClient.connect()'
+ * should establish a websocket connection.
+ */
+ char const *host;
+ /* Zero-terminated hostname or address to connect to.
+ * Eg "example.com", "127.0.0.1".
+ * MUST NOT be NULL.
+ */
+ uint16_t port;
+ /* TCP port to connect to.
+ * If zero, the default port gets used: 80 for plain (`ws://`),
+ * 443 for TLS (`wss://`) connections.
+ */
+ char const *url;
+ /* The "request target" (aka resource name) of the HTTP upgrade
+ * request, as of
+ * "https://www.rfc-editor.org/rfc/rfc6455#section-4.1".
+ * Eg "/foo/bar?blubb=true". Zero-terminated. MUST begin with '/'.
+ * If NULL, "/" gets used.
+ */
+ char **hdrs;
+ /* Additional request headers. Zero-terminated list of key/value
+ * pairs. Eg {"Sec-WebSocket-Protocol", "chat", NULL}.
+ * Same format as 'Qntan_HttpClient_Exchg_Opts.hdrs'.
+ * MAY be NULL, aka no additional headers at all.
+ * WARN: Headers the implementation has to generate itself (like
+ * `Host`, `Upgrade`, `Connection`, `Sec-WebSocket-Key` and
+ * `Sec-WebSocket-Version`) MUST NOT be provided here! Passing
+ * them anyway is UNDEFINED BEHAVIOR!
+ */
+ uint8_t isTls;
+ /* Zero means plain websocket (`ws://`).
+ * Nonzero means websocket over TLS (`wss://`).
+ */
+ Qntan_Cls cls;
+ /* Closure (aka context) which gets passed to `onRspHdr` and
+ * `onWebsocketMsg` below.
+ */
+ void (*onRspHdr)( char const*proto, int code, char const*msg, char const*const*hdrs, Qntan_Cls );
+ /* Called ONCE with the servers response to our upgrade request.
+ * Gets called before `onDone` of `connect()`.
+ * Same format & meaning as
+ * 'Qntan_HttpClient_Exchg_Opts.onRspHdr'.
+ * 'code' usually is 101 (aka "Switching Protocols"). Any other
+ * code means the handshake failed, in which case `connect()`
+ * will report a negative error via its `onDone`.
+ * MAY be NULL.
+ */
+ void (*onWebsocketMsg)(int flgs,char const*buf,int buf_len,Qntan_Cls a2,void(*onMsgConsumed)(Qntan_Cls a3),Qntan_Cls a3);
+ /* Called as soon there's event data available. `a2` holds the
+ * `cls` given above.
+ * Same meaning as the `onWebsocketMsg` callback of
+ * 'Qntan_Websocket.reset()':
+ * `flgs & 1` means `buf` is of type `utf8`.
+ * `flgs & 2` this is "just another chunk" to the same message as before.
+ * `flgs & 4` means this is the last chunk of this message.
+ * `flgs & 8` means this is a `PING` message.
+ * `flgs & 16` means this is a `PONG` message.
+ * `flgs & 32` this is a "connection-close" websocket message.
+ * WARN: MUST NOT be NULL.
+ * WARN: 'buf' (and everything it points to) is only valid upon
+ * return from `onWebsocketMsg`.
+ * `onMsgConsumed` MUST be called exactly once, passing `a3`, as
+ * soon the current message got consumed. Only then the next
+ * message will be delivered.
+ */
+};
+
+#ifndef Qntan_WebsocketClient_connect
+# define Qntan_WebsocketClient_connect(A, B, C, D) (*A)->connect(A, B, C, D)
+#endif
+#ifndef Qntan_WebsocketClient_sendMsg
+# define Qntan_WebsocketClient_sendMsg(A, B, C, D, E, F) (*A)->sendMsg(A, B, C, D, E, F)
+#endif
+#ifndef Qntan_WebsocketClient_ping
+# define Qntan_WebsocketClient_ping(A, B, C, D) (*A)->ping(A, B, C, D)
+#endif
+#ifndef Qntan_WebsocketClient_pong
+# define Qntan_WebsocketClient_pong(A, B, C, D) (*A)->pong(A, B, C, D)
+#endif
+#ifndef Qntan_WebsocketClient_shutdown
+# define Qntan_WebsocketClient_shutdown(A, B, C, D, E) (*A)->shutdown(A, B, C, D, E)
+#endif
+
+struct Qntan_WebsocketClient {
+ /* An asynchronous (callback based) websocket CLIENT for
+ * https://www.rfc-editor.org/info/rfc6455/
+ * It is the counterpart to 'Qntan_Websocket', which is the CoDec
+ * meant to be used on the server side.
+ *
+ * WARN: DRAFT! Not established yet. Members and signatures
+ * likely will change!
+ * HINT: An implementation SHOULD do the framing by reusing
+ * 'Qntan_Websocket'.
+ * TODO: Clarify lifetime of an instance (aka how to create and
+ * how to destroy one).
+ */
+ //
+ void (*connect)( struct Qntan_WebsocketClient**,
+ struct Qntan_WebsocketClient_Connect_Opts*opts,
+ void(*onDone)(int err,Qntan_Cls cls), Qntan_Cls cls );
+ /* Bring up a new websocket connection as described by `opts`.
+ * The implementation will
+ * 1. resolve `opts->host` and TCP-connect to `opts->port`,
+ * 2. do the TLS handshake, iff `opts->isTls` is set,
+ * 3. send the HTTP upgrade request,
+ * 4. validate the servers response
+ * ("https://www.rfc-editor.org/rfc/rfc6455#section-4.1").
+ * On success, incoming messages get delivered via
+ * `opts->onWebsocketMsg`.
+ *
+ * @param onDone
+ * Called exactly once, on the EventLoop.
+ * @param err
+ * Negative on errors. Eg `-ECONNREFUSED`, `-ETIMEDOUT` or
+ * `-EPROTO` (eg server did not answer `101 Switching
+ * Protocols`, or `Sec-WebSocket-Accept` did not match).
+ * Zero on success, aka the connection is ready to use.
+ * @param opts
+ * MUST stay valid until `onDone` returned. Same is true for
+ * everything `opts` points to.
+ *
+ * WARN: Calls to `connect()` MUST NOT overlap.
+ * Calls to `sendMsg()`, `ping()`, `pong()` and `shutdown()` MUST
+ * ONLY happen after `onDone` reported success.
+ * TODO: Is it allowed to `connect()` again on the same instance
+ * as soon the previous connection got closed?
+ */
+ //
+ void (*sendMsg)( struct Qntan_WebsocketClient**,
+ char const *payload, int64_t payload_len, int flg,
+ void(*onDone)(int,Qntan_Cls), Qntan_Cls );
+ /* Same idea as 'Qntan_Websocket.sendMsg()':
+ * For a regular message, `flg` is usually set to zero.
+ * `flg & 1` indicates this msg is fragmented and more frames will
+ * follow.
+ * `flg & 2` sets the message to be a `text` frame. If not set,
+ * the message defaults to carry `binary` payload.
+ * `flg & 4` indicates that this is the last message on this
+ * websocket channel. If set, this has the same effect of calling
+ * `ws.shutdown()` after sending the message.
+ * 'payload' MUST stay valid until `onDone` got called.
+ * Calls to `sendMsg()` MUST NOT overlap. So as long a send call
+ * is in progress, `sendMsg()` MUST NOT be called meanwhile.
+ * `onDone`: negative `int` indicates errors.
+ */
+ //
+ void (*ping)( struct Qntan_WebsocketClient**,
+ char const*payload, int payload_len, int flg );
+ /* WARN: Signature likely will change soon.
+ *
+ * Send a ping message. Same as 'Qntan_Websocket.ping()'.
+ */
+ //
+ void (*pong)( struct Qntan_WebsocketClient**,
+ char const*payload, int payload_len, int flg );
+ /* WARN: Signature likely will change soon.
+ *
+ * Send a pong reply. `payload` MUST contain the data received in
+ * the `ping` we respond to. Same as 'Qntan_Websocket.pong()'.
+ */
+ //
+ void (*shutdown)( struct Qntan_WebsocketClient**,
+ char const*payload, int payload_len,
+ void(*onDone)(int err,Qntan_Cls arg), Qntan_Cls arg );
+ /* Same as 'Qntan_Websocket.shutdown()': Indicates we're done
+ * sending messages. The implementation will send a
+ * `ConnectionClose` (if none sent yet) message and then
+ * TCP-FIN the underlying connection.
+ *
+ * `payload_len` MUST be in range `0..125`. `payload[0]` (if status
+ * present) MUST be the most significant octet of the status code of
+ * type u16. `payload[1]` (if status present) MUST be the least
+ * significant octet of the status code of type u16.
+ *
+ * `err` below zero indicates errors.
+ */
+};
+
+
+
+
+
+
+
+
+
+
#ifndef Qntan_XmlDec_pause
# define Qntan_XmlDec_pause(A) (*A)->pause(A)
#endif