Skip to content

Commit

Permalink
optimize http server (#5073)
Browse files Browse the repository at this point in the history
* Optimize http server

* test

* fix udp port

* fix code

* optimize code

* addr cache with http_server_coro

* valgrind

* valgrind [2]

* fix

* revert

---------

Co-authored-by: NathanFreeman <[email protected]>
  • Loading branch information
matyhtf and NathanFreeman authored Jun 20, 2023
1 parent efec677 commit 55f8561
Show file tree
Hide file tree
Showing 7 changed files with 324 additions and 84 deletions.
3 changes: 2 additions & 1 deletion ext-src/php_swoole_http.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ struct Context {
uchar http2 : 1;

http2::Stream *stream;
std::shared_ptr<String> write_buffer;
String *write_buffer;

#ifdef SW_HAVE_COMPRESSION
int8_t compression_level;
Expand All @@ -166,6 +166,7 @@ struct Context {
size_t current_form_data_name_len;
zval *current_multipart_header;
String *form_data_buffer;
zend_string *addr_cache;

std::string upload_tmp_dir;

Expand Down
140 changes: 105 additions & 35 deletions ext-src/swoole_http_request.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ static int multipart_body_on_data(multipart_parser *p, const char *at, size_t le
static int multipart_body_on_header_complete(multipart_parser *p);
static int multipart_body_on_data_end(multipart_parser *p);

static zval *swoole_request_read_property(
zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv);

static zval *swoole_request_write_property(zend_object *zobj, zend_string *name, zval *value, void **cache_slot);

static HashTable *swoole_request_get_properties_for(zend_object *obj, zend_prop_purpose purpose);

static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) {
HttpContext *ctx = (HttpContext *) parser->data;
ctx->request.path = estrndup(at, length);
Expand Down Expand Up @@ -157,13 +164,14 @@ bool HttpContext::parse_multipart_data(const char *at, size_t length) {
zend_class_entry *swoole_http_request_ce;
static zend_object_handlers swoole_http_request_handlers;

typedef struct {
struct HttpRequestObject {
HttpContext *ctx;
bool init_fd = false;
zend_object std;
} http_request_t;
};

static sw_inline http_request_t *php_swoole_http_request_fetch_object(zend_object *obj) {
return (http_request_t *) ((char *) obj - swoole_http_request_handlers.offset);
static sw_inline HttpRequestObject *php_swoole_http_request_fetch_object(zend_object *obj) {
return (HttpRequestObject *) ((char *) obj - swoole_http_request_handlers.offset);
}

HttpContext *php_swoole_http_request_get_context(zval *zobject) {
Expand All @@ -175,26 +183,24 @@ void php_swoole_http_request_set_context(zval *zobject, HttpContext *ctx) {
}

static void php_swoole_http_request_free_object(zend_object *object) {
http_request_t *request = php_swoole_http_request_fetch_object(object);
HttpRequestObject *request = php_swoole_http_request_fetch_object(object);
HttpContext *ctx = request->ctx;
zval zobject, *ztmpfiles;

ZVAL_OBJ(&zobject, object);
ztmpfiles = sw_zend_read_property_ex(swoole_http_request_ce, &zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_TMPFILES), 0);
if (ZVAL_IS_ARRAY(ztmpfiles)) {
zval *z_file_path;
SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmpfiles), z_file_path) {
if (Z_TYPE_P(z_file_path) != IS_STRING) {
continue;
}
unlink(Z_STRVAL_P(z_file_path));
if (SG(rfc1867_uploaded_files)) {
zend_hash_str_del(SG(rfc1867_uploaded_files), Z_STRVAL_P(z_file_path), Z_STRLEN_P(z_file_path));

if (ctx) {
zval *ztmpfiles = ctx->request.ztmpfiles;
if (ztmpfiles && ZVAL_IS_ARRAY(ztmpfiles)) {
zval *z_file_path;
SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(ztmpfiles), z_file_path) {
if (Z_TYPE_P(z_file_path) != IS_STRING) {
continue;
}
unlink(Z_STRVAL_P(z_file_path));
if (SG(rfc1867_uploaded_files)) {
zend_hash_str_del(SG(rfc1867_uploaded_files), Z_STRVAL_P(z_file_path), Z_STRLEN_P(z_file_path));
}
}
SW_HASHTABLE_FOREACH_END();
}
SW_HASHTABLE_FOREACH_END();
}
if (ctx) {
ctx->request.zobject = nullptr;
ctx->free();
}
Expand All @@ -203,7 +209,7 @@ static void php_swoole_http_request_free_object(zend_object *object) {
}

static zend_object *php_swoole_http_request_create_object(zend_class_entry *ce) {
http_request_t *request = (http_request_t *) zend_object_alloc(sizeof(http_request_t), ce);
HttpRequestObject *request = (HttpRequestObject *) zend_object_alloc(sizeof(HttpRequestObject), ce);
zend_object_std_init(&request->std, ce);
object_properties_init(&request->std, ce);
request->std.handlers = &swoole_http_request_handlers;
Expand All @@ -217,7 +223,6 @@ static PHP_METHOD(swoole_http_request, parse);
static PHP_METHOD(swoole_http_request, isCompleted);
static PHP_METHOD(swoole_http_request, getMethod);
static PHP_METHOD(swoole_http_request, getContent);
static PHP_METHOD(swoole_http_request, __destruct);
SW_EXTERN_C_END

// clang-format off
Expand All @@ -230,7 +235,6 @@ const zend_function_entry swoole_http_request_methods[] =
PHP_ME(swoole_http_request, parse, arginfo_class_Swoole_Http_Request_parse, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http_request, isCompleted, arginfo_class_Swoole_Http_Request_isCompleted, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http_request, getMethod, arginfo_class_Swoole_Http_Request_getMethod, ZEND_ACC_PUBLIC)
PHP_ME(swoole_http_request, __destruct, arginfo_class_Swoole_Http_Request___destruct, ZEND_ACC_PUBLIC)
PHP_FE_END
};
// clang-format on
Expand All @@ -243,7 +247,7 @@ void php_swoole_http_request_minit(int module_number) {
SW_SET_CLASS_CUSTOM_OBJECT(swoole_http_request,
php_swoole_http_request_create_object,
php_swoole_http_request_free_object,
http_request_t,
HttpRequestObject,
std);

zend_declare_property_long(swoole_http_request_ce, ZEND_STRL("fd"), 0, ZEND_ACC_PUBLIC);
Expand All @@ -255,11 +259,20 @@ void php_swoole_http_request_minit(int module_number) {
zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("files"), ZEND_ACC_PUBLIC);
zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("post"), ZEND_ACC_PUBLIC);
zend_declare_property_null(swoole_http_request_ce, ZEND_STRL("tmpfiles"), ZEND_ACC_PUBLIC);

swoole_http_request_handlers.read_property = swoole_request_read_property;
swoole_http_request_handlers.write_property = swoole_request_write_property;
swoole_http_request_handlers.get_properties_for = swoole_request_get_properties_for;
}

static int http_request_on_query_string(swoole_http_parser *parser, const char *at, size_t length) {
HttpContext *ctx = (HttpContext *) parser->data;
add_assoc_stringl_ex(ctx->request.zserver, ZEND_STRL("query_string"), (char *) at, length);

zval tmp;
HashTable *ht = Z_ARR_P(ctx->request.zserver);
ZVAL_STRINGL(&tmp, (char *) at, length);
zend_hash_str_add(ht, ZEND_STRL("query_string"), &tmp);

// parse url params
sapi_module.treat_data(PARSE_STRING,
estrndup(at, length), // it will be freed by treat_data
Expand Down Expand Up @@ -438,15 +451,28 @@ static int http_request_on_headers_complete(swoole_http_parser *parser) {

ctx->keepalive = swoole_http_should_keep_alive(parser);

add_assoc_string(zserver, "request_method", http_get_method_name(parser->method));
add_assoc_stringl_ex(zserver, ZEND_STRL("request_uri"), ctx->request.path, ctx->request.path_len);
zval tmp;
HashTable *ht = Z_ARR_P(zserver);
ZVAL_STRING(&tmp, http_get_method_name(parser->method));
zend_hash_str_add(ht, ZEND_STRL("request_method"), &tmp);

ZVAL_STRINGL(&tmp, ctx->request.path, ctx->request.path_len);
zend_hash_str_add(ht, ZEND_STRL("request_uri"), &tmp);

// path_info should be decoded
zend_string *zstr_path = zend_string_init(ctx->request.path, ctx->request.path_len, 0);
ZSTR_LEN(zstr_path) = php_url_decode(ZSTR_VAL(zstr_path), ZSTR_LEN(zstr_path));
add_assoc_str_ex(zserver, ZEND_STRL("path_info"), zstr_path);
add_assoc_long_ex(zserver, ZEND_STRL("request_time"), time(nullptr));
add_assoc_double_ex(zserver, ZEND_STRL("request_time_float"), microtime());
add_assoc_string(zserver, "server_protocol", (char *) (ctx->request.version == 101 ? "HTTP/1.1" : "HTTP/1.0"));
ZVAL_STR(&tmp, zstr_path);
zend_hash_str_add(ht, ZEND_STRL("path_info"), &tmp);

ZVAL_LONG(&tmp, time(nullptr));
zend_hash_str_add(ht, ZEND_STRL("request_time"), &tmp);

ZVAL_DOUBLE(&tmp, microtime());
zend_hash_str_add(ht, ZEND_STRL("request_time_float"), &tmp);

ZVAL_STRING(&tmp, (char *) (ctx->request.version == 101 ? "HTTP/1.1" : "HTTP/1.0"));
zend_hash_str_add(ht, ZEND_STRL("server_protocol"), &tmp);

ctx->current_header_name = nullptr;

Expand Down Expand Up @@ -828,9 +854,55 @@ const char *HttpContext::get_content_encoding() {
return nullptr;
}
}

#endif

static void swoole_request_read_fd_property(zend_object *object, HttpContext *ctx) {
zend_update_property_long(swoole_http_request_ce, object, ZEND_STRL("fd"), ctx->fd);
}

/**
* Swoole\\Http\\Request::$fd is not immediately needed so we create it when user needs it.
*/
static zval *swoole_request_read_property(
zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv) {
HttpRequestObject *request = php_swoole_http_request_fetch_object(object);
HttpContext *ctx = request->ctx;
zval *property = zend_std_read_property(object, name, type, nullptr, rv);

if (strcasecmp(ZSTR_VAL(name), "fd") == 0 && !request->init_fd) {
request->init_fd = true;
swoole_request_read_fd_property(object, ctx);
}

return property;
}

/**
* user overwrites Swoole\\Http\\Request::$fd so we don't need to init it.
*/
static zval *swoole_request_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) {
if (strcasecmp(ZSTR_VAL(name), "fd") == 0) {
HttpRequestObject *request = php_swoole_http_request_fetch_object(object);
request->init_fd = true;
}

return zend_std_write_property(object, name, value, cache_slot);
}

/**
* for json_encode and serialize
*/
static HashTable *swoole_request_get_properties_for(zend_object *object, zend_prop_purpose purpose) {
HttpRequestObject *request = php_swoole_http_request_fetch_object(object);
HttpContext *ctx = request->ctx;
if (!request->init_fd) {
request->init_fd = true;
swoole_request_read_fd_property(object, ctx);
}

return zend_std_get_properties_for(object, purpose);
}

static PHP_METHOD(swoole_http_request, getContent) {
HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS);
if (UNEXPECTED(!ctx)) {
Expand Down Expand Up @@ -981,5 +1053,3 @@ static PHP_METHOD(swoole_http_request, isCompleted) {
}
RETURN_BOOL(ctx->completed);
}

static PHP_METHOD(swoole_http_request, __destruct) {}
Loading

0 comments on commit 55f8561

Please sign in to comment.