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