bc30b3c0d1adf50b3e90c4e000f197dd0871dea0
[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 #include <ext/date/php_date.h>
16 #include <ext/standard/php_string.h>
17
18
19 static void set_option(zval *options, const char *name_str, size_t name_len, int type, const void *value_ptr, size_t value_len TSRMLS_DC)
20 {
21 if (Z_TYPE_P(options) == IS_OBJECT) {
22 /* stupid non-const api */
23 char *name = estrndup(name_str, name_len);
24 if (value_ptr) {
25 switch (type) {
26 case IS_DOUBLE:
27 zend_update_property_double(Z_OBJCE_P(options), options, name, name_len, *(double *)value_ptr TSRMLS_CC);
28 break;
29 case IS_LONG:
30 zend_update_property_long(Z_OBJCE_P(options), options, name, name_len, *(long *)value_ptr TSRMLS_CC);
31 break;
32 case IS_STRING:
33 zend_update_property_stringl(Z_OBJCE_P(options), options, name, name_len, value_ptr, value_len TSRMLS_CC);
34 break;
35 }
36 } else {
37 zend_update_property_null(Z_OBJCE_P(options), options, name, name_len TSRMLS_CC);
38 }
39 efree(name);
40 } else {
41 convert_to_array(options);
42 if (value_ptr) {
43 switch (type) {
44 case IS_DOUBLE:
45 add_assoc_double_ex(options, name_str, name_len + 1, *(double *)value_ptr);
46 break;
47 case IS_LONG:
48 add_assoc_long_ex(options, name_str, name_len + 1, *(long *)value_ptr);
49 break;
50 case IS_STRING: {
51 char *value = estrndup(value_ptr, value_len);
52 add_assoc_stringl_ex(options, name_str, name_len + 1, value, value_len, 0);
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 char *name = estrndup(name_str, name_len);
67 val = zend_read_property(Z_OBJCE_P(options), options, name, name_len, 0 TSRMLS_CC);
68 efree(name);
69 } else {
70 if (SUCCESS == zend_symtable_find(Z_ARRVAL_P(options), name_str, name_len + 1, (void *) &valptr)) {
71 val = *valptr;
72 } else {
73 val = NULL;
74 }
75 }
76 if (val) {
77 Z_ADDREF_P(val);
78 }
79 return val;
80 }
81
82 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 TSRMLS_DC)
83 {
84 int ret, free_etag = 0;
85 char *header, *etag;
86 zval *zetag, *zbody = NULL;
87
88 if ( !(header = php_http_env_get_request_header(header_str, header_len, NULL TSRMLS_CC))
89 || !(zbody = get_option(options, ZEND_STRL("body") TSRMLS_CC))
90 || !(Z_TYPE_P(zbody) == IS_OBJECT)
91 || !instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)
92 ) {
93 STR_FREE(header);
94 if (zbody) {
95 zval_ptr_dtor(&zbody);
96 }
97 return PHP_HTTP_CACHE_NO;
98 }
99
100 if ((zetag = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
101 zval *zetag_copy = php_http_ztyp(IS_STRING, zetag);
102 zval_ptr_dtor(&zetag);
103 zetag = zetag_copy;
104 }
105
106 if (zetag && Z_STRLEN_P(zetag)) {
107 etag = Z_STRVAL_P(zetag);
108 } else {
109 etag = php_http_message_body_etag(((php_http_message_body_object_t *) zend_object_store_get_object(zbody TSRMLS_CC))->body);
110 set_option(options, ZEND_STRL("etag"), IS_STRING, etag, strlen(etag) TSRMLS_CC);
111 free_etag = 1;
112 }
113
114 if (zbody) {
115 zval_ptr_dtor(&zbody);
116 }
117
118 if (zetag) {
119 zval_ptr_dtor(&zetag);
120 }
121
122 ret = php_http_match(header, etag, PHP_HTTP_MATCH_WORD);
123
124 if (free_etag) {
125 efree(etag);
126 }
127 efree(header);
128
129 return ret ? PHP_HTTP_CACHE_HIT : PHP_HTTP_CACHE_MISS;
130 }
131
132 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 TSRMLS_DC)
133 {
134 char *header;
135 time_t ums, lm = 0;
136 zval *zbody = NULL, *zlm;
137
138 if ( !(header = php_http_env_get_request_header(header_str, header_len, NULL TSRMLS_CC))
139 || !(zbody = get_option(options, ZEND_STRL("body") TSRMLS_CC))
140 || !(Z_TYPE_P(zbody) == IS_OBJECT)
141 || !instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)
142 ) {
143 STR_FREE(header);
144 if (zbody) {
145 zval_ptr_dtor(&zbody);
146 }
147 return PHP_HTTP_CACHE_NO;
148 }
149
150 if ((zlm = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
151 zval *zlm_copy = php_http_ztyp(IS_LONG, zlm);
152 zval_ptr_dtor(&zlm);
153 zlm = zlm_copy;
154 }
155
156 if (zlm && Z_LVAL_P(zlm) > 0) {
157 lm = Z_LVAL_P(zlm);
158 } else {
159 lm = php_http_message_body_mtime(((php_http_message_body_object_t *) zend_object_store_get_object(zbody TSRMLS_CC))->body);
160 set_option(options, ZEND_STRL("lastModified"), IS_LONG, &lm, 0 TSRMLS_CC);
161 }
162
163 if (zlm) {
164 zval_ptr_dtor(&zlm);
165 }
166
167 ums = php_parse_date(header, NULL);
168 efree(header);
169
170 if (ums > 0 && ums <= lm) {
171 return PHP_HTTP_CACHE_HIT;
172 } else {
173 return PHP_HTTP_CACHE_MISS;
174 }
175 }
176
177 static size_t output(void *context, const char *buf, size_t len TSRMLS_DC)
178 {
179 php_http_env_response_t *r = context;
180
181 PHPWRITE(buf, len);
182
183 /* we really only need to flush when throttling is enabled,
184 because we push the data as fast as possible anyway if not */
185 if (r->throttle.delay >= PHP_HTTP_DIFFSEC) {
186 if (php_output_get_level(TSRMLS_C)) {
187 php_output_flush_all(TSRMLS_C);
188 }
189 if (!(php_output_get_status(TSRMLS_C) & PHP_OUTPUT_IMPLICITFLUSH)) {
190 sapi_flush(TSRMLS_C);
191 }
192 php_http_sleep(r->throttle.delay);
193 }
194 return len;
195 }
196
197 #define php_http_env_response_send_done(r) php_http_env_response_send_data((r), NULL, 0)
198 static STATUS php_http_env_response_send_data(php_http_env_response_t *r, const char *buf, size_t len)
199 {
200 TSRMLS_FETCH_FROM_CTX(r->ts);
201 size_t chunk = r->throttle.chunk ? r->throttle.chunk : PHP_HTTP_SENDBUF_SIZE;
202
203 if (r->content.encoder) {
204 char *enc_str = NULL;
205 size_t enc_len = 0;
206
207 if (buf) {
208 if (SUCCESS != php_http_encoding_stream_update(r->content.encoder, buf, len, &enc_str, &enc_len)) {
209 return FAILURE;
210 }
211 } else {
212 if (SUCCESS != php_http_encoding_stream_finish(r->content.encoder, &enc_str, &enc_len)) {
213 return FAILURE;
214 }
215 }
216
217 if (enc_str) {
218 php_http_buffer_chunked_output(&r->buffer, enc_str, enc_len, buf ? chunk : 0, output, r TSRMLS_CC);
219 STR_FREE(enc_str);
220 }
221 } else {
222 php_http_buffer_chunked_output(&r->buffer, buf, len, buf ? chunk : 0, output, r TSRMLS_CC);
223 }
224
225 return SUCCESS;
226 }
227
228 PHP_HTTP_API php_http_env_response_t *php_http_env_response_init(php_http_env_response_t *r, zval *options TSRMLS_DC)
229 {
230 php_http_env_response_t *free_r = NULL;
231
232 if (!r) {
233 r = free_r = emalloc(sizeof(*r));
234 }
235 memset(r, 0, sizeof(*r));
236
237 r->buffer = php_http_buffer_init(NULL);
238
239 Z_ADDREF_P(options);
240 r->options = options;
241
242 TSRMLS_SET_CTX(r->ts);
243
244 return r;
245 }
246
247 PHP_HTTP_API void php_http_env_response_dtor(php_http_env_response_t *r)
248 {
249 php_http_buffer_free(&r->buffer);
250 zval_ptr_dtor(&r->options);
251 STR_FREE(r->content.type);
252 STR_FREE(r->content.encoding);
253 if (r->content.encoder) {
254 php_http_encoding_stream_free(&r->content.encoder);
255 }
256 }
257
258 PHP_HTTP_API void php_http_env_response_free(php_http_env_response_t **r)
259 {
260 if (*r) {
261 php_http_env_response_dtor(*r);
262 efree(*r);
263 *r = NULL;
264 }
265 }
266
267 static STATUS php_http_env_response_send_head(php_http_env_response_t *r)
268 {
269 STATUS ret = SUCCESS;
270 zval *zoption, *options = r->options;
271 TSRMLS_FETCH_FROM_CTX(r->ts);
272
273 if (r->done) {
274 return ret;
275 }
276
277 if ((zoption = get_option(options, ZEND_STRL("responseCode") TSRMLS_CC))) {
278 zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
279
280 zval_ptr_dtor(&zoption);
281 if (Z_LVAL_P(zoption_copy) > 0) {
282 ret = php_http_env_set_response_code(Z_LVAL_P(zoption_copy) TSRMLS_CC);
283 }
284 zval_ptr_dtor(&zoption_copy);
285 }
286
287 if (ret != SUCCESS) {
288 return ret;
289 }
290
291 if ((zoption = get_option(options, ZEND_STRL("httpVersion") TSRMLS_CC))) {
292 php_http_version_t v;
293 zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
294
295 zval_ptr_dtor(&zoption);
296 if (Z_STRLEN_P(zoption_copy) && php_http_version_parse(&v, Z_STRVAL_P(zoption_copy) TSRMLS_CC)) {
297 ret = php_http_env_set_response_protocol_version(&v TSRMLS_CC);
298 php_http_version_dtor(&v);
299 }
300 zval_ptr_dtor(&zoption_copy);
301 }
302
303 if (ret != SUCCESS) {
304 return ret;
305 }
306
307 if ((zoption = get_option(options, ZEND_STRL("headers") TSRMLS_CC))) {
308 if (Z_TYPE_P(zoption) == IS_ARRAY) {
309 zval **val;
310 HashPosition pos;
311 php_http_array_hashkey_t key = php_http_array_hashkey_init(0);
312
313 FOREACH_KEYVAL(pos, zoption, key, val) {
314 if (key.type == HASH_KEY_IS_STRING) {
315 if (SUCCESS != (ret = php_http_env_set_response_header_value(0, key.str, key.len - 1, *val, 1 TSRMLS_CC))) {
316 break;
317 }
318 }
319 }
320 }
321 zval_ptr_dtor(&zoption);
322 }
323
324 if (ret != SUCCESS) {
325 return ret;
326 }
327
328 if ((zoption = get_option(options, ZEND_STRL("contentType") TSRMLS_CC))) {
329 zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
330
331 zval_ptr_dtor(&zoption);
332 if (Z_STRLEN_P(zoption_copy)) {
333 PHP_HTTP_CHECK_CONTENT_TYPE(Z_STRVAL_P(zoption_copy), ret = FAILURE) else {
334 if (SUCCESS == (ret = php_http_env_set_response_header_format(0, 1 TSRMLS_CC, "Content-Type: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy)))) {
335 r->content.type = estrndup(Z_STRVAL_P(zoption_copy), Z_STRLEN_P(zoption_copy));
336 }
337 }
338 }
339 zval_ptr_dtor(&zoption_copy);
340 }
341
342 if (ret != SUCCESS) {
343 return ret;
344 }
345
346 if (r->range.status == PHP_HTTP_RANGE_OK) {
347 if (zend_hash_num_elements(&r->range.values) == 1) {
348 zval **range, **begin, **end;
349
350 if (SUCCESS != zend_hash_index_find(&r->range.values, 0, (void *) &range)
351 || SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 0, (void *) &begin)
352 || SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 1, (void *) &end)
353 ) {
354 /* this should never happen */
355 zend_hash_destroy(&r->range.values);
356 php_http_env_set_response_code(500 TSRMLS_CC);
357 ret = FAILURE;
358 } else {
359 ret = php_http_env_set_response_header_format(206, 1 TSRMLS_CC, "Content-Range: bytes %ld-%ld/%zu", Z_LVAL_PP(begin), Z_LVAL_PP(end), r->content.length);
360 }
361 } else {
362 php_http_boundary(r->range.boundary, sizeof(r->range.boundary) TSRMLS_CC);
363 ret = php_http_env_set_response_header_format(206, 1 TSRMLS_CC, "Content-Type: multipart/byteranges; boundary=%s", r->range.boundary);
364 }
365 } else {
366 if ((zoption = get_option(options, ZEND_STRL("cacheControl") TSRMLS_CC))) {
367 zval *zoption_copy = php_http_ztyp(IS_STRING, zoption);
368
369 zval_ptr_dtor(&zoption);
370 if (Z_STRLEN_P(zoption_copy)) {
371 ret = php_http_env_set_response_header_format(0, 1 TSRMLS_CC, "Cache-Control: %.*s", Z_STRLEN_P(zoption_copy), Z_STRVAL_P(zoption_copy) TSRMLS_CC);
372 }
373 zval_ptr_dtor(&zoption_copy);
374 }
375
376 if (ret != SUCCESS) {
377 return ret;
378 }
379
380 if ((zoption = get_option(options, ZEND_STRL("contentDisposition") TSRMLS_CC))) {
381 zval *zoption_copy = php_http_ztyp(IS_ARRAY, zoption);
382 zval **zdisposition, **zfilename = NULL;
383
384 zval_ptr_dtor(&zoption);
385 if (SUCCESS == zend_hash_find(Z_ARRVAL_P(zoption_copy), ZEND_STRS("disposition"), (void *) &zdisposition)) {
386 zval *zdisposition_copy = php_http_ztyp(IS_LONG, *zdisposition);
387 char *tmp = NULL;
388
389 switch (Z_LVAL_P(zdisposition_copy)) {
390 case PHP_HTTP_CONTENT_DISPOSITION_NONE:
391 ret = php_http_env_set_response_header_value(0, ZEND_STRL("Content-Disposition"), NULL, 1 TSRMLS_CC);
392 break;
393 case PHP_HTTP_CONTENT_DISPOSITION_INLINE:
394 tmp = "inline";
395 break;
396 case PHP_HTTP_CONTENT_DISPOSITION_ATTACHMENT:
397 tmp = "attachment";
398 break;
399 }
400
401 if (tmp) {
402 if (SUCCESS != zend_hash_find(Z_ARRVAL_P(zoption_copy), ZEND_STRS("filename"), (void *) &zfilename)) {
403 ret = php_http_env_set_response_header_format(0, 1 TSRMLS_CC, "Content-Disposition: %s", tmp);
404 } else {
405 zval *zfilename_copy = php_http_ztyp(IS_STRING, *zfilename);
406
407 if (!Z_STRLEN_P(zfilename_copy)) {
408 ret = php_http_env_set_response_header_format(0, 1 TSRMLS_CC, "Content-Disposition: %s", tmp);
409 } else {
410 int new_f_len;
411 char *new_f_str = php_addslashes(estrndup(Z_STRVAL_P(zfilename_copy), Z_STRLEN_P(zfilename_copy)), Z_STRLEN_P(zfilename_copy), &new_f_len, 0 TSRMLS_CC);
412
413 ret = php_http_env_set_response_header_format(0, 1 TSRMLS_CC, "Content-Disposition: %s; filename=\"%.*s\"", tmp, new_f_len, new_f_str);
414 STR_FREE(new_f_str);
415 }
416
417 zval_ptr_dtor(&zfilename_copy);
418 }
419 }
420 zval_ptr_dtor(&zdisposition_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("contentEncoding") TSRMLS_CC))) {
430 zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
431 zval zsupported;
432 HashTable *result = NULL;
433
434 zval_ptr_dtor(&zoption);
435 switch (Z_LVAL_P(zoption_copy)) {
436 case PHP_HTTP_CONTENT_ENCODING_GZIP:
437 INIT_PZVAL(&zsupported);
438 array_init(&zsupported);
439 add_next_index_stringl(&zsupported, ZEND_STRL("none"), 1);
440 add_next_index_stringl(&zsupported, ZEND_STRL("gzip"), 1);
441 add_next_index_stringl(&zsupported, ZEND_STRL("deflate"), 1);
442
443 if ((result = php_http_negotiate_encoding(Z_ARRVAL(zsupported) TSRMLS_CC))) {
444 char *key_str = NULL;
445 uint key_len = 0;
446
447 zend_hash_internal_pointer_reset(result);
448 if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(result, &key_str, &key_len, NULL, 0, NULL)) {
449 if (!strcmp(key_str, "gzip")) {
450 if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_GZIP TSRMLS_CC))) {
451 ret = FAILURE;
452 } else if (SUCCESS == (ret = php_http_env_set_response_header(0, ZEND_STRL("Content-Encoding: gzip"), 1 TSRMLS_CC))) {
453 r->content.encoding = estrndup(key_str, key_len - 1);
454 }
455 } else if (!strcmp(key_str, "deflate")) {
456 if (!(r->content.encoder = php_http_encoding_stream_init(NULL, php_http_encoding_stream_get_deflate_ops(), PHP_HTTP_DEFLATE_TYPE_ZLIB TSRMLS_CC))) {
457 ret = FAILURE;
458 } else if (SUCCESS == (ret = php_http_env_set_response_header(0, ZEND_STRL("Content-Encoding: deflate"), 1 TSRMLS_CC))) {
459 r->content.encoding = estrndup(key_str, key_len - 1);
460 }
461 } else {
462 ret = php_http_env_set_response_header_value(0, ZEND_STRL("Content-Encoding"), NULL, 0 TSRMLS_CC);
463 }
464
465 if (SUCCESS == ret) {
466 ret = php_http_env_set_response_header(0, ZEND_STRL("Vary: Accept-Encoding"), 0 TSRMLS_CC);
467 }
468 }
469
470 zend_hash_destroy(result);
471 FREE_HASHTABLE(result);
472 }
473
474 zval_dtor(&zsupported);
475 break;
476
477 case PHP_HTTP_CONTENT_ENCODING_NONE:
478 default:
479 ret = php_http_env_set_response_header_value(0, ZEND_STRL("Content-Encoding"), NULL, 0 TSRMLS_CC);
480 break;
481 }
482 zval_ptr_dtor(&zoption_copy);
483 }
484
485 if (SUCCESS != ret) {
486 return ret;
487 }
488
489 switch (php_http_env_is_response_cached_by_etag(options, ZEND_STRL("If-None-Match") TSRMLS_CC)) {
490 case PHP_HTTP_CACHE_MISS:
491 break;
492
493 case PHP_HTTP_CACHE_NO:
494 if (PHP_HTTP_CACHE_HIT != php_http_env_is_response_cached_by_last_modified(options, ZEND_STRL("If-Modified-Since") TSRMLS_CC)) {
495 break;
496 }
497 /* fallthrough */
498
499 case PHP_HTTP_CACHE_HIT:
500 ret = php_http_env_set_response_code(304 TSRMLS_CC);
501 r->done = 1;
502 break;
503 }
504
505 if ((zoption = get_option(options, ZEND_STRL("etag") TSRMLS_CC))) {
506 ret = php_http_env_set_response_header_value(0, ZEND_STRL("ETag"), zoption, 1 TSRMLS_CC);
507 zval_ptr_dtor(&zoption);
508 }
509 if ((zoption = get_option(options, ZEND_STRL("lastModified") TSRMLS_CC))) {
510 zval *zoption_copy = php_http_ztyp(IS_LONG, zoption);
511
512 zval_ptr_dtor(&zoption);
513 if (Z_LVAL_P(zoption_copy)) {
514 char *date = php_format_date(ZEND_STRL(PHP_HTTP_DATE_FORMAT), Z_LVAL_P(zoption_copy), 0 TSRMLS_CC);
515 if (date) {
516 ret = php_http_env_set_response_header_format(0, 1 TSRMLS_CC, "Last-Modified: %s", date);
517 efree(date);
518 }
519 }
520 zval_ptr_dtor(&zoption_copy);
521 }
522 }
523
524 return ret;
525 }
526
527 static STATUS php_http_env_response_send_body(php_http_env_response_t *r)
528 {
529 STATUS ret = SUCCESS;
530 zval *zbody;
531 TSRMLS_FETCH_FROM_CTX(r->ts);
532
533 if (r->done) {
534 return ret;
535 }
536
537 if ( (zbody = get_option(r->options, ZEND_STRL("body") TSRMLS_CC))
538 && (Z_TYPE_P(zbody) == IS_OBJECT)
539 && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)
540 ) {
541 php_http_message_body_object_t *obj = zend_object_store_get_object(zbody TSRMLS_CC);
542
543 if (r->range.status == PHP_HTTP_RANGE_OK) {
544 if (zend_hash_num_elements(&r->range.values) == 1) {
545 /* single range */
546 zval **range, **begin, **end;
547
548 if (SUCCESS != zend_hash_index_find(&r->range.values, 0, (void *) &range)
549 || SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 0, (void *) &begin)
550 || SUCCESS != zend_hash_index_find(Z_ARRVAL_PP(range), 1, (void *) &end)
551 ) {
552 /* this should never happen */
553 zend_hash_destroy(&r->range.values);
554 php_http_env_set_response_code(500 TSRMLS_CC);
555 ret = FAILURE;
556 } else {
557 /* send chunk */
558 php_http_message_body_to_callback(obj->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);
559 php_http_env_response_send_done(r);
560 zend_hash_destroy(&r->range.values);
561 ret = SUCCESS;
562 }
563 } else {
564 /* send multipart/byte-ranges message */
565 HashPosition pos;
566 zval **chunk;
567
568 FOREACH_HASH_VAL(pos, &r->range.values, chunk) {
569 zval **begin, **end;
570
571 if (IS_ARRAY == Z_TYPE_PP(chunk)
572 && SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(chunk), 0, (void *) &begin)
573 && IS_LONG == Z_TYPE_PP(begin)
574 && SUCCESS == zend_hash_index_find(Z_ARRVAL_PP(chunk), 1, (void *) &end)
575 && IS_LONG == Z_TYPE_PP(end)
576 ) {
577 php_http_buffer_appendf(r->buffer,
578 PHP_HTTP_CRLF
579 "--%s" PHP_HTTP_CRLF
580 "Content-Type: %s" PHP_HTTP_CRLF
581 "Content-Range: bytes %ld-%ld/%zu" PHP_HTTP_CRLF PHP_HTTP_CRLF,
582 /* - */
583 r->range.boundary,
584 r->content.type ? r->content.type : "application/octet-stream",
585 Z_LVAL_PP(begin),
586 Z_LVAL_PP(end),
587 r->content.length
588 );
589 php_http_message_body_to_callback(obj->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);
590 }
591 }
592 php_http_buffer_appendf(r->buffer, PHP_HTTP_CRLF "--%s--", r->range.boundary);
593 php_http_env_response_send_done(r);
594 zend_hash_destroy(&r->range.values);
595 }
596
597 } else {
598 php_http_message_body_to_callback(obj->body, (php_http_pass_callback_t) php_http_env_response_send_data, r, 0, 0);
599 php_http_env_response_send_done(r);
600 }
601 }
602 if (zbody) {
603 zval_ptr_dtor(&zbody);
604 }
605 return ret;
606 }
607
608 PHP_HTTP_API STATUS php_http_env_response_send(php_http_env_response_t *r)
609 {
610 zval *zbody;
611 TSRMLS_FETCH_FROM_CTX(r->ts);
612
613 /* check for ranges */
614 if ( (zbody = get_option(r->options, ZEND_STRL("body") TSRMLS_CC))
615 && (Z_TYPE_P(zbody) == IS_OBJECT)
616 && instanceof_function(Z_OBJCE_P(zbody), php_http_message_body_class_entry TSRMLS_CC)
617 ) {
618 php_http_message_body_object_t *obj = zend_object_store_get_object(zbody TSRMLS_CC);
619
620 r->content.length = php_http_message_body_size(obj->body);
621 zval_ptr_dtor(&zbody);
622
623 if (SUCCESS != php_http_env_set_response_header(0, ZEND_STRL("Accept-Ranges: bytes"), 1 TSRMLS_CC)) {
624 return FAILURE;
625 } else {
626 zend_hash_init(&r->range.values, 0, NULL, ZVAL_PTR_DTOR, 0);
627 r->range.status = php_http_env_get_request_ranges(&r->range.values, r->content.length TSRMLS_CC);
628
629 switch (r->range.status) {
630 case PHP_HTTP_RANGE_NO:
631 zend_hash_destroy(&r->range.values);
632 break;
633
634 case PHP_HTTP_RANGE_ERR:
635 if (php_http_env_got_request_header(ZEND_STRL("If-Range") TSRMLS_CC)) {
636 r->range.status = PHP_HTTP_RANGE_NO;
637 zend_hash_destroy(&r->range.values);
638 } else {
639 r->done = 1;
640 zend_hash_destroy(&r->range.values);
641 if (SUCCESS != php_http_env_set_response_header_format(416, 1 TSRMLS_CC, "Content-Range: bytes */%zu", r->content.length)) {
642 return FAILURE;
643 }
644 }
645 break;
646
647 case PHP_HTTP_RANGE_OK:
648 if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Range") TSRMLS_CC)
649 || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Range") TSRMLS_CC)
650 ) {
651 r->range.status = PHP_HTTP_RANGE_NO;
652 zend_hash_destroy(&r->range.values);
653 break;
654 }
655 if (PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_etag(r->options, ZEND_STRL("If-Match") TSRMLS_CC)
656 || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("If-Unmodified-Since") TSRMLS_CC)
657 || PHP_HTTP_CACHE_MISS == php_http_env_is_response_cached_by_last_modified(r->options, ZEND_STRL("Unless-Modified-Since") TSRMLS_CC)
658 ) {
659 r->done = 1;
660 zend_hash_destroy(&r->range.values);
661 if (SUCCESS != php_http_env_set_response_code(412 TSRMLS_CC)) {
662 return FAILURE;
663 }
664 break;
665 }
666
667 break;
668 }
669 }
670 } else if (zbody) {
671 zval_ptr_dtor(&zbody);
672 }
673
674 if (SUCCESS != php_http_env_response_send_head(r)) {
675 return FAILURE;
676 }
677
678 if (SUCCESS != php_http_env_response_send_body(r)) {
679 return FAILURE;
680 }
681 return SUCCESS;
682 }
683
684 zend_class_entry *php_http_env_response_class_entry;
685
686 #undef PHP_HTTP_BEGIN_ARGS
687 #undef PHP_HTTP_EMPTY_ARGS
688 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpEnvResponse, method, 0, req_args)
689 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpEnvResponse, method, 0)
690 #define PHP_HTTP_ENV_RESPONSE_ME(method, visibility) PHP_ME(HttpEnvResponse, method, PHP_HTTP_ARGS(HttpEnvResponse, method), visibility)
691
692 PHP_HTTP_EMPTY_ARGS(__construct);
693
694 PHP_HTTP_BEGIN_ARGS(setContentType, 1)
695 PHP_HTTP_ARG_VAL(content_type, 0)
696 PHP_HTTP_END_ARGS;
697
698 PHP_HTTP_BEGIN_ARGS(setContentEncoding, 1)
699 PHP_HTTP_ARG_VAL(content_encoding, 0)
700 PHP_HTTP_END_ARGS;
701
702 PHP_HTTP_BEGIN_ARGS(setContentDisposition, 1)
703 PHP_HTTP_ARG_VAL(content_disposition, 0)
704 PHP_HTTP_ARG_VAL(filename, 0)
705 PHP_HTTP_END_ARGS;
706
707 PHP_HTTP_BEGIN_ARGS(setCacheControl, 1)
708 PHP_HTTP_ARG_VAL(cache_control, 0)
709 PHP_HTTP_END_ARGS;
710
711 PHP_HTTP_BEGIN_ARGS(setLastModified, 1)
712 PHP_HTTP_ARG_VAL(last_modified, 0)
713 PHP_HTTP_END_ARGS;
714
715 PHP_HTTP_BEGIN_ARGS(isCachedByLastModified, 0)
716 PHP_HTTP_ARG_VAL(header_name, 0)
717 PHP_HTTP_END_ARGS;
718
719 PHP_HTTP_BEGIN_ARGS(setEtag, 1)
720 PHP_HTTP_ARG_VAL(etag, 0)
721 PHP_HTTP_END_ARGS;
722
723 PHP_HTTP_BEGIN_ARGS(isCachedByEtag, 0)
724 PHP_HTTP_ARG_VAL(header_name, 0)
725 PHP_HTTP_END_ARGS;
726
727 PHP_HTTP_BEGIN_ARGS(setThrottleRate, 1)
728 PHP_HTTP_ARG_VAL(chunk_size, 0)
729 PHP_HTTP_ARG_VAL(delay, 0)
730 PHP_HTTP_END_ARGS;
731
732 PHP_HTTP_EMPTY_ARGS(send);
733
734
735 zend_function_entry php_http_env_response_method_entry[] = {
736 PHP_HTTP_ENV_RESPONSE_ME(__construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
737 PHP_HTTP_ENV_RESPONSE_ME(setContentType, ZEND_ACC_PUBLIC)
738 PHP_HTTP_ENV_RESPONSE_ME(setContentDisposition, ZEND_ACC_PUBLIC)
739 PHP_HTTP_ENV_RESPONSE_ME(setContentEncoding, ZEND_ACC_PUBLIC)
740 PHP_HTTP_ENV_RESPONSE_ME(setCacheControl, ZEND_ACC_PUBLIC)
741 PHP_HTTP_ENV_RESPONSE_ME(setLastModified, ZEND_ACC_PUBLIC)
742 PHP_HTTP_ENV_RESPONSE_ME(isCachedByLastModified, ZEND_ACC_PUBLIC)
743 PHP_HTTP_ENV_RESPONSE_ME(setEtag, ZEND_ACC_PUBLIC)
744 PHP_HTTP_ENV_RESPONSE_ME(isCachedByEtag, ZEND_ACC_PUBLIC)
745 PHP_HTTP_ENV_RESPONSE_ME(setThrottleRate, ZEND_ACC_PUBLIC)
746
747 PHP_HTTP_ENV_RESPONSE_ME(send, ZEND_ACC_PUBLIC)
748
749 EMPTY_FUNCTION_ENTRY
750 };
751
752
753 PHP_METHOD(HttpEnvResponse, __construct)
754 {
755 with_error_handling(EH_THROW, php_http_exception_class_entry) {
756 if (SUCCESS == zend_parse_parameters_none()) {
757 php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
758
759 with_error_handling(EH_THROW, php_http_exception_class_entry) {
760 obj->message = php_http_message_init_env(obj->message, PHP_HTTP_RESPONSE TSRMLS_CC);
761 } end_error_handling();
762 }
763 } end_error_handling();
764
765 }
766
767 PHP_METHOD(HttpEnvResponse, setContentType)
768 {
769 char *ct_str = NULL;
770 int ct_len = 0;
771
772 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &ct_str, &ct_len)) {
773 set_option(getThis(), ZEND_STRL("contentType"), IS_STRING, ct_str, ct_len TSRMLS_CC);
774 }
775 }
776
777 PHP_METHOD(HttpEnvResponse, setContentDisposition)
778 {
779 long cd;
780 char *file_str = NULL;
781 int file_len = 0;
782
783 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|s!", &cd, &file_str, &file_len)) {
784 zval *arr;
785
786 MAKE_STD_ZVAL(arr);
787 array_init(arr);
788 add_assoc_long_ex(arr, ZEND_STRS("disposition"), cd);
789 if (file_len) {
790 add_assoc_stringl_ex(arr, ZEND_STRS("filename"), file_str, file_len, 1);
791 }
792 zend_update_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("contentDisposition"), arr TSRMLS_CC);
793 zval_ptr_dtor(&arr);
794 }
795 }
796
797 PHP_METHOD(HttpEnvResponse, setContentEncoding)
798 {
799 long ce;
800
801 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ce)) {
802 set_option(getThis(), ZEND_STRL("contentEncoding"), IS_LONG, &ce, 0 TSRMLS_CC);
803 }
804 }
805
806 PHP_METHOD(HttpEnvResponse, setCacheControl)
807 {
808 char *cc_str = NULL;
809 int cc_len = 0;
810
811 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &cc_str, &cc_len)) {
812 set_option(getThis(), ZEND_STRL("cacheControl"), IS_STRING, cc_str, cc_len TSRMLS_CC);
813 }
814 }
815
816 PHP_METHOD(HttpEnvResponse, setLastModified)
817 {
818 long last_modified;
819
820 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &last_modified)) {
821 set_option(getThis(), ZEND_STRL("lastModified"), IS_LONG, &last_modified, 0 TSRMLS_CC);
822 }
823 }
824
825 PHP_METHOD(HttpEnvResponse, isCachedByLastModified)
826 {
827 char *header_name_str = NULL;
828 int header_name_len = 0;
829
830 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
831 if (!header_name_str || !header_name_len) {
832 header_name_str = "If-Modified-Since";
833 header_name_len = lenof("If-Modified-Since");
834 }
835 RETURN_LONG(php_http_env_is_response_cached_by_last_modified(getThis(), header_name_str, header_name_len TSRMLS_CC));
836 }
837 RETURN_FALSE;
838 }
839
840 PHP_METHOD(HttpEnvResponse, setEtag)
841 {
842 char *etag_str = NULL;
843 int etag_len = 0;
844
845 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &etag_str, &etag_len)) {
846 set_option(getThis(), ZEND_STRL("etag"), IS_STRING, etag_str, etag_len TSRMLS_CC);
847 }
848 }
849
850 PHP_METHOD(HttpEnvResponse, isCachedByEtag)
851 {
852 char *header_name_str = NULL;
853 int header_name_len = 0;
854
855 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &header_name_str, &header_name_len)) {
856 if (!header_name_str || !header_name_len) {
857 header_name_str = "If-None-Match";
858 header_name_len = lenof("If-None-Match");
859 }
860 RETURN_LONG(php_http_env_is_response_cached_by_etag(getThis(), header_name_str, header_name_len TSRMLS_CC));
861 }
862 RETURN_FALSE;
863 }
864
865 PHP_METHOD(HttpEnvResponse, setThrottleRate)
866 {
867 long chunk_size;
868 double delay = 1;
869
870 if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|d", &chunk_size, &delay)) {
871 long chunk_size_long = (long) chunk_size;
872
873 set_option(getThis(), ZEND_STRL("throttleDelay"), IS_DOUBLE, &delay, 0 TSRMLS_CC);
874 set_option(getThis(), ZEND_STRL("throttleChunk"), IS_LONG, &chunk_size_long, 0 TSRMLS_CC);
875 RETURN_TRUE;
876 }
877 RETURN_FALSE;
878 }
879
880 PHP_METHOD(HttpEnvResponse, send)
881 {
882 RETVAL_FALSE;
883
884 if (SUCCESS == zend_parse_parameters_none()) {
885 php_http_env_response_t *r = php_http_env_response_init(NULL, getThis() TSRMLS_CC);
886
887 if (r) {
888 RETVAL_SUCCESS(php_http_env_response_send(r));
889 }
890
891 php_http_env_response_free(&r);
892 }
893 }
894
895 PHP_MINIT_FUNCTION(http_env_response)
896 {
897 PHP_HTTP_REGISTER_CLASS(http\\Env, Response, http_env_response, php_http_message_class_entry, 0);
898
899 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_DISPOSITION_NONE"), PHP_HTTP_CONTENT_DISPOSITION_NONE TSRMLS_CC);
900 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_DISPOSITION_INLINE"), PHP_HTTP_CONTENT_DISPOSITION_INLINE TSRMLS_CC);
901 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_DISPOSITION_ATTACHMENT"), PHP_HTTP_CONTENT_DISPOSITION_ATTACHMENT TSRMLS_CC);
902
903 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_NONE"), PHP_HTTP_CONTENT_ENCODING_NONE TSRMLS_CC);
904 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CONTENT_ENCODING_GZIP"), PHP_HTTP_CONTENT_ENCODING_GZIP TSRMLS_CC);
905
906 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_NO"), PHP_HTTP_CACHE_NO TSRMLS_CC);
907 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_HIT"), PHP_HTTP_CACHE_HIT TSRMLS_CC);
908 zend_declare_class_constant_long(php_http_env_response_class_entry, ZEND_STRL("CACHE_MISS"), PHP_HTTP_CACHE_MISS TSRMLS_CC);
909
910 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentType"), ZEND_ACC_PROTECTED TSRMLS_CC);
911 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentDisposition"), ZEND_ACC_PROTECTED TSRMLS_CC);
912 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("contentEncoding"), ZEND_ACC_PROTECTED TSRMLS_CC);
913 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("cacheControl"), ZEND_ACC_PROTECTED TSRMLS_CC);
914 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("etag"), ZEND_ACC_PROTECTED TSRMLS_CC);
915 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("lastModified"), ZEND_ACC_PROTECTED TSRMLS_CC);
916 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleDelay"), ZEND_ACC_PROTECTED TSRMLS_CC);
917 zend_declare_property_null(php_http_env_response_class_entry, ZEND_STRL("throttleChunk"), ZEND_ACC_PROTECTED TSRMLS_CC);
918
919 return SUCCESS;
920 }
921
922
923 /*
924 * Local variables:
925 * tab-width: 4
926 * c-basic-offset: 4
927 * End:
928 * vim600: noet sw=4 ts=4 fdm=marker
929 * vim<600: noet sw=4 ts=4
930 */