Line data Source code
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 46 : static void set_option(zval *options, const char *name_str, size_t name_len, int type, void *value_ptr, size_t value_len TSRMLS_DC)
16 : {
17 46 : if (Z_TYPE_P(options) == IS_OBJECT) {
18 46 : if (value_ptr) {
19 46 : switch (type) {
20 : case IS_DOUBLE:
21 1 : zend_update_property_double(Z_OBJCE_P(options), options, name_str, name_len, *(double *)value_ptr TSRMLS_CC);
22 1 : break;
23 : case IS_LONG:
24 21 : zend_update_property_long(Z_OBJCE_P(options), options, name_str, name_len, *(long *)value_ptr TSRMLS_CC);
25 21 : break;
26 : case IS_STRING:
27 20 : zend_update_property_stringl(Z_OBJCE_P(options), options, name_str, name_len, value_ptr, value_len TSRMLS_CC);
28 20 : break;
29 : case IS_OBJECT:
30 4 : zend_update_property(Z_OBJCE_P(options), options, name_str, name_len, value_ptr TSRMLS_CC);
31 4 : break;
32 : }
33 : } else {
34 0 : zend_update_property_null(Z_OBJCE_P(options), options, name_str, name_len TSRMLS_CC);
35 : }
36 : } else {
37 0 : convert_to_array(options);
38 0 : if (value_ptr) {
39 0 : switch (type) {
40 : case IS_DOUBLE:
41 0 : add_assoc_double_ex(options, name_str, name_len + 1, *(double *)value_ptr);
42 0 : break;
43 : case IS_LONG:
44 0 : add_assoc_long_ex(options, name_str, name_len + 1, *(long *)value_ptr);
45 0 : break;
46 : case IS_STRING: {
47 0 : char *value = estrndup(value_ptr, value_len);
48 0 : add_assoc_stringl_ex(options, name_str, name_len + 1, value, value_len, 0);
49 0 : break;
50 : case IS_OBJECT:
51 0 : Z_ADDREF_P(value_ptr);
52 0 : add_assoc_zval_ex(options, name_str, name_len + 1, value_ptr);
53 0 : break;
54 : }
55 : }
56 : } else {
57 0 : add_assoc_null_ex(options, name_str, name_len + 1);
58 : }
59 : }
60 46 : }
61 290 : static zval *get_option(zval *options, const char *name_str, size_t name_len TSRMLS_DC)
62 : {
63 : zval *val, **valptr;
64 :
65 290 : if (Z_TYPE_P(options) == IS_OBJECT) {
66 290 : val = zend_read_property(Z_OBJCE_P(options), options, name_str, name_len, 0 TSRMLS_CC);
67 : } else {
68 0 : if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(options), name_str, name_len + 1, (void *) &valptr)) {
69 0 : val = *valptr;
70 : } else {
71 0 : val = NULL;
72 : }
73 : }
74 290 : if (val) {
75 290 : Z_ADDREF_P(val);
76 : }
77 290 : return val;
78 : }
79 75 : static php_http_message_body_t *get_body(zval *options TSRMLS_DC)
80 : {
81 : zval *zbody;
82 75 : php_http_message_body_t *body = NULL;
83 :
84 75 : if ((zbody = get_option(options, ZEND_STRL("body") TSRMLS_CC))) {
85 75 : if ((Z_TYPE_P(zbody) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)) {
86 75 : php_http_message_body_object_t *body_obj = zend_object_store_get_object(zbody TSRMLS_CC);
87 :
88 75 : body = body_obj->body;
89 : }
90 75 : zval_ptr_dtor(&zbody);
91 : }
92 :
93 75 : return body;
94 : }
95 21 : static php_http_message_t *get_request(zval *options TSRMLS_DC)
96 : {
97 : zval *zrequest;
98 21 : php_http_message_t *request = NULL;
99 :
100 21 : if ((zrequest = get_option(options, ZEND_STRL("request") TSRMLS_CC))) {
101 21 : if (Z_TYPE_P(zrequest) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zrequest), php_http_message_class_entry TSRMLS_CC)) {
102 6 : php_http_message_object_t *request_obj = zend_object_store_get_object(zrequest TSRMLS_CC);
103 :
104 6 : request = request_obj->message;
105 : }
106 21 : zval_ptr_dtor(&zrequest);
107 : }
108 :
109 21 : return request;
110 : }
111 :
112 21 : php_http_cache_status_t php_http_env_is_response_cached_by_etag(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC)
113 : {
114 21 : php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
115 21 : int free_etag = 0;
116 21 : char *header = NULL, *etag;
117 : php_http_message_body_t *body;
118 : zval *zetag;
119 :
120 :
121 21 : if (!(body = get_body(options TSRMLS_CC))) {
122 0 : return ret;
123 : }
124 :
125 21 : if ((zetag = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
126 21 : zval *zetag_copy = php_http_ztyp(IS_STRING, zetag);
127 21 : zval_ptr_dtor(&zetag);
128 21 : zetag = zetag_copy;
129 : }
130 :
131 21 : if (zetag && Z_STRLEN_P(zetag)) {
132 10 : etag = Z_STRVAL_P(zetag);
133 11 : } else if ((etag = php_http_message_body_etag(body))) {
134 11 : set_option(options, ZEND_STRL("etag"), IS_STRING, etag, strlen(etag) TSRMLS_CC);
135 11 : free_etag = 1;
136 : }
137 :
138 21 : if (zetag) {
139 21 : zval_ptr_dtor(&zetag);
140 : }
141 :
142 21 : if (etag && (header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
143 2 : ret = php_http_match(header, etag, PHP_HTTP_MATCH_WORD) ? PHP_HTTP_CACHE_HIT : PHP_HTTP_CACHE_MISS;
144 : }
145 :
146 21 : if (free_etag) {
147 11 : efree(etag);
148 : }
149 :
150 21 : STR_FREE(header);
151 21 : return ret;
152 : }
153 :
154 23 : php_http_cache_status_t php_http_env_is_response_cached_by_last_modified(zval *options, const char *header_str, size_t header_len, php_http_message_t *request TSRMLS_DC)
155 : {
156 23 : php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
157 : char *header;
158 23 : time_t ums, lm = 0;
159 : php_http_message_body_t *body;
160 : zval *zlm;
161 :
162 23 : if (!(body = get_body(options TSRMLS_CC))) {
163 0 : return ret;
164 : }
165 :
166 23 : if ((zlm = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
167 23 : zval *zlm_copy = php_http_ztyp(IS_LONG, zlm);
168 23 : zval_ptr_dtor(&zlm);
169 23 : zlm = zlm_copy;
170 : }
171 :
172 23 : if (zlm && Z_LVAL_P(zlm) > 0) {
173 10 : lm = Z_LVAL_P(zlm);
174 : } else {
175 13 : lm = php_http_message_body_mtime(body);
176 13 : set_option(options, ZEND_STRL("lastModified"), IS_LONG, &lm, 0 TSRMLS_CC);
177 : }
178 :
179 23 : if (zlm) {
180 23 : zval_ptr_dtor(&zlm);
181 : }
182 :
183 23 : if ((header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
184 4 : ums = php_parse_date(header, NULL);
185 :
186 4 : if (ums > 0 && ums >= lm) {
187 4 : ret = PHP_HTTP_CACHE_HIT;
188 : } else {
189 0 : ret = PHP_HTTP_CACHE_MISS;
190 : }
191 : }
192 :
193 23 : STR_FREE(header);
194 23 : return ret;
195 : }
196 :
197 12 : static zend_bool php_http_env_response_is_cacheable(php_http_env_response_t *r, php_http_message_t *request)
198 : {
199 : TSRMLS_FETCH_FROM_CTX(r->ts);
200 :
201 12 : if (r->ops->get_status(r) >= 400) {
202 0 : return 0;
203 : }
204 :
205 12 : if (php_http_env_got_request_header(ZEND_STRL("Authorization"), request TSRMLS_CC)) {
206 0 : return 0;
207 : }
208 :
209 12 : if (-1 == php_http_select_str(php_http_env_get_request_method(request TSRMLS_CC), 2, "HEAD", "GET")) {
210 1 : return 0;
211 : }
212 :
213 11 : return 1;
214 : }
215 :
216 20 : static size_t output(void *context, char *buf, size_t len TSRMLS_DC)
217 : {
218 20 : php_http_env_response_t *r = context;
219 :
220 20 : if (SUCCESS != r->ops->write(r, buf, len)) {
221 2 : return (size_t) -1;
222 : }
223 :
224 : /* we really only need to flush when throttling is enabled,
225 : because we push the data as fast as possible anyway if not */
226 18 : if (r->throttle.delay >= PHP_HTTP_DIFFSEC) {
227 6 : r->ops->flush(r);
228 6 : php_http_sleep(r->throttle.delay);
229 : }
230 18 : return len;
231 : }
232 :
233 : #define php_http_env_response_send_done(r) php_http_env_response_send_data((r), NULL, 0)
234 47 : static STATUS php_http_env_response_send_data(php_http_env_response_t *r, const char *buf, size_t len)
235 : {
236 47 : size_t chunks_sent, chunk = r->throttle.chunk ? r->throttle.chunk : PHP_HTTP_SENDBUF_SIZE;
237 : TSRMLS_FETCH_FROM_CTX(r->ts);
238 :
239 47 : if (r->content.encoder) {
240 6 : char *enc_str = NULL;
241 6 : size_t enc_len = 0;
242 :
243 6 : if (buf) {
244 3 : if (SUCCESS != php_http_encoding_stream_update(r->content.encoder, buf, len, &enc_str, &enc_len)) {
245 0 : return FAILURE;
246 : }
247 : } else {
248 3 : if (SUCCESS != php_http_encoding_stream_finish(r->content.encoder, &enc_str, &enc_len)) {
249 0 : return FAILURE;
250 : }
251 : }
252 :
253 6 : if (!enc_str) {
254 0 : return SUCCESS;
255 : }
256 6 : chunks_sent = php_http_buffer_chunked_output(&r->buffer, enc_str, enc_len, buf ? chunk : 0, output, r TSRMLS_CC);
257 6 : STR_FREE(enc_str);
258 : } else {
259 41 : chunks_sent = php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC);
260 : }
261 :
262 47 : return chunks_sent != (size_t) -1 ? SUCCESS : FAILURE;
263 : }
264 :
265 17 : php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options, php_http_env_response_ops_t *ops, void *init_arg TSRMLS_DC)
266 : {
267 : zend_bool free_r;
268 :
269 17 : if ((free_r = !r)) {
270 8 : r = emalloc(sizeof(*r));
271 : }
272 17 : memset(r, 0, sizeof(*r));
273 :
274 17 : if (ops) {
275 8 : r->ops = ops;
276 : } else {
277 9 : r->ops = php_http_env_response_get_sapi_ops();
278 : }
279 :
280 17 : r->buffer = php_http_buffer_init(NULL);
281 :
282 17 : Z_ADDREF_P(options);
283 17 : r->options = options;
284 :
285 : TSRMLS_SET_CTX(r->ts);
286 :
287 17 : if (r->ops->init && (SUCCESS != r->ops->init(r, init_arg))) {
288 0 : if (free_r) {
289 0 : php_http_env_response_free(&r);
290 : } else {
291 0 : php_http_env_response_dtor(r);
292 0 : r = NULL;
293 : }
294 : }
295 :
296 17 : return r;
297 : }
298 :
299 17 : void php_http_env_response_dtor(php_http_env_response_t *r)
300 : {
301 17 : if (r->ops->dtor) {
302 8 : r->ops->dtor(r);
303 : }
304 17 : php_http_buffer_free(&r->buffer);
305 17 : zval_ptr_dtor(&r->options);
306 17 : STR_FREE(r->content.type);
307 17 : STR_FREE(r->content.encoding);
308 17 : if (r->content.encoder) {
309 3 : php_http_encoding_stream_free(&r->content.encoder);
310 : }
311 17 : }
312 :
313 8 : void php_http_env_response_free(php_http_env_response_t **r)
314 : {
315 8 : if (*r) {
316 8 : php_http_env_response_dtor(*r);
317 8 : efree(*r);
318 8 : *r = NULL;
319 : }
320 8 : }
321 :
322 17 : static STATUS php_http_env_response_send_head(php_http_env_response_t *r, php_http_message_t *request)
323 : {
324 17 : STATUS ret = SUCCESS;
325 17 : zval *zoption, *options = r->options;
326 : TSRMLS_FETCH_FROM_CTX(r->ts);
327 :
328 17 : if (r->done) {
329 1 : return ret;
330 : }
331 :
332 16 : if ((zoption = get_option(options, ZEND_STRL("responseCode") TSRMLS_CC))) {
333 16 : zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
334 :
335 16 : zval_ptr_dtor(&zoption);
336 16 : if (Z_LVAL_P(zoption_copy) > 0) {
337 16 : ret = r->ops->set_status(r, Z_LVAL_P(zoption_copy));
338 : }
339 16 : zval_ptr_dtor(&zoption_copy);
340 : }
341 :
342 16 : if (ret != SUCCESS) {
343 0 : return ret;
344 : }
345 :
346 16 : if ((zoption = get_option(options, ZEND_STRL("httpVersion") TSRMLS_CC))) {
347 : php_http_version_t v;
348 16 : zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
349 :
350 16 : zval_ptr_dtor(&zoption);
351 16 : if (Z_STRLEN_P(zoption_copy) && php_http_version_parse(&v, Z_STRVAL_P(zoption_copy) TSRMLS_CC)) {
352 16 : ret = r->ops->set_protocol_version(r, &v);
353 16 : php_http_version_dtor(&v);
354 : }
355 16 : zval_ptr_dtor(&zoption_copy);
356 : }
357 :
358 16 : if (ret != SUCCESS) {
359 0 : return ret;
360 : }
361 :
362 16 : if ((zoption = get_option(options, ZEND_STRL("headers") TSRMLS_CC))) {
363 16 : if (Z_TYPE_P(zoption) == IS_ARRAY) {
364 16 : php_http_header_to_callback(Z_ARRVAL_P(zoption), 0, (php_http_pass_format_callback_t) r->ops->set_header, r TSRMLS_CC);
365 : }
366 16 : zval_ptr_dtor(&zoption);
367 : }
368 :
369 16 : if (ret != SUCCESS) {
370 0 : return ret;
371 : }
372 :
373 16 : if ((zoption = get_option(options, ZEND_STRL("contentType") TSRMLS_CC))) {
374 16 : zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
375 :
376 16 : zval_ptr_dtor(&zoption);
377 16 : if (Z_STRLEN_P(zoption_copy) && strchr(Z_STRVAL_P(zoption_copy), '/')) {
378 3 : if (SUCCESS == (ret = r->ops->set_header(r, "Content-Type: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy)))) {
379 3 : r->content.type = estrndup(Z_STRVAL_P(zoption_copy), Z_STRLEN_P(zoption_copy));
380 : }
381 : }
382 16 : zval_ptr_dtor(&zoption_copy);
383 : }
384 :
385 16 : if (ret != SUCCESS) {
386 0 : return ret;
387 : }
388 :
389 16 : if (r->range.status == PHP_HTTP_RANGE_OK) {
390 4 : if (zend_hash_num_elements(&r->range.values) == 1) {
391 : zval **range, **begin, **end;
392 :
393 3 : if ( 1 == php_http_array_list(&r->range.values TSRMLS_CC, 1, &range)
394 3 : && 2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end)
395 : ) {
396 6 : if (SUCCESS == (ret = r->ops->set_status(r, 206))) {
397 3 : ret = r->ops->set_header(r, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin), Z_LVAL_PP(end), r->content.length);
398 : }
399 : } else {
400 : /* this should never happen */
401 0 : zend_hash_destroy(&r->range.values);
402 0 : ret = FAILURE;
403 : }
404 : } else {
405 1 : php_http_boundary(r->range.boundary, sizeof(r->range.boundary) TSRMLS_CC);
406 1 : if (SUCCESS == (ret = r->ops->set_status(r, 206))) {
407 1 : ret = r->ops->set_header(r, "Content-Type: multipart/byteranges; boundary=%s", r->range.boundary);
408 : }
409 : }
410 : } else {
411 12 : if ((zoption = get_option(options, ZEND_STRL("cacheControl") TSRMLS_CC))) {
412 12 : zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
413 :
414 12 : zval_ptr_dtor(&zoption);
415 12 : if (Z_STRLEN_P(zoption_copy)) {
416 2 : ret = r->ops->set_header(r, "Cache-Control: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy));
417 : }
418 12 : zval_ptr_dtor(&zoption_copy);
419 : }
420 :
421 12 : if (ret != SUCCESS) {
422 0 : return ret;
423 : }
424 :
425 12 : if ((zoption = get_option(options, ZEND_STRL("contentDisposition") TSRMLS_CC))) {
426 12 : zval *zoption_copy = php_http_ztyp(IS_ARRAY, zoption);
427 : php_http_buffer_t buf;
428 :
429 12 : php_http_buffer_init(&buf);
430 12 : if (php_http_params_to_string(&buf, Z_ARRVAL_P(zoption_copy), ZEND_STRL(","), ZEND_STRL(";"), ZEND_STRL("="), PHP_HTTP_PARAMS_DEFAULT TSRMLS_CC)) {
431 12 : if (buf.used) {
432 1 : ret = r->ops->set_header(r, "Content-Disposition: %.*s", buf.used, buf.data);
433 : }
434 : }
435 :
436 12 : php_http_buffer_dtor(&buf);
437 12 : zval_ptr_dtor(&zoption_copy);
438 12 : zval_ptr_dtor(&zoption);
439 : }
440 :
441 12 : if (ret != SUCCESS) {
442 0 : return ret;
443 : }
444 :
445 12 : if ((zoption = get_option(options, ZEND_STRL("contentEncoding") TSRMLS_CC))) {
446 12 : zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
447 : zval zsupported;
448 12 : HashTable *result = NULL;
449 :
450 12 : zval_ptr_dtor(&zoption);
451 12 : switch (Z_LVAL_P(zoption_copy)) {
452 : case PHP_HTTP_CONTENT_ENCODING_GZIP:
453 3 : INIT_PZVAL(&zsupported);
454 3 : array_init(&zsupported);
455 3 : add_next_index_stringl(&zsupported, ZEND_STRL("none"), 1);
456 3 : add_next_index_stringl(&zsupported, ZEND_STRL("gzip"), 1);
457 3 : add_next_index_stringl(&zsupported, ZEND_STRL("deflate"), 1);
458 :
459 3 : if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported), request TSRMLS_CC))) {
460 3 : char *key_str = NULL;
461 3 : uint key_len = 0;
462 :
463 3 : zend_hash_internal_pointer_reset(result);
464 3 : if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key_str, &key_len, NULL, 0, NULL)) {
465 3 : if (!strcmp(key_str, "gzip")) {
466 2 : if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC))) {
467 0 : ret = FAILURE;
468 2 : } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: gzip"))) {
469 2 : r->content.encoding = estrndup(key_str, key_len - 1);
470 : }
471 1 : } else if (!strcmp(key_str, "deflate")) {
472 1 : if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC))) {
473 0 : ret = FAILURE;
474 1 : } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: deflate"))) {
475 1 : r->content.encoding = estrndup(key_str, key_len - 1);
476 : }
477 : } else {
478 0 : ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
479 : }
480 :
481 3 : if (SUCCESS == ret) {
482 3 : ret = r->ops->add_header(r, "Vary: Accept-Encoding");
483 : }
484 : }
485 :
486 3 : zend_hash_destroy(result);
487 3 : FREE_HASHTABLE(result);
488 : }
489 :
490 3 : zval_dtor(&zsupported);
491 3 : break;
492 :
493 : case PHP_HTTP_CONTENT_ENCODING_NONE:
494 : default:
495 9 : ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
496 9 : break;
497 : }
498 12 : zval_ptr_dtor(&zoption_copy);
499 : }
500 :
501 12 : if (SUCCESS != ret) {
502 0 : return ret;
503 : }
504 :
505 12 : if (php_http_env_response_is_cacheable(r, request)) {
506 11 : switch (php_http_env_is_response_cached_by_etag(options, ZEND_STRL("If-None-Match"), request TSRMLS_CC)) {
507 : case PHP_HTTP_CACHE_MISS:
508 2 : break;
509 :
510 : case PHP_HTTP_CACHE_NO:
511 9 : if (PHP_HTTP_CACHE_HIT != php_http_env_is_response_cached_by_last_modified(options, ZEND_STRL("If-Modified-Since"), request TSRMLS_CC)) {
512 7 : break;
513 : }
514 : /* no break */
515 :
516 : case PHP_HTTP_CACHE_HIT:
517 2 : ret = r->ops->set_status(r, 304);
518 2 : r->done = 1;
519 2 : break;
520 : }
521 :
522 11 : if ((zoption = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
523 11 : zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
524 :
525 11 : zval_ptr_dtor(&zoption);
526 11 : if (*Z_STRVAL_P(zoption_copy) != '"' && strncmp(Z_STRVAL_P(zoption_copy), "W/\"", 3)) {
527 11 : ret = r->ops->set_header(r, "ETag: \"%s\"", Z_STRVAL_P(zoption_copy));
528 : } else {
529 0 : ret = r->ops->set_header(r, "ETag: %s", Z_STRVAL_P(zoption_copy));
530 : }
531 11 : zval_ptr_dtor(&zoption_copy);
532 : }
533 11 : if ((zoption = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
534 11 : zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
535 :
536 11 : zval_ptr_dtor(&zoption);
537 11 : if (Z_LVAL_P(zoption_copy)) {
538 7 : char *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), Z_LVAL_P(zoption_copy), 0 TSRMLS_CC);
539 7 : if (date) {
540 7 : ret = r->ops->set_header(r, "Last-Modified: %s", date);
541 7 : efree(date);
542 : }
543 : }
544 11 : zval_ptr_dtor(&zoption_copy);
545 : }
546 : }
547 : }
548 :
549 16 : return ret;
550 : }
551 :
552 17 : static STATUS php_http_env_response_send_body(php_http_env_response_t *r)
553 : {
554 17 : STATUS ret = SUCCESS;
555 : zval *zoption;
556 : php_http_message_body_t *body;
557 : TSRMLS_FETCH_FROM_CTX(r->ts);
558 :
559 17 : if (r->done) {
560 3 : return ret;
561 : }
562 :
563 14 : if ((body = get_body(r->options TSRMLS_CC))) {
564 14 : if ((zoption = get_option(r->options, ZEND_STRL("throttleDelay") TSRMLS_CC))) {
565 14 : if (Z_TYPE_P(zoption) == IS_DOUBLE) {
566 1 : r->throttle.delay = Z_DVAL_P(zoption);
567 : }
568 14 : zval_ptr_dtor(&zoption);
569 : }
570 14 : if ((zoption = get_option(r->options, ZEND_STRL("throttleChunk") TSRMLS_CC))) {
571 14 : if (Z_TYPE_P(zoption) == IS_LONG) {
572 1 : r->throttle.chunk = Z_LVAL_P(zoption);
573 : }
574 14 : zval_ptr_dtor(&zoption);
575 : }
576 :
577 14 : if (r->range.status == PHP_HTTP_RANGE_OK) {
578 4 : if (zend_hash_num_elements(&r->range.values) == 1) {
579 : /* single range */
580 : zval **range, **begin, **end;
581 :
582 3 : if ( 1 == php_http_array_list(&r->range.values TSRMLS_CC, 1, &range)
583 3 : && 2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end)
584 : ) {
585 : /* send chunk */
586 3 : ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
587 3 : if (ret == SUCCESS) {
588 3 : ret = php_http_env_response_send_done(r);
589 : }
590 3 : zend_hash_destroy(&r->range.values);
591 : } else {
592 : /* this should never happen */
593 0 : zend_hash_destroy(&r->range.values);
594 0 : r->ops->set_status(r, 500);
595 0 : ret = FAILURE;
596 : }
597 :
598 : } else {
599 : /* send multipart/byte-ranges message */
600 : HashPosition pos;
601 : zval **chunk;
602 :
603 6 : FOREACH_HASH_VAL(pos, &r->range.values, chunk) {
604 : zval **begin, **end;
605 :
606 5 : if (2 == php_http_array_list(Z_ARRVAL_PP(chunk) TSRMLS_CC, 2, &begin, &end)) {
607 25 : php_http_buffer_appendf(r->buffer,
608 : PHP_HTTP_CRLF
609 : "--%s" PHP_HTTP_CRLF
610 : "Content-Type: %s" PHP_HTTP_CRLF
611 : "Content-Range: bytes %ld-%ld/%zu" PHP_HTTP_CRLF PHP_HTTP_CRLF,
612 : /* - */
613 5 : r->range.boundary,
614 5 : r->content.type ? r->content.type : "application/octet-stream",
615 5 : Z_LVAL_PP(begin),
616 5 : Z_LVAL_PP(end),
617 : r->content.length
618 : );
619 5 : ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, Z_LVAL_PP(begin), Z_LVAL_PP(end) - Z_LVAL_PP(begin) + 1);
620 : }
621 : }
622 :
623 1 : if (ret == SUCCESS) {
624 1 : php_http_buffer_appendf(r->buffer, PHP_HTTP_CRLF "--%s--", r->range.boundary);
625 1 : ret = php_http_env_response_send_done(r);
626 : }
627 1 : zend_hash_destroy(&r->range.values);
628 : }
629 :
630 : } else {
631 10 : ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, 0, 0);
632 10 : if (ret == SUCCESS) {
633 10 : ret = php_http_env_response_send_done(r);
634 : }
635 : }
636 : }
637 14 : return ret;
638 : }
639 :
640 17 : STATUS php_http_env_response_send(php_http_env_response_t *r)
641 : {
642 : php_http_message_t *request;
643 : php_http_message_body_t *body;
644 : TSRMLS_FETCH_FROM_CTX(r->ts);
645 :
646 17 : request = get_request(r->options TSRMLS_CC);
647 :
648 : /* check for ranges */
649 17 : if ((body = get_body(r->options TSRMLS_CC))) {
650 17 : r->content.length = php_http_message_body_size(body);
651 :
652 17 : if (SUCCESS != r->ops->set_header(r, "Accept-Ranges: bytes")) {
653 0 : return FAILURE;
654 : } else {
655 17 : zend_hash_init(&r->range.values, 0, NULL, ZVAL_PTR_DTOR, 0);
656 17 : r->range.status = php_http_env_get_request_ranges(&r->range.values, r->content.length, request TSRMLS_CC);
657 :
658 17 : switch (r->range.status) {
659 : case PHP_HTTP_RANGE_NO:
660 12 : zend_hash_destroy(&r->range.values);
661 12 : break;
662 :
663 : case PHP_HTTP_RANGE_ERR:
664 1 : if (php_http_env_got_request_header(ZEND_STRL("If-Range"), request TSRMLS_CC)) {
665 0 : r->range.status = PHP_HTTP_RANGE_NO;
666 0 : zend_hash_destroy(&r->range.values);
667 : } else {
668 1 : r->done = 1;
669 1 : zend_hash_destroy(&r->range.values);
670 1 : if (SUCCESS != r->ops->set_status(r, 416)) {
671 0 : return FAILURE;
672 : }
673 1 : if (SUCCESS != r->ops->set_header(r, "Content-Range: bytes */%zu", r->content.length)) {
674 0 : return FAILURE;
675 : }
676 : }
677 1 : break;
678 :
679 : case PHP_HTTP_RANGE_OK:
680 4 : if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
681 4 : || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
682 : ) {
683 0 : r->range.status = PHP_HTTP_RANGE_NO;
684 0 : zend_hash_destroy(&r->range.values);
685 0 : break;
686 : }
687 4 : if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Match"), request TSRMLS_CC)
688 4 : || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Unmodified-Since"), request TSRMLS_CC)
689 4 : || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("Unless-Modified-Since"), request TSRMLS_CC)
690 : ) {
691 0 : r->done = 1;
692 0 : zend_hash_destroy(&r->range.values);
693 0 : if (SUCCESS != r->ops->set_status(r, 412)) {
694 0 : return FAILURE;
695 : }
696 0 : break;
697 : }
698 :
699 4 : break;
700 : }
701 : }
702 : }
703 :
704 17 : if (SUCCESS != php_http_env_response_send_head(r, request)) {
705 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response headers");
706 0 : return FAILURE;
707 : }
708 :
709 17 : if (SUCCESS != php_http_env_response_send_body(r)) {
710 1 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response body");
711 1 : return FAILURE;
712 : }
713 :
714 16 : if (SUCCESS != r->ops->finish(r)) {
715 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to finish response");
716 0 : return FAILURE;
717 : }
718 :
719 16 : return SUCCESS;
720 : }
721 :
722 7 : static long php_http_env_response_sapi_get_status(php_http_env_response_t *r)
723 : {
724 : TSRMLS_FETCH_FROM_CTX(r->ts);
725 :
726 7 : return php_http_env_get_response_code(TSRMLS_C);
727 : }
728 13 : static STATUS php_http_env_response_sapi_set_status(php_http_env_response_t *r, long http_code)
729 : {
730 : TSRMLS_FETCH_FROM_CTX(r->ts);
731 :
732 13 : return php_http_env_set_response_code(http_code TSRMLS_CC);
733 : }
734 9 : static STATUS php_http_env_response_sapi_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v)
735 : {
736 : TSRMLS_FETCH_FROM_CTX(r->ts);
737 :
738 9 : return php_http_env_set_response_protocol_version(v TSRMLS_CC);
739 : }
740 37 : static STATUS php_http_env_response_sapi_set_header(php_http_env_response_t *r, const char *fmt, ...)
741 : {
742 : STATUS ret;
743 : va_list args;
744 : TSRMLS_FETCH_FROM_CTX(r->ts);
745 :
746 37 : va_start(args, fmt);
747 37 : ret = php_http_env_set_response_header_va(0, 1, fmt, args TSRMLS_CC);
748 37 : va_end(args);
749 :
750 37 : return ret;
751 : }
752 2 : static STATUS php_http_env_response_sapi_add_header(php_http_env_response_t *r, const char *fmt, ...)
753 : {
754 : STATUS ret;
755 : va_list args;
756 : TSRMLS_FETCH_FROM_CTX(r->ts);
757 :
758 2 : va_start(args, fmt);
759 2 : ret = php_http_env_set_response_header_va(0, 0, fmt, args TSRMLS_CC);
760 2 : va_end(args);
761 :
762 2 : return ret;
763 : }
764 5 : static STATUS php_http_env_response_sapi_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len)
765 : {
766 : TSRMLS_FETCH_FROM_CTX(r->ts);
767 :
768 5 : return php_http_env_set_response_header_value(0, header_str, header_len, NULL, 1 TSRMLS_CC);
769 : }
770 12 : static STATUS php_http_env_response_sapi_write(php_http_env_response_t *r, const char *data_str, size_t data_len)
771 : {
772 : TSRMLS_FETCH_FROM_CTX(r->ts);
773 :
774 12 : if (0 < PHPWRITE(data_str, data_len)) {
775 12 : return SUCCESS;
776 : }
777 0 : return FAILURE;
778 : }
779 6 : static STATUS php_http_env_response_sapi_flush(php_http_env_response_t *r)
780 : {
781 : TSRMLS_FETCH_FROM_CTX(r->ts);
782 :
783 : #if PHP_VERSION_ID >= 50400
784 6 : if (php_output_get_level(TSRMLS_C)) {
785 0 : php_output_flush_all(TSRMLS_C);
786 : }
787 6 : if (!(php_output_get_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) {
788 6 : sapi_flush(TSRMLS_C);
789 : }
790 : #else
791 : php_end_ob_buffer(1, 1 TSRMLS_CC);
792 : sapi_flush(TSRMLS_C);
793 : #endif
794 :
795 6 : return SUCCESS;
796 : }
797 9 : static STATUS php_http_env_response_sapi_finish(php_http_env_response_t *r)
798 : {
799 9 : return SUCCESS;
800 : }
801 :
802 : static php_http_env_response_ops_t php_http_env_response_sapi_ops = {
803 : NULL,
804 : NULL,
805 : php_http_env_response_sapi_get_status,
806 : php_http_env_response_sapi_set_status,
807 : php_http_env_response_sapi_set_protocol_version,
808 : php_http_env_response_sapi_set_header,
809 : php_http_env_response_sapi_add_header,
810 : php_http_env_response_sapi_del_header,
811 : php_http_env_response_sapi_write,
812 : php_http_env_response_sapi_flush,
813 : php_http_env_response_sapi_finish
814 : };
815 :
816 9 : php_http_env_response_ops_t *php_http_env_response_get_sapi_ops(void)
817 : {
818 9 : return &php_http_env_response_sapi_ops;
819 : }
820 :
821 : typedef struct php_http_env_response_stream_ctx {
822 : HashTable header;
823 : php_http_version_t version;
824 : long status_code;
825 :
826 : php_stream *stream;
827 :
828 : unsigned started:1;
829 : unsigned finished:1;
830 : } php_http_env_response_stream_ctx_t;
831 :
832 8 : static STATUS php_http_env_response_stream_init(php_http_env_response_t *r, void *init_arg)
833 : {
834 : php_http_env_response_stream_ctx_t *ctx;
835 : TSRMLS_FETCH_FROM_CTX(r->ts);
836 :
837 8 : ctx = ecalloc(1, sizeof(*ctx));
838 :
839 8 : ctx->stream = init_arg;
840 8 : if (SUCCESS != zend_list_addref(ctx->stream->rsrc_id)) {
841 0 : efree(ctx);
842 0 : return FAILURE;
843 : }
844 8 : zend_hash_init(&ctx->header, 0, NULL, ZVAL_PTR_DTOR, 0);
845 8 : php_http_version_init(&ctx->version, 1, 1 TSRMLS_CC);
846 8 : ctx->status_code = 200;
847 :
848 8 : r->ctx = ctx;
849 :
850 8 : return SUCCESS;
851 : }
852 8 : static void php_http_env_response_stream_dtor(php_http_env_response_t *r)
853 : {
854 8 : php_http_env_response_stream_ctx_t *ctx = r->ctx;
855 : TSRMLS_FETCH_FROM_CTX(r->ts);
856 :
857 8 : zend_hash_destroy(&ctx->header);
858 8 : zend_list_delete(ctx->stream->rsrc_id);
859 8 : efree(ctx);
860 8 : r->ctx = NULL;
861 8 : }
862 8 : static void php_http_env_response_stream_header(php_http_env_response_stream_ctx_t *ctx, HashTable *header TSRMLS_DC)
863 : {
864 : HashPosition pos;
865 : zval **val;
866 :
867 36 : FOREACH_HASH_VAL(pos, &ctx->header, val) {
868 28 : if (Z_TYPE_PP(val) == IS_ARRAY) {
869 0 : php_http_env_response_stream_header(ctx, Z_ARRVAL_PP(val) TSRMLS_CC);
870 : } else {
871 28 : php_stream_write(ctx->stream, Z_STRVAL_PP(val), Z_STRLEN_PP(val));
872 28 : php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
873 : }
874 : }
875 8 : }
876 8 : static STATUS php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC)
877 : {
878 8 : if (ctx->started || ctx->finished) {
879 0 : return FAILURE;
880 : }
881 :
882 8 : php_stream_printf(ctx->stream TSRMLS_CC, "HTTP/%u.%u %ld %s" PHP_HTTP_CRLF, ctx->version.major, ctx->version.minor, ctx->status_code, php_http_env_get_response_status_for_code(ctx->status_code));
883 8 : php_http_env_response_stream_header(ctx, &ctx->header TSRMLS_CC);
884 8 : php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
885 8 : ctx->started = 1;
886 8 : return SUCCESS;
887 : }
888 5 : static long php_http_env_response_stream_get_status(php_http_env_response_t *r)
889 : {
890 5 : php_http_env_response_stream_ctx_t *ctx = r->ctx;
891 :
892 5 : return ctx->status_code;
893 : }
894 10 : static STATUS php_http_env_response_stream_set_status(php_http_env_response_t *r, long http_code)
895 : {
896 10 : php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
897 :
898 10 : if (stream_ctx->started || stream_ctx->finished) {
899 0 : return FAILURE;
900 : }
901 :
902 10 : stream_ctx->status_code = http_code;
903 :
904 10 : return SUCCESS;
905 : }
906 7 : static STATUS php_http_env_response_stream_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v)
907 : {
908 7 : php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
909 :
910 7 : if (stream_ctx->started || stream_ctx->finished) {
911 0 : return FAILURE;
912 : }
913 :
914 7 : memcpy(&stream_ctx->version, v, sizeof(stream_ctx->version));
915 :
916 7 : return SUCCESS;
917 : }
918 29 : static STATUS php_http_env_response_stream_set_header_ex(php_http_env_response_t *r, zend_bool replace, const char *fmt, va_list argv)
919 : {
920 29 : php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
921 29 : char *header_end, *header_str = NULL;
922 29 : size_t header_len = 0;
923 : zval *zheader, **zheader_ptr;
924 :
925 29 : if (stream_ctx->started || stream_ctx->finished) {
926 0 : return FAILURE;
927 : }
928 :
929 29 : header_len = vspprintf(&header_str, 0, fmt, argv);
930 :
931 29 : if (!(header_end = strchr(header_str, ':'))) {
932 0 : efree(header_str);
933 0 : return FAILURE;
934 : }
935 :
936 29 : *header_end = '\0';
937 :
938 29 : if (!replace && (SUCCESS == zend_hash_find(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader_ptr))) {
939 0 : convert_to_array(*zheader_ptr);
940 0 : *header_end = ':';
941 0 : return add_next_index_stringl(*zheader_ptr, header_str, header_len, 0);
942 : } else {
943 29 : MAKE_STD_ZVAL(zheader);
944 29 : ZVAL_STRINGL(zheader, header_str, header_len, 0);
945 :
946 29 : if (SUCCESS != zend_hash_update(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader, sizeof(zval *), NULL)) {
947 0 : zval_ptr_dtor(&zheader);
948 0 : return FAILURE;
949 : }
950 :
951 29 : *header_end = ':';
952 29 : return SUCCESS;
953 : }
954 : }
955 28 : static STATUS php_http_env_response_stream_set_header(php_http_env_response_t *r, const char *fmt, ...)
956 : {
957 : STATUS ret;
958 : va_list argv;
959 :
960 28 : va_start(argv, fmt);
961 28 : ret = php_http_env_response_stream_set_header_ex(r, 1, fmt, argv);
962 28 : va_end(argv);
963 :
964 28 : return ret;
965 : }
966 1 : static STATUS php_http_env_response_stream_add_header(php_http_env_response_t *r, const char *fmt, ...)
967 : {
968 : STATUS ret;
969 : va_list argv;
970 :
971 1 : va_start(argv, fmt);
972 1 : ret = php_http_env_response_stream_set_header_ex(r, 0, fmt, argv);
973 1 : va_end(argv);
974 :
975 1 : return ret;
976 : }
977 4 : static STATUS php_http_env_response_stream_del_header(php_http_env_response_t *r, const char *header_str, size_t header_len)
978 : {
979 4 : php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
980 :
981 4 : if (stream_ctx->started || stream_ctx->finished) {
982 0 : return FAILURE;
983 : }
984 :
985 4 : zend_hash_del(&stream_ctx->header, header_str, header_len + 1);
986 4 : return SUCCESS;
987 : }
988 8 : static STATUS php_http_env_response_stream_write(php_http_env_response_t *r, const char *data_str, size_t data_len)
989 : {
990 8 : php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
991 : TSRMLS_FETCH_FROM_CTX(r->ts);
992 :
993 8 : if (stream_ctx->finished) {
994 0 : return FAILURE;
995 : }
996 8 : if (!stream_ctx->started) {
997 7 : if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
998 0 : return FAILURE;
999 : }
1000 : }
1001 :
1002 8 : if (data_len != php_stream_write(stream_ctx->stream, data_str, data_len)) {
1003 2 : return FAILURE;
1004 : }
1005 :
1006 6 : return SUCCESS;
1007 : }
1008 0 : static STATUS php_http_env_response_stream_flush(php_http_env_response_t *r)
1009 : {
1010 0 : php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
1011 : TSRMLS_FETCH_FROM_CTX(r->ts);
1012 :
1013 0 : if (stream_ctx->finished) {
1014 0 : return FAILURE;
1015 : }
1016 0 : if (!stream_ctx->started) {
1017 0 : if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
1018 0 : return FAILURE;
1019 : }
1020 : }
1021 :
1022 0 : return php_stream_flush(stream_ctx->stream);
1023 : }
1024 7 : static STATUS php_http_env_response_stream_finish(php_http_env_response_t *r)
1025 : {
1026 7 : php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
1027 : TSRMLS_FETCH_FROM_CTX(r->ts);
1028 :
1029 7 : if (stream_ctx->finished) {
1030 0 : return FAILURE;
1031 : }
1032 7 : if (!stream_ctx->started) {
1033 1 : if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
1034 0 : return FAILURE;
1035 : }
1036 : }
1037 :
1038 7 : stream_ctx->finished = 1;
1039 :
1040 7 : return SUCCESS;
1041 : }
1042 :
1043 : static php_http_env_response_ops_t php_http_env_response_stream_ops = {
1044 : php_http_env_response_stream_init,
1045 : php_http_env_response_stream_dtor,
1046 : php_http_env_response_stream_get_status,
1047 : php_http_env_response_stream_set_status,
1048 : php_http_env_response_stream_set_protocol_version,
1049 : php_http_env_response_stream_set_header,
1050 : php_http_env_response_stream_add_header,
1051 : php_http_env_response_stream_del_header,
1052 : php_http_env_response_stream_write,
1053 : php_http_env_response_stream_flush,
1054 : php_http_env_response_stream_finish
1055 : };
1056 :
1057 8 : php_http_env_response_ops_t *php_http_env_response_get_stream_ops(void)
1058 : {
1059 8 : return &php_http_env_response_stream_ops;
1060 : }
1061 :
1062 : #define PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj) \
1063 : do { \
1064 : if (!obj->message) { \
1065 : obj->message = php_http_message_init_env(NULL, PHP_HTTP_RESPONSE TSRMLS_CC); \
1066 : } \
1067 : } while (0)
1068 :
1069 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___construct, 0, 0, 0)
1070 : ZEND_END_ARG_INFO();
1071 20 : static PHP_METHOD(HttpEnvResponse, __construct)
1072 : {
1073 : php_http_message_object_t *obj;
1074 :
1075 20 : php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
1076 :
1077 20 : obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1078 :
1079 20 : php_http_expect(obj->message = php_http_message_init_env(obj->message, PHP_HTTP_RESPONSE TSRMLS_CC), unexpected_val, return);
1080 : }
1081 :
1082 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse___invoke, 0, 0, 1)
1083 : ZEND_ARG_INFO(0, ob_string)
1084 : ZEND_ARG_INFO(0, ob_flags)
1085 : ZEND_END_ARG_INFO();
1086 3 : static PHP_METHOD(HttpEnvResponse, __invoke)
1087 : {
1088 : char *ob_str;
1089 : int ob_len;
1090 3 : long ob_flags = 0;
1091 :
1092 3 : if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &ob_str, &ob_len, &ob_flags)) {
1093 3 : php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1094 :
1095 3 : PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj);
1096 :
1097 3 : php_http_message_object_init_body_object(obj);
1098 3 : php_http_message_body_append(obj->message->body, ob_str, ob_len);
1099 : #if PHP_VERSION_ID >= 50400
1100 3 : RETURN_TRUE;
1101 : #else
1102 : RETURN_EMPTY_STRING();
1103 : #endif
1104 : }
1105 : }
1106 :
1107 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEnvRequest, 0, 0, 1)
1108 : ZEND_ARG_OBJ_INFO(0, env_request, http\\Message, 1)
1109 : ZEND_END_ARG_INFO();
1110 4 : static PHP_METHOD(HttpEnvResponse, setEnvRequest)
1111 : {
1112 4 : zval *env_req = NULL;
1113 :
1114 8 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O", &env_req, php_http_message_class_entry), invalid_arg, return);
1115 :
1116 4 : set_option(getThis(), ZEND_STRL("request"), IS_OBJECT, env_req, 0 TSRMLS_CC);
1117 4 : RETVAL_ZVAL(getThis(), 1, 0);
1118 : }
1119 :
1120 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentType, 0, 0, 1)
1121 : ZEND_ARG_INFO(0, content_type)
1122 : ZEND_END_ARG_INFO();
1123 3 : static PHP_METHOD(HttpEnvResponse, setContentType)
1124 : {
1125 3 : char *ct_str = NULL;
1126 3 : int ct_len = 0;
1127 :
1128 6 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &ct_str, &ct_len), invalid_arg, return);
1129 :
1130 3 : set_option(getThis(), ZEND_STRL("contentType"), IS_STRING, ct_str, ct_len TSRMLS_CC);
1131 3 : RETVAL_ZVAL(getThis(), 1, 0);
1132 : }
1133 :
1134 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentDisposition, 0, 0, 1)
1135 : ZEND_ARG_ARRAY_INFO(0, disposition_params, 1)
1136 : ZEND_END_ARG_INFO();
1137 3 : static PHP_METHOD(HttpEnvResponse, setContentDisposition)
1138 : {
1139 : zval *zdisposition;
1140 :
1141 6 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zdisposition), invalid_arg, return);
1142 :
1143 3 : zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("contentDisposition"), zdisposition TSRMLS_CC);
1144 3 : RETVAL_ZVAL(getThis(), 1, 0);
1145 : }
1146 :
1147 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setContentEncoding, 0, 0, 1)
1148 : ZEND_ARG_INFO(0, content_encoding)
1149 : ZEND_END_ARG_INFO();
1150 3 : static PHP_METHOD(HttpEnvResponse, setContentEncoding)
1151 : {
1152 : long ce;
1153 :
1154 6 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ce), invalid_arg, return);
1155 :
1156 3 : set_option(getThis(), ZEND_STRL("contentEncoding"), IS_LONG, &ce, 0 TSRMLS_CC);
1157 3 : RETVAL_ZVAL(getThis(), 1, 0);
1158 : }
1159 :
1160 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setCacheControl, 0, 0, 1)
1161 : ZEND_ARG_INFO(0, cache_control)
1162 : ZEND_END_ARG_INFO();
1163 2 : static PHP_METHOD(HttpEnvResponse, setCacheControl)
1164 : {
1165 2 : char *cc_str = NULL;
1166 2 : int cc_len = 0;
1167 :
1168 4 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &cc_str, &cc_len), invalid_arg, return);
1169 :
1170 2 : set_option(getThis(), ZEND_STRL("cacheControl"), IS_STRING, cc_str, cc_len TSRMLS_CC);
1171 2 : RETVAL_ZVAL(getThis(), 1, 0);
1172 : }
1173 :
1174 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setLastModified, 0, 0, 1)
1175 : ZEND_ARG_INFO(0, last_modified)
1176 : ZEND_END_ARG_INFO();
1177 4 : static PHP_METHOD(HttpEnvResponse, setLastModified)
1178 : {
1179 : long last_modified;
1180 :
1181 8 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &last_modified), invalid_arg, return);
1182 :
1183 4 : set_option(getThis(), ZEND_STRL("lastModified"), IS_LONG, &last_modified, 0 TSRMLS_CC);
1184 4 : RETVAL_ZVAL(getThis(), 1, 0);
1185 : }
1186 :
1187 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByLastModified, 0, 0, 0)
1188 : ZEND_ARG_INFO(0, header_name)
1189 : ZEND_END_ARG_INFO();
1190 2 : static PHP_METHOD(HttpEnvResponse, isCachedByLastModified)
1191 : {
1192 2 : char *header_name_str = NULL;
1193 2 : int header_name_len = 0;
1194 :
1195 2 : if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
1196 2 : if (!header_name_str || !header_name_len) {
1197 0 : header_name_str = "If-Modified-Since";
1198 0 : header_name_len = lenof("If-Modified-Since");
1199 : }
1200 :
1201 2 : RETURN_LONG(php_http_env_is_response_cached_by_last_modified(getThis(), header_name_str, header_name_len, get_request(getThis() TSRMLS_CC) TSRMLS_CC));
1202 : }
1203 : }
1204 :
1205 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setEtag, 0, 0, 1)
1206 : ZEND_ARG_INFO(0, etag)
1207 : ZEND_END_ARG_INFO();
1208 4 : static PHP_METHOD(HttpEnvResponse, setEtag)
1209 : {
1210 4 : char *etag_str = NULL;
1211 4 : int etag_len = 0;
1212 :
1213 8 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &etag_str, &etag_len), invalid_arg, return);
1214 :
1215 4 : set_option(getThis(), ZEND_STRL("etag"), IS_STRING, etag_str, etag_len TSRMLS_CC);
1216 4 : RETVAL_ZVAL(getThis(), 1, 0);
1217 : }
1218 :
1219 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_isCachedByEtag, 0, 0, 0)
1220 : ZEND_ARG_INFO(0, header_name)
1221 : ZEND_END_ARG_INFO();
1222 2 : static PHP_METHOD(HttpEnvResponse, isCachedByEtag)
1223 : {
1224 2 : char *header_name_str = NULL;
1225 2 : int header_name_len = 0;
1226 :
1227 2 : if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
1228 2 : if (!header_name_str || !header_name_len) {
1229 0 : header_name_str = "If-None-Match";
1230 0 : header_name_len = lenof("If-None-Match");
1231 : }
1232 2 : RETURN_LONG(php_http_env_is_response_cached_by_etag(getThis(), header_name_str, header_name_len, get_request(getThis() TSRMLS_CC) TSRMLS_CC));
1233 : }
1234 : }
1235 :
1236 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_setThrottleRate, 0, 0, 1)
1237 : ZEND_ARG_INFO(0, chunk_size)
1238 : ZEND_ARG_INFO(0, delay)
1239 : ZEND_END_ARG_INFO();
1240 1 : static PHP_METHOD(HttpEnvResponse, setThrottleRate)
1241 : {
1242 : long chunk_size;
1243 1 : double delay = 1;
1244 :
1245 2 : php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|d", &chunk_size, &delay), invalid_arg, return);
1246 :
1247 1 : set_option(getThis(), ZEND_STRL("throttleDelay"), IS_DOUBLE, &delay, 0 TSRMLS_CC);
1248 1 : set_option(getThis(), ZEND_STRL("throttleChunk"), IS_LONG, &chunk_size, 0 TSRMLS_CC);
1249 1 : RETVAL_ZVAL(getThis(), 1, 0);
1250 : }
1251 :
1252 : ZEND_BEGIN_ARG_INFO_EX(ai_HttpEnvResponse_send, 0, 0, 0)
1253 : ZEND_ARG_INFO(0, stream)
1254 : ZEND_END_ARG_INFO();
1255 17 : static PHP_METHOD(HttpEnvResponse, send)
1256 : {
1257 17 : zval *zstream = NULL;
1258 17 : php_stream *s = NULL;
1259 :
1260 17 : if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &zstream)) {
1261 : /* first flush the output layer to avoid conflicting headers and output;
1262 : * also, ob_start($thisEnvResponse) might have been called */
1263 : #if PHP_VERSION_ID >= 50400
1264 17 : php_output_end_all(TSRMLS_C);
1265 : #else
1266 : php_end_ob_buffers(1 TSRMLS_CC);
1267 : #endif
1268 17 : if (zstream) {
1269 : php_http_env_response_t *r;
1270 :
1271 8 : php_stream_from_zval(s, &zstream);
1272 8 : r = php_http_env_response_init(NULL, getThis(), php_http_env_response_get_stream_ops(), s TSRMLS_CC);
1273 8 : if (!r) {
1274 0 : RETURN_FALSE;
1275 : }
1276 :
1277 8 : RETVAL_BOOL(SUCCESS == php_http_env_response_send(r));
1278 8 : php_http_env_response_free(&r);
1279 : } else {
1280 : php_http_env_response_t r;
1281 :
1282 9 : if (!php_http_env_response_init(&r, getThis(), NULL, NULL TSRMLS_CC)) {
1283 0 : RETURN_FALSE;
1284 : }
1285 :
1286 9 : RETVAL_BOOL(SUCCESS == php_http_env_response_send(&r));
1287 9 : php_http_env_response_dtor(&r);
1288 : }
1289 : }
1290 : }
1291 :
1292 : static zend_function_entry php_http_env_response_methods[] = {
1293 : PHP_ME(HttpEnvResponse, __construct, ai_HttpEnvResponse___construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
1294 : PHP_ME(HttpEnvResponse, __invoke, ai_HttpEnvResponse___invoke, ZEND_ACC_PUBLIC)
1295 : PHP_ME(HttpEnvResponse, setEnvRequest, ai_HttpEnvResponse_setEnvRequest, ZEND_ACC_PUBLIC)
1296 : PHP_ME(HttpEnvResponse, setContentType, ai_HttpEnvResponse_setContentType, ZEND_ACC_PUBLIC)
1297 : PHP_ME(HttpEnvResponse, setContentDisposition, ai_HttpEnvResponse_setContentDisposition, ZEND_ACC_PUBLIC)
1298 : PHP_ME(HttpEnvResponse, setContentEncoding, ai_HttpEnvResponse_setContentEncoding, ZEND_ACC_PUBLIC)
1299 : PHP_ME(HttpEnvResponse, setCacheControl, ai_HttpEnvResponse_setCacheControl, ZEND_ACC_PUBLIC)
1300 : PHP_ME(HttpEnvResponse, setLastModified, ai_HttpEnvResponse_setLastModified, ZEND_ACC_PUBLIC)
1301 : PHP_ME(HttpEnvResponse, isCachedByLastModified, ai_HttpEnvResponse_isCachedByLastModified, ZEND_ACC_PUBLIC)
1302 : PHP_ME(HttpEnvResponse, setEtag, ai_HttpEnvResponse_setEtag, ZEND_ACC_PUBLIC)
1303 : PHP_ME(HttpEnvResponse, isCachedByEtag, ai_HttpEnvResponse_isCachedByEtag, ZEND_ACC_PUBLIC)
1304 : PHP_ME(HttpEnvResponse, setThrottleRate, ai_HttpEnvResponse_setThrottleRate, ZEND_ACC_PUBLIC)
1305 : PHP_ME(HttpEnvResponse, send, ai_HttpEnvResponse_send, ZEND_ACC_PUBLIC)
1306 : EMPTY_FUNCTION_ENTRY
1307 : };
1308 :
1309 : zend_class_entry *php_http_env_response_class_entry;
1310 :
1311 408 : PHP_MINIT_FUNCTION(http_env_response)
1312 : {
1313 408 : zend_class_entry ce = {0};
1314 :
1315 408 : INIT_NS_CLASS_ENTRY(ce, "http\\Env", "Response", php_http_env_response_methods);
1316 408 : php_http_env_response_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC);
1317 :
1318 408 : zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_NONE"), PHP_HTTP_CONTENT_ENCODING_NONE TSRMLS_CC);
1319 408 : zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_GZIP"), PHP_HTTP_CONTENT_ENCODING_GZIP TSRMLS_CC);
1320 :
1321 408 : zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_NO"), PHP_HTTP_CACHE_NO TSRMLS_CC);
1322 408 : zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_HIT"), PHP_HTTP_CACHE_HIT TSRMLS_CC);
1323 408 : zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS TSRMLS_CC);
1324 :
1325 408 : zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC);
1326 408 : zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED TSRMLS_CC);
1327 408 : zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentDisposition"), ZEND_ACC_PROTECTED TSRMLS_CC);
1328 408 : zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentEncoding"), ZEND_ACC_PROTECTED TSRMLS_CC);
1329 408 : zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cacheControl"), ZEND_ACC_PROTECTED TSRMLS_CC);
1330 408 : zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("etag"), ZEND_ACC_PROTECTED TSRMLS_CC);
1331 408 : zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("lastModified"), ZEND_ACC_PROTECTED TSRMLS_CC);
1332 408 : zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleDelay"), ZEND_ACC_PROTECTED TSRMLS_CC);
1333 408 : zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleChunk"), ZEND_ACC_PROTECTED TSRMLS_CC);
1334 :
1335 408 : return SUCCESS;
1336 : }
1337 :
1338 :
1339 : /*
1340 : * Local variables:
1341 : * tab-width: 4
1342 : * c-basic-offset: 4
1343 : * End:
1344 : * vim600: noet sw=4 ts=4 fdm=marker
1345 : * vim<600: noet sw=4 ts=4
1346 : */
|