Skip to content

Commit 519598d

Browse files
committed
fix http_client memory leak in PHP7.
1 parent 3a82f7a commit 519598d

File tree

4 files changed

+135
-29
lines changed

4 files changed

+135
-29
lines changed

swoole_config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@
227227
#define SW_HTTP_DATE_FORMAT "D, d M Y H:i:s T"
228228
//#define SW_HTTP_100_CONTINUE
229229

230+
#define SW_HTTP_CLIENT_USERAGENT "swoole-http-client"
231+
230232
#define SW_WEBSOCKET_SERVER_SOFTWARE "swoole-websocket-server"
231233
#define SW_WEBSOCKET_VERSION "13"
232234
#define SW_WEBSOCKET_KEY_LENGTH 16

swoole_http.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,5 +129,6 @@ extern zend_class_entry swoole_http_request_ce;
129129
extern zend_class_entry *swoole_http_request_class_entry_ptr;
130130

131131
extern swString *swoole_http_buffer;
132+
extern swString *swoole_zlib_buffer;
132133

133134
#endif /* SWOOLE_HTTP_H_ */

swoole_http_client.c

Lines changed: 131 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,21 @@
1616

1717
#include "php_swoole.h"
1818
#include "thirdparty/php_http_parser.h"
19+
1920
#include "ext/standard/basic_functions.h"
2021
#include "ext/standard/php_http.h"
2122
#include "ext/standard/base64.h"
23+
2224
#include "websocket.h"
2325

26+
#ifdef SW_HAVE_ZLIB
27+
#include <zlib.h>
28+
#endif
29+
2430
#ifdef SW_ASYNC_HTTPCLIENT
2531

32+
extern swString *swoole_zlib_buffer;
33+
2634
static swString *http_client_buffer;
2735

2836
enum http_client_state
@@ -42,8 +50,6 @@ typedef struct
4250
zval *request_header;
4351
zval *request_body;
4452
char *request_method;
45-
zval *response_header;
46-
zval *response_body;
4753

4854
} http_client_callback;
4955

@@ -59,15 +65,16 @@ typedef struct
5965

6066
char *tmp_header_field_name;
6167
zend_size_t tmp_header_field_name_len;
62-
63-
char *body;
6468

6569
php_http_parser parser;
70+
6671
swString *buffer;
72+
swString *body;
6773

6874
int state: 8; //0 wait 1 ready 2 busy
6975
int keep_alive :1; //0 no 1 keep
7076
int upgrade :1;
77+
int gzip: 1;
7178

7279
} http_client;
7380

@@ -218,6 +225,20 @@ static int http_client_execute(zval *zobject, char *uri, zend_size_t uri_len, zv
218225
return SW_ERR;
219226
}
220227

228+
if (http->body == NULL)
229+
{
230+
http->body = swString_new(SW_HTTP_RESPONSE_INIT_SIZE);
231+
if (http->body == NULL)
232+
{
233+
swoole_php_fatal_error(E_ERROR, "[1] swString_new(%d) failed.", SW_HTTP_RESPONSE_INIT_SIZE);
234+
return SW_ERR;
235+
}
236+
}
237+
else
238+
{
239+
swString_clear(http->body);
240+
}
241+
221242
http->uri = estrdup(uri);
222243
http->uri_len = uri_len;
223244

@@ -247,7 +268,13 @@ static int http_client_execute(zval *zobject, char *uri, zend_size_t uri_len, zv
247268
return SW_ERR;
248269
}
249270

271+
#if PHP_MAJOR_VERSION >= 7
272+
cli->object = (zval *) emalloc(sizeof(zval));
273+
ZVAL_DUP(cli->object, zobject);
274+
#else
250275
cli->object = zobject;
276+
#endif
277+
251278
cli->reactor_fdtype = PHP_SWOOLE_FD_STREAM_CLIENT;
252279
cli->onReceive = http_client_onReceive;
253280
cli->onConnect = http_client_onConnect;
@@ -271,6 +298,14 @@ void swoole_http_client_init(int module_number TSRMLS_DC)
271298
{
272299
swoole_php_fatal_error(E_ERROR, "[1] swString_new(%d) failed.", SW_HTTP_RESPONSE_INIT_SIZE);
273300
}
301+
302+
#ifdef SW_HAVE_ZLIB
303+
swoole_zlib_buffer = swString_new(2048);
304+
if (!swoole_zlib_buffer)
305+
{
306+
swoole_php_fatal_error(E_ERROR, "[2] swString_new(%d) failed.", SW_HTTP_RESPONSE_INIT_SIZE);
307+
}
308+
#endif
274309
}
275310

276311
/**
@@ -629,6 +664,7 @@ static int http_client_send_http_request(zval *zobject TSRMLS_DC)
629664
uint32_t keylen;
630665
int keytype;
631666
zval *value;
667+
632668
if (send_header && Z_TYPE_P(send_header) == IS_ARRAY)
633669
{
634670
SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(send_header), key, keylen, keytype, value)
@@ -658,6 +694,13 @@ static int http_client_send_http_request(zval *zobject TSRMLS_DC)
658694
http_client_swString_append_headers(http_client_buffer, ZEND_STRL("Host"), ZEND_STRL("keep-alive"));
659695
}
660696

697+
#ifdef SW_HAVE_ZLIB
698+
if (sw_zend_hash_find(Z_ARRVAL_P(send_header), ZEND_STRS("Accept-Encoding"), (void **) &value) == FAILURE)
699+
{
700+
http_client_swString_append_headers(http_client_buffer, ZEND_STRL("Accept-Encoding"), ZEND_STRL("gzip"));
701+
}
702+
#endif
703+
661704
if (post_data)
662705
{
663706
char post_len_str[16];
@@ -822,15 +865,10 @@ static PHP_METHOD(swoole_http_client, __construct)
822865
swoole_set_object(getThis(), NULL);
823866

824867
zval *headers;
825-
SW_MAKE_STD_ZVAL(headers);
868+
SW_ALLOC_INIT_ZVAL(headers);
826869
array_init(headers);
827870
zend_update_property(swoole_http_client_class_entry_ptr, getThis(), ZEND_STRL("headers"), headers TSRMLS_CC);
828871

829-
zval *body;
830-
SW_MAKE_STD_ZVAL(body);
831-
SW_ZVAL_STRING(body, "", 1);
832-
zend_update_property(swoole_http_client_class_entry_ptr, getThis(), ZEND_STRL("body"), body TSRMLS_CC);
833-
834872
http_client_callback *hcc;
835873
hcc = (http_client_callback*) emalloc(sizeof(http_client_callback));
836874
bzero(hcc, sizeof(http_client_callback));
@@ -847,7 +885,7 @@ static PHP_METHOD(swoole_http_client, __destruct)
847885
http_client_set_cb(getThis(), ZEND_STRL("finish"), NULL TSRMLS_CC);
848886
http_client_set_cb(getThis(), ZEND_STRL("close"), NULL TSRMLS_CC);
849887
http_client_set_cb(getThis(), ZEND_STRL("error"), NULL TSRMLS_CC);
850-
888+
851889
http_client_callback *hcc = swoole_get_property(getThis(), 0);
852890

853891
if (hcc->request_header)
@@ -867,7 +905,7 @@ static PHP_METHOD(swoole_http_client, __destruct)
867905
}
868906
efree(hcc);
869907
swoole_set_property(getThis(), 0, NULL);
870-
908+
871909
//printf("zim_swoole_http_client___destruct()\n");
872910
http_client *http = swoole_get_object(getThis());
873911
if (http)
@@ -1014,30 +1052,84 @@ static int http_client_parser_on_header_value(php_http_parser *parser, const cha
10141052
{
10151053
http->upgrade = 1;
10161054
}
1055+
#ifdef SW_HAVE_ZLIB
1056+
else if (strcasecmp(header_name, "Content-Encoding") == 0 && strncasecmp(at, "gzip", length) == 0)
1057+
{
1058+
http->gzip = 1;
1059+
}
1060+
#endif
10171061
efree(header_name);
10181062
return 0;
10191063
}
10201064

1065+
static int http_response_uncompress(char *body, int length)
1066+
{
1067+
z_stream stream;
1068+
memset(&stream, 0, sizeof(stream));
1069+
1070+
if (Z_OK != inflateInit2(&stream, MAX_WBITS+16))
1071+
{
1072+
swWarn("inflateInit2() failed.");
1073+
return SW_ERR;
1074+
}
1075+
1076+
int status = 0;
1077+
1078+
stream.avail_in = length;
1079+
stream.next_in = (Bytef *) body;
1080+
1081+
swString_clear(swoole_zlib_buffer);
1082+
1083+
#if 0
1084+
printf(SW_START_LINE"\nstatus=%d\tavail_in=%ld,\tavail_out=%ld,\ttotal_in=%ld,\ttotal_out=%ld\n", status, stream.avail_in, stream.avail_out,
1085+
stream.total_in, stream.total_out);
1086+
#endif
1087+
1088+
while (1)
1089+
{
1090+
stream.avail_out = swoole_zlib_buffer->size - stream.total_out;
1091+
stream.next_out = (Bytef *) (swoole_zlib_buffer->str + stream.total_out);
1092+
1093+
status = inflate(&stream, Z_SYNC_FLUSH);
1094+
1095+
#if 0
1096+
printf("status=%d\tavail_in=%ld,\tavail_out=%ld,\ttotal_in=%ld,\ttotal_out=%ld\n", status, stream.avail_in, stream.avail_out,
1097+
stream.total_in, stream.total_out);
1098+
#endif
1099+
1100+
if (status == Z_STREAM_END)
1101+
{
1102+
swoole_zlib_buffer->length = stream.total_out;
1103+
inflateEnd(&stream);
1104+
return SW_OK;
1105+
}
1106+
else if (status == Z_OK)
1107+
{
1108+
if (stream.total_out >= swoole_zlib_buffer->size)
1109+
{
1110+
swString_extend(swoole_zlib_buffer, swoole_zlib_buffer->size * 2);
1111+
}
1112+
}
1113+
else
1114+
{
1115+
inflateEnd(&stream);
1116+
return SW_ERR;
1117+
}
1118+
}
1119+
return SW_ERR;
1120+
}
1121+
10211122
static int http_client_parser_on_body(php_http_parser *parser, const char *at, size_t length)
10221123
{
10231124
#if PHP_MAJOR_VERSION < 7
10241125
TSRMLS_FETCH_FROM_CTX(sw_thread_ctx ? sw_thread_ctx : NULL);
10251126
#endif
10261127

10271128
http_client* http = (http_client*) parser->data;
1028-
zval* zobject = (zval*) http->cli->object;
1029-
1030-
zval *body = sw_zend_read_property(swoole_http_client_class_entry_ptr, zobject, ZEND_STRL("body"), 0 TSRMLS_CC);
1031-
zval *tmp;
1032-
SW_MAKE_STD_ZVAL(tmp);
1033-
SW_ZVAL_STRINGL(tmp, at, length, 1);
1034-
#if PHP_MAJOR_VERSION < 7
1035-
add_string_to_string(body, body, tmp);
1036-
#else
1037-
concat_function(body, body, tmp);
1038-
#endif
1039-
sw_zval_ptr_dtor(&tmp);
1040-
1129+
if (swString_append_ptr(http->body, (char *)at, length) < 0)
1130+
{
1131+
return -1;
1132+
}
10411133
return 0;
10421134
}
10431135

@@ -1064,6 +1156,20 @@ static int http_client_parser_on_message_complete(php_http_parser *parser)
10641156
zval **args[1];
10651157
args[0] = &zobject;
10661158

1159+
if (http->gzip)
1160+
{
1161+
if (http_response_uncompress(http->body->str, http->body->length) == SW_ERR)
1162+
{
1163+
swWarn("error");
1164+
return 0;
1165+
}
1166+
zend_update_property_stringl(swoole_http_client_class_entry_ptr, zobject, ZEND_STRL("body"), swoole_zlib_buffer->str, swoole_zlib_buffer->length TSRMLS_CC);
1167+
}
1168+
else
1169+
{
1170+
zend_update_property_stringl(swoole_http_client_class_entry_ptr, zobject, ZEND_STRL("body"), http->body->str, http->body->length TSRMLS_CC);
1171+
}
1172+
10671173
if (zcallback == NULL || ZVAL_IS_NULL(zcallback))
10681174
{
10691175
swoole_php_fatal_error(E_WARNING, "swoole_http_client object have not receive callback.");

swoole_http_server.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,8 @@ static uint8_t http_merge_global_flag = 0;
4141
static uint8_t http_merge_request_flag = 0;
4242

4343
swString *swoole_http_buffer;
44-
swString *swoole_http_form_data_buffer;
45-
46-
#ifdef SW_HAVE_ZLIB
4744
swString *swoole_zlib_buffer;
48-
#endif
45+
swString *swoole_http_form_data_buffer;
4946

5047
enum http_response_flag
5148
{

0 commit comments

Comments
 (0)