1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 * Copyright (c) 2019 Intel Corporation
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /** @file mqtt_transport_websocket.c
9 *
10 * @brief Internal functions to handle transport over Websocket.
11 */
12
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(net_mqtt_websocket, CONFIG_MQTT_LOG_LEVEL);
15
16 #include <errno.h>
17 #include <zephyr/net/socket.h>
18 #include <zephyr/net/mqtt.h>
19 #include <zephyr/net/websocket.h>
20
21 #include "mqtt_os.h"
22 #include "mqtt_transport.h"
23
mqtt_client_websocket_connect(struct mqtt_client * client)24 int mqtt_client_websocket_connect(struct mqtt_client *client)
25 {
26 const char *extra_headers[] = {
27 "Sec-WebSocket-Protocol: mqtt\r\n",
28 NULL
29 };
30 int transport_sock;
31 int ret;
32
33 if (client->transport.type == MQTT_TRANSPORT_NON_SECURE_WEBSOCKET) {
34 ret = mqtt_client_tcp_connect(client);
35 if (ret < 0) {
36 return ret;
37 }
38
39 transport_sock = client->transport.tcp.sock;
40 }
41 #if defined(CONFIG_MQTT_LIB_TLS)
42 else if (client->transport.type == MQTT_TRANSPORT_SECURE_WEBSOCKET) {
43 ret = mqtt_client_tls_connect(client);
44 if (ret < 0) {
45 return ret;
46 }
47
48 transport_sock = client->transport.tls.sock;
49 }
50 #endif
51 else {
52 return -EINVAL;
53 }
54
55 if (client->transport.websocket.config.url == NULL) {
56 client->transport.websocket.config.url = "/mqtt";
57 }
58
59 if (client->transport.websocket.config.host == NULL) {
60 client->transport.websocket.config.host = "localhost";
61 }
62
63 /* If application needs to set some extra header options, then
64 * it can set the optional_headers_cb. In this case the app
65 * will need to also send "Sec-WebSocket-Protocol: mqtt\r\n"
66 * field as the optional_headers field is ignored if the callback
67 * is set.
68 */
69 client->transport.websocket.config.optional_headers = extra_headers;
70
71 client->transport.websocket.sock = websocket_connect(
72 transport_sock,
73 &client->transport.websocket.config,
74 client->transport.websocket.timeout,
75 NULL);
76 if (client->transport.websocket.sock < 0) {
77 NET_ERR("Websocket connect failed (%d)",
78 client->transport.websocket.sock);
79
80 (void)close(transport_sock);
81 return client->transport.websocket.sock;
82 }
83
84 NET_DBG("Connect completed");
85
86 return 0;
87 }
88
mqtt_client_websocket_write(struct mqtt_client * client,const uint8_t * data,uint32_t datalen)89 int mqtt_client_websocket_write(struct mqtt_client *client, const uint8_t *data,
90 uint32_t datalen)
91 {
92 uint32_t offset = 0U;
93 int ret;
94
95 while (offset < datalen) {
96 ret = websocket_send_msg(client->transport.websocket.sock,
97 data + offset, datalen - offset,
98 WEBSOCKET_OPCODE_DATA_BINARY,
99 true, true, SYS_FOREVER_MS);
100 if (ret < 0) {
101 return -errno;
102 }
103
104 offset += ret;
105 }
106
107 return 0;
108 }
109
mqtt_client_websocket_write_msg(struct mqtt_client * client,const struct msghdr * message)110 int mqtt_client_websocket_write_msg(struct mqtt_client *client,
111 const struct msghdr *message)
112 {
113 enum websocket_opcode opcode = WEBSOCKET_OPCODE_DATA_BINARY;
114 bool final = false;
115 ssize_t len;
116 ssize_t ret;
117 int i;
118
119 len = 0;
120 for (i = 0; i < message->msg_iovlen; i++) {
121 if (i == message->msg_iovlen - 1) {
122 final = true;
123 }
124
125 ret = websocket_send_msg(client->transport.websocket.sock,
126 message->msg_iov[i].iov_base,
127 message->msg_iov[i].iov_len, opcode,
128 true, final, SYS_FOREVER_MS);
129 if (ret < 0) {
130 return ret;
131 }
132
133 opcode = WEBSOCKET_OPCODE_CONTINUE;
134 len += ret;
135 }
136
137 return len;
138 }
139
mqtt_client_websocket_read(struct mqtt_client * client,uint8_t * data,uint32_t buflen,bool shall_block)140 int mqtt_client_websocket_read(struct mqtt_client *client, uint8_t *data,
141 uint32_t buflen, bool shall_block)
142 {
143 int32_t timeout = SYS_FOREVER_MS;
144 uint32_t message_type = 0U;
145 int ret;
146
147 if (!shall_block) {
148 timeout = 0;
149 }
150
151 ret = websocket_recv_msg(client->transport.websocket.sock,
152 data, buflen, &message_type, NULL, timeout);
153 if (ret >= 0 && message_type > 0) {
154 if (message_type & WEBSOCKET_FLAG_CLOSE) {
155 return 0;
156 }
157
158 if ((ret == 0) || !(message_type & WEBSOCKET_FLAG_BINARY)) {
159 return -EAGAIN;
160 }
161 }
162 if (ret == -ENOTCONN) {
163 ret = 0;
164 }
165
166 return ret;
167 }
168
mqtt_client_websocket_disconnect(struct mqtt_client * client)169 int mqtt_client_websocket_disconnect(struct mqtt_client *client)
170 {
171 NET_INFO("Closing socket %d", client->transport.websocket.sock);
172
173 return websocket_disconnect(client->transport.websocket.sock);
174 }
175