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