Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.1]optimize http response #5445

Merged
merged 4 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
168 changes: 71 additions & 97 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,73 @@ 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
matyhtf marked this conversation as resolved.
Show resolved Hide resolved
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);
matyhtf marked this conversation as resolved.
Show resolved Hide resolved
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 +378,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 +408,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))) {
matyhtf marked this conversation as resolved.
Show resolved Hide resolved
continue;
}
http_buffer->append(ZEND_STRL("Set-Cookie: "));
Expand All @@ -440,6 +427,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 +468,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 +740,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,14 +751,13 @@ 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) {
Expand Down Expand Up @@ -807,47 +786,42 @@ void HttpContext::end(zval *zdata, zval *return_value) {
}
#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
3 changes: 2 additions & 1 deletion tests/swoole_curl/ssl/version.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ require __DIR__ . '/../../include/bootstrap.php';

$cm = new \SwooleTest\CurlManager();
$cm->run(function ($host) {
$userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0';
matyhtf marked this conversation as resolved.
Show resolved Hide resolved
$ch = curl_init();

curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
Expand Down
Loading