Skip to content

Commit 88c225c

Browse files
committed
standard/http_fopen_wrapper: use zend_string for Location header value
- Use newer zend_string APIs - Make logic more explicit and understandable - Prevent some strlen() recomputations
1 parent 05bc761 commit 88c225c

1 file changed

Lines changed: 49 additions & 49 deletions

File tree

ext/standard/http_fopen_wrapper.c

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,9 @@ static zend_result php_stream_handle_proxy_authorization_header(const char *s, s
139139
typedef struct _php_stream_http_response_header_info {
140140
php_stream_filter *transfer_encoding;
141141
size_t file_size;
142+
zend_string *location;
142143
bool error;
143144
bool follow_location;
144-
char *location;
145-
size_t location_len;
146145
} php_stream_http_response_header_info;
147146

148147
static void php_stream_http_response_header_info_init(
@@ -281,13 +280,11 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w
281280
zend_string_efree(last_header_line);
282281
return NULL;
283282
}
284-
if (header_info->location_len == 0) {
285-
header_info->location = emalloc(last_header_value_len + 1);
286-
} else if (header_info->location_len <= last_header_value_len) {
287-
header_info->location = erealloc(header_info->location, last_header_value_len + 1);
283+
/* Previous location header encountered */
284+
if (UNEXPECTED(header_info->location )) {
285+
zend_string_release_ex(header_info->location, false);
288286
}
289-
header_info->location_len = last_header_value_len;
290-
memcpy(header_info->location, last_header_value, last_header_value_len + 1);
287+
header_info->location = zend_string_init(last_header_value, last_header_value_len, false);
291288
} else if (zend_string_starts_with_literal_ci(last_header_line, "Content-Type:")) {
292289
php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, last_header_value, 0);
293290
} else if (zend_string_starts_with_literal_ci(last_header_line, "Content-Length:")) {
@@ -1037,7 +1034,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
10371034
}
10381035

10391036
if (header_info.location != NULL)
1040-
php_stream_notify_info(context, PHP_STREAM_NOTIFY_REDIRECTED, header_info.location, 0);
1037+
php_stream_notify_info(context, PHP_STREAM_NOTIFY_REDIRECTED, ZSTR_VAL(header_info.location), 0);
10411038

10421039
php_stream_close(stream);
10431040
stream = NULL;
@@ -1048,63 +1045,66 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
10481045
}
10491046

10501047
if (header_info.location != NULL) {
1048+
zend_string *new_path = NULL;
1049+
1050+
/* Redirection location must be a URI with schema,
1051+
* if we don't have a schema, the location is within the existing schema and host */
1052+
if (ZSTR_LEN(header_info.location) < 8 ||
1053+
(!zend_string_starts_with_literal_ci(header_info.location, "http://") &&
1054+
!zend_string_starts_with_literal_ci(header_info.location, "https://") &&
1055+
!zend_string_starts_with_literal_ci(header_info.location, "ftp://") &&
1056+
!zend_string_starts_with_literal_ci(header_info.location, "ftps://"))
1057+
) {
1058+
zend_string *loc_path = NULL;
10511059

1052-
char *new_path = NULL;
1053-
1054-
if (strlen(header_info.location) < 8 ||
1055-
(strncasecmp(header_info.location, "http://", sizeof("http://")-1) &&
1056-
strncasecmp(header_info.location, "https://", sizeof("https://")-1) &&
1057-
strncasecmp(header_info.location, "ftp://", sizeof("ftp://")-1) &&
1058-
strncasecmp(header_info.location, "ftps://", sizeof("ftps://")-1)))
1059-
{
1060-
char *loc_path = NULL;
1061-
if (*header_info.location != '/') {
1062-
if (*(header_info.location+1) != '\0' && resource->path) {
1060+
/* if we don't have an absolute location we need to determine it */
1061+
if (ZSTR_VAL(header_info.location)[0] != '/') {
1062+
if (ZSTR_VAL(header_info.location)[1] != '\0' && resource->path) {
1063+
/* find last '/' to determine relative path */
10631064
char *s = strrchr(ZSTR_VAL(resource->path), '/');
10641065
if (!s) {
1065-
s = ZSTR_VAL(resource->path);
1066-
if (!ZSTR_LEN(resource->path)) {
1067-
zend_string_release_ex(resource->path, 0);
1068-
resource->path = ZSTR_INIT_LITERAL("/", 0);
1069-
s = ZSTR_VAL(resource->path);
1070-
} else {
1071-
*s = '/';
1072-
}
1073-
}
1074-
s[1] = '\0';
1075-
if (resource->path &&
1076-
ZSTR_VAL(resource->path)[0] == '/' &&
1077-
ZSTR_VAL(resource->path)[1] == '\0') {
1078-
spprintf(&loc_path, 0, "%s%s", ZSTR_VAL(resource->path), header_info.location);
1066+
/* No path segment, just prefix relative location with '/' */
1067+
loc_path = zend_string_concat2(
1068+
ZEND_STRL("/"),
1069+
ZSTR_VAL(header_info.location), ZSTR_LEN(header_info.location)
1070+
);
10791071
} else {
1080-
spprintf(&loc_path, 0, "%s/%s", ZSTR_VAL(resource->path), header_info.location);
1072+
size_t offset_to_last_slash = s - ZSTR_VAL(resource->path);
1073+
ZEND_ASSERT(ZSTR_VAL(resource->path)[offset_to_last_slash] == '/');
1074+
loc_path = zend_string_concat2(
1075+
ZSTR_VAL(resource->path), offset_to_last_slash + 1,
1076+
ZSTR_VAL(header_info.location), ZSTR_LEN(header_info.location)
1077+
);
10811078
}
10821079
} else {
1083-
spprintf(&loc_path, 0, "/%s", header_info.location);
1080+
loc_path = zend_string_concat2(
1081+
ZEND_STRL("/"),
1082+
ZSTR_VAL(header_info.location), ZSTR_LEN(header_info.location)
1083+
);
10841084
}
10851085
} else {
10861086
loc_path = header_info.location;
10871087
header_info.location = NULL;
10881088
}
10891089
if ((use_ssl && resource->port != 443) || (!use_ssl && resource->port != 80)) {
1090-
spprintf(&new_path, 0, "%s://%s:" ZEND_LONG_FMT "%s", ZSTR_VAL(resource->scheme),
1091-
ZSTR_VAL(resource->host), resource->port, loc_path);
1090+
new_path = zend_strpprintf(0, "%s://%s:" ZEND_LONG_FMT "%s", ZSTR_VAL(resource->scheme),
1091+
ZSTR_VAL(resource->host), resource->port, ZSTR_VAL(loc_path));
10921092
} else {
1093-
spprintf(&new_path, 0, "%s://%s%s", ZSTR_VAL(resource->scheme),
1094-
ZSTR_VAL(resource->host), loc_path);
1093+
new_path = zend_strpprintf(0, "%s://%s%s", ZSTR_VAL(resource->scheme),
1094+
ZSTR_VAL(resource->host), ZSTR_VAL(loc_path));
10951095
}
1096-
efree(loc_path);
1096+
zend_string_release_ex(loc_path, false);
10971097
} else {
10981098
new_path = header_info.location;
10991099
header_info.location = NULL;
11001100
}
11011101

11021102
php_uri_struct_free(resource);
11031103
/* check for invalid redirection URLs */
1104-
if ((resource = php_uri_parse_to_struct(uri_parser, new_path, strlen(new_path), PHP_URI_COMPONENT_READ_MODE_RAW, true)) == NULL) {
1104+
if ((resource = php_uri_parse_to_struct(uri_parser, ZSTR_VAL(new_path), ZSTR_LEN(new_path), PHP_URI_COMPONENT_READ_MODE_RAW, true)) == NULL) {
11051105
php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl,
1106-
"Invalid redirect URL! %s", new_path);
1107-
efree(new_path);
1106+
"Invalid redirect URL! %s", ZSTR_VAL(new_path));
1107+
zend_string_release_ex(new_path, false);
11081108
goto out;
11091109
}
11101110

@@ -1116,7 +1116,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
11161116
while (s < e) { \
11171117
if (iscntrl(*s)) { \
11181118
php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl, \
1119-
"Invalid redirect URL! %s", new_path); \
1119+
"Invalid redirect URL! %s", ZSTR_VAL(new_path)); \
11201120
efree(new_path); \
11211121
goto out; \
11221122
} \
@@ -1125,7 +1125,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
11251125
} \
11261126
}
11271127
/* check for control characters in login, password & path */
1128-
if (strncasecmp(new_path, "http://", sizeof("http://") - 1) || strncasecmp(new_path, "https://", sizeof("https://") - 1)) {
1128+
if (zend_string_starts_with_literal_ci(new_path, "http://") || zend_string_starts_with_literal_ci(new_path, "https://")) {
11291129
CHECK_FOR_CNTRL_CHARS(resource->user);
11301130
CHECK_FOR_CNTRL_CHARS(resource->password);
11311131
CHECK_FOR_CNTRL_CHARS(resource->path);
@@ -1138,9 +1138,9 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
11381138
new_flags |= HTTP_WRAPPER_KEEP_METHOD;
11391139
}
11401140
stream = php_stream_url_wrap_http_ex(
1141-
wrapper, new_path, mode, options, opened_path, context,
1141+
wrapper, ZSTR_VAL(new_path), mode, options, opened_path, context,
11421142
--redirect_max, new_flags, response_header STREAMS_CC);
1143-
efree(new_path);
1143+
zend_string_release_ex(new_path, false);
11441144
} else {
11451145
php_stream_wrapper_log_warn(wrapper, context, options, ProtocolError,
11461146
"HTTP request failed! %s", tmp_line);
@@ -1155,7 +1155,7 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
11551155
}
11561156

11571157
if (header_info.location != NULL) {
1158-
efree(header_info.location);
1158+
zend_string_release_ex(header_info.location, false);
11591159
}
11601160

11611161
if (resource) {

0 commit comments

Comments
 (0)