performance improvements
[m6w6/ext-http] / src / php_http_info.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2014, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "php_http_api.h"
14
15 php_http_info_t *php_http_info_init(php_http_info_t *i)
16 {
17 if (!i) {
18 i = emalloc(sizeof(*i));
19 }
20
21 memset(i, 0, sizeof(*i));
22
23 return i;
24 }
25
26 void php_http_info_dtor(php_http_info_t *i)
27 {
28 switch (i->type) {
29 case PHP_HTTP_REQUEST:
30 PTR_SET(PHP_HTTP_INFO(i).request.method, NULL);
31 PTR_SET(PHP_HTTP_INFO(i).request.url, NULL);
32 break;
33
34 case PHP_HTTP_RESPONSE:
35 PTR_SET(PHP_HTTP_INFO(i).response.status, NULL);
36 break;
37
38 default:
39 break;
40 }
41 }
42
43 void php_http_info_free(php_http_info_t **i)
44 {
45 if (*i) {
46 php_http_info_dtor(*i);
47 efree(*i);
48 *i = NULL;
49 }
50 }
51
52 void php_http_info_to_string(php_http_info_t *info, char **str, size_t *len, const char *eol)
53 {
54 char *tmp = NULL;
55
56 if (info->http.version.major == 2) {
57 if (info->type == PHP_HTTP_REQUEST) {
58 *len = spprintf(str, 0, "%s %s HTTP/2%s",
59 info->http.info.request.method?info->http.info.request.method:"UNKNOWN",
60 info->http.info.request.method&&!strcasecmp(info->http.info.request.method,"CONNECT")?(
61 info->http.info.request.url?php_http_url_authority_to_string(info->http.info.request.url, &(tmp), NULL):"0"):(
62 info->http.info.request.url?php_http_url_to_string(info->http.info.request.url, &(tmp), NULL, 0):"/"),
63 eol);
64 } else if (info->type == PHP_HTTP_RESPONSE) {
65 *len = spprintf(str, 0, "HTTP/2 %d%s%s%s",
66 info->http.info.response.code?info->http.info.response.code:200,
67 info->http.info.response.status&&*info->http.info.response.status ? " ":"",
68 STR_PTR(info->http.info.response.status),
69 eol);
70 }
71 } else if (info->type == PHP_HTTP_REQUEST) {
72 *len = spprintf(str, 0, "%s %s HTTP/%u.%u%s",
73 info->http.info.request.method?info->http.info.request.method:"UNKNOWN",
74 info->http.info.request.method&&!strcasecmp(info->http.info.request.method,"CONNECT")?(
75 info->http.info.request.url?php_http_url_authority_to_string(info->http.info.request.url, &(tmp), NULL):"0"):(
76 info->http.info.request.url?php_http_url_to_string(info->http.info.request.url, &(tmp), NULL, 0):"/"),
77 info->http.version.major||info->http.version.minor?info->http.version.major:1,
78 info->http.version.major||info->http.version.minor?info->http.version.minor:1,
79 eol);
80 } else if (info->type == PHP_HTTP_RESPONSE){
81 *len = spprintf(str, 0, "HTTP/%u.%u %d%s%s%s",
82 info->http.version.major||info->http.version.minor?info->http.version.major:1,
83 info->http.version.major||info->http.version.minor?info->http.version.minor:1,
84 info->http.info.response.code?info->http.info.response.code:200,
85 info->http.info.response.status&&*info->http.info.response.status ? " ":"",
86 STR_PTR(info->http.info.response.status),
87 eol);
88 }
89
90 PTR_FREE(tmp);
91 }
92
93 php_http_info_t *php_http_info_parse(php_http_info_t *info, const char *pre_header)
94 {
95 const char *end, *http, *off;
96 zend_bool free_info = !info;
97
98 /* sane parameter */
99 if (UNEXPECTED((!pre_header) || (!*pre_header))) {
100 return NULL;
101 }
102
103 /* where's the end of the line */
104 if (UNEXPECTED(!(end = php_http_locate_eol(pre_header, NULL)))) {
105 end = pre_header + strlen(pre_header);
106 }
107
108 /* there must be HTTP/1.x in the line */
109 if (!(http = php_http_locate_str(pre_header, end - pre_header, "HTTP/", lenof("HTTP/")))) {
110 return NULL;
111 }
112
113 info = php_http_info_init(info);
114
115 if (UNEXPECTED(!php_http_version_parse(&info->http.version, http))) {
116 if (free_info) {
117 php_http_info_free(&info);
118 }
119 return NULL;
120 }
121
122 /* clumsy fix for changed libcurl behaviour in 7.49.1, see https://github.com/curl/curl/issues/888 */
123 off = &http[lenof("HTTP/X")];
124 if (info->http.version.major < 2 || (info->http.version.major == 2 && *off == '.')) {
125 off += 2;
126 }
127
128 /* and nothing than SPACE or NUL after HTTP/X(.x) */
129 if (UNEXPECTED(*off && (!PHP_HTTP_IS_CTYPE(space, *off)))) {
130 if (free_info) {
131 php_http_info_free(&info);
132 }
133 return NULL;
134 }
135
136 #if 0
137 {
138 char *line = estrndup(pre_header, end - pre_header);
139 fprintf(stderr, "http_parse_info('%s')\n", line);
140 efree(line);
141 }
142 #endif
143
144 /* is response */
145 if (pre_header == http) {
146 const char *status = NULL, *code = off;
147
148 info->type = PHP_HTTP_RESPONSE;
149 while (code < end && ' ' == *code) ++code;
150 if (EXPECTED(end > code)) {
151 /* rfc7230#3.1.2 The status-code element is a 3-digit integer code */
152 PHP_HTTP_INFO(info).response.code = 100*(*code++ - '0');
153 PHP_HTTP_INFO(info).response.code += 10*(*code++ - '0');
154 PHP_HTTP_INFO(info).response.code += *code++ - '0';
155 if (PHP_HTTP_INFO(info).response.code < 100 || PHP_HTTP_INFO(info).response.code > 599) {
156 if (free_info) {
157 php_http_info_free(&info);
158 }
159 return NULL;
160 }
161 status = code;
162 } else {
163 PHP_HTTP_INFO(info).response.code = 0;
164 }
165 if (EXPECTED(status && end > status)) {
166 while (' ' == *status) ++status;
167 PHP_HTTP_INFO(info).response.status = estrndup(status, end - status);
168 } else {
169 PHP_HTTP_INFO(info).response.status = NULL;
170 }
171
172 return info;
173 }
174
175 /* is request */
176 else if (*(http - 1) == ' ' && (!*off || *off == '\r' || *off == '\n')) {
177 const char *url = strchr(pre_header, ' ');
178
179 info->type = PHP_HTTP_REQUEST;
180 if (EXPECTED(url && http > url)) {
181 size_t url_len = url - pre_header;
182
183 PHP_HTTP_INFO(info).request.method = estrndup(pre_header, url_len);
184
185 while (' ' == *url) ++url;
186 while (' ' == *(http-1)) --http;
187
188 if (EXPECTED(http > url)) {
189 /* CONNECT presents an authority only */
190 if (UNEXPECTED(strcasecmp(PHP_HTTP_INFO(info).request.method, "CONNECT"))) {
191 PHP_HTTP_INFO(info).request.url = php_http_url_parse(url, http - url, PHP_HTTP_URL_STDFLAGS);
192 } else {
193 PHP_HTTP_INFO(info).request.url = php_http_url_parse_authority(url, http - url, PHP_HTTP_URL_STDFLAGS);
194 }
195 if (!PHP_HTTP_INFO(info).request.url) {
196 PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
197 return NULL;
198 }
199 } else {
200 PTR_SET(PHP_HTTP_INFO(info).request.method, NULL);
201 return NULL;
202 }
203 } else {
204 PHP_HTTP_INFO(info).request.method = NULL;
205 PHP_HTTP_INFO(info).request.url = NULL;
206 }
207
208 return info;
209 }
210
211 /* some darn header containing HTTP/X(.x) */
212 else {
213 if (free_info) {
214 php_http_info_free(&info);
215 }
216 return NULL;
217 }
218 }
219
220 /*
221 * Local variables:
222 * tab-width: 4
223 * c-basic-offset: 4
224 * End:
225 * vim600: noet sw=4 ts=4 fdm=marker
226 * vim<600: noet sw=4 ts=4
227 */
228