2 * Copyright 2016 IoT.bzh
3 * Author: José Bollo <jose.bollo@iot.bzh>
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
30 #include "utils-upoll.h"
32 static ssize_t aws_writev(struct afb_ws *ws, const struct iovec *iov, int iovcnt);
33 static ssize_t aws_readv(struct afb_ws *ws, const struct iovec *iov, int iovcnt);
34 static void aws_disconnect(struct afb_ws *ws);
35 static void aws_on_close(struct afb_ws *ws, uint16_t code, size_t size);
36 static void aws_on_text(struct afb_ws *ws, int last, size_t size);
37 static void aws_on_binary(struct afb_ws *ws, int last, size_t size);
38 static void aws_on_continue(struct afb_ws *ws, int last, size_t size);
39 static void aws_on_readable(struct afb_ws *ws);
40 static void aws_on_hangup(struct afb_ws *ws);
42 static struct websock_itf aws_itf = {
43 .writev = (void*)aws_writev,
44 .readv = (void*)aws_readv,
45 .disconnect = (void*)aws_disconnect,
49 .on_close = (void*)aws_on_close,
50 .on_text = (void*)aws_on_text,
51 .on_binary = (void*)aws_on_binary,
52 .on_continue = (void*)aws_on_continue,
65 enum { none, text, binary } type;
66 const struct afb_ws_itf *itf;
73 struct afb_ws *afb_ws_create(int fd, const struct afb_ws_itf *itf, void *closure)
75 struct afb_ws *result;
79 result = malloc(sizeof * result);
86 result->closure = closure;
88 result->ws = websock_create_v13(&aws_itf, result);
89 if (result->ws == NULL)
92 result->up = upoll_open(result->fd, result);
93 if (result->up == NULL)
96 result->buffer.buffer = NULL;
97 result->buffer.size = 0;
99 upoll_on_readable(result->up, (void*)aws_on_readable);
100 upoll_on_hangup(result->up, (void*)aws_on_hangup);
105 websock_destroy(result->ws);
113 void afb_ws_disconnect(struct afb_ws *ws)
115 struct upoll *up = ws->up;
116 struct websock *wsi = ws->ws;
120 websock_destroy(wsi);
123 void afb_ws_close(struct afb_ws *ws, uint16_t code)
125 websock_close_code(ws->ws, code);
128 void afb_ws_text(struct afb_ws *ws, const char *text, size_t length)
130 websock_text(ws->ws, text, length);
133 void afb_ws_binary(struct afb_ws *ws, const void *data, size_t length)
135 websock_binary(ws->ws, data, length);
138 static ssize_t aws_writev(struct afb_ws *ws, const struct iovec *iov, int iovcnt)
142 rc = writev(ws->fd, iov, iovcnt);
143 } while(rc == -1 && errno == EINTR);
147 static ssize_t aws_readv(struct afb_ws *ws, const struct iovec *iov, int iovcnt)
151 rc = readv(ws->fd, iov, iovcnt);
152 } while(rc == -1 && errno == EINTR);
156 static void aws_on_readable(struct afb_ws *ws)
158 websock_dispatch(ws->ws);
161 static void aws_on_hangup(struct afb_ws *ws)
163 afb_ws_disconnect(ws);
166 static void aws_disconnect(struct afb_ws *ws)
168 afb_ws_disconnect(ws);
171 static inline struct buf aws_pick_buffer(struct afb_ws *ws)
173 struct buf result = ws->buffer;
174 ws->buffer.buffer = NULL;
179 static int aws_read(struct afb_ws *ws, size_t size)
185 buffer = realloc(ws->buffer.buffer, ws->buffer.size + size + 1);
188 ws->buffer.buffer = buffer;
189 sz = websock_read(ws->ws, &buffer[ws->buffer.size], size);
190 if ((size_t)sz != size)
192 ws->buffer.size += size;
197 static void aws_on_close(struct afb_ws *ws, uint16_t code, size_t size)
202 if (ws->itf->on_close == NULL)
203 websock_drop(ws->ws);
206 b = aws_pick_buffer(ws);
207 ws->itf->on_close(ws->closure, code, b.buffer, b.size);
211 static void aws_on_text(struct afb_ws *ws, int last, size_t size)
213 if (ws->type != none) {
214 websock_drop(ws->ws);
215 websock_close_code(ws->ws, WEBSOCKET_CODE_PROTOCOL_ERROR);
216 } else if (ws->itf->on_text == NULL) {
217 websock_drop(ws->ws);
218 websock_close_code(ws->ws, WEBSOCKET_CODE_CANT_ACCEPT);
221 aws_on_continue(ws, last, size);
225 static void aws_on_binary(struct afb_ws *ws, int last, size_t size)
227 if (ws->type != none) {
228 websock_drop(ws->ws);
229 websock_close_code(ws->ws, WEBSOCKET_CODE_PROTOCOL_ERROR);
230 } else if (ws->itf->on_binary == NULL) {
231 websock_drop(ws->ws);
232 websock_close_code(ws->ws, WEBSOCKET_CODE_CANT_ACCEPT);
235 aws_on_continue(ws, last, size);
239 static void aws_on_continue(struct afb_ws *ws, int last, size_t size)
244 if (ws->type == none) {
245 websock_drop(ws->ws);
246 websock_close_code(ws->ws, WEBSOCKET_CODE_PROTOCOL_ERROR);
248 if (!aws_read(ws, size)) {
249 aws_on_close(ws, WEBSOCKET_CODE_ABNORMAL, 0);
251 istxt = ws->type == text;
253 b = aws_pick_buffer(ws);
254 b.buffer[b.size] = 0;
255 (istxt ? ws->itf->on_text : ws->itf->on_binary)(ws->closure, b.buffer, b.size);