static int swPort_websocket_onPackage(swConnection *conn, char *data, uint32_t length) { swString frame; bzero(&frame, sizeof(frame)); frame.str = data; frame.length = length; swString send_frame; bzero(&send_frame, sizeof(send_frame)); char buf[32]; send_frame.str = buf; send_frame.size = sizeof(buf); swWebSocket_frame ws; swWebSocket_decode(&ws, &frame); size_t offset; switch (ws.header.OPCODE) { case WEBSOCKET_OPCODE_CONTINUATION_FRAME: case WEBSOCKET_OPCODE_TEXT_FRAME: case WEBSOCKET_OPCODE_BINARY_FRAME: offset = length - ws.payload_length - 2; data[offset] = ws.header.FIN; data[offset + 1] = ws.header.OPCODE; swReactorThread_dispatch(conn, data + offset, length - offset); break; case WEBSOCKET_OPCODE_PING: if (length == 2) { return SW_ERR; } swWebSocket_encode(&send_frame, data += 2, length - 2, WEBSOCKET_OPCODE_PONG, 1, 0); swConnection_send(conn, send_frame.str, send_frame.length, 0); break; case WEBSOCKET_OPCODE_PONG: return SW_ERR; case WEBSOCKET_OPCODE_CONNECTION_CLOSE: if (0x7d < (length - 2)) { return SW_ERR; } send_frame.str[0] = 0x88; send_frame.str[1] = 0x00; send_frame.length = 2; swConnection_send(conn, send_frame.str, 2, 0); return SW_ERR; } return SW_OK; }
static PHP_METHOD(swoole_websocket_server, push) { zval *zdata; long fd = 0; long opcode = WEBSOCKET_OPCODE_TEXT_FRAME; zend_bool fin = 1; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz|lb", &fd, &zdata, &opcode, &fin) == FAILURE) { return; } if (fd <= 0) { swoole_php_fatal_error(E_WARNING, "fd[%d] is invalid.", (int )fd); RETURN_FALSE; } if (opcode > WEBSOCKET_OPCODE_PONG) { swoole_php_fatal_error(E_WARNING, "opcode max 10"); RETURN_FALSE; } char *data; int length = php_swoole_get_send_data(zdata, &data TSRMLS_CC); if (length < 0) { RETURN_FALSE; } else if (length == 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "data is empty."); RETURN_FALSE; } swConnection *conn = swWorker_get_connection(SwooleG.serv, fd); if (!conn || conn->websocket_status < WEBSOCKET_STATUS_HANDSHAKE) { swoole_php_fatal_error(E_WARNING, "connection[%d] is not a websocket client.", (int ) fd); RETURN_FALSE; } swString_clear(swoole_http_buffer); swWebSocket_encode(swoole_http_buffer, data, length, opcode, (int) fin, 0); SW_CHECK_RETURN(swServer_tcp_send(SwooleG.serv, fd, swoole_http_buffer->str, swoole_http_buffer->length)); }
int swWebSocket_dispatch_frame(swConnection *conn, char *data, uint32_t length) { swString frame; bzero(&frame, sizeof(frame)); frame.str = data; frame.length = length; swString send_frame; bzero(&send_frame, sizeof(send_frame)); char buf[128]; send_frame.str = buf; send_frame.size = sizeof(buf); swWebSocket_frame ws; swWebSocket_decode(&ws, &frame); swString *frame_buffer; int frame_length; swListenPort *port; size_t offset; switch (ws.header.OPCODE) { case WEBSOCKET_OPCODE_CONTINUATION_FRAME: frame_buffer = conn->websocket_buffer; if (frame_buffer == NULL) { swWarn("bad frame[opcode=0]. remote_addr=%s:%d.", swConnection_get_ip(conn), swConnection_get_port(conn)); return SW_ERR; } offset = length - ws.payload_length; frame_length = length - offset; port = swServer_get_port(SwooleG.serv, conn->fd); //frame data overflow if (frame_buffer->length + frame_length > port->protocol.package_max_length) { swWarn("websocket frame is too big, remote_addr=%s:%d.", swConnection_get_ip(conn), swConnection_get_port(conn)); return SW_ERR; } //merge incomplete data swString_append_ptr(frame_buffer, data + offset, frame_length); //frame is finished, do dispatch if (ws.header.FIN) { swReactorThread_dispatch(conn, frame_buffer->str, frame_buffer->length); swString_free(frame_buffer); conn->websocket_buffer = NULL; } break; case WEBSOCKET_OPCODE_TEXT_FRAME: case WEBSOCKET_OPCODE_BINARY_FRAME: offset = length - ws.payload_length - 2; data[offset] = 1; data[offset + 1] = ws.header.OPCODE; if (!ws.header.FIN) { if (conn->websocket_buffer) { swWarn("merging incomplete frame, bad request. remote_addr=%s:%d.", swConnection_get_ip(conn), swConnection_get_port(conn)); return SW_ERR; } conn->websocket_buffer = swString_dup(data + offset, length - offset); } else { swReactorThread_dispatch(conn, data + offset, length - offset); } break; case WEBSOCKET_OPCODE_PING: if (length >= (sizeof(buf) - 2)) { swWarn("ping frame application data is too big. remote_addr=%s:%d.", swConnection_get_ip(conn), swConnection_get_port(conn)); return SW_ERR; } else if (length == 2) { swWebSocket_encode(&send_frame, NULL, 0, WEBSOCKET_OPCODE_PONG, 1, 0); } else { swWebSocket_encode(&send_frame, data += 2, length - 2, WEBSOCKET_OPCODE_PONG, 1, 0); } swConnection_send(conn, send_frame.str, send_frame.length, 0); break; case WEBSOCKET_OPCODE_PONG: break; case WEBSOCKET_OPCODE_CONNECTION_CLOSE: if (0x7d < (length - 2)) { return SW_ERR; } send_frame.str[0] = 0x88; send_frame.str[1] = 0x00; send_frame.length = 2; swConnection_send(conn, send_frame.str, 2, 0); return SW_ERR; default: swWarn("unknown opcode [%d].", ws.header.OPCODE); break; } return SW_OK; }