back to dev
[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 TSRMLS_DC)
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 TSRMLS_CC);
22 break;
23 case IS_LONG:
24 zend_update_property_long(Z_OBJCE_P(options), options, name_str, name_len, *(long *)value_ptr TSRMLS_CC);
25 break;
26 case IS_STRING:
27 zend_update_property_stringl(Z_OBJCE_P(options), options, name_str, name_len, value_ptr, value_len TSRMLS_CC);
28 break;
29 case IS_OBJECT:
30 zend_update_property(Z_OBJCE_P(options), options, name_str, name_len, value_ptr TSRMLS_CC);
31 break;
32 }
33 } else {
34 zend_update_property_null(Z_OBJCE_P(options), options, name_str, name_len TSRMLS_CC);
35 }
36 } else {
37 convert_to_array(options);
38 if (value_ptr) {
39 switch (type) {
40 case IS_DOUBLE:
41 add_assoc_double_ex(options, name_str, name_len + 1, *(double *)value_ptr);
42 break;
43 case IS_LONG:
44 add_assoc_long_ex(options, name_str, name_len + 1, *(long *)value_ptr);
45 break;
46 case IS_STRING: {
47 char *value = estrndup(value_ptr, value_len);
48 add_assoc_stringl_ex(options, name_str, name_len + 1, value, value_len, 0);
49 break;
50 case IS_OBJECT:
51 Z_ADDREF_P(value_ptr);
52 add_assoc_zval_ex(options, name_str, name_len + 1, value_ptr);
53 break;
54 }
55 }
56 } else {
57 add_assoc_null_ex(options, name_str, name_len + 1);
58 }
59 }
60 }
61 static zval *get_option(zval *options, const char *name_str, size_t name_len TSRMLS_DC)
62 {
63 zval *val, **valptr;
64
65 if (Z_TYPE_P(options) == IS_OBJECT) {
66 val = zend_read_property(Z_OBJCE_P(options), options, name_str, name_len, 0 TSRMLS_CC);
67 } else {
68 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(options), name_str, name_len + 1, (void *) &valptr)) {
69 val = *valptr;
70 } else {
71 val = NULL;
72 }
73 }
74 if (val) {
75 Z_ADDREF_P(val);
76 }
77 return val;
78 }
79 static php_http_message_body_t *get_body(zval *options TSRMLS_DC)
80 {
81 zval *zbody;
82 php_http_message_body_t *body = NULL;
83
84 if ((zbody = get_option(options, ZEND_STRL("body") TSRMLS_CC))) {
85 if ((Z_TYPE_P(zbody) == IS_OBJECT) && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)) {
86 php_http_message_body_object_t *body_obj = zend_object_store_get_object(zbody TSRMLS_CC);
87
88 body = body_obj->body;
89 }
90 zval_ptr_dtor(&zbody);
91 }
92
93 return body;
94 }
95 static php_http_message_t *get_request(zval *options TSRMLS_DC)
96 {
97 zval *zrequest;
98 php_http_message_t *request = NULL;
99
100 if ((zrequest = get_option(options, ZEND_STRL("request") TSRMLS_CC))) {
101 if (Z_TYPE_P(zrequest) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zrequest), php_http_message_class_entry TSRMLS_CC)) {
102 php_http_message_object_t *request_obj = zend_object_store_get_object(zrequest TSRMLS_CC);
103
104 request = request_obj->message;
105 }
106 zval_ptr_dtor(&zrequest);
107 }
108
109 return request;
110 }
111
112 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 php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
115 int free_etag = 0;
116 char *header = NULL, *etag;
117 php_http_message_body_t *body;
118 zval *zetag;
119
120
121 if (!(body = get_body(options TSRMLS_CC))) {
122 return ret;
123 }
124
125 if ((zetag = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
126 zval *zetag_copy = php_http_ztyp(IS_STRING, zetag);
127 zval_ptr_dtor(&zetag);
128 zetag = zetag_copy;
129 }
130
131 if (zetag && Z_STRLEN_P(zetag)) {
132 etag = Z_STRVAL_P(zetag);
133 } else if ((etag = php_http_message_body_etag(body))) {
134 set_option(options, ZEND_STRL("etag"), IS_STRING, etag, strlen(etag) TSRMLS_CC);
135 free_etag = 1;
136 }
137
138 if (zetag) {
139 zval_ptr_dtor(&zetag);
140 }
141
142 if (etag && (header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
143 ret = php_http_match(header, etag, PHP_HTTP_MATCH_WORD) ? PHP_HTTP_CACHE_HIT : PHP_HTTP_CACHE_MISS;
144 }
145
146 if (free_etag) {
147 efree(etag);
148 }
149
150 STR_FREE(header);
151 return ret;
152 }
153
154 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 php_http_cache_status_t ret = PHP_HTTP_CACHE_NO;
157 char *header;
158 time_t ums, lm = 0;
159 php_http_message_body_t *body;
160 zval *zlm;
161
162 if (!(body = get_body(options TSRMLS_CC))) {
163 return ret;
164 }
165
166 if ((zlm = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
167 zval *zlm_copy = php_http_ztyp(IS_LONG, zlm);
168 zval_ptr_dtor(&zlm);
169 zlm = zlm_copy;
170 }
171
172 if (zlm && Z_LVAL_P(zlm) > 0) {
173 lm = Z_LVAL_P(zlm);
174 } else {
175 lm = php_http_message_body_mtime(body);
176 set_option(options, ZEND_STRL("lastModified"), IS_LONG, &lm, 0 TSRMLS_CC);
177 }
178
179 if (zlm) {
180 zval_ptr_dtor(&zlm);
181 }
182
183 if ((header = php_http_env_get_request_header(header_str, header_len, NULL, request TSRMLS_CC))) {
184 ums = php_parse_date(header, NULL);
185
186 if (ums > 0 && ums >= lm) {
187 ret = PHP_HTTP_CACHE_HIT;
188 } else {
189 ret = PHP_HTTP_CACHE_MISS;
190 }
191 }
192
193 STR_FREE(header);
194 return ret;
195 }
196
197 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 if (r->ops->get_status(r) >= 400) {
202 return 0;
203 }
204
205 if (php_http_env_got_request_header(ZEND_STRL("Authorization"), request TSRMLS_CC)) {
206 return 0;
207 }
208
209 if (-1 == php_http_select_str(php_http_env_get_request_method(request TSRMLS_CC), 2, "HEAD", "GET")) {
210 return 0;
211 }
212
213 return 1;
214 }
215
216 static size_t output(void *context, char *buf, size_t len TSRMLS_DC)
217 {
218 php_http_env_response_t *r = context;
219
220 if (SUCCESS != r->ops->write(r, buf, len)) {
221 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 if (r->throttle.delay >= PHP_HTTP_DIFFSEC) {
227 r->ops->flush(r);
228 php_http_sleep(r->throttle.delay);
229 }
230 return len;
231 }
232
233 #define php_http_env_response_send_done(r) php_http_env_response_send_data((r), NULL, 0)
234 static STATUS php_http_env_response_send_data(php_http_env_response_t *r, const char *buf, size_t len)
235 {
236 size_t chunks_sent, chunk = r->throttle.chunk ? r->throttle.chunk : PHP_HTTP_SENDBUF_SIZE;
237 TSRMLS_FETCH_FROM_CTX(r->ts);
238
239 if (r->content.encoder) {
240 char *enc_str = NULL;
241 size_t enc_len = 0;
242
243 if (buf) {
244 if (SUCCESS != php_http_encoding_stream_update(r->content.encoder, buf, len, &enc_str, &enc_len)) {
245 return FAILURE;
246 }
247 } else {
248 if (SUCCESS != php_http_encoding_stream_finish(r->content.encoder, &enc_str, &enc_len)) {
249 return FAILURE;
250 }
251 }
252
253 if (!enc_str) {
254 return SUCCESS;
255 }
256 chunks_sent = php_http_buffer_chunked_output(&r->buffer, enc_str, enc_len, buf ? chunk : 0, output, r TSRMLS_CC);
257 STR_FREE(enc_str);
258 } else {
259 chunks_sent = php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC);
260 }
261
262 return chunks_sent != (size_t) -1 ? SUCCESS : FAILURE;
263 }
264
265 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 if ((free_r = !r)) {
270 r = emalloc(sizeof(*r));
271 }
272 memset(r, 0, sizeof(*r));
273
274 if (ops) {
275 r->ops = ops;
276 } else {
277 r->ops = php_http_env_response_get_sapi_ops();
278 }
279
280 r->buffer = php_http_buffer_init(NULL);
281
282 Z_ADDREF_P(options);
283 r->options = options;
284
285 TSRMLS_SET_CTX(r->ts);
286
287 if (r->ops->init && (SUCCESS != r->ops->init(r, init_arg))) {
288 if (free_r) {
289 php_http_env_response_free(&r);
290 } else {
291 php_http_env_response_dtor(r);
292 r = NULL;
293 }
294 }
295
296 return r;
297 }
298
299 void php_http_env_response_dtor(php_http_env_response_t *r)
300 {
301 if (r->ops->dtor) {
302 r->ops->dtor(r);
303 }
304 php_http_buffer_free(&r->buffer);
305 zval_ptr_dtor(&r->options);
306 STR_FREE(r->content.type);
307 STR_FREE(r->content.encoding);
308 if (r->content.encoder) {
309 php_http_encoding_stream_free(&r->content.encoder);
310 }
311 }
312
313 void php_http_env_response_free(php_http_env_response_t **r)
314 {
315 if (*r) {
316 php_http_env_response_dtor(*r);
317 efree(*r);
318 *r = NULL;
319 }
320 }
321
322 static STATUS php_http_env_response_send_head(php_http_env_response_t *r, php_http_message_t *request)
323 {
324 STATUS ret = SUCCESS;
325 zval *zoption, *options = r->options;
326 TSRMLS_FETCH_FROM_CTX(r->ts);
327
328 if (r->done) {
329 return ret;
330 }
331
332 if ((zoption = get_option(options, ZEND_STRL("headers") TSRMLS_CC))) {
333 if (Z_TYPE_P(zoption) == IS_ARRAY) {
334 php_http_header_to_callback(Z_ARRVAL_P(zoption), 0, (php_http_pass_format_callback_t) r->ops->set_header, r TSRMLS_CC);
335 }
336 zval_ptr_dtor(&zoption);
337 }
338
339 if (ret != SUCCESS) {
340 return ret;
341 }
342
343 if ((zoption = get_option(options, ZEND_STRL("responseCode") TSRMLS_CC))) {
344 zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
345
346 zval_ptr_dtor(&zoption);
347 if (Z_LVAL_P(zoption_copy) > 0) {
348 ret = r->ops->set_status(r, Z_LVAL_P(zoption_copy));
349 }
350 zval_ptr_dtor(&zoption_copy);
351 }
352
353 if (ret != SUCCESS) {
354 return ret;
355 }
356
357 if ((zoption = get_option(options, ZEND_STRL("httpVersion") TSRMLS_CC))) {
358 php_http_version_t v;
359 zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
360
361 zval_ptr_dtor(&zoption);
362 if (Z_STRLEN_P(zoption_copy) && php_http_version_parse(&v, Z_STRVAL_P(zoption_copy) TSRMLS_CC)) {
363 ret = r->ops->set_protocol_version(r, &v);
364 php_http_version_dtor(&v);
365 }
366 zval_ptr_dtor(&zoption_copy);
367 }
368
369 if (ret != SUCCESS) {
370 return ret;
371 }
372
373 if ((zoption = get_option(options, ZEND_STRL("contentType") TSRMLS_CC))) {
374 zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
375
376 zval_ptr_dtor(&zoption);
377 if (Z_STRLEN_P(zoption_copy) && strchr(Z_STRVAL_P(zoption_copy), '/')) {
378 if (SUCCESS == (ret = r->ops->set_header(r, "Content-Type: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy)))) {
379 r->content.type = estrndup(Z_STRVAL_P(zoption_copy), Z_STRLEN_P(zoption_copy));
380 }
381 }
382 zval_ptr_dtor(&zoption_copy);
383 }
384
385 if (ret != SUCCESS) {
386 return ret;
387 }
388
389 if (r->range.status == PHP_HTTP_RANGE_OK) {
390 if (zend_hash_num_elements(&r->range.values) == 1) {
391 zval **range, **begin, **end;
392
393 if ( 1 == php_http_array_list(&r->range.values TSRMLS_CC, 1, &range)
394 && 2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end)
395 ) {
396 if (SUCCESS == (ret = r->ops->set_status(r, 206))) {
397 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 zend_hash_destroy(&r->range.values);
402 ret = FAILURE;
403 }
404 } else {
405 php_http_boundary(r->range.boundary, sizeof(r->range.boundary) TSRMLS_CC);
406 if (SUCCESS == (ret = r->ops->set_status(r, 206))) {
407 ret = r->ops->set_header(r, "Content-Type: multipart/byteranges; boundary=%s", r->range.boundary);
408 }
409 }
410 } else {
411 if ((zoption = get_option(options, ZEND_STRL("cacheControl") TSRMLS_CC))) {
412 zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
413
414 zval_ptr_dtor(&zoption);
415 if (Z_STRLEN_P(zoption_copy)) {
416 ret = r->ops->set_header(r, "Cache-Control: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy));
417 }
418 zval_ptr_dtor(&zoption_copy);
419 }
420
421 if (ret != SUCCESS) {
422 return ret;
423 }
424
425 if ((zoption = get_option(options, ZEND_STRL("contentDisposition") TSRMLS_CC))) {
426 zval *zoption_copy = php_http_ztyp(IS_ARRAY, zoption);
427 php_http_buffer_t buf;
428
429 php_http_buffer_init(&buf);
430 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 if (buf.used) {
432 ret = r->ops->set_header(r, "Content-Disposition: %.*s", buf.used, buf.data);
433 }
434 }
435
436 php_http_buffer_dtor(&buf);
437 zval_ptr_dtor(&zoption_copy);
438 zval_ptr_dtor(&zoption);
439 }
440
441 if (ret != SUCCESS) {
442 return ret;
443 }
444
445 if ((zoption = get_option(options, ZEND_STRL("contentEncoding") TSRMLS_CC))) {
446 zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
447 zval zsupported;
448 HashTable *result = NULL;
449
450 zval_ptr_dtor(&zoption);
451 switch (Z_LVAL_P(zoption_copy)) {
452 case PHP_HTTP_CONTENT_ENCODING_GZIP:
453 INIT_PZVAL(&zsupported);
454 array_init(&zsupported);
455 add_next_index_stringl(&zsupported, ZEND_STRL("none"), 1);
456 add_next_index_stringl(&zsupported, ZEND_STRL("gzip"), 1);
457 add_next_index_stringl(&zsupported, ZEND_STRL("deflate"), 1);
458
459 if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported), request TSRMLS_CC))) {
460 char *key_str = NULL;
461 uint key_len = 0;
462
463 zend_hash_internal_pointer_reset(result);
464 if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key_str, &key_len, NULL, 0, NULL)) {
465 if (!strcmp(key_str, "gzip")) {
466 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 ret = FAILURE;
468 } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: gzip"))) {
469 r->content.encoding = estrndup(key_str, key_len - 1);
470 }
471 } else if (!strcmp(key_str, "deflate")) {
472 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 ret = FAILURE;
474 } else if (SUCCESS == (ret = r->ops->set_header(r, "Content-Encoding: deflate"))) {
475 r->content.encoding = estrndup(key_str, key_len - 1);
476 }
477 } else {
478 ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
479 }
480
481 if (SUCCESS == ret) {
482 ret = r->ops->add_header(r, "Vary: Accept-Encoding");
483 }
484 }
485
486 zend_hash_destroy(result);
487 FREE_HASHTABLE(result);
488 }
489
490 zval_dtor(&zsupported);
491 break;
492
493 case PHP_HTTP_CONTENT_ENCODING_NONE:
494 default:
495 ret = r->ops->del_header(r, ZEND_STRL("Content-Encoding"));
496 break;
497 }
498 zval_ptr_dtor(&zoption_copy);
499 }
500
501 if (SUCCESS != ret) {
502 return ret;
503 }
504
505 if (php_http_env_response_is_cacheable(r, request)) {
506 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 break;
509
510 case PHP_HTTP_CACHE_NO:
511 if (PHP_HTTP_CACHE_HIT != php_http_env_is_response_cached_by_last_modified(options, ZEND_STRL("If-Modified-Since"), request TSRMLS_CC)) {
512 break;
513 }
514 /* no break */
515
516 case PHP_HTTP_CACHE_HIT:
517 ret = r->ops->set_status(r, 304);
518 r->done = 1;
519 break;
520 }
521
522 if ((zoption = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
523 zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
524
525 zval_ptr_dtor(&zoption);
526 if (*Z_STRVAL_P(zoption_copy) != '"' && strncmp(Z_STRVAL_P(zoption_copy), "W/\"", 3)) {
527 ret = r->ops->set_header(r, "ETag: \"%s\"", Z_STRVAL_P(zoption_copy));
528 } else {
529 ret = r->ops->set_header(r, "ETag: %s", Z_STRVAL_P(zoption_copy));
530 }
531 zval_ptr_dtor(&zoption_copy);
532 }
533 if ((zoption = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
534 zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
535
536 zval_ptr_dtor(&zoption);
537 if (Z_LVAL_P(zoption_copy)) {
538 char *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), Z_LVAL_P(zoption_copy), 0 TSRMLS_CC);
539 if (date) {
540 ret = r->ops->set_header(r, "Last-Modified: %s", date);
541 efree(date);
542 }
543 }
544 zval_ptr_dtor(&zoption_copy);
545 }
546 }
547 }
548
549 return ret;
550 }
551
552 static STATUS php_http_env_response_send_body(php_http_env_response_t *r)
553 {
554 STATUS ret = SUCCESS;
555 zval *zoption;
556 php_http_message_body_t *body;
557 TSRMLS_FETCH_FROM_CTX(r->ts);
558
559 if (r->done) {
560 return ret;
561 }
562
563 if ((body = get_body(r->options TSRMLS_CC))) {
564 if ((zoption = get_option(r->options, ZEND_STRL("throttleDelay") TSRMLS_CC))) {
565 if (Z_TYPE_P(zoption) == IS_DOUBLE) {
566 r->throttle.delay = Z_DVAL_P(zoption);
567 }
568 zval_ptr_dtor(&zoption);
569 }
570 if ((zoption = get_option(r->options, ZEND_STRL("throttleChunk") TSRMLS_CC))) {
571 if (Z_TYPE_P(zoption) == IS_LONG) {
572 r->throttle.chunk = Z_LVAL_P(zoption);
573 }
574 zval_ptr_dtor(&zoption);
575 }
576
577 if (r->range.status == PHP_HTTP_RANGE_OK) {
578 if (zend_hash_num_elements(&r->range.values) == 1) {
579 /* single range */
580 zval **range, **begin, **end;
581
582 if ( 1 == php_http_array_list(&r->range.values TSRMLS_CC, 1, &range)
583 && 2 == php_http_array_list(Z_ARRVAL_PP(range) TSRMLS_CC, 2, &begin, &end)
584 ) {
585 /* send chunk */
586 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 if (ret == SUCCESS) {
588 ret = php_http_env_response_send_done(r);
589 }
590 zend_hash_destroy(&r->range.values);
591 } else {
592 /* this should never happen */
593 zend_hash_destroy(&r->range.values);
594 r->ops->set_status(r, 500);
595 ret = FAILURE;
596 }
597
598 } else {
599 /* send multipart/byte-ranges message */
600 HashPosition pos;
601 zval **chunk;
602
603 FOREACH_HASH_VAL(pos, &r->range.values, chunk) {
604 zval **begin, **end;
605
606 if (2 == php_http_array_list(Z_ARRVAL_PP(chunk) TSRMLS_CC, 2, &begin, &end)) {
607 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 r->range.boundary,
614 r->content.type ? r->content.type : "application/octet-stream",
615 Z_LVAL_PP(begin),
616 Z_LVAL_PP(end),
617 r->content.length
618 );
619 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 if (ret == SUCCESS) {
624 php_http_buffer_appendf(r->buffer, PHP_HTTP_CRLF "--%s--", r->range.boundary);
625 ret = php_http_env_response_send_done(r);
626 }
627 zend_hash_destroy(&r->range.values);
628 }
629
630 } else {
631 ret = php_http_message_body_to_callback(body, (php_http_pass_callback_t) php_http_env_response_send_data, r, 0, 0);
632 if (ret == SUCCESS) {
633 ret = php_http_env_response_send_done(r);
634 }
635 }
636 }
637 return ret;
638 }
639
640 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 request = get_request(r->options TSRMLS_CC);
647
648 /* check for ranges */
649 if ((body = get_body(r->options TSRMLS_CC))) {
650 r->content.length = php_http_message_body_size(body);
651
652 if (SUCCESS != r->ops->set_header(r, "Accept-Ranges: bytes")) {
653 return FAILURE;
654 } else {
655 zend_hash_init(&r->range.values, 0, NULL, ZVAL_PTR_DTOR, 0);
656 r->range.status = php_http_env_get_request_ranges(&r->range.values, r->content.length, request TSRMLS_CC);
657
658 switch (r->range.status) {
659 case PHP_HTTP_RANGE_NO:
660 zend_hash_destroy(&r->range.values);
661 break;
662
663 case PHP_HTTP_RANGE_ERR:
664 if (php_http_env_got_request_header(ZEND_STRL("If-Range"), request TSRMLS_CC)) {
665 r->range.status = PHP_HTTP_RANGE_NO;
666 zend_hash_destroy(&r->range.values);
667 } else {
668 r->done = 1;
669 zend_hash_destroy(&r->range.values);
670 if (SUCCESS != r->ops->set_status(r, 416)) {
671 return FAILURE;
672 }
673 if (SUCCESS != r->ops->set_header(r, "Content-Range: bytes */%zu", r->content.length)) {
674 return FAILURE;
675 }
676 }
677 break;
678
679 case PHP_HTTP_RANGE_OK:
680 if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
681 || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Range"), request TSRMLS_CC)
682 ) {
683 r->range.status = PHP_HTTP_RANGE_NO;
684 zend_hash_destroy(&r->range.values);
685 break;
686 }
687 if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Match"), request TSRMLS_CC)
688 || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Unmodified-Since"), request TSRMLS_CC)
689 || 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 r->done = 1;
692 zend_hash_destroy(&r->range.values);
693 if (SUCCESS != r->ops->set_status(r, 412)) {
694 return FAILURE;
695 }
696 break;
697 }
698
699 break;
700 }
701 }
702 }
703
704 if (SUCCESS != php_http_env_response_send_head(r, request)) {
705 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response headers");
706 return FAILURE;
707 }
708
709 if (SUCCESS != php_http_env_response_send_body(r)) {
710 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to send response body");
711 return FAILURE;
712 }
713
714 if (SUCCESS != r->ops->finish(r)) {
715 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to finish response");
716 return FAILURE;
717 }
718
719 return SUCCESS;
720 }
721
722 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 return php_http_env_get_response_code(TSRMLS_C);
727 }
728 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 return php_http_env_set_response_code(http_code TSRMLS_CC);
733 }
734 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 return php_http_env_set_response_protocol_version(v TSRMLS_CC);
739 }
740 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 va_start(args, fmt);
747 ret = php_http_env_set_response_header_va(0, 1, fmt, args TSRMLS_CC);
748 va_end(args);
749
750 return ret;
751 }
752 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 va_start(args, fmt);
759 ret = php_http_env_set_response_header_va(0, 0, fmt, args TSRMLS_CC);
760 va_end(args);
761
762 return ret;
763 }
764 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 return php_http_env_set_response_header_value(0, header_str, header_len, NULL, 1 TSRMLS_CC);
769 }
770 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 if (0 < PHPWRITE(data_str, data_len)) {
775 return SUCCESS;
776 }
777 return FAILURE;
778 }
779 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 if (php_output_get_level(TSRMLS_C)) {
785 php_output_flush_all(TSRMLS_C);
786 }
787 if (!(php_output_get_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) {
788 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 return SUCCESS;
796 }
797 static STATUS php_http_env_response_sapi_finish(php_http_env_response_t *r)
798 {
799 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 php_http_env_response_ops_t *php_http_env_response_get_sapi_ops(void)
817 {
818 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 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 ctx = ecalloc(1, sizeof(*ctx));
838
839 ctx->stream = init_arg;
840 if (SUCCESS != zend_list_addref(ctx->stream->rsrc_id)) {
841 efree(ctx);
842 return FAILURE;
843 }
844 zend_hash_init(&ctx->header, 0, NULL, ZVAL_PTR_DTOR, 0);
845 php_http_version_init(&ctx->version, 1, 1 TSRMLS_CC);
846 ctx->status_code = 200;
847
848 r->ctx = ctx;
849
850 return SUCCESS;
851 }
852 static void php_http_env_response_stream_dtor(php_http_env_response_t *r)
853 {
854 php_http_env_response_stream_ctx_t *ctx = r->ctx;
855 TSRMLS_FETCH_FROM_CTX(r->ts);
856
857 zend_hash_destroy(&ctx->header);
858 zend_list_delete(ctx->stream->rsrc_id);
859 efree(ctx);
860 r->ctx = NULL;
861 }
862 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 FOREACH_HASH_VAL(pos, &ctx->header, val) {
868 if (Z_TYPE_PP(val) == IS_ARRAY) {
869 php_http_env_response_stream_header(ctx, Z_ARRVAL_PP(val) TSRMLS_CC);
870 } else {
871 php_stream_write(ctx->stream, Z_STRVAL_PP(val), Z_STRLEN_PP(val));
872 php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
873 }
874 }
875 }
876 static STATUS php_http_env_response_stream_start(php_http_env_response_stream_ctx_t *ctx TSRMLS_DC)
877 {
878 if (ctx->started || ctx->finished) {
879 return FAILURE;
880 }
881
882 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 php_http_env_response_stream_header(ctx, &ctx->header TSRMLS_CC);
884 php_stream_write_string(ctx->stream, PHP_HTTP_CRLF);
885 ctx->started = 1;
886 return SUCCESS;
887 }
888 static long php_http_env_response_stream_get_status(php_http_env_response_t *r)
889 {
890 php_http_env_response_stream_ctx_t *ctx = r->ctx;
891
892 return ctx->status_code;
893 }
894 static STATUS php_http_env_response_stream_set_status(php_http_env_response_t *r, long http_code)
895 {
896 php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
897
898 if (stream_ctx->started || stream_ctx->finished) {
899 return FAILURE;
900 }
901
902 stream_ctx->status_code = http_code;
903
904 return SUCCESS;
905 }
906 static STATUS php_http_env_response_stream_set_protocol_version(php_http_env_response_t *r, php_http_version_t *v)
907 {
908 php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
909
910 if (stream_ctx->started || stream_ctx->finished) {
911 return FAILURE;
912 }
913
914 memcpy(&stream_ctx->version, v, sizeof(stream_ctx->version));
915
916 return SUCCESS;
917 }
918 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 php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
921 char *header_end, *header_str = NULL;
922 size_t header_len = 0;
923 zval *zheader, **zheader_ptr;
924
925 if (stream_ctx->started || stream_ctx->finished) {
926 return FAILURE;
927 }
928
929 header_len = vspprintf(&header_str, 0, fmt, argv);
930
931 if (!(header_end = strchr(header_str, ':'))) {
932 efree(header_str);
933 return FAILURE;
934 }
935
936 *header_end = '\0';
937
938 if (!replace && (SUCCESS == zend_hash_find(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader_ptr))) {
939 convert_to_array(*zheader_ptr);
940 *header_end = ':';
941 return add_next_index_stringl(*zheader_ptr, header_str, header_len, 0);
942 } else {
943 MAKE_STD_ZVAL(zheader);
944 ZVAL_STRINGL(zheader, header_str, header_len, 0);
945
946 if (SUCCESS != zend_hash_update(&stream_ctx->header, header_str, header_end - header_str + 1, (void *) &zheader, sizeof(zval *), NULL)) {
947 zval_ptr_dtor(&zheader);
948 return FAILURE;
949 }
950
951 *header_end = ':';
952 return SUCCESS;
953 }
954 }
955 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 va_start(argv, fmt);
961 ret = php_http_env_response_stream_set_header_ex(r, 1, fmt, argv);
962 va_end(argv);
963
964 return ret;
965 }
966 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 va_start(argv, fmt);
972 ret = php_http_env_response_stream_set_header_ex(r, 0, fmt, argv);
973 va_end(argv);
974
975 return ret;
976 }
977 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 php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
980
981 if (stream_ctx->started || stream_ctx->finished) {
982 return FAILURE;
983 }
984
985 zend_hash_del(&stream_ctx->header, header_str, header_len + 1);
986 return SUCCESS;
987 }
988 static STATUS php_http_env_response_stream_write(php_http_env_response_t *r, const char *data_str, size_t data_len)
989 {
990 php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
991 TSRMLS_FETCH_FROM_CTX(r->ts);
992
993 if (stream_ctx->finished) {
994 return FAILURE;
995 }
996 if (!stream_ctx->started) {
997 if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
998 return FAILURE;
999 }
1000 }
1001
1002 if (data_len != php_stream_write(stream_ctx->stream, data_str, data_len)) {
1003 return FAILURE;
1004 }
1005
1006 return SUCCESS;
1007 }
1008 static STATUS php_http_env_response_stream_flush(php_http_env_response_t *r)
1009 {
1010 php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
1011 TSRMLS_FETCH_FROM_CTX(r->ts);
1012
1013 if (stream_ctx->finished) {
1014 return FAILURE;
1015 }
1016 if (!stream_ctx->started) {
1017 if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
1018 return FAILURE;
1019 }
1020 }
1021
1022 return php_stream_flush(stream_ctx->stream);
1023 }
1024 static STATUS php_http_env_response_stream_finish(php_http_env_response_t *r)
1025 {
1026 php_http_env_response_stream_ctx_t *stream_ctx = r->ctx;
1027 TSRMLS_FETCH_FROM_CTX(r->ts);
1028
1029 if (stream_ctx->finished) {
1030 return FAILURE;
1031 }
1032 if (!stream_ctx->started) {
1033 if (SUCCESS != php_http_env_response_stream_start(stream_ctx TSRMLS_CC)) {
1034 return FAILURE;
1035 }
1036 }
1037
1038 stream_ctx->finished = 1;
1039
1040 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 php_http_env_response_ops_t *php_http_env_response_get_stream_ops(void)
1058 {
1059 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 static PHP_METHOD(HttpEnvResponse, __construct)
1072 {
1073 php_http_message_object_t *obj;
1074
1075 php_http_expect(SUCCESS == zend_parse_parameters_none(), invalid_arg, return);
1076
1077 obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1078
1079 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 static PHP_METHOD(HttpEnvResponse, __invoke)
1087 {
1088 char *ob_str;
1089 int ob_len;
1090 long ob_flags = 0;
1091
1092 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &ob_str, &ob_len, &ob_flags)) {
1093 php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
1094
1095 PHP_HTTP_ENV_RESPONSE_OBJECT_INIT(obj);
1096
1097 php_http_message_object_init_body_object(obj);
1098 php_http_message_body_append(obj->message->body, ob_str, ob_len);
1099 #if PHP_VERSION_ID >= 50400
1100 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 static PHP_METHOD(HttpEnvResponse, setEnvRequest)
1111 {
1112 zval *env_req = NULL;
1113
1114 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 set_option(getThis(), ZEND_STRL("request"), IS_OBJECT, env_req, 0 TSRMLS_CC);
1117 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 static PHP_METHOD(HttpEnvResponse, setContentType)
1124 {
1125 char *ct_str = NULL;
1126 int ct_len = 0;
1127
1128 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &ct_str, &ct_len), invalid_arg, return);
1129
1130 set_option(getThis(), ZEND_STRL("contentType"), IS_STRING, ct_str, ct_len TSRMLS_CC);
1131 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 static PHP_METHOD(HttpEnvResponse, setContentDisposition)
1138 {
1139 zval *zdisposition;
1140
1141 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &zdisposition), invalid_arg, return);
1142
1143 zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("contentDisposition"), zdisposition TSRMLS_CC);
1144 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 static PHP_METHOD(HttpEnvResponse, setContentEncoding)
1151 {
1152 long ce;
1153
1154 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ce), invalid_arg, return);
1155
1156 set_option(getThis(), ZEND_STRL("contentEncoding"), IS_LONG, &ce, 0 TSRMLS_CC);
1157 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 static PHP_METHOD(HttpEnvResponse, setCacheControl)
1164 {
1165 char *cc_str = NULL;
1166 int cc_len = 0;
1167
1168 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &cc_str, &cc_len), invalid_arg, return);
1169
1170 set_option(getThis(), ZEND_STRL("cacheControl"), IS_STRING, cc_str, cc_len TSRMLS_CC);
1171 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 static PHP_METHOD(HttpEnvResponse, setLastModified)
1178 {
1179 long last_modified;
1180
1181 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &last_modified), invalid_arg, return);
1182
1183 set_option(getThis(), ZEND_STRL("lastModified"), IS_LONG, &last_modified, 0 TSRMLS_CC);
1184 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 static PHP_METHOD(HttpEnvResponse, isCachedByLastModified)
1191 {
1192 char *header_name_str = NULL;
1193 int header_name_len = 0;
1194
1195 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
1196 if (!header_name_str || !header_name_len) {
1197 header_name_str = "If-Modified-Since";
1198 header_name_len = lenof("If-Modified-Since");
1199 }
1200
1201 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 static PHP_METHOD(HttpEnvResponse, setEtag)
1209 {
1210 char *etag_str = NULL;
1211 int etag_len = 0;
1212
1213 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &etag_str, &etag_len), invalid_arg, return);
1214
1215 set_option(getThis(), ZEND_STRL("etag"), IS_STRING, etag_str, etag_len TSRMLS_CC);
1216 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 static PHP_METHOD(HttpEnvResponse, isCachedByEtag)
1223 {
1224 char *header_name_str = NULL;
1225 int header_name_len = 0;
1226
1227 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
1228 if (!header_name_str || !header_name_len) {
1229 header_name_str = "If-None-Match";
1230 header_name_len = lenof("If-None-Match");
1231 }
1232 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 static PHP_METHOD(HttpEnvResponse, setThrottleRate)
1241 {
1242 long chunk_size;
1243 double delay = 1;
1244
1245 php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|d", &chunk_size, &delay), invalid_arg, return);
1246
1247 set_option(getThis(), ZEND_STRL("throttleDelay"), IS_DOUBLE, &delay, 0 TSRMLS_CC);
1248 set_option(getThis(), ZEND_STRL("throttleChunk"), IS_LONG, &chunk_size, 0 TSRMLS_CC);
1249 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 static PHP_METHOD(HttpEnvResponse, send)
1256 {
1257 zval *zstream = NULL;
1258 php_stream *s = NULL;
1259
1260 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 php_output_end_all(TSRMLS_C);
1265 #else
1266 php_end_ob_buffers(1 TSRMLS_CC);
1267 #endif
1268 if (zstream) {
1269 php_http_env_response_t *r;
1270
1271 php_stream_from_zval(s, &zstream);
1272 r = php_http_env_response_init(NULL, getThis(), php_http_env_response_get_stream_ops(), s TSRMLS_CC);
1273 if (!r) {
1274 RETURN_FALSE;
1275 }
1276
1277 RETVAL_BOOL(SUCCESS == php_http_env_response_send(r));
1278 php_http_env_response_free(&r);
1279 } else {
1280 php_http_env_response_t r;
1281
1282 if (!php_http_env_response_init(&r, getThis(), NULL, NULL TSRMLS_CC)) {
1283 RETURN_FALSE;
1284 }
1285
1286 RETVAL_BOOL(SUCCESS == php_http_env_response_send(&r));
1287 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 PHP_MINIT_FUNCTION(http_env_response)
1312 {
1313 zend_class_entry ce = {0};
1314
1315 INIT_NS_CLASS_ENTRY(ce, "http\\Env", "Response", php_http_env_response_methods);
1316 php_http_env_response_class_entry = zend_register_internal_class_ex(&ce, php_http_message_class_entry, NULL TSRMLS_CC);
1317
1318 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_NONE"), PHP_HTTP_CONTENT_ENCODING_NONE TSRMLS_CC);
1319 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 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_NO"), PHP_HTTP_CACHE_NO TSRMLS_CC);
1322 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_HIT"), PHP_HTTP_CACHE_HIT TSRMLS_CC);
1323 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS TSRMLS_CC);
1324
1325 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("request"), ZEND_ACC_PROTECTED TSRMLS_CC);
1326 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED TSRMLS_CC);
1327 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentDisposition"), ZEND_ACC_PROTECTED TSRMLS_CC);
1328 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentEncoding"), ZEND_ACC_PROTECTED TSRMLS_CC);
1329 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cacheControl"), ZEND_ACC_PROTECTED TSRMLS_CC);
1330 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("etag"), ZEND_ACC_PROTECTED TSRMLS_CC);
1331 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("lastModified"), ZEND_ACC_PROTECTED TSRMLS_CC);
1332 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleDelay"), ZEND_ACC_PROTECTED TSRMLS_CC);
1333 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleChunk"), ZEND_ACC_PROTECTED TSRMLS_CC);
1334
1335 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 */