Skip to content

Commit

Permalink
[5.1]optimize http response (#5445)
Browse files Browse the repository at this point in the history
* optimize http response

* optimize code

* optimize code

* fix scope
  • Loading branch information
NathanFreeman authored Aug 22, 2024
1 parent e65e6d0 commit cf8c93d
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 121 deletions.
9 changes: 5 additions & 4 deletions ext-src/php_swoole_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -705,11 +705,12 @@ static sw_inline void sw_zend_update_property_null_ex(zend_class_entry *scope, z
zend_update_property_ex(scope, SW_Z8_OBJ_P(object), s, &tmp);
}

static sw_inline zval *sw_zend_read_property_ex(zend_class_entry *ce, zval *obj, zend_string *s, int silent) {
zval rv, *property = zend_read_property_ex(ce, SW_Z8_OBJ_P(obj), s, silent, &rv);
static sw_inline zval *sw_zend_read_property_ex(zend_class_entry *ce, zval *zobject, zend_string *name, int silent) {
zval *zv = zend_hash_find(&ce->properties_info, name);
zend_property_info *property_info = (zend_property_info *) Z_PTR_P(zv);
zval *property = OBJ_PROP(SW_Z8_OBJ_P(zobject), property_info->offset);
if (UNEXPECTED(property == &EG(uninitialized_zval))) {
sw_zend_update_property_null_ex(ce, obj, s);
return zend_read_property_ex(ce, SW_Z8_OBJ_P(obj), s, silent, &rv);
ZVAL_NULL(property);
}
return property;
}
Expand Down
210 changes: 94 additions & 116 deletions ext-src/swoole_http_response.cc
Original file line number Diff line number Diff line change
Expand Up @@ -237,17 +237,12 @@ static PHP_METHOD(swoole_http_response, write) {
}
}

struct {
char *str;
size_t length;
} http_body;
size_t length = php_swoole_get_send_data(zdata, &http_body.str);
char *data = nullptr;
size_t length = php_swoole_get_send_data(zdata, &data);

if (length == 0) {
php_swoole_error(E_WARNING, "data to send is empty");
RETURN_FALSE;
} else {
http_body.length = length;
}

// Why not enable compression?
Expand All @@ -257,12 +252,12 @@ static PHP_METHOD(swoole_http_response, write) {
// **and the data in each chunk is not compressed individually.**
// The remote endpoint then decodes the stream by concatenating the chunks and decompressing the result.
http_buffer->clear();
char *hex_string = swoole_dec2hex(http_body.length, 16);
char *hex_string = swoole_dec2hex(length, 16);
int hex_len = strlen(hex_string);
//"%.*s\r\n%.*s\r\n", hex_len, hex_string, body.length, body.str
http_buffer->append(hex_string, hex_len);
http_buffer->append(ZEND_STRL("\r\n"));
http_buffer->append(http_body.str, http_body.length);
http_buffer->append(data, length);
http_buffer->append(ZEND_STRL("\r\n"));
sw_free(hex_string);

Expand Down Expand Up @@ -291,80 +286,77 @@ static int parse_header_name(const char *key, size_t keylen) {
static void http_set_date_header(String *response) {
static struct {
time_t time;
size_t len;
char buf[64];
zend_string *date = nullptr;
} cache{};

time_t now = time(nullptr);
if (now != cache.time) {
char *date_str = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0);
cache.len = strlen(date_str);
memcpy(cache.buf, date_str, cache.len);
efree(date_str);
if (cache.date) {
zend_string_release(cache.date);
}

cache.time = now;
cache.date = php_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0);
}
response->append(ZEND_STRL("Date: "));
response->append(cache.buf, cache.len);
response->append(ZSTR_VAL(cache.date), ZSTR_LEN(cache.date));
response->append(ZEND_STRL("\r\n"));
}

static void add_custom_header(String *response, const char *key, size_t l_key, zval *value) {
static void add_custom_header(String *http_buffer, const char *key, size_t l_key, zval *value) {
if (ZVAL_IS_NULL(value)) {
return;
}

zend::String str_value(value);
str_value.rtrim();
if (swoole_http_has_crlf(str_value.val(), str_value.len())) {
return;
}
response->append(key, l_key);
response->append(SW_STRL(": "));
response->append(str_value.val(), str_value.len());
response->append(SW_STRL("\r\n"));

http_buffer->append(key, l_key);
http_buffer->append(SW_STRL(": "));
http_buffer->append(str_value.val(), str_value.len());
http_buffer->append(SW_STRL("\r\n"));
}

void HttpContext::build_header(String *http_buffer, const char *body, size_t length) {
assert(send_header_ == 0);

/**
* http status line
*/
if (!response.reason) {
const char *status = HttpServer::get_status_message(response.status);
http_buffer->append(ZEND_STRL("HTTP/1.1 "));
http_buffer->append((char *) status, strlen(status));
http_buffer->append(ZEND_STRL("\r\n"));
} else {
http_buffer->append(ZEND_STRL("HTTP/1.1 "));
// http status line
http_buffer->append(ZEND_STRL("HTTP/1.1 "));
if (response.reason) {
http_buffer->append(response.status);
http_buffer->append(ZEND_STRL(" "));
http_buffer->append(response.reason, strlen(response.reason));
http_buffer->append(ZEND_STRL("\r\n"));
} else {
const char *status = HttpServer::get_status_message(response.status);
http_buffer->append((char *) status, strlen(status));
}
http_buffer->append(ZEND_STRL("\r\n"));

// http headers
uint32_t header_flags = 0x0;

/**
* http header
*/
zval *zheader =
sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_HEADER), 0);
if (ZVAL_IS_ARRAY(zheader)) {
#ifdef SW_HAVE_COMPRESSION
zend_string *content_type = nullptr;
#endif
zval *zvalue;
zend_string *string_key;
zend_ulong num_key;

#ifdef SW_HAVE_COMPRESSION
zend_string *content_type = nullptr;
#endif
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(zheader), num_key, string_key, zvalue) {
if (!string_key) {
string_key = zend_long_to_str(num_key);
} else {
zend_string_addref(string_key);
}
zend::String key(string_key, false);

int key_header = parse_header_name(ZSTR_VAL(string_key), ZSTR_LEN(string_key));

if (key_header > 0) {
#ifdef SW_HAVE_COMPRESSION
if (key_header == HTTP_HEADER_CONTENT_TYPE && accept_compression && compression_types) {
Expand All @@ -390,6 +382,7 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len
"You have set 'Transfer-Encoding', 'Content-Length' will be ignored");
continue;
}

header_flags |= key_header;
if (ZVAL_IS_STRING(zvalue) && Z_STRLEN_P(zvalue) == 0) {
continue;
Expand Down Expand Up @@ -419,15 +412,13 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len
#endif
}

/**
* http cookies
*/
// http cookies
zval *zcookie =
sw_zend_read_property_ex(swoole_http_response_ce, response.zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE), 0);
if (ZVAL_IS_ARRAY(zcookie)) {
zval *zvalue;
SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zcookie), zvalue) {
if (Z_TYPE_P(zvalue) != IS_STRING) {
if (Z_TYPE_P(zvalue) != IS_STRING || swoole_http_has_crlf(Z_STRVAL_P(zvalue), Z_STRLEN_P(zvalue))) {
continue;
}
http_buffer->append(ZEND_STRL("Set-Cookie: "));
Expand All @@ -440,6 +431,7 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len
if (!(header_flags & HTTP_HEADER_SERVER)) {
http_buffer->append(ZEND_STRL("Server: " SW_HTTP_SERVER_SOFTWARE "\r\n"));
}

if (!(header_flags & HTTP_HEADER_DATE)) {
http_set_date_header(http_buffer);
}
Expand Down Expand Up @@ -480,9 +472,9 @@ void HttpContext::build_header(String *http_buffer, const char *body, size_t len
if (!(header_flags & HTTP_HEADER_CONTENT_LENGTH)) {
http_buffer->append(ZEND_STRL("Content-Length: "));

char content_length2[128];
int convert_result = swoole_itoa(content_length2, length);
http_buffer->append(content_length2, convert_result);
char result[128];
int convert_result = swoole_itoa(result, length);
http_buffer->append(result, convert_result);
http_buffer->append(ZEND_STRL("\r\n"));
}
}
Expand Down Expand Up @@ -752,16 +744,8 @@ bool HttpContext::send_file(const char *file, uint32_t l_file, off_t offset, siz
}

void HttpContext::end(zval *zdata, zval *return_value) {
struct {
char *str;
size_t length;
} http_body;
if (zdata) {
http_body.length = php_swoole_get_send_data(zdata, &http_body.str);
} else {
http_body.length = 0;
http_body.str = nullptr;
}
char *data = nullptr;
size_t length = zdata ? php_swoole_get_send_data(zdata, &data) : 0;

if (send_chunked) {
if (send_trailer_) {
Expand All @@ -771,83 +755,77 @@ void HttpContext::end(zval *zdata, zval *return_value) {
send_trailer(return_value);
send_trailer_ = 0;
} else {
if (!send(this, ZEND_STRL("0\r\n\r\n"))) {
if (!send(this, ZEND_STRL(SW_HTTP_CHUNK_EOF))) {
RETURN_FALSE;
}
}
send_chunked = 0;
} else {
String *http_buffer = get_write_buffer();
http_buffer->clear();
return;
}

#ifdef SW_HAVE_ZLIB
if (upgrade) {
Server *serv = nullptr;
Connection *conn = nullptr;
if (!co_socket) {
serv = (Server *) private_data;
conn = serv->get_connection_verify(fd);
}
bool enable_websocket_compression = co_socket ? websocket_compression : serv->websocket_compression;
bool accept_websocket_compression = false;
zval *pData;
if (enable_websocket_compression && request.zobject &&
(pData = zend_hash_str_find(Z_ARRVAL_P(request.zheader), ZEND_STRL("sec-websocket-extensions"))) &&
Z_TYPE_P(pData) == IS_STRING) {
std::string value(Z_STRVAL_P(pData), Z_STRLEN_P(pData));
if (value.substr(0, value.find_first_of(';')) == "permessage-deflate") {
accept_websocket_compression = true;
set_header(ZEND_STRL("Sec-Websocket-Extensions"), ZEND_STRL(SW_WEBSOCKET_EXTENSION_DEFLATE), false);
}
}
websocket_compression = accept_websocket_compression;
if (conn) {
conn->websocket_compression = accept_websocket_compression;
if (upgrade) {
Server *serv = nullptr;
Connection *conn = nullptr;
if (!co_socket) {
serv = (Server *) private_data;
conn = serv->get_connection_verify(fd);
}
bool enable_websocket_compression = co_socket ? websocket_compression : serv->websocket_compression;
bool accept_websocket_compression = false;
zval *pData;
if (enable_websocket_compression && request.zobject &&
(pData = zend_hash_str_find(Z_ARRVAL_P(request.zheader), ZEND_STRL("sec-websocket-extensions"))) &&
Z_TYPE_P(pData) == IS_STRING) {
std::string value(Z_STRVAL_P(pData), Z_STRLEN_P(pData));
if (value.substr(0, value.find_first_of(';')) == "permessage-deflate") {
accept_websocket_compression = true;
set_header(ZEND_STRL("Sec-Websocket-Extensions"), ZEND_STRL(SW_WEBSOCKET_EXTENSION_DEFLATE), false);
}
}
websocket_compression = accept_websocket_compression;
if (conn) {
conn->websocket_compression = accept_websocket_compression;
}
}
#endif

build_header(http_buffer, http_body.str, http_body.length);

char *send_body_str;
size_t send_body_len;
String *http_buffer = get_write_buffer();
http_buffer->clear();
build_header(http_buffer, data, length);

if (http_body.length > 0) {
if (length > 0) {
#ifdef SW_HAVE_COMPRESSION
if (content_compressed) {
send_body_str = zlib_buffer->str;
send_body_len = zlib_buffer->length;
} else
if (content_compressed) {
data = zlib_buffer->str;
length = zlib_buffer->length;
}
#endif
{
send_body_str = http_body.str;
send_body_len = http_body.length;

if (length > SW_HTTP_MAX_APPEND_DATA) {
if (!send(this, http_buffer->str, http_buffer->length)) {
send_header_ = 0;
RETURN_FALSE;
}
// send twice to reduce memory copy
if (send_body_len < swoole_pagesize()) {
if (http_buffer->append(send_body_str, send_body_len) < 0) {
send_header_ = 0;
RETURN_FALSE;
}
} else {
if (!send(this, http_buffer->str, http_buffer->length)) {
send_header_ = 0;
RETURN_FALSE;
}
if (!send(this, send_body_str, send_body_len)) {
end_ = 1;
close(this);
RETURN_FALSE;
}
goto _skip_copy;

if (!send(this, data, length)) {
end_ = 1;
close(this);
RETURN_FALSE;
}
goto _skip_copy;
} else {
if (http_buffer->append(data, length) < 0) {
send_header_ = 0;
RETURN_FALSE;
}
}
}

if (!send(this, http_buffer->str, http_buffer->length)) {
end_ = 1;
close(this);
RETURN_FALSE;
}
if (!send(this, http_buffer->str, http_buffer->length)) {
end_ = 1;
close(this);
RETURN_FALSE;
}

_skip_copy:
Expand Down
1 change: 1 addition & 0 deletions include/swoole_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@
#define SW_HTTP_UPLOAD_FILE "Swoole-Upload-File"
#define SW_HTTP_CHUNK_EOF "0\r\n\r\n"
#define SW_HTTP_DEFAULT_CONTENT_TYPE "text/html"
#define SW_HTTP_MAX_APPEND_DATA 16384

// #define SW_HTTP_100_CONTINUE
#define SW_HTTP_100_CONTINUE_PACKET "HTTP/1.1 100 Continue\r\n\r\n"
Expand Down
3 changes: 3 additions & 0 deletions tests/include/api/curl_multi.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ function swoole_test_curl_multi($options = []) {
function swoole_test_curl_multi_ex($mh, $options = []) {
$ch1 = curl_init();
$ch2 = curl_init();
$userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0';

// 设置URL和相应的选项
curl_setopt($ch1, CURLOPT_URL, "https://www.baidu.com/");
curl_setopt($ch1, CURLOPT_HEADER, 0);
curl_setopt($ch2, CURLOPT_USERAGENT, $userAgent);
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch2, CURLOPT_URL, "https://www.zhihu.com/");
curl_setopt($ch2, CURLOPT_HEADER, 0);
curl_setopt($ch2, CURLOPT_USERAGENT, $userAgent);
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1);

$mh = curl_multi_init();
Expand Down
Loading

0 comments on commit cf8c93d

Please sign in to comment.