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