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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize memory usage #5265

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions ext-src/php_swoole.cc
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,10 @@ PHP_MINIT_FUNCTION(swoole) {

// init bug report message
bug_report_message_init();
if (strcmp("cli", sapi_module.name) == 0 || strcmp("phpdbg", sapi_module.name) == 0 ||
strcmp("embed", sapi_module.name) == 0 || strcmp("micro", sapi_module.name) == 0) {
SWOOLE_G(cli) = 1;
}

SW_INIT_CLASS_ENTRY_EX2(
swoole_exception, "Swoole\\Exception", nullptr, nullptr, zend_ce_exception, zend_get_std_object_handlers());
Expand Down
115 changes: 114 additions & 1 deletion ext-src/php_swoole_http.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,119 @@ class Session;

namespace http {

struct ByteBuffer {
const char *version = "HTTP/1.1 ";
size_t version_length = 9;
const char *status = nullptr;
size_t status_length = 0;
const char *reason = nullptr;
size_t reason_length = 0;

size_t position = 0;
size_t http_response_length = 0;

std::vector<size_t> lengths;
std::vector<const char *> headers;
std::vector<zend_string *> free_list;

ByteBuffer(int total) {
lengths.reserve(total);
headers.reserve(total);
free_list.reserve(total);
}

~ByteBuffer() {
for (auto iter = free_list.begin(); iter != free_list.end(); iter++) {
zend_string_release(*iter);
}
}

inline void add_status(const char *_status, const char *_reason) {
status = _status;
status_length = strlen(status);
reason = _reason;
reason_length = reason ? strlen(reason) : 0;

// calculate http status line length
http_response_length = version_length + status_length + SW_CRLF_LEN;
if (reason) {
http_response_length += reason_length + 1;
}
}

inline void add_header(zend_string *key, zend_string *value) {
zend_string_addref(key);
zend_string_addref(value);
free_list.emplace_back(key);
free_list.emplace_back(value);
add_header(ZSTR_VAL(key), ZSTR_LEN(key), ZSTR_VAL(value), ZSTR_LEN(value));
}

inline void add_header(const char *key, size_t key_length, const char *value, size_t value_length) {
headers.emplace_back(key);
lengths.emplace_back(key_length);
headers.emplace_back(value);
lengths.emplace_back(value_length);

if (value) {
http_response_length += key_length + value_length + SW_CRLF_LEN + 2;
} else {
// When the value is a nullptr, it means that this response header has a fixed value.
http_response_length += key_length;
}
}

inline size_t get_protocol_length(size_t length = 0) {
// calculate http protocol length
return http_response_length + length + SW_CRLF_LEN;
}

inline void append(char *protocol, const char *data, size_t length) {
memcpy(protocol + position, data, length);
position += length;
}

void write_protocol(char *protocol, const char *data, size_t length) {
append(protocol, version, version_length);
append(protocol, status, status_length);
if (reason) {
append(protocol, " ", 1);
append(protocol, reason, reason_length);
}

append(protocol, SW_CRLF, SW_CRLF_LEN);

size_t key_length = 0;
size_t value_length = 0;
const char *key = nullptr;
const char *value = nullptr;
size_t i = 0;
size_t count = lengths.size();

while (i < count) {
key_length = lengths[i];
value_length = lengths[i + 1];
key = headers[i++];
value = headers[i++];

if (value == nullptr) {
append(protocol, key, key_length);
continue;
}

append(protocol, key, key_length);
append(protocol, ": ", 2);
append(protocol, value, value_length);
append(protocol, SW_CRLF, SW_CRLF_LEN);
}

append(protocol, SW_CRLF, SW_CRLF_LEN);
if (data) {
append(protocol, data, length);
}
}
};

struct Request {
int version;
char *path;
Expand Down Expand Up @@ -193,7 +306,7 @@ struct Context {
bool send_file(const char *file, uint32_t l_file, off_t offset, size_t length);
void send_trailer(zval *return_value);
String *get_write_buffer();
void build_header(String *http_buffer, const char *body, size_t length);
bool start_send(const char *body, size_t length);
ssize_t build_trailer(String *http_buffer);

size_t get_content_length() {
Expand Down
16 changes: 7 additions & 9 deletions ext-src/php_swoole_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -718,11 +718,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 Expand Up @@ -955,11 +956,8 @@ static sw_inline int php_swoole_check_reactor() {
}
}

static sw_inline char *php_swoole_format_date(char *format, size_t format_len, time_t ts, int localtime) {
zend_string *time = php_format_date(format, format_len, ts, localtime);
char *return_str = estrndup(ZSTR_VAL(time), ZSTR_LEN(time));
zend_string_release(time);
return return_str;
static sw_inline zend_string *php_swoole_format_date(char *format, size_t format_len, time_t ts, int localtime) {
return php_format_date(format, format_len, ts, localtime);
}

static sw_inline char *php_swoole_url_encode(const char *value, size_t value_len, size_t *exten) {
Expand Down
8 changes: 4 additions & 4 deletions ext-src/swoole_http2_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,11 @@ static void http2_server_set_date_header(Http2::HeaderSet *headers) {

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);
zend_string *date = php_swoole_format_date((char *) ZEND_STRL(SW_HTTP_DATE_FORMAT), now, 0);
memcpy(cache.buf, ZSTR_VAL(date), ZSTR_LEN(date));
cache.len = ZSTR_LEN(date);
cache.time = now;
efree(date_str);
zend_string_release(date);
}
headers->add(ZEND_STRL("date"), cache.buf, cache.len);
}
Expand Down
34 changes: 18 additions & 16 deletions ext-src/swoole_http_request.cc
Original file line number Diff line number Diff line change
Expand Up @@ -342,16 +342,15 @@ bool swoole_http_token_list_contains_value(const char *at, size_t length, const
static int http_request_on_header_value(swoole_http_parser *parser, const char *at, size_t length) {
HttpContext *ctx = (HttpContext *) parser->data;
zval *zheader = ctx->request.zheader;
char *header_name = ctx->current_header_name;
size_t header_len = ctx->current_header_name_len;
char header_name[header_len];
zend_str_tolower_copy(header_name, ctx->current_header_name, header_len);

if (ctx->parse_cookie && SW_STREQ(header_name, header_len, "cookie")) {
if (ctx->parse_cookie && SW_STRCASEEQ(header_name, header_len, "cookie")) {
zval *zcookie = swoole_http_init_and_read_property(
swoole_http_request_ce, ctx->request.zobject, &ctx->request.zcookie, SW_ZSTR_KNOWN(SW_ZEND_STR_COOKIE));
swoole_http_parse_cookie(zcookie, at, length);
return 0;
} else if (SW_STREQ(header_name, header_len, "upgrade") &&
} else if (SW_STRCASEEQ(header_name, header_len, "upgrade") &&
swoole_http_token_list_contains_value(at, length, "websocket")) {
ctx->websocket = 1;
if (ctx->co_socket) {
Expand All @@ -372,7 +371,7 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char *
}
} else if ((parser->method == PHP_HTTP_POST || parser->method == PHP_HTTP_PUT ||
parser->method == PHP_HTTP_DELETE || parser->method == PHP_HTTP_PATCH) &&
SW_STREQ(header_name, header_len, "content-type")) {
SW_STRCASEEQ(header_name, header_len, "content-type")) {
if (SW_STR_ISTARTS_WITH(at, length, "application/x-www-form-urlencoded")) {
ctx->request.post_form_urlencoded = 1;
} else if (SW_STR_ISTARTS_WITH(at, length, "multipart/form-data")) {
Expand All @@ -387,11 +386,11 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char *
}
}
#ifdef SW_HAVE_COMPRESSION
else if (ctx->enable_compression && SW_STREQ(header_name, header_len, "accept-encoding")) {
else if (ctx->enable_compression && SW_STRCASEEQ(header_name, header_len, "accept-encoding")) {
ctx->set_compression_method(at, length);
}
#endif
else if (SW_STREQ(header_name, header_len, "transfer-encoding") && SW_STR_ISTARTS_WITH(at, length, "chunked")) {
else if (SW_STRCASEEQ(header_name, header_len, "transfer-encoding") && SW_STR_ISTARTS_WITH(at, length, "chunked")) {
ctx->recv_chunked = 1;
}

Expand All @@ -402,24 +401,27 @@ static int http_request_on_header_value(swoole_http_parser *parser, const char *
/**
* some common request header key
*/
if (SW_STREQ(header_name, header_len, "host")) {
if (SW_STRCASEEQ(header_name, header_len, "host")) {
zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_HOST), &tmp);
} else if (SW_STREQ(header_name, header_len, "user-agent")) {
} else if (SW_STRCASEEQ(header_name, header_len, "user-agent")) {
zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_USER_AGENT), &tmp);
} else if (SW_STREQ(header_name, header_len, "accept")) {
} else if (SW_STRCASEEQ(header_name, header_len, "accept")) {
zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_ACCEPT), &tmp);
} else if (SW_STREQ(header_name, header_len, "content-type")) {
} else if (SW_STRCASEEQ(header_name, header_len, "content-type")) {
zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONTENT_TYPE), &tmp);
} else if (SW_STREQ(header_name, header_len, "content-length")) {
} else if (SW_STRCASEEQ(header_name, header_len, "content-length")) {
zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONTENT_LENGTH), &tmp);
} else if (SW_STREQ(header_name, header_len, "authorization")) {
} else if (SW_STRCASEEQ(header_name, header_len, "authorization")) {
zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_AUTHORIZATION), &tmp);
} else if (SW_STREQ(header_name, header_len, "connection")) {
} else if (SW_STRCASEEQ(header_name, header_len, "connection")) {
zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_CONNECTION), &tmp);
} else if (SW_STREQ(header_name, header_len, "accept-encoding")) {
} else if (SW_STRCASEEQ(header_name, header_len, "accept-encoding")) {
zend_hash_update(Z_ARR_P(zheader), SW_ZSTR_KNOWN(SW_ZEND_STR_ACCEPT_ENCODING), &tmp);
} else {
zend_hash_str_update(Z_ARR_P(zheader), header_name, header_len, &tmp);
char *new_header_name = estrndup(header_name, header_len);
zend_str_tolower_copy(new_header_name, header_name, header_len);
zend_hash_str_update(Z_ARR_P(zheader), new_header_name, header_len, &tmp);
efree(new_header_name);
}

return 0;
Expand Down