- ditch HttpMessage::__construct() and ::setRaw
[m6w6/ext-http] / http_message_api.c
1 /*
2 +----------------------------------------------------------------------+
3 | PECL :: http |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.0 of the PHP license, that |
6 | is bundled with this package in the file LICENSE, and is available |
7 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
8 | If you did not receive a copy of the PHP license and are unable to |
9 | obtain it through the world-wide-web, please send a note to |
10 | license@php.net so we can mail you a copy immediately. |
11 +----------------------------------------------------------------------+
12 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
13 +----------------------------------------------------------------------+
14 */
15
16 /* $Id$ */
17
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21
22 #include "php.h"
23 #include "php_http.h"
24 #include "php_http_std_defs.h"
25 #include "php_http_message_api.h"
26 #include "php_http_api.h"
27 #include "php_http_headers_api.h"
28
29 #include "phpstr/phpstr.h"
30
31 #define http_message_parse_nested(msg, begin, length) _http_message_parse_nested((msg), (begin), (length) TSRMLS_CC)
32 static inline http_message *_http_message_parse_nested(http_message *msg, const char *begin, size_t length TSRMLS_DC)
33 {
34 http_message *new;
35
36 while (isspace(*begin)) {
37 ++begin;
38 if (!length--) {
39 return NULL;
40 }
41 }
42
43 if (new = http_message_parse(begin, length)) {
44 new->nested = msg;
45 return new;
46 }
47 return NULL;
48 }
49
50 PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_message_type type)
51 {
52 if (!message) {
53 message = ecalloc(1, sizeof(http_message));
54 }
55
56 message->type = type;
57 message->nested = NULL;
58 phpstr_init(&message->body);
59 zend_hash_init(&message->hdrs, 0, NULL, ZVAL_PTR_DTOR, 0);
60
61 return message;
62 }
63
64 PHP_HTTP_API http_message *_http_message_parse_ex(http_message *msg, const char *message, size_t message_length TSRMLS_DC)
65 {
66 char *body = NULL;
67 size_t header_length = 0;
68 zend_bool free_msg = msg ? 0 : 1;
69
70 if (message_length < HTTP_MSG_MIN_SIZE) {
71 return NULL;
72 }
73
74 if (!message) {
75 return NULL;
76 }
77
78 msg = http_message_init(msg);
79
80 if (body = strstr(message, HTTP_CRLF HTTP_CRLF)) {
81 body += lenof(HTTP_CRLF HTTP_CRLF);
82 header_length = body - message;
83 } else {
84 header_length = message_length;
85 }
86
87 if (SUCCESS != http_parse_headers_cb((char *)message, header_length, &msg->hdrs, 1, http_message_parse_headers_callback, (void **) &msg)) {
88 if (free_msg) {
89 http_message_free(msg);
90 }
91 return NULL;
92 }
93
94 if (body) {
95 zval **c;
96 http_message *nested;
97
98 if (SUCCESS == zend_hash_find(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void **) &c)) {
99 long len = atol(Z_STRVAL_PP(c));
100 phpstr_from_string_ex(PHPSTR(msg), body, len);
101 if (nested = http_message_parse_nested(msg, body + len, message + message_length - body - len)) {
102 return nested;
103 }
104 } else if (
105 SUCCESS == zend_hash_find(&msg->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"), (void **) &c) &&
106 !strcasecmp("chunked", Z_STRVAL_PP(c))) {
107
108 char *decoded, *end;
109 size_t decoded_len;
110
111 if (end = http_chunked_decode(body, message_length - header_length, &decoded, &decoded_len)) {
112 phpstr_from_string_ex(PHPSTR(msg), decoded, decoded_len);
113 efree(decoded);
114 if (nested = http_message_parse_nested(msg, end, message + message_length - end)) {
115 return nested;
116 }
117 }
118 } else {
119 phpstr_from_string_ex(PHPSTR(msg), body, message_length - header_length);
120 }
121 }
122
123 return msg;
124 }
125
126 PHP_HTTP_API void _http_message_parse_headers_callback(void **message, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC)
127 {
128 http_message *old = (http_message *) *message;
129 http_message *new;
130
131 if (old->type || zend_hash_num_elements(&old->hdrs) || PHPSTR_LEN(old)) {
132 new = http_message_new();
133
134 new->nested = old;
135 *message = new;
136 *headers = &new->hdrs;
137 } else {
138 new = old;
139 }
140
141 // response
142 if (!strncmp(http_line, "HTTP/1.", lenof("HTTP/1."))) {
143 new->type = HTTP_MSG_RESPONSE;
144 new->info.response.http_version = atof(http_line + lenof("HTTP/"));
145 new->info.response.code = atoi(http_line + lenof("HTTP/1.1 "));
146 } else
147 // request
148 if (!strncmp(http_line + line_length - lenof("HTTP/1.1"), "HTTP/1.", lenof("HTTP/1."))) {
149 const char *method_sep_uri = strchr(http_line, ' ');
150
151 new->type = HTTP_MSG_REQUEST;
152 new->info.request.http_version = atof(http_line + line_length - lenof("1.1"));
153 new->info.request.method = estrndup(http_line, method_sep_uri - http_line);
154 new->info.request.URI = estrndup(method_sep_uri + 1, http_line + line_length - method_sep_uri - 1 - lenof(" HTTP/1.1"));
155 }
156 }
157
158 PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length)
159 {
160 phpstr str;
161 char *key, *data;
162 ulong idx;
163 zval **header;
164
165 phpstr_init_ex(&str, 4096, 0);
166
167 do {
168
169 switch (msg->type)
170 {
171 case HTTP_MSG_REQUEST:
172 phpstr_appendf(&str, "%s %s HTTP/%1.1f" HTTP_CRLF,
173 msg->info.request.method,
174 msg->info.request.URI,
175 msg->info.request.http_version);
176 break;
177
178 case HTTP_MSG_RESPONSE:
179 phpstr_appendf(&str, "HTTP/%1.1f %d" HTTP_CRLF,
180 msg->info.response.http_version,
181 msg->info.response.code);
182 break;
183 }
184
185 FOREACH_HASH_KEYVAL(&msg->hdrs, key, idx, header) {
186 if (key) {
187 zval **single_header;
188
189 switch (Z_TYPE_PP(header))
190 {
191 case IS_STRING:
192 phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(header));
193 break;
194
195 case IS_ARRAY:
196 FOREACH_VAL(*header, single_header) {
197 phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(single_header));
198 }
199 break;
200 }
201
202 key = NULL;
203 }
204 }
205
206 phpstr_appends(&str, HTTP_CRLF);
207 phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg));
208 phpstr_appends(&str, HTTP_CRLF);
209
210 } while (msg = msg->nested);
211
212 data = phpstr_data(&str, string, length);
213 if (!string) {
214 efree(data);
215 }
216
217 phpstr_dtor(&str);
218 }
219
220 PHP_HTTP_API void _http_message_dtor(http_message *message)
221 {
222 if (message) {
223 zend_hash_destroy(&message->hdrs);
224 phpstr_dtor(PHPSTR(message));
225 if (message->type == HTTP_MSG_REQUEST) {
226 if (message->info.request.method) {
227 efree(message->info.request.method);
228 message->info.request.method = NULL;
229 }
230 if (message->info.request.URI) {
231 efree(message->info.request.URI);
232 message->info.request.URI = NULL;
233 }
234 }
235 }
236 }
237
238 PHP_HTTP_API void _http_message_free(http_message *message)
239 {
240 if (message) {
241 if (message->nested) {
242 http_message_free(message->nested);
243 message->nested = NULL;
244 }
245 http_message_dtor(message);
246 efree(message);
247 }
248 }
249
250 /*
251 * Local variables:
252 * tab-width: 4
253 * c-basic-offset: 4
254 * End:
255 * vim600: noet sw=4 ts=4 fdm=marker
256 * vim<600: noet sw=4 ts=4
257 */
258