/* function sendBlock(content, options): Number */ static EjsNumber *ws_sendBlock(Ejs *ejs, EjsWebSocket *ws, int argc, EjsObj **argv) { EjsByteArray *ba; EjsAny *content, *vp; ssize nbytes; cchar *str; int last, mode, type, flags; assert(argc == 2); if (ws->conn->state < HTTP_STATE_PARSED && !waitForHttpState(ws, HTTP_STATE_PARSED, -1, 1)) { return ESV(null); } content = argv[0]; last = ejsGetPropertyByName(ejs, argv[1], EN("last")) != ESV(false); if ((vp = ejsGetPropertyByName(ejs, argv[1], EN("mode"))) != 0) { mode = (int) ejsGetNumber(ejs, vp); if (mode != HTTP_BUFFER && mode != HTTP_BLOCK && mode != HTTP_NON_BLOCK) { ejsThrowArgError(ejs, "Bad message mode"); return 0; } } else { mode = HTTP_BUFFER; } if ((vp = ejsGetPropertyByName(ejs, argv[1], EN("type"))) != 0) { type = (int) ejsGetNumber(ejs, vp); if (type != WS_MSG_CONT && type != WS_MSG_TEXT && type != WS_MSG_BINARY) { ejsThrowArgError(ejs, "Bad message type"); return 0; } } else { type = WS_MSG_TEXT; } flags = mode; if (!last) { flags |= HTTP_MORE; } if (ejsIs(ejs, content, ByteArray)) { ba = (EjsByteArray*) content; nbytes = ejsGetByteArrayAvailableData(ba); nbytes = httpSendBlock(ws->conn, type, (cchar*) &ba->value[ba->readPosition], nbytes, flags); } else { str = ejsToMulti(ejs, content); nbytes = httpSendBlock(ws->conn, type, str, slen(str), flags); } if (nbytes < 0) { ejsThrowIOError(ejs, "Cannot send block"); return 0; } return ejsCreateNumber(ejs, (MprNumber) nbytes); }
/* function send(...content): Number */ static EjsNumber *ws_send(Ejs *ejs, EjsWebSocket *ws, int argc, EjsObj **argv) { EjsArray *args; EjsByteArray *ba; EjsAny *arg; ssize nbytes; int i; args = (EjsArray*) argv[0]; if (ws->conn->state < HTTP_STATE_PARSED && !waitForHttpState(ws, HTTP_STATE_PARSED, -1, 1)) { return ESV(null); } nbytes = 0; for (i = 0; i < args->length; i++) { if ((arg = ejsGetProperty(ejs, args, i)) != 0) { if (ejsIs(ejs, arg, ByteArray)) { ba = (EjsByteArray*) arg; nbytes = ejsGetByteArrayAvailableData(ba); nbytes = httpSendBlock(ws->conn, WS_MSG_BINARY, (cchar*) &ba->value[ba->readPosition], nbytes, HTTP_BLOCK); } else { nbytes = httpSend(ws->conn, ejsToMulti(ejs, arg)); } if (nbytes < 0) { return ESV(null); } } } return ejsCreateNumber(ejs, (MprNumber) nbytes); }
/* Send message to a connection */ static void chat(Msg *msg) { HttpConn *conn; HttpPacket *packet; conn = msg->conn; packet = msg->packet; httpSendBlock(conn, packet->type, httpGetPacketStart(packet), httpGetPacketLength(packet), 0); }
/* Event callback. Invoked for incoming web socket messages and other events of interest. We're interested in the WRITABLE event. */ static void output_callback(HttpConn *conn, int event, int arg) { Output *output; ssize len, wrote; int flags, type; char buf[MPR_BUFSIZE]; /* Get a writable event when the socket can absorb more data */ if (event == HTTP_EVENT_WRITABLE) { output = getData(); do { if ((len = mprReadFile(output->file, buf, sizeof(buf))) > 0) { /* Set the HTTP_MORE flag on every write except the last. This means each write is sent as a separate frame. The first frame has the type of WS_MSG_TEXT, all others must be continuation frames. */ flags = HTTP_NON_BLOCK; if ((output->written + len) < output->info.size) { flags |= HTTP_MORE; } type = output->written == 0 ? WS_MSG_TEXT : WS_MSG_CONT; /* Send the next chunk as a WebSockets frame using a non-blocking write. This may return having written only a portion of the requested data. */ if ((wrote = httpSendBlock(conn, type, buf, len, flags)) < 0) { httpError(conn, HTTP_CODE_INTERNAL_SERVER_ERROR, "Cannot send message of %d bytes", len); return; } output->written += wrote; if (wrote < len) { /* Reposition if the send returned having written less than requested */ mprSeekFile(output->file, SEEK_CUR, wrote - len); break; } } else { httpSendClose(conn, WS_STATUS_OK, "OK"); break; } } while (len > 0); } else if (event == HTTP_EVENT_APP_CLOSE) { mprLog(0, "output.c: close event. Status status %d, orderly closed %d, reason %s", arg, httpWebSocketOrderlyClosed(conn), httpGetWebSocketCloseReason(conn)); } else if (event == HTTP_EVENT_ERROR) { mprLog(0, "output.c: error event"); } }
/* Event callback. Invoked for incoming web socket messages and other events of interest. */ static void echo_callback(HttpConn *conn, int event, int arg) { HttpPacket *packet; if (event == HTTP_EVENT_READABLE) { /* Grab the packet off the read queue. */ packet = httpGetPacket(conn->readq); if (packet->type == WS_MSG_TEXT || packet->type == WS_MSG_BINARY) { /* Echo back the contents */ httpSendBlock(conn, packet->type, httpGetPacketStart(packet), httpGetPacketLength(packet), 0); } } else if (event == HTTP_EVENT_APP_CLOSE) { mprLog("info echo", 0, "close event. Status status %d, orderly closed %d, reason %s", arg, httpWebSocketOrderlyClosed(conn), httpGetWebSocketCloseReason(conn)); } else if (event == HTTP_EVENT_ERROR) { mprLog("info echo", 0, "error event"); } }