summaryrefslogtreecommitdiffstats
path: root/mqtt_websockets/src/include/ws_client.h
blob: de4fac40bf329b6c2cb8a24f73d86d63a71e5c8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (C) 2020 Timotej Šiškovič
// SPDX-License-Identifier: GPL-3.0-only
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with this program.
// If not, see <https://www.gnu.org/licenses/>.

#ifndef WS_CLIENT_H
#define WS_CLIENT_H

#include "ringbuffer.h"
#include "mqtt_wss_log.h"

#include <stdint.h>

#define WS_CLIENT_NEED_MORE_BYTES     0x10
#define WS_CLIENT_PARSING_DONE        0x11
#define WS_CLIENT_CONNECTION_CLOSED   0x12
#define WS_CLIENT_PROTOCOL_ERROR     -0x10
#define WS_CLIENT_BUFFER_FULL        -0x11
#define WS_CLIENT_INTERNAL_ERROR     -0x12

enum websocket_client_conn_state {
    WS_RAW = 0,
    WS_HANDSHAKE,
    WS_ESTABLISHED,
    WS_ERROR,        // connection has to be restarted if this is reached
    WS_CONN_CLOSED_GRACEFUL
};

enum websocket_client_hdr_parse_state {
    WS_HDR_HTTP = 0,        // need to check HTTP/1.1
    WS_HDR_RC,              // need to read HTTP code
    WS_HDR_ENDLINE,         // need to read rest of the first line
    WS_HDR_PARSE_HEADERS,   // rest of the header until CRLF CRLF
    WS_HDR_PARSE_DONE,
    WS_HDR_ALL_DONE
};

enum websocket_client_rx_ws_parse_state {
    WS_FIRST_2BYTES = 0,
    WS_PAYLOAD_EXTENDED_16,
    WS_PAYLOAD_EXTENDED_64,
    WS_PAYLOAD_DATA, // BINARY payload to be passed to MQTT
    WS_PAYLOAD_CONNECTION_CLOSE,
    WS_PAYLOAD_CONNECTION_CLOSE_EC,
    WS_PAYLOAD_CONNECTION_CLOSE_MSG,
    WS_PAYLOAD_SKIP_UNKNOWN_PAYLOAD,
    WS_PAYLOAD_PING_REQ_PAYLOAD, // PING payload to be sent back as PONG
    WS_PACKET_DONE
};

enum websocket_opcode {
    WS_OP_CONTINUATION_FRAME = 0x0,
    WS_OP_TEXT_FRAME         = 0x1,
    WS_OP_BINARY_FRAME       = 0x2,
    WS_OP_CONNECTION_CLOSE   = 0x8,
    WS_OP_PING               = 0x9,
    WS_OP_PONG               = 0xA
};

struct ws_op_close_payload {
    uint16_t ec;
    char *reason;
};

struct http_header {
    char *key;
    char *value;
    struct http_header *next;
};

typedef struct websocket_client {
    enum websocket_client_conn_state state;

    struct ws_handshake {
        enum websocket_client_hdr_parse_state hdr_state;
        char *nonce_reply;
        int nonce_matched;
        int http_code;
        char *http_reply_msg;
        struct http_header *headers;
        struct http_header *headers_tail;
        int hdr_count;
    } hs;

    struct ws_rx {
        enum websocket_client_rx_ws_parse_state parse_state;
        enum websocket_opcode opcode;
        uint64_t payload_length;
        uint64_t payload_processed;
        union {
            struct ws_op_close_payload op_close;
            char *ping_msg;
        } specific_data;
    } rx;

    rbuf_t buf_read;    // from SSL
    rbuf_t buf_write;   // to SSL and then to socket
    // TODO if ringbuffer gets multiple tail support
    // we can work without buf_to_mqtt and thus reduce
    // memory usage and remove one more memcpy buf_read->buf_to_mqtt
    rbuf_t buf_to_mqtt; // RAW data for MQTT lib

    int entropy_fd;

    // careful host is borrowed, don't free
    char **host;
    mqtt_wss_log_ctx_t log;
} ws_client;

ws_client *ws_client_new(size_t buf_size, char **host, mqtt_wss_log_ctx_t log);
void ws_client_destroy(ws_client *client);
void ws_client_reset(ws_client *client);

int ws_client_start_handshake(ws_client *client);

int ws_client_want_write(ws_client *client);

int ws_client_process(ws_client *client);

int ws_client_send(ws_client *client, enum websocket_opcode frame_type, const char *data, size_t size);

#endif /* WS_CLIENT_H */