static int websocket_handshake(swListenPort *port, http_context *ctx) { #if PHP_MAJOR_VERSION < 7 TSRMLS_FETCH_FROM_CTX(sw_thread_ctx ? sw_thread_ctx : NULL); #endif zval *header = ctx->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)); php_swoole_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")); if (port->websocket_subprotocol) { swString_append_ptr(swoole_http_buffer, ZEND_STRL("Sec-WebSocket-Protocol: ")); swString_append_ptr(swoole_http_buffer, port->websocket_subprotocol, port->websocket_subprotocol_length); swString_append_ptr(swoole_http_buffer, ZEND_STRL("\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, ctx->fd, swoole_http_buffer->str, swoole_http_buffer->length); }
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)); }
static PHP_METHOD(swoole_mysql, query) { zval *callback; swString sql; bzero(&sql, sizeof(sql)); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &sql.str, &sql.length, &callback) == FAILURE) { return; } if (sql.length <= 0) { swoole_php_fatal_error(E_WARNING, "Query is empty."); RETURN_FALSE; } mysql_client *client = swoole_get_object(getThis()); if (!client) { swoole_php_fatal_error(E_WARNING, "object is not instanceof swoole_mysql."); RETURN_FALSE; } if (!client->cli) { swoole_php_fatal_error(E_WARNING, "mysql connection#%d is closed.", client->fd); RETURN_FALSE; } if (client->state != SW_MYSQL_STATE_QUERY) { swoole_php_fatal_error(E_WARNING, "mysql client is waiting response, cannot send new sql query."); RETURN_FALSE; } sw_zval_add_ref(&callback); client->callback = sw_zval_dup(callback); swString_clear(mysql_request_buffer); if (mysql_request(&sql, mysql_request_buffer) < 0) { RETURN_FALSE; } //send query if (SwooleG.main_reactor->write(SwooleG.main_reactor, client->fd, mysql_request_buffer->str, mysql_request_buffer->length) < 0) { //connection is closed if (swConnection_error(errno) == SW_CLOSE) { zend_update_property_bool(swoole_mysql_class_entry_ptr, getThis(), ZEND_STRL("connected"), 0 TSRMLS_CC); zend_update_property_bool(swoole_mysql_class_entry_ptr, getThis(), ZEND_STRL("errno"), 2006 TSRMLS_CC); } RETURN_FALSE; } else { client->state = SW_MYSQL_STATE_READ_START; RETURN_TRUE; } }
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; }
static sw_inline int swProtocol_split_package_by_eof(swProtocol *protocol, void *object, swString *buffer) { #if 0 static count; count ++; #endif char stack_buf[SW_BUFFER_SIZE_BIG]; int eof_pos; if (buffer->length - buffer->offset < protocol->package_eof_len) { eof_pos = -1; } else { eof_pos = swoole_strnpos(buffer->str + buffer->offset, buffer->length - buffer->offset, protocol->package_eof, protocol->package_eof_len); } //swNotice("#[0] count=%d, length=%ld, size=%ld, offset=%ld", count, buffer->length, buffer->size, buffer->offset); //waiting for more data if (eof_pos < 0) { buffer->offset = buffer->length - protocol->package_eof_len; return buffer->length; } uint32_t length = buffer->offset + eof_pos + protocol->package_eof_len; //swNotice("#[4] count=%d, length=%d", count, length); protocol->onPackage(object, buffer->str, length); //there are remaining data if (length < buffer->length) { uint32_t remaining_length = buffer->length - length; char *remaining_data = buffer->str + length; //swNotice("#[5] count=%d, remaining_length=%d", count, remaining_length); while (1) { if (remaining_length < protocol->package_eof_len) { goto wait_more_data; } eof_pos = swoole_strnpos(remaining_data, remaining_length, protocol->package_eof, protocol->package_eof_len); if (eof_pos < 0) { wait_more_data: //swNotice("#[1] count=%d, remaining_length=%d, length=%d", count, remaining_length, length); memcpy(stack_buf, remaining_data, remaining_length); memcpy(buffer->str, stack_buf, remaining_length); buffer->length = remaining_length; buffer->offset = 0; return remaining_length; } else { length = eof_pos + protocol->package_eof_len; protocol->onPackage(object, remaining_data, length); //swNotice("#[2] count=%d, remaining_length=%d, length=%d", count, remaining_length, length); remaining_data += length; remaining_length -= length; } } } //swNotice("#[3] length=%ld, size=%ld, offset=%ld", buffer->length, buffer->size, buffer->offset); swString_clear(buffer); return 0; }
/** * @return SW_ERR: close the connection * @return SW_OK: continue */ int swProtocol_recv_check_eof(swProtocol *protocol, swConnection *conn, swString *buffer) { int recv_again = SW_FALSE; int buf_size; recv_data: buf_size = buffer->size - buffer->length; char *buf_ptr = buffer->str + buffer->length; if (buf_size > SW_BUFFER_SIZE) { buf_size = SW_BUFFER_SIZE; } int n = swConnection_recv(conn, buf_ptr, buf_size, 0); //swNotice("ReactorThread: recv[len=%d]", n); if (n < 0) { switch (swConnection_error(errno)) { case SW_ERROR: swSysError("recv from socket#%d failed.", conn->fd); return SW_OK; case SW_CLOSE: return SW_ERR; default: return SW_OK; } } else if (n == 0) { return SW_ERR; } else { conn->last_time = SwooleGS->now; buffer->length += n; if (buffer->length < protocol->package_eof_len) { return SW_OK; } if (protocol->split_by_eof) { if (swProtocol_split_package_by_eof(protocol, conn, buffer) == 0) { return SW_OK; } else { recv_again = SW_TRUE; } } else if (memcmp(buffer->str + buffer->length - protocol->package_eof_len, protocol->package_eof, protocol->package_eof_len) == 0) { protocol->onPackage(conn, buffer->str, buffer->length); swString_clear(buffer); return SW_OK; } //over max length, will discard if (buffer->length == protocol->package_max_length) { swWarn("Package is too big. package_length=%d", (int )buffer->length); return SW_ERR; } //buffer is full, may have not read data if (buffer->length == buffer->size) { recv_again = SW_TRUE; if (buffer->size < protocol->package_max_length) { uint32_t extend_size = swoole_size_align(buffer->size * 2, SwooleG.pagesize); if (extend_size > protocol->package_max_length) { extend_size = protocol->package_max_length; } if (swString_extend(buffer, extend_size) < 0) { return SW_ERR; } } } //no eof if (recv_again) { goto recv_data; } } return SW_OK; }
/** * @return SW_ERR: close the connection * @return SW_OK: continue */ int swProtocol_recv_check_length(swProtocol *protocol, swConnection *conn, swString *buffer) { char *recvbuf; uint32_t recvbuf_size; do_recv: recvbuf = buffer->str + buffer->length; recvbuf_size = buffer->offset > 0 ? buffer->offset - buffer->length : protocol->package_length_offset + protocol->package_length_size; int n = swConnection_recv(conn, recvbuf, recvbuf_size, 0); if (n < 0) { switch (swConnection_error(errno)) { case SW_ERROR: swSysError("recv from socket#%d failed.", conn->fd); return SW_OK; case SW_CLOSE: return SW_ERR; default: return SW_OK; } } else if (n == 0) { return SW_ERR; } else { conn->last_time = SwooleGS->now; buffer->length += n; if (conn->recv_wait) { if (buffer->length == buffer->offset) { protocol->onPackage(conn, buffer->str, buffer->length); conn->recv_wait = 0; swString_clear(buffer); } return SW_OK; } else { int package_length = protocol->get_package_length(protocol, conn, buffer->str, buffer->length); //invalid package, close connection. if (package_length < 0) { return SW_ERR; } //no length else if (package_length == 0) { return SW_OK; } //get length success else { if (buffer->size < package_length) { if (swString_extend(buffer, package_length) < 0) { return SW_ERR; } } conn->recv_wait = 1; buffer->offset = package_length + buffer->length; goto do_recv; } } } return SW_OK; }
static int swoole_mysql_onRead(swReactor *reactor, swEvent *event) { #if PHP_MAJOR_VERSION < 7 TSRMLS_FETCH_FROM_CTX(sw_thread_ctx ? sw_thread_ctx : NULL); #endif mysql_client *client = event->socket->object; int sock = event->fd; zval *zobject = client->object; swString *buffer = client->buffer; int ret; zval **args[2]; zval *callback = NULL; zval *retval = NULL; zval *result = NULL; while(1) { ret = recv(sock, buffer->str + buffer->length, buffer->size - buffer->length, 0); if (ret < 0) { if (errno == EINTR) { continue; } else { switch (swConnection_error(errno)) { case SW_ERROR: swSysError("Read from socket[%d] failed.", event->fd); return SW_ERR; case SW_CLOSE: goto close_fd; case SW_WAIT: goto parse_response; default: return SW_ERR; } } } else if (ret == 0) { close_fd: if (client->state == SW_MYSQL_STATE_READ_END) { goto parse_response; } sw_zend_call_method_with_0_params(&zobject, swoole_mysql_class_entry_ptr, NULL, "close", &retval); if (retval) { sw_zval_ptr_dtor(&retval); } if (client->callback) { args[0] = &zobject; args[1] = &result; SW_ALLOC_INIT_ZVAL(result); ZVAL_BOOL(result, 0); callback = client->callback; if (sw_call_user_function_ex(EG(function_table), NULL, callback, &retval, 2, args, 0, NULL TSRMLS_CC) != SUCCESS) { swoole_php_fatal_error(E_WARNING, "swoole_async_mysql callback[2] handler error."); } if (result) { sw_zval_ptr_dtor(&result); } sw_zval_ptr_dtor(&callback); client->callback = NULL; client->state = SW_MYSQL_STATE_QUERY; if (retval) { sw_zval_ptr_dtor(&retval); } } return SW_OK; } else { buffer->length += ret; //recv again if (buffer->length == buffer->size) { if (swString_extend(buffer, buffer->size * 2) < 0) { swoole_php_fatal_error(E_ERROR, "malloc failed."); reactor->del(SwooleG.main_reactor, event->fd); } continue; } parse_response: if (mysql_response(client) < 0) { return SW_OK; } //remove from eventloop reactor->del(reactor, event->fd); zend_update_property_long(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("affected_rows"), client->response.affected_rows TSRMLS_CC); zend_update_property_long(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("insert_id"), client->response.insert_id TSRMLS_CC); client->state = SW_MYSQL_STATE_QUERY; args[0] = &zobject; //OK if (client->response.response_type == 0) { SW_ALLOC_INIT_ZVAL(result); ZVAL_BOOL(result, 1); } //ERROR else if (client->response.response_type == 255) { SW_ALLOC_INIT_ZVAL(result); ZVAL_BOOL(result, 0); zend_update_property_string(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("error"), client->response.server_msg TSRMLS_CC); zend_update_property_long(swoole_mysql_class_entry_ptr, zobject, ZEND_STRL("errno"), client->response.error_code TSRMLS_CC); } //ResultSet else { result = client->response.result_array; } args[1] = &result; callback = client->callback; if (sw_call_user_function_ex(EG(function_table), NULL, callback, &retval, 2, args, 0, NULL TSRMLS_CC) != SUCCESS) { swoole_php_fatal_error(E_WARNING, "swoole_async_mysql callback[2] handler error."); reactor->del(SwooleG.main_reactor, event->fd); } /* free memory */ if (retval) { sw_zval_ptr_dtor(&retval); } if (result) { sw_zval_ptr_dtor(&result); #if PHP_MAJOR_VERSION > 5 efree(result); #endif } //free callback object sw_zval_ptr_dtor(&callback); //clear buffer swString_clear(client->buffer); if (client->response.columns) { efree(client->response.columns); } if (client->object) { sw_zval_ptr_dtor(&client->object); } #if PHP_MAJOR_VERSION > 5 if (client->response.result_array) { efree(client->response.result_array); } #endif bzero(&client->response, sizeof(client->response)); return SW_OK; } } return SW_OK; }
static sw_inline int swProtocol_split_package_by_eof(swProtocol *protocol, swConnection *conn, swString *buffer) { #ifdef SW_LOG_TRACE_OPEN static int count; count++; #endif int eof_pos; if (buffer->length - buffer->offset < protocol->package_eof_len) { eof_pos = -1; } else { eof_pos = swoole_strnpos(buffer->str + buffer->offset, buffer->length - buffer->offset, protocol->package_eof, protocol->package_eof_len); } swTraceLog(SW_TRACE_EOF_PROTOCOL, "#[0] count=%d, length=%ld, size=%ld, offset=%ld.", count, buffer->length, buffer->size, (long)buffer->offset); //waiting for more data if (eof_pos < 0) { buffer->offset = buffer->length - protocol->package_eof_len; return SW_CONTINUE; } uint32_t length = buffer->offset + eof_pos + protocol->package_eof_len; swTraceLog(SW_TRACE_EOF_PROTOCOL, "#[4] count=%d, length=%d", count, length); if (protocol->onPackage(conn, buffer->str, length) < 0) { return SW_CLOSE; } if (conn->removed) { return SW_OK; } //there are remaining data if (length < buffer->length) { uint32_t remaining_length = buffer->length - length; char *remaining_data = buffer->str + length; swTraceLog(SW_TRACE_EOF_PROTOCOL, "#[5] count=%d, remaining_length=%d", count, remaining_length); while (1) { if (remaining_length < protocol->package_eof_len) { goto wait_more_data; } eof_pos = swoole_strnpos(remaining_data, remaining_length, protocol->package_eof, protocol->package_eof_len); if (eof_pos < 0) { wait_more_data: swTraceLog(SW_TRACE_EOF_PROTOCOL, "#[1] count=%d, remaining_length=%d, length=%d", count, remaining_length, length); memmove(buffer->str, remaining_data, remaining_length); buffer->length = remaining_length; buffer->offset = 0; #ifdef SW_USE_OPENSSL if (conn->ssl) { return SW_CONTINUE; } else #endif { return SW_OK; } } else { length = eof_pos + protocol->package_eof_len; if (protocol->onPackage(conn, remaining_data, length) < 0) { return SW_CLOSE; } if (conn->removed) { return SW_OK; } swTraceLog(SW_TRACE_EOF_PROTOCOL, "#[2] count=%d, remaining_length=%d, length=%d", count, remaining_length, length); remaining_data += length; remaining_length -= length; } } } swTraceLog(SW_TRACE_EOF_PROTOCOL, "#[3] length=%ld, size=%ld, offset=%ld", buffer->length, buffer->size, (long)buffer->offset); swString_clear(buffer); #ifdef SW_USE_OPENSSL if (conn->ssl) { return SW_CONTINUE; } #endif return SW_OK; }
/** * @return SW_ERR: close the connection * @return SW_OK: continue */ int swProtocol_recv_check_length(swProtocol *protocol, swConnection *conn, swString *buffer) { int package_length; uint32_t recv_size; char swap[SW_BUFFER_SIZE]; do_recv: if (buffer->offset > 0) { recv_size = buffer->offset - buffer->length; } else { recv_size = protocol->package_length_offset + protocol->package_length_size; } int ret = swConnection_recv(conn, buffer->str + buffer->length, recv_size, 0); if (ret < 0) { switch (swConnection_error(errno)) { case SW_ERROR: swSysError("recv(%d, %d) failed.", conn->fd, recv_size); return SW_OK; case SW_CLOSE: conn->close_errno = errno; return SW_ERR; default: return SW_OK; } } else if (ret == 0) { return SW_ERR; } else { buffer->length += ret; if (conn->recv_wait) { if (buffer->length >= buffer->offset) { do_dispatch: ret = protocol->onPackage(conn, buffer->str, buffer->offset); conn->recv_wait = 0; int remaining_length = buffer->length - buffer->offset; if (remaining_length > 0) { assert(remaining_length < sizeof(swap)); memcpy(swap, buffer->str + buffer->offset, remaining_length); memcpy(buffer->str, swap, remaining_length); buffer->offset = 0; buffer->length = remaining_length; goto do_get_length; } else { swString_clear(buffer); return ret; } } else { return SW_OK; } } else { do_get_length: package_length = protocol->get_package_length(protocol, conn, buffer->str, buffer->length); //invalid package, close connection. if (package_length < 0) { return SW_ERR; } //no length else if (package_length == 0) { return SW_OK; } else if (package_length > protocol->package_max_length) { swWarn("package is too big, remote_addr=%s:%d, length=%d.", swConnection_get_ip(conn), swConnection_get_port(conn), package_length); return SW_ERR; } //get length success else { if (buffer->size < package_length) { if (swString_extend(buffer, package_length) < 0) { return SW_ERR; } } conn->recv_wait = 1; buffer->offset = package_length; if (buffer->length >= package_length) { goto do_dispatch; } else { goto do_recv; } } } } return SW_OK; }
/** * @return SW_ERR: close the connection * @return SW_OK: continue */ int swProtocol_recv_check_eof(swProtocol *protocol, swConnection *conn, swString *buffer) { int recv_again = SW_FALSE; int buf_size; recv_data: buf_size = buffer->size - buffer->length; char *buf_ptr = buffer->str + buffer->length; if (buf_size > SW_BUFFER_SIZE_STD) { buf_size = SW_BUFFER_SIZE_STD; } int n = swConnection_recv(conn, buf_ptr, buf_size, 0); if (n < 0) { switch (swConnection_error(errno)) { case SW_ERROR: swSysError("recv from socket#%d failed.", conn->fd); return SW_OK; case SW_CLOSE: conn->close_errno = errno; return SW_ERR; default: return SW_OK; } } else if (n == 0) { return SW_ERR; } else { buffer->length += n; if (buffer->length < protocol->package_eof_len) { return SW_OK; } if (protocol->split_by_eof) { int retval = swProtocol_split_package_by_eof(protocol, conn, buffer); if (retval == SW_CONTINUE) { recv_again = SW_TRUE; } else if (retval == SW_CLOSE) { return SW_ERR; } else { return SW_OK; } } else if (memcmp(buffer->str + buffer->length - protocol->package_eof_len, protocol->package_eof, protocol->package_eof_len) == 0) { if (protocol->onPackage(conn, buffer->str, buffer->length) < 0) { return SW_ERR; } if (conn->removed) { return SW_OK; } swString_clear(buffer); #ifdef SW_USE_OPENSSL if (conn->ssl && SSL_pending(conn->ssl) > 0) { goto recv_data; } #endif return SW_OK; } //over max length, will discard if (buffer->length == protocol->package_max_length) { swWarn("Package is too big. package_length=%d", (int )buffer->length); return SW_ERR; } //buffer is full, may have not read data if (buffer->length == buffer->size) { recv_again = SW_TRUE; if (buffer->size < protocol->package_max_length) { uint32_t extend_size = swoole_size_align(buffer->size * 2, SwooleG.pagesize); if (extend_size > protocol->package_max_length) { extend_size = protocol->package_max_length; } if (swString_extend(buffer, extend_size) < 0) { return SW_ERR; } } } //no eof if (recv_again) { goto recv_data; } } return SW_OK; }
/** * @return SW_ERR: close the connection * @return SW_OK: continue */ int swProtocol_recv_check_length(swProtocol *protocol, swConnection *conn, swString *buffer) { int package_length; uint8_t package_length_size = protocol->get_package_length_size ? protocol->get_package_length_size(conn) : protocol->package_length_size; uint32_t recv_size; if (conn->skip_recv) { conn->skip_recv = 0; goto do_get_length; } do_recv: if (conn->active == 0) { return SW_OK; } if (buffer->offset > 0) { recv_size = buffer->offset - buffer->length; } else { recv_size = protocol->package_length_offset + package_length_size; } int n = swConnection_recv(conn, buffer->str + buffer->length, recv_size, 0); if (n < 0) { switch (swConnection_error(errno)) { case SW_ERROR: swSysError("recv(%d, %d) failed.", conn->fd, recv_size); return SW_OK; case SW_CLOSE: conn->close_errno = errno; return SW_ERR; default: return SW_OK; } } else if (n == 0) { return SW_ERR; } else { buffer->length += n; if (conn->recv_wait) { if (buffer->length >= buffer->offset) { do_dispatch: if (protocol->onPackage(conn, buffer->str, buffer->offset) < 0) { return SW_ERR; } if (conn->removed) { return SW_OK; } conn->recv_wait = 0; int remaining_length = buffer->length - buffer->offset; if (remaining_length > 0) { memmove(buffer->str, buffer->str + buffer->offset, remaining_length); buffer->offset = 0; buffer->length = remaining_length; goto do_get_length; } else { swString_clear(buffer); #ifdef SW_USE_OPENSSL if (conn->ssl) { goto do_recv; } #endif } } return SW_OK; } else { do_get_length: package_length = protocol->get_package_length(protocol, conn, buffer->str, buffer->length); //invalid package, close connection. if (package_length < 0) { return SW_ERR; } //no length else if (package_length == 0) { return SW_OK; } else if (package_length > protocol->package_max_length) { swWarn("package is too big, remote_addr=%s:%d, length=%d.", swConnection_get_ip(conn), swConnection_get_port(conn), package_length); return SW_ERR; } //get length success else { if (buffer->size < package_length) { if (swString_extend(buffer, package_length) < 0) { return SW_ERR; } } conn->recv_wait = 1; buffer->offset = package_length; if (buffer->length >= package_length) { goto do_dispatch; } else { goto do_recv; } } } } return SW_OK; }
/** * @return SW_ERR: close the connection * @return SW_OK: continue */ int swProtocol_recv_check_length(swProtocol *protocol, swConnection *conn, swString *buffer) { char *recvbuf; int ret; uint32_t recvbuf_size; do_recv: recvbuf = buffer->str + buffer->length; recvbuf_size = buffer->offset > 0 ? buffer->offset - buffer->length : protocol->package_length_offset + protocol->package_length_size; int n = swConnection_recv(conn, recvbuf, recvbuf_size, 0); if (n < 0) { switch (swConnection_error(errno)) { case SW_ERROR: swSysError("recv(%d, %p, %d) failed.", conn->fd, recvbuf, recvbuf_size); return SW_OK; case SW_CLOSE: return SW_ERR; default: return SW_OK; } } else if (n == 0) { return SW_ERR; } else { buffer->length += n; if (conn->recv_wait) { if (buffer->length == buffer->offset) { do_package: ret = protocol->onPackage(conn, buffer->str, buffer->length); conn->recv_wait = 0; swString_clear(buffer); return ret; } else { return SW_OK; } } else { int package_length = protocol->get_package_length(protocol, conn, buffer->str, buffer->length); //invalid package, close connection. if (package_length < 0) { return SW_ERR; } //no length else if (package_length == 0) { return SW_OK; } else if (package_length > protocol->package_max_length) { swWarn("package is too big, remote_addr=%s:%d, length=%d.", swConnection_get_ip(conn), swConnection_get_port(conn), package_length); return SW_ERR; } //get length success else { if (buffer->size < package_length) { if (swString_extend(buffer, package_length) < 0) { return SW_ERR; } } conn->recv_wait = 1; buffer->offset = package_length; if (buffer->length == package_length) { goto do_package; } else { goto do_recv; } } } } return SW_OK; }
static int http_client_coro_execute(zval *zobject, char *uri, zend_size_t uri_len TSRMLS_DC) { if (uri_len <= 0) { swoole_php_fatal_error(E_WARNING, "path is empty."); return SW_ERR; } http_client *http = swoole_get_object(zobject); //http is not null when keeping alive if (http) { //http not ready if (http->state != HTTP_CLIENT_STATE_READY) { //swWarn("fd=%d, state=%d, active=%d, keep_alive=%d", http->cli->socket->fd, http->state, http->cli->socket->active, http->keep_alive); swoole_php_fatal_error(E_WARNING, "Operation now in progress phase %d.", http->state); return SW_ERR; } else if (!http->cli->socket->active) { swoole_php_fatal_error(E_WARNING, "connection#%d is closed.", http->cli->socket->fd); return SW_ERR; } } else { php_swoole_check_reactor(); http = http_client_create(zobject TSRMLS_CC); } if (http == NULL) { return SW_ERR; } if (http->body == NULL) { http->body = swString_new(SW_HTTP_RESPONSE_INIT_SIZE); if (http->body == NULL) { swoole_php_fatal_error(E_ERROR, "[1] swString_new(%d) failed.", SW_HTTP_RESPONSE_INIT_SIZE); return SW_ERR; } } else { swString_clear(http->body); } if (http->uri) { efree(http->uri); } http->uri = estrdup(uri); http->uri_len = uri_len; //if connection exists if (http->cli) { http_client_coro_send_http_request(zobject TSRMLS_CC); return SW_OK; } swClient *cli = php_swoole_client_new(zobject, http->host, http->host_len, http->port); if (cli == NULL) { return SW_ERR; } http->cli = cli; zval *ztmp; HashTable *vht; zval *zset = sw_zend_read_property(swoole_http_client_coro_class_entry_ptr, zobject, ZEND_STRL("setting"), 1 TSRMLS_CC); if (zset && !ZVAL_IS_NULL(zset)) { vht = Z_ARRVAL_P(zset); /** * timeout */ if (php_swoole_array_get_value(vht, "timeout", ztmp)) { convert_to_double(ztmp); http->timeout = (double) Z_DVAL_P(ztmp); } /** * keep_alive */ if (php_swoole_array_get_value(vht, "keep_alive", ztmp)) { convert_to_boolean(ztmp); http->keep_alive = (int) Z_LVAL_P(ztmp); } //client settings php_swoole_client_check_setting(http->cli, zset TSRMLS_CC); } if (cli->socket->active == 1) { swoole_php_fatal_error(E_WARNING, "swoole_http_client is already connected."); return SW_ERR; } #if PHP_MAJOR_VERSION < 7 sw_zval_add_ref(&zobject); #endif cli->object = zobject; //sw_copy_to_stack(cli->object, hcc->_object); cli->open_eof_check = 0; cli->open_length_check = 0; cli->reactor_fdtype = PHP_SWOOLE_FD_STREAM_CLIENT; cli->onReceive = http_client_coro_onReceive; cli->onConnect = http_client_coro_onConnect; cli->onClose = http_client_coro_onClose; cli->onError = http_client_coro_onError; return cli->connect(cli, http->host, http->port, http->timeout, 0); }
static int swClient_onStreamRead(swReactor *reactor, swEvent *event) { int n; swClient *cli = event->socket->object; char *buf = cli->buffer->str + cli->buffer->length; long buf_size = cli->buffer->size - cli->buffer->length; if (cli->http_proxy && cli->http_proxy->state != SW_HTTP_PROXY_STATE_READY) { #ifdef SW_USE_OPENSSL if (cli->open_ssl) { int n = swConnection_recv(event->socket, buf, buf_size, 0); if (n <= 0) { goto __close; } cli->buffer->length += n; if (cli->buffer->length < sizeof(SW_HTTPS_PROXY_HANDSHAKE_RESPONSE) - 1) { return SW_OK; } if (swClient_https_proxy_handshake(cli) < 0) { swoole_error_log(SW_LOG_NOTICE, SW_ERROR_HTTP_PROXY_HANDSHAKE_ERROR, "failed to handshake with http proxy."); goto connect_fail; } else { cli->http_proxy->state = SW_HTTP_PROXY_STATE_READY; swString_clear(cli->buffer); } if (swClient_enable_ssl_encrypt(cli) < 0) { goto connect_fail; } else { if (swClient_ssl_handshake(cli) < 0) { goto connect_fail; } else { cli->socket->ssl_state = SW_SSL_STATE_WAIT_STREAM; } return SwooleG.main_reactor->set(SwooleG.main_reactor, event->fd, SW_FD_STREAM_CLIENT | SW_EVENT_WRITE); } if (cli->onConnect) { execute_onConnect(cli); } return SW_OK; } #endif } if (cli->socks5_proxy && cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { int n = swConnection_recv(event->socket, buf, buf_size, 0); if (n <= 0) { goto __close; } if (swSocks5_connect(cli, buf, buf_size) < 0) { goto __close; } if (cli->socks5_proxy->state != SW_SOCKS5_STATE_READY) { return SW_OK; } #ifdef SW_USE_OPENSSL if (cli->open_ssl) { if (swClient_enable_ssl_encrypt(cli) < 0) { connect_fail: cli->socket->active = 0; cli->close(cli); if (cli->onError) { cli->onError(cli); } } else { if (swClient_ssl_handshake(cli) < 0) { goto connect_fail; } else { cli->socket->ssl_state = SW_SSL_STATE_WAIT_STREAM; } return SwooleG.main_reactor->set(SwooleG.main_reactor, event->fd, SW_FD_STREAM_CLIENT | SW_EVENT_WRITE); } } else #endif { if (cli->onConnect) { execute_onConnect(cli); } } return SW_OK; } #ifdef SW_USE_OPENSSL if (cli->open_ssl && cli->socket->ssl_state == SW_SSL_STATE_WAIT_STREAM) { if (swClient_ssl_handshake(cli) < 0) { return cli->close(cli); } if (cli->socket->ssl_state != SW_SSL_STATE_READY) { return SW_OK; } //ssl handshake sucess else if (cli->onConnect) { execute_onConnect(cli); } } #endif /** * redirect stream data to other socket */ if (cli->redirect) { int ret = 0; n = swConnection_recv(event->socket, buf, buf_size, 0); if (n < 0) { goto __error; } else if (n == 0) { goto __close; } if (cli->_redirect_to_socket) { ret = SwooleG.main_reactor->write(SwooleG.main_reactor, cli->_redirect_to_socket, buf, n); } else if (cli->_redirect_to_session) { if (SwooleG.serv->send(SwooleG.serv, cli->_redirect_to_session, buf, n) < 0) { if (SwooleG.error >= SW_ERROR_SESSION_CLOSED_BY_SERVER || SwooleG.error >= SW_ERROR_SESSION_INVALID_ID) { goto __close; } } else { return SW_OK; } } else { ret = swSocket_write_blocking(cli->_redirect_to_file, buf, n); } if (ret < 0) { goto __error; } return SW_OK; } if (cli->open_eof_check || cli->open_length_check) { swConnection *conn = cli->socket; swProtocol *protocol = &cli->protocol; if (cli->open_eof_check) { n = swProtocol_recv_check_eof(protocol, conn, cli->buffer); } else { n = swProtocol_recv_check_length(protocol, conn, cli->buffer); } if (n < 0) { return cli->close(cli); } else { return SW_OK; } } #ifdef SW_CLIENT_RECV_AGAIN recv_again: #endif n = swConnection_recv(event->socket, buf, buf_size, 0); if (n < 0) { __error: switch (swConnection_error(errno)) { case SW_ERROR: swSysError("Read from socket[%d] failed.", event->fd); return SW_OK; case SW_CLOSE: goto __close; case SW_WAIT: return SW_OK; default: return SW_OK; } } else if (n == 0) { __close: return cli->close(cli); } else { cli->onReceive(cli, buf, n); #ifdef SW_CLIENT_RECV_AGAIN if (n == buf_size) { goto recv_again; } #endif return SW_OK; } return SW_OK; }