00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "asterisk.h"
00031
00032 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 417310 $")
00033
00034 #include "asterisk/module.h"
00035 #include "asterisk/http.h"
00036 #include "asterisk/astobj2.h"
00037 #include "asterisk/strings.h"
00038 #include "asterisk/file.h"
00039 #include "asterisk/unaligned.h"
00040
00041 #define AST_API_MODULE
00042 #include "asterisk/http_websocket.h"
00043
00044
00045 #define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
00046
00047
00048 #define MAX_PROTOCOL_BUCKETS 7
00049
00050
00051 #define MAXIMUM_FRAME_SIZE 8192
00052
00053
00054
00055
00056 #define DEFAULT_RECONSTRUCTION_CEILING 16384
00057
00058
00059 #define MAXIMUM_RECONSTRUCTION_CEILING 16384
00060
00061
00062
00063
00064
00065
00066
00067
00068 #define MAX_WS_HDR_SZ 14
00069 #define MIN_WS_HDR_SZ 2
00070
00071
00072 struct ast_websocket {
00073 FILE *f;
00074 int fd;
00075 struct ast_sockaddr address;
00076 enum ast_websocket_opcode opcode;
00077 size_t payload_len;
00078 char *payload;
00079 size_t reconstruct;
00080 int timeout;
00081 unsigned int secure:1;
00082 unsigned int closing:1;
00083 unsigned int close_sent:1;
00084 };
00085
00086
00087 struct websocket_protocol {
00088 char *name;
00089 ast_websocket_callback callback;
00090 };
00091
00092
00093 static struct ao2_container *protocols;
00094
00095
00096 static int protocol_hash_fn(const void *obj, const int flags)
00097 {
00098 const struct websocket_protocol *protocol = obj;
00099 const char *name = obj;
00100
00101 return ast_str_case_hash(flags & OBJ_KEY ? name : protocol->name);
00102 }
00103
00104
00105 static int protocol_cmp_fn(void *obj, void *arg, int flags)
00106 {
00107 const struct websocket_protocol *protocol1 = obj, *protocol2 = arg;
00108 const char *protocol = arg;
00109
00110 return !strcasecmp(protocol1->name, flags & OBJ_KEY ? protocol : protocol2->name) ? CMP_MATCH | CMP_STOP : 0;
00111 }
00112
00113
00114 static void protocol_destroy_fn(void *obj)
00115 {
00116 struct websocket_protocol *protocol = obj;
00117 ast_free(protocol->name);
00118 }
00119
00120
00121 static void session_destroy_fn(void *obj)
00122 {
00123 struct ast_websocket *session = obj;
00124
00125 ast_websocket_close(session, 0);
00126
00127 if (session->f) {
00128 fclose(session->f);
00129 ast_verb(2, "WebSocket connection from '%s' closed\n", ast_sockaddr_stringify(&session->address));
00130 }
00131
00132 ast_free(session->payload);
00133 }
00134
00135 int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_websocket_callback callback)
00136 {
00137 struct websocket_protocol *protocol;
00138
00139 ao2_lock(protocols);
00140
00141
00142 if ((protocol = ao2_find(protocols, name, OBJ_KEY | OBJ_NOLOCK))) {
00143 ao2_ref(protocol, -1);
00144 ao2_unlock(protocols);
00145 return -1;
00146 }
00147
00148 if (!(protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn))) {
00149 ao2_unlock(protocols);
00150 return -1;
00151 }
00152
00153 if (!(protocol->name = ast_strdup(name))) {
00154 ao2_ref(protocol, -1);
00155 ao2_unlock(protocols);
00156 return -1;
00157 }
00158
00159 protocol->callback = callback;
00160
00161 ao2_link_flags(protocols, protocol, OBJ_NOLOCK);
00162 ao2_unlock(protocols);
00163 ao2_ref(protocol, -1);
00164
00165 ast_verb(2, "WebSocket registered sub-protocol '%s'\n", name);
00166
00167 return 0;
00168 }
00169
00170 int AST_OPTIONAL_API_NAME(ast_websocket_remove_protocol)(const char *name, ast_websocket_callback callback)
00171 {
00172 struct websocket_protocol *protocol;
00173
00174 if (!(protocol = ao2_find(protocols, name, OBJ_KEY))) {
00175 return -1;
00176 }
00177
00178 if (protocol->callback != callback) {
00179 ao2_ref(protocol, -1);
00180 return -1;
00181 }
00182
00183 ao2_unlink(protocols, protocol);
00184 ao2_ref(protocol, -1);
00185
00186 ast_verb(2, "WebSocket unregistered sub-protocol '%s'\n", name);
00187
00188 return 0;
00189 }
00190
00191
00192 int AST_OPTIONAL_API_NAME(ast_websocket_close)(struct ast_websocket *session, uint16_t reason)
00193 {
00194 char frame[4] = { 0, };
00195 int res;
00196
00197 if (session->close_sent) {
00198 return 0;
00199 }
00200
00201 frame[0] = AST_WEBSOCKET_OPCODE_CLOSE | 0x80;
00202 frame[1] = 2;
00203
00204
00205 put_unaligned_uint16(&frame[2], htons(reason ? reason : 1000));
00206
00207 session->closing = 1;
00208 session->close_sent = 1;
00209
00210 ao2_lock(session);
00211 res = ast_careful_fwrite(session->f, session->fd, frame, 4, session->timeout);
00212 ao2_unlock(session);
00213
00214 return res;
00215 }
00216
00217
00218
00219 int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t actual_length)
00220 {
00221 size_t header_size = 2;
00222 char *frame;
00223 uint64_t length = 0;
00224
00225 if (actual_length < 126) {
00226 length = actual_length;
00227 } else if (actual_length < (1 << 16)) {
00228 length = 126;
00229
00230 header_size += 2;
00231 } else {
00232 length = 127;
00233
00234 header_size += 8;
00235 }
00236
00237 frame = ast_alloca(header_size);
00238 memset(frame, 0, sizeof(*frame));
00239
00240 frame[0] = opcode | 0x80;
00241 frame[1] = length;
00242
00243
00244 if (length == 126) {
00245 put_unaligned_uint16(&frame[2], htons(actual_length));
00246 } else if (length == 127) {
00247 put_unaligned_uint64(&frame[2], htonl(actual_length));
00248 }
00249
00250 ao2_lock(session);
00251 if (session->closing) {
00252 ao2_unlock(session);
00253 return -1;
00254 }
00255
00256 if (ast_careful_fwrite(session->f, session->fd, frame, header_size, session->timeout)) {
00257 ao2_unlock(session);
00258 return -1;
00259 }
00260
00261 if (ast_careful_fwrite(session->f, session->fd, payload, actual_length, session->timeout)) {
00262 ao2_unlock(session);
00263 return -1;
00264 }
00265 fflush(session->f);
00266 ao2_unlock(session);
00267
00268 return 0;
00269 }
00270
00271 void AST_OPTIONAL_API_NAME(ast_websocket_reconstruct_enable)(struct ast_websocket *session, size_t bytes)
00272 {
00273 session->reconstruct = MIN(bytes, MAXIMUM_RECONSTRUCTION_CEILING);
00274 }
00275
00276 void AST_OPTIONAL_API_NAME(ast_websocket_reconstruct_disable)(struct ast_websocket *session)
00277 {
00278 session->reconstruct = 0;
00279 }
00280
00281 void AST_OPTIONAL_API_NAME(ast_websocket_ref)(struct ast_websocket *session)
00282 {
00283 ao2_ref(session, +1);
00284 }
00285
00286 void AST_OPTIONAL_API_NAME(ast_websocket_unref)(struct ast_websocket *session)
00287 {
00288 ao2_ref(session, -1);
00289 }
00290
00291 int AST_OPTIONAL_API_NAME(ast_websocket_fd)(struct ast_websocket *session)
00292 {
00293 return session->closing ? -1 : session->fd;
00294 }
00295
00296 struct ast_sockaddr * AST_OPTIONAL_API_NAME(ast_websocket_remote_address)(struct ast_websocket *session)
00297 {
00298 return &session->address;
00299 }
00300
00301 int AST_OPTIONAL_API_NAME(ast_websocket_is_secure)(struct ast_websocket *session)
00302 {
00303 return session->secure;
00304 }
00305
00306 int AST_OPTIONAL_API_NAME(ast_websocket_set_nonblock)(struct ast_websocket *session)
00307 {
00308 int flags;
00309
00310 if ((flags = fcntl(session->fd, F_GETFL)) == -1) {
00311 return -1;
00312 }
00313
00314 flags |= O_NONBLOCK;
00315
00316 if ((flags = fcntl(session->fd, F_SETFL, flags)) == -1) {
00317 return -1;
00318 }
00319
00320 return 0;
00321 }
00322
00323 int AST_OPTIONAL_API_NAME(ast_websocket_set_timeout)(struct ast_websocket *session, int timeout)
00324 {
00325 session->timeout = timeout;
00326
00327 return 0;
00328 }
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358 static inline int ws_safe_read(struct ast_websocket *session, char *buf, int len, enum ast_websocket_opcode *opcode)
00359 {
00360 int sanity;
00361 size_t rlen;
00362 int xlen = len;
00363 char *rbuf = buf;
00364 for (sanity = 10; sanity; sanity--) {
00365 clearerr(session->f);
00366 rlen = fread(rbuf, 1, xlen, session->f);
00367 if (0 == rlen && ferror(session->f) && errno != EAGAIN) {
00368 ast_log(LOG_ERROR, "Error reading from web socket: %s\n", strerror(errno));
00369 (*opcode) = AST_WEBSOCKET_OPCODE_CLOSE;
00370 session->closing = 1;
00371 return -1;
00372 }
00373 xlen = (xlen - rlen);
00374 rbuf = rbuf + rlen;
00375 if (0 == xlen) {
00376 break;
00377 }
00378 if (ast_wait_for_input(session->fd, 1000) < 0) {
00379 ast_log(LOG_ERROR, "ast_wait_for_input returned err: %s\n", strerror(errno));
00380 (*opcode) = AST_WEBSOCKET_OPCODE_CLOSE;
00381 session->closing = 1;
00382 return -1;
00383 }
00384 }
00385 if (!sanity) {
00386 ast_log(LOG_WARNING, "Websocket seems unresponsive, disconnecting ...\n");
00387 (*opcode) = AST_WEBSOCKET_OPCODE_CLOSE;
00388 session->closing = 1;
00389 return -1;
00390 }
00391 return 0;
00392 }
00393
00394 int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented)
00395 {
00396 char buf[MAXIMUM_FRAME_SIZE] = "";
00397 int fin = 0;
00398 int mask_present = 0;
00399 char *mask = NULL, *new_payload = NULL;
00400 size_t options_len = 0, frame_size = 0;
00401
00402 *payload = NULL;
00403 *payload_len = 0;
00404 *fragmented = 0;
00405
00406 if (ws_safe_read(session, &buf[0], MIN_WS_HDR_SZ, opcode)) {
00407 return 0;
00408 }
00409 frame_size += MIN_WS_HDR_SZ;
00410
00411
00412 *opcode = buf[0] & 0xf;
00413 *payload_len = buf[1] & 0x7f;
00414 if (*opcode == AST_WEBSOCKET_OPCODE_TEXT || *opcode == AST_WEBSOCKET_OPCODE_BINARY || *opcode == AST_WEBSOCKET_OPCODE_CONTINUATION ||
00415 *opcode == AST_WEBSOCKET_OPCODE_PING || *opcode == AST_WEBSOCKET_OPCODE_PONG) {
00416 fin = (buf[0] >> 7) & 1;
00417 mask_present = (buf[1] >> 7) & 1;
00418
00419
00420 options_len += mask_present ? 4 : 0;
00421 options_len += (*payload_len == 126) ? 2 : (*payload_len == 127) ? 8 : 0;
00422 if (options_len) {
00423
00424 if (ws_safe_read(session, &buf[frame_size], options_len, opcode)) {
00425 return 0;
00426 }
00427 frame_size += options_len;
00428 }
00429
00430 if (*payload_len == 126) {
00431
00432 *payload_len = ntohs(get_unaligned_uint16(&buf[2]));
00433 mask = &buf[4];
00434 } else if (*payload_len == 127) {
00435
00436 *payload_len = ntohl(get_unaligned_uint64(&buf[2]));
00437 mask = &buf[10];
00438 } else {
00439
00440 mask = &buf[2];
00441 }
00442
00443
00444 *payload = &buf[frame_size];
00445 frame_size = frame_size + (*payload_len);
00446 if (frame_size > MAXIMUM_FRAME_SIZE) {
00447 ast_log(LOG_WARNING, "Cannot fit huge websocket frame of %zu bytes\n", frame_size);
00448
00449 ast_websocket_close(session, 1009);
00450 return -1;
00451 }
00452
00453 if (ws_safe_read(session, (*payload), (*payload_len), opcode)) {
00454 return 0;
00455 }
00456
00457
00458 if (mask_present) {
00459 unsigned int pos;
00460 for (pos = 0; pos < *payload_len; pos++) {
00461 (*payload)[pos] ^= mask[pos % 4];
00462 }
00463 }
00464
00465 if (!(new_payload = ast_realloc(session->payload, (session->payload_len + *payload_len)))) {
00466 ast_log(LOG_WARNING, "Failed allocation: %p, %zu, %"PRIu64"\n",
00467 session->payload, session->payload_len, *payload_len);
00468 *payload_len = 0;
00469 ast_websocket_close(session, 1009);
00470 return 0;
00471 }
00472
00473
00474 if ((*opcode == AST_WEBSOCKET_OPCODE_PING) && (ast_websocket_write(session, AST_WEBSOCKET_OPCODE_PONG, *payload, *payload_len))) {
00475 *payload_len = 0;
00476 ast_websocket_close(session, 1009);
00477 return 0;
00478 }
00479
00480 session->payload = new_payload;
00481 memcpy((session->payload + session->payload_len), (*payload), (*payload_len));
00482 session->payload_len += *payload_len;
00483
00484 if (!fin && session->reconstruct && (session->payload_len < session->reconstruct)) {
00485
00486 if (*opcode != AST_WEBSOCKET_OPCODE_CONTINUATION) {
00487 session->opcode = *opcode;
00488 }
00489 *opcode = AST_WEBSOCKET_OPCODE_CONTINUATION;
00490 *payload_len = 0;
00491 *payload = NULL;
00492 } else {
00493 if (*opcode == AST_WEBSOCKET_OPCODE_CONTINUATION) {
00494 if (!fin) {
00495
00496 *fragmented = 1;
00497 } else {
00498
00499 *opcode = session->opcode;
00500 }
00501 }
00502 *payload_len = session->payload_len;
00503 *payload = session->payload;
00504 session->payload_len = 0;
00505 }
00506 } else if (*opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
00507
00508 if ((*payload_len) && (new_payload = ast_realloc(session->payload, *payload_len))) {
00509 if (ws_safe_read(session, &buf[frame_size], (*payload_len), opcode)) {
00510 return 0;
00511 }
00512 session->payload = new_payload;
00513 memcpy(session->payload, &buf[frame_size], *payload_len);
00514 *payload = session->payload;
00515 frame_size += (*payload_len);
00516 }
00517
00518 session->closing = 1;
00519 } else {
00520 ast_log(LOG_WARNING, "WebSocket unknown opcode %u\n", *opcode);
00521
00522
00523 ast_websocket_close(session, 1003);
00524 }
00525
00526 return 0;
00527 }
00528
00529
00530 static int websocket_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
00531 {
00532 struct ast_variable *v;
00533 char *upgrade = NULL, *key = NULL, *key1 = NULL, *key2 = NULL, *protos = NULL, *requested_protocols = NULL, *protocol = NULL;
00534 int version = 0, flags = 1;
00535 struct websocket_protocol *protocol_handler = NULL;
00536 struct ast_websocket *session;
00537
00538
00539 if (method != AST_HTTP_GET) {
00540 ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
00541 return -1;
00542 }
00543
00544
00545 for (v = headers; v; v = v->next) {
00546 if (!strcasecmp(v->name, "Upgrade")) {
00547 upgrade = ast_strip(ast_strdupa(v->value));
00548 } else if (!strcasecmp(v->name, "Sec-WebSocket-Key")) {
00549 key = ast_strip(ast_strdupa(v->value));
00550 } else if (!strcasecmp(v->name, "Sec-WebSocket-Key1")) {
00551 key1 = ast_strip(ast_strdupa(v->value));
00552 } else if (!strcasecmp(v->name, "Sec-WebSocket-Key2")) {
00553 key2 = ast_strip(ast_strdupa(v->value));
00554 } else if (!strcasecmp(v->name, "Sec-WebSocket-Protocol")) {
00555 requested_protocols = ast_strip(ast_strdupa(v->value));
00556 protos = ast_strdupa(requested_protocols);
00557 } else if (!strcasecmp(v->name, "Sec-WebSocket-Version")) {
00558 if (sscanf(v->value, "%30d", &version) != 1) {
00559 version = 0;
00560 }
00561 }
00562 }
00563
00564
00565 if (!upgrade || strcasecmp(upgrade, "websocket")) {
00566 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - did not request WebSocket\n",
00567 ast_sockaddr_stringify(&ser->remote_address));
00568 ast_http_error(ser, 426, "Upgrade Required", NULL);
00569 return -1;
00570 } else if (ast_strlen_zero(requested_protocols)) {
00571 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols requested\n",
00572 ast_sockaddr_stringify(&ser->remote_address));
00573 fputs("HTTP/1.1 400 Bad Request\r\n"
00574 "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
00575 return -1;
00576 } else if (key1 && key2) {
00577
00578
00579 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '00/76' chosen\n",
00580 ast_sockaddr_stringify(&ser->remote_address));
00581 fputs("HTTP/1.1 400 Bad Request\r\n"
00582 "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
00583 return 0;
00584 }
00585
00586
00587 while ((protocol = strsep(&requested_protocols, ","))) {
00588 if ((protocol_handler = ao2_find(protocols, ast_strip(protocol), OBJ_KEY))) {
00589 break;
00590 }
00591 }
00592
00593
00594 if (!protocol_handler) {
00595 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols out of '%s' supported\n",
00596 ast_sockaddr_stringify(&ser->remote_address), protos);
00597 fputs("HTTP/1.1 400 Bad Request\r\n"
00598 "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
00599 return 0;
00600 }
00601
00602
00603 if (version == 7 || version == 8 || version == 13) {
00604
00605
00606
00607 char *combined, base64[64];
00608 unsigned combined_length;
00609 uint8_t sha[20];
00610
00611 combined_length = (key ? strlen(key) : 0) + strlen(WEBSOCKET_GUID) + 1;
00612 if (!key || combined_length > 8192) {
00613 fputs("HTTP/1.1 400 Bad Request\r\n"
00614 "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
00615 ao2_ref(protocol_handler, -1);
00616 return 0;
00617 }
00618
00619 if (!(session = ao2_alloc(sizeof(*session), session_destroy_fn))) {
00620 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted\n",
00621 ast_sockaddr_stringify(&ser->remote_address));
00622 fputs("HTTP/1.1 400 Bad Request\r\n"
00623 "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
00624 ao2_ref(protocol_handler, -1);
00625 return 0;
00626 }
00627 session->timeout = AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT;
00628
00629 combined = ast_alloca(combined_length);
00630 snprintf(combined, combined_length, "%s%s", key, WEBSOCKET_GUID);
00631 ast_sha1_hash_uint(sha, combined);
00632 ast_base64encode(base64, (const unsigned char*)sha, 20, sizeof(base64));
00633
00634 fprintf(ser->f, "HTTP/1.1 101 Switching Protocols\r\n"
00635 "Upgrade: %s\r\n"
00636 "Connection: Upgrade\r\n"
00637 "Sec-WebSocket-Accept: %s\r\n"
00638 "Sec-WebSocket-Protocol: %s\r\n\r\n",
00639 upgrade,
00640 base64,
00641 protocol);
00642 fflush(ser->f);
00643 } else {
00644
00645
00646 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '%d' chosen\n",
00647 ast_sockaddr_stringify(&ser->remote_address), version ? version : 75);
00648 fputs("HTTP/1.1 400 Bad Request\r\n"
00649 "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
00650 ao2_ref(protocol_handler, -1);
00651 return 0;
00652 }
00653
00654
00655 if (setsockopt(ser->fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
00656 ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to enable keepalive\n",
00657 ast_sockaddr_stringify(&ser->remote_address));
00658 fputs("HTTP/1.1 400 Bad Request\r\n"
00659 "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
00660 ao2_ref(session, -1);
00661 ao2_ref(protocol_handler, -1);
00662 return 0;
00663 }
00664
00665 ast_verb(2, "WebSocket connection from '%s' for protocol '%s' accepted using version '%d'\n", ast_sockaddr_stringify(&ser->remote_address), protocol, version);
00666
00667
00668 session->f = ser->f;
00669 session->fd = ser->fd;
00670 ast_sockaddr_copy(&session->address, &ser->remote_address);
00671 session->opcode = -1;
00672 session->reconstruct = DEFAULT_RECONSTRUCTION_CEILING;
00673 session->secure = ser->ssl ? 1 : 0;
00674
00675
00676 protocol_handler->callback(session, get_vars, headers);
00677 ao2_ref(protocol_handler, -1);
00678
00679
00680
00681
00682
00683
00684 ser->f = NULL;
00685 ser->fd = -1;
00686
00687 return 0;
00688 }
00689
00690 static struct ast_http_uri websocketuri = {
00691 .callback = websocket_callback,
00692 .description = "Asterisk HTTP WebSocket",
00693 .uri = "ws",
00694 .has_subtree = 0,
00695 .data = NULL,
00696 .key = __FILE__,
00697 };
00698
00699
00700 static void websocket_echo_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
00701 {
00702 int flags, res;
00703
00704 ast_debug(1, "Entering WebSocket echo loop\n");
00705
00706 if ((flags = fcntl(ast_websocket_fd(session), F_GETFL)) == -1) {
00707 goto end;
00708 }
00709
00710 flags |= O_NONBLOCK;
00711
00712 if (fcntl(ast_websocket_fd(session), F_SETFL, flags) == -1) {
00713 goto end;
00714 }
00715
00716 while ((res = ast_wait_for_input(ast_websocket_fd(session), -1)) > 0) {
00717 char *payload;
00718 uint64_t payload_len;
00719 enum ast_websocket_opcode opcode;
00720 int fragmented;
00721
00722 if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {
00723
00724 ast_log(LOG_WARNING, "Read failure during WebSocket echo loop\n");
00725 break;
00726 }
00727
00728 if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
00729 ast_websocket_write(session, opcode, payload, payload_len);
00730 } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
00731 break;
00732 } else {
00733 ast_debug(1, "Ignored WebSocket opcode %u\n", opcode);
00734 }
00735 }
00736
00737 end:
00738 ast_debug(1, "Exitting WebSocket echo loop\n");
00739 ast_websocket_unref(session);
00740 }
00741
00742 static int load_module(void)
00743 {
00744 protocols = ao2_container_alloc(MAX_PROTOCOL_BUCKETS, protocol_hash_fn, protocol_cmp_fn);
00745 ast_http_uri_link(&websocketuri);
00746 ast_websocket_add_protocol("echo", websocket_echo_callback);
00747
00748 return 0;
00749 }
00750
00751 static int unload_module(void)
00752 {
00753 ast_websocket_remove_protocol("echo", websocket_echo_callback);
00754 ast_http_uri_unlink(&websocketuri);
00755 ao2_ref(protocols, -1);
00756
00757 return 0;
00758 }
00759
00760 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "HTTP WebSocket Support",
00761 .load = load_module,
00762 .unload = unload_module,
00763 .load_pri = AST_MODPRI_CHANNEL_DEPEND,
00764 );