static sw_inline void http_client_swString_append_headers(swString* swStr, char* key, zend_size_t key_len, char* data, zend_size_t data_len) { swString_append_ptr(swStr, key, key_len); swString_append_ptr(swStr, ZEND_STRL(": ")); swString_append_ptr(swStr, data, data_len); swString_append_ptr(swStr, ZEND_STRL("\r\n")); }
void swWebSocket_encode(swString *buffer, char *data, size_t length, char opcode, int finish, int mask) { int pos = 0; char frame_header[16]; /** * frame header */ frame_header[pos++] = FRAME_SET_FIN(finish) | FRAME_SET_OPCODE(opcode); if (length < 126) { frame_header[pos++] = FRAME_SET_MASK(mask) | FRAME_SET_LENGTH(length, 0); } else { if (length < 65536) { frame_header[pos++] = FRAME_SET_MASK(mask) | 126; } else { frame_header[pos++] = FRAME_SET_MASK(mask) | 127; frame_header[pos++] = FRAME_SET_LENGTH(length, 7); frame_header[pos++] = FRAME_SET_LENGTH(length, 6); frame_header[pos++] = FRAME_SET_LENGTH(length, 5); frame_header[pos++] = FRAME_SET_LENGTH(length, 4); frame_header[pos++] = FRAME_SET_LENGTH(length, 3); frame_header[pos++] = FRAME_SET_LENGTH(length, 2); } frame_header[pos++] = FRAME_SET_LENGTH(length, 1); frame_header[pos++] = FRAME_SET_LENGTH(length, 0); } swString_append_ptr(buffer, frame_header, pos); /** * frame body */ if (data && length > 0) { if (mask) { char *_mask_data = SW_WEBSOCKET_MASK_DATA; swString_append_ptr(buffer, _mask_data, SW_WEBSOCKET_MASK_LEN); char *_data = buffer->str + buffer->length; swString_append_ptr(buffer, data, length); int i; for (i = 0; i < length; i++) { _data[i] ^= _mask_data[i % SW_WEBSOCKET_MASK_LEN]; } } else { swString_append_ptr(buffer, data, length); } } }
static int websocket_handshake(swoole_http_client *client) { #if PHP_MAJOR_VERSION < 7 TSRMLS_FETCH_FROM_CTX(sw_thread_ctx ? sw_thread_ctx : NULL); #endif zval *header = client->request.zheader; HashTable *ht = Z_ARRVAL_P(header); zval *pData; if (sw_zend_hash_find(ht, ZEND_STRS("sec-websocket-key"), (void **) &pData) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "header no sec-websocket-key"); return SW_ERR; } convert_to_string(pData); swString_clear(swoole_http_buffer); swString_append_ptr(swoole_http_buffer, ZEND_STRL("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n")); int n; char sec_websocket_accept[128]; memcpy(sec_websocket_accept, Z_STRVAL_P(pData), Z_STRLEN_P(pData)); memcpy(sec_websocket_accept + Z_STRLEN_P(pData), SW_WEBSOCKET_GUID, sizeof(SW_WEBSOCKET_GUID) - 1); char sha1_str[20]; bzero(sha1_str, sizeof(sha1_str)); sha1(sec_websocket_accept, Z_STRLEN_P(pData) + sizeof(SW_WEBSOCKET_GUID) - 1, (unsigned char *) sha1_str); char encoded_str[50]; bzero(encoded_str, sizeof(encoded_str)); n = swBase64_encode((unsigned char *) sha1_str, sizeof(sha1_str), encoded_str); char _buf[128]; n = snprintf(_buf, sizeof(_buf), "Sec-WebSocket-Accept: %*s\r\n", n, encoded_str); swString_append_ptr(swoole_http_buffer, _buf, n); swString_append_ptr(swoole_http_buffer, ZEND_STRL("Sec-WebSocket-Version: "SW_WEBSOCKET_VERSION"\r\n")); swString_append_ptr(swoole_http_buffer, ZEND_STRL("Server: "SW_WEBSOCKET_SERVER_SOFTWARE"\r\n\r\n")); swTrace("websocket header len:%ld\n%s \n", swoole_http_buffer->length, swoole_http_buffer->str); return swServer_tcp_send(SwooleG.serv, client->fd, swoole_http_buffer->str, swoole_http_buffer->length); }
static int websocket_handshake(http_client *client) { //HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\nSec-WebSocket-Version: %s\r\nKeepAlive: off\r\nContent-Length: 0\r\nServer: ZWebSocket\r\n TSRMLS_FETCH_FROM_CTX(sw_thread_ctx ? sw_thread_ctx : NULL); zval *header = zend_read_property(swoole_http_request_class_entry_ptr, client->zrequest, ZEND_STRL("header"), 1 TSRMLS_CC); HashTable *ht = Z_ARRVAL_P(header); zval **pData; if(zend_hash_find(ht, ZEND_STRS("sec-websocket-key") , (void **) &pData) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "header no sec-websocket-key"); return SW_ERR; } convert_to_string(*pData); // swTrace("key: %s len:%d\n", Z_STRVAL_PP(pData), Z_STRLEN_PP(pData)); swString *buf = swString_new(256); swString_append_ptr(buf, ZEND_STRL("HTTP/1.1 101 Switching Protocols\r\n")); swString_append_ptr(buf, ZEND_STRL("Upgrade: websocket\r\nConnection: Upgrade\r\n")); swString *shaBuf = swString_new(Z_STRLEN_PP(pData)+36); swString_append_ptr(shaBuf, Z_STRVAL_PP(pData), Z_STRLEN_PP(pData)); swString_append_ptr(shaBuf, ZEND_STRL(SW_WEBSOCKET_GUID)); char data_str[20]; // bzero(data_str, sizeof(data_str)); // swTrace("sha1 start:%s\n", shaBuf->str); sha1(shaBuf->str, (unsigned char *) data_str); char encoded_value[50]; bzero(encoded_value, sizeof(encoded_value)); // swTrace("base64_encode start:%d\n", sizeof(data_str)); swBase64_encode((unsigned char *) data_str, 20, encoded_value); // swTrace("base64_encode end:%s %d %d\n", encoded_value, encoded_len, strlen(encoded_value)); char _buf[128]; int n = 0; n = snprintf(_buf, strlen(encoded_value)+25, "Sec-WebSocket-Accept: %s\r\n", encoded_value); // efree(data_str); // efree(encoded_value); swString_free(shaBuf); // swTrace("accept value: %s\n", _buf); swString_append_ptr(buf, _buf, n); swString_append_ptr(buf, ZEND_STRL("Sec-WebSocket-Version: 13\r\n")); swString_append_ptr(buf, ZEND_STRL("Server: swoole-websocket\r\n\r\n")); swTrace("websocket header len:%zd\n%s \n", buf->length, buf->str); int ret = swServer_tcp_send(SwooleG.serv, client->fd, buf->str, buf->length); swString_free(buf); // swTrace("handshake send: %d lenght: %d\n", client->fd, ret); return ret; }
int swWorker_onTask(swFactory *factory, swEventData *task) { swServer *serv = factory->ptr; swString *package = NULL; swDgramPacket *header; #ifdef SW_USE_OPENSSL swConnection *conn; #endif factory->last_from_id = task->info.from_id; //worker busy serv->workers[SwooleWG.id].status = SW_WORKER_BUSY; switch (task->info.type) { //no buffer case SW_EVENT_TCP: //ringbuffer shm package case SW_EVENT_PACKAGE: //discard data if (swWorker_discard_data(serv, task) == SW_TRUE) { break; } do_task: { serv->onReceive(serv, task); SwooleWG.request_count++; sw_atomic_fetch_add(&SwooleStats->request_count, 1); } if (task->info.type == SW_EVENT_PACKAGE_END) { package->length = 0; } break; //chunk package case SW_EVENT_PACKAGE_START: case SW_EVENT_PACKAGE_END: //discard data if (swWorker_discard_data(serv, task) == SW_TRUE) { break; } package = swWorker_get_buffer(serv, task->info.from_id); //merge data to package buffer memcpy(package->str + package->length, task->data, task->info.len); package->length += task->info.len; //package end if (task->info.type == SW_EVENT_PACKAGE_END) { goto do_task; } break; case SW_EVENT_UDP: case SW_EVENT_UDP6: case SW_EVENT_UNIX_DGRAM: package = swWorker_get_buffer(serv, task->info.from_id); swString_append_ptr(package, task->data, task->info.len); if (package->offset == 0) { header = (swDgramPacket *) package->str; package->offset = header->length; } //one packet if (package->offset == package->length - sizeof(swDgramPacket)) { SwooleWG.request_count++; sw_atomic_fetch_add(&SwooleStats->request_count, 1); serv->onPacket(serv, task); swString_clear(package); } break; case SW_EVENT_CLOSE: #ifdef SW_USE_OPENSSL conn = swServer_connection_verify(serv, task->info.fd); if (conn && conn->ssl_client_cert.length) { free(conn->ssl_client_cert.str); bzero(&conn->ssl_client_cert, sizeof(conn->ssl_client_cert.str)); } #endif factory->end(factory, task->info.fd); break; case SW_EVENT_CONNECT: #ifdef SW_USE_OPENSSL //SSL client certificate if (task->info.len > 0) { conn = swServer_connection_verify(serv, task->info.fd); conn->ssl_client_cert.str = strndup(task->data, task->info.len); conn->ssl_client_cert.size = conn->ssl_client_cert.length = task->info.len; } #endif if (serv->onConnect) { serv->onConnect(serv, &task->info); } break; case SW_EVENT_FINISH: serv->onFinish(serv, task); break; case SW_EVENT_PIPE_MESSAGE: serv->onPipeMessage(serv, task); break; default: swWarn("[Worker] error event[type=%d]", (int )task->info.type); break; } //worker idle serv->workers[SwooleWG.id].status = SW_WORKER_IDLE; //maximum number of requests, process will exit. if (!SwooleWG.run_always && SwooleWG.request_count >= SwooleWG.max_request) { SwooleG.running = 0; SwooleG.main_reactor->running = 0; } return SW_OK; }
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; }
/** * For Http Protocol */ static int swPort_onRead_http(swReactor *reactor, swListenPort *port, swEvent *event) { swConnection *conn = event->socket; swServer *serv = reactor->ptr; if (conn->websocket_status >= WEBSOCKET_STATUS_HANDSHAKE) { if (conn->http_upgrade == 0) { swHttpRequest_free(conn); conn->websocket_status = WEBSOCKET_STATUS_ACTIVE; conn->http_upgrade = 1; } return swPort_onRead_check_length(reactor, port, event); } #ifdef SW_USE_HTTP2 if (conn->http2_stream) { _parse_frame: return swPort_onRead_check_length(reactor, port, event); } #endif int n = 0; char *buf; int buf_len; swHttpRequest *request = NULL; swProtocol *protocol = &port->protocol; //new http request if (conn->object == NULL) { request = sw_malloc(sizeof(swHttpRequest)); bzero(request, sizeof(swHttpRequest)); conn->object = request; } else { request = (swHttpRequest *) conn->object; } if (!request->buffer) { request->buffer = swString_new(SW_HTTP_HEADER_MAX_SIZE); //alloc memory failed. if (!request->buffer) { swReactorThread_onClose(reactor, event); return SW_ERR; } } swString *buffer = request->buffer; recv_data: buf = buffer->str + buffer->length; buf_len = buffer->size - buffer->length; n = swConnection_recv(conn, buf, buf_len, 0); if (n < 0) { switch (swConnection_error(errno)) { case SW_ERROR: swSysError("recv from connection#%d failed.", event->fd); return SW_OK; case SW_CLOSE: conn->close_errno = errno; goto close_fd; default: return SW_OK; } } else if (n == 0) { close_fd: swHttpRequest_free(conn); swReactorThread_onClose(reactor, event); return SW_OK; } else { buffer->length += n; if (request->method == 0 && swHttpRequest_get_protocol(request) < 0) { if (request->excepted == 0 && request->buffer->length < SW_HTTP_HEADER_MAX_SIZE) { return SW_OK; } swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, "get protocol failed."); #ifdef SW_HTTP_BAD_REQUEST_TIP if (swConnection_send(conn, SW_STRL(SW_HTTP_BAD_REQUEST_TIP), 0) < 0) { swSysError("send() failed."); } #endif goto close_fd; } if (request->method > HTTP_PRI) { swWarn("method no support"); goto close_fd; } #ifdef SW_USE_HTTP2 else if (request->method == HTTP_PRI) { conn->http2_stream = 1; swHttp2_send_setting_frame(protocol, conn); if (n == sizeof(SW_HTTP2_PRI_STRING) - 1) { swHttpRequest_free(conn); return SW_OK; } swString *buffer = swServer_get_buffer(serv, event->fd); if (!buffer) { goto close_fd; } swString_append_ptr(buffer, buf + (sizeof(SW_HTTP2_PRI_STRING) - 1), n - (sizeof(SW_HTTP2_PRI_STRING) - 1)); swHttpRequest_free(conn); conn->skip_recv = 1; goto _parse_frame; } #endif //http header is not the end if (request->header_length == 0) { if (swHttpRequest_get_header_length(request) < 0) { if (buffer->size == buffer->length) { swWarn("[2]http header is too long."); goto close_fd; } else { goto recv_data; } } } //http body if (request->content_length == 0) { swTraceLog(SW_TRACE_SERVER, "content-length=%u, keep-alive=%d", request->content_length, request->keep_alive); // content length field not found if (swHttpRequest_get_header_info(request) < 0) { /* the request is really no body */ if (buffer->length == request->header_length) { /** * send static file content directly in the reactor thread */ if (!(serv->enable_static_handler && swPort_http_static_handler(request, conn))) { /** * dynamic request, dispatch to worker */ swReactorThread_dispatch(conn, buffer->str, buffer->length); } swHttpRequest_free(conn); return SW_OK; } else if (buffer->size == buffer->length) { swWarn("[0]http header is too long."); goto close_fd; } /* wait more data */ else { goto recv_data; } } else if (request->content_length > (protocol->package_max_length - request->header_length)) { swWarn("Content-Length is too big, MaxSize=[%d].", protocol->package_max_length - request->header_length); goto close_fd; } } //total length uint32_t request_size = request->header_length + request->content_length; if (request_size > buffer->size && swString_extend(buffer, request_size) < 0) { goto close_fd; } //discard the redundant data if (buffer->length > request_size) { buffer->length = request_size; } if (buffer->length == request_size) { swReactorThread_dispatch(conn, buffer->str, buffer->length); swHttpRequest_free(conn); } else { #ifdef SW_HTTP_100_CONTINUE //Expect: 100-continue if (swHttpRequest_has_expect_header(request)) { swSendData _send; _send.data = "HTTP/1.1 100 Continue\r\n\r\n"; _send.length = strlen(_send.data); int send_times = 0; direct_send: n = swConnection_send(conn, _send.data, _send.length, 0); if (n < _send.length) { _send.data += n; _send.length -= n; send_times++; if (send_times < 10) { goto direct_send; } else { swWarn("send http header failed"); } } } else { swTrace("PostWait: request->content_length=%d, buffer->length=%zd, request->header_length=%d\n", request->content_length, buffer->length, request->header_length); } #endif goto recv_data; } } return SW_OK; }