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