I, moron
[m6w6/ext-http] / http_message_api.c
1 /*
2
3 +----------------------------------------------------------------------+
4
5 | PECL :: http |
6
7 +----------------------------------------------------------------------+
8
9 | This source file is subject to version 3.0 of the PHP license, that |
10
11 | is bundled with this package in the file LICENSE, and is available |
12
13 | through the world-wide-web at http://www.php.net/license/3_0.txt. |
14
15 | If you did not receive a copy of the PHP license and are unable to |
16
17 | obtain it through the world-wide-web, please send a note to |
18
19 | license@php.net so we can mail you a copy immediately. |
20
21 +----------------------------------------------------------------------+
22
23 | Copyright (c) 2004-2005 Michael Wallner <mike@php.net> |
24
25 +----------------------------------------------------------------------+
26
27 */
28
29
30
31 /* $Id$ */
32
33
34
35 #ifdef HAVE_CONFIG_H
36
37 # include "config.h"
38
39 #endif
40
41
42
43 #include "php.h"
44
45 #include "php_http.h"
46
47 #include "php_http_std_defs.h"
48
49 #include "php_http_api.h"
50
51 #include "php_http_message_api.h"
52
53 #include "php_http_headers_api.h"
54
55 #include "php_http_send_api.h"
56
57 #include "php_http_request_api.h"
58
59 #include "php_http_url_api.h"
60
61
62
63 #include "phpstr/phpstr.h"
64
65
66
67 #define http_message_headers_cb _http_message_headers_cb
68
69 static void _http_message_headers_cb(const char *http_line, HashTable **headers, void **message TSRMLS_DC)
70
71 {
72
73 size_t line_length;
74
75 char *crlf = NULL;
76
77 http_message *new, *old = (http_message *) *message;
78
79
80
81 if (crlf = strstr(http_line, HTTP_CRLF)) {
82
83 line_length = crlf - http_line;
84
85 } else {
86
87 line_length = strlen(http_line);
88
89 }
90
91
92
93 if (old->type || zend_hash_num_elements(&old->hdrs) || PHPSTR_LEN(old)) {
94
95 new = http_message_new();
96
97
98
99 new->parent = old;
100
101 *message = new;
102
103 *headers = &new->hdrs;
104
105 } else {
106
107 new = old;
108
109 }
110
111
112
113 while (isspace(http_line[line_length-1])) --line_length;
114
115
116
117 // response
118
119 if (!strncmp(http_line, "HTTP/1.", lenof("HTTP/1."))) {
120
121 new->type = HTTP_MSG_RESPONSE;
122
123 new->info.response.http_version = atof(http_line + lenof("HTTP/"));
124
125 new->info.response.code = atoi(http_line + lenof("HTTP/1.1 "));
126
127 } else
128
129 // request
130
131 if (!strncmp(http_line + line_length - lenof("HTTP/1.1"), "HTTP/1.", lenof("HTTP/1."))) {
132
133 const char *method_sep_uri = strchr(http_line, ' ');
134
135 new->type = HTTP_MSG_REQUEST;
136
137 new->info.request.http_version = atof(http_line + line_length - lenof("1.1"));
138
139 new->info.request.method = estrndup(http_line, method_sep_uri - http_line);
140
141 new->info.request.URI = estrndup(method_sep_uri + 1, http_line + line_length - method_sep_uri - 1 - lenof(" HTTP/1.1"));
142
143 }
144
145 }
146
147
148
149 #define http_message_init_type _http_message_init_type
150
151 static inline void _http_message_init_type(http_message *message, http_message_type type)
152
153 {
154
155 switch (message->type = type)
156
157 {
158
159 case HTTP_MSG_RESPONSE:
160
161 message->info.response.http_version = .0;
162
163 message->info.response.code = 0;
164
165 break;
166
167
168
169 case HTTP_MSG_REQUEST:
170
171 message->info.request.http_version = .0;
172
173 message->info.request.method = NULL;
174
175 message->info.request.URI = NULL;
176
177 break;
178
179
180
181 case HTTP_MSG_NONE:
182
183 default:
184
185 break;
186
187 }
188
189 }
190
191
192
193 #define http_message_header(m, h) _http_message_header_ex((m), (h), sizeof(h))
194
195 #define http_message_header_ex _http_message_header_ex
196
197 static inline zval *_http_message_header_ex(http_message *msg, char *key_str, size_t key_len)
198
199 {
200
201 zval **header;
202
203 if (SUCCESS == zend_hash_find(&msg->hdrs, key_str, key_len, (void **) &header)) {
204
205 return *header;
206
207 }
208
209 return NULL;
210
211 }
212
213
214
215 PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_message_type type)
216
217 {
218
219 if (!message) {
220
221 message = ecalloc(1, sizeof(http_message));
222
223 }
224
225
226
227 http_message_init_type(message, type);
228
229 message->parent = NULL;
230
231 phpstr_init(&message->body);
232
233 zend_hash_init(&message->hdrs, 0, NULL, ZVAL_PTR_DTOR, 0);
234
235
236
237 return message;
238
239 }
240
241
242
243
244
245 PHP_HTTP_API void _http_message_set_type(http_message *message, http_message_type type)
246
247 {
248
249 /* just act if different */
250
251 if (type != message->type) {
252
253
254
255 /* free request info */
256
257 if (message->type == HTTP_MSG_REQUEST) {
258
259 if (message->info.request.method) {
260
261 efree(message->info.request.method);
262
263 }
264
265 if (message->info.request.URI) {
266
267 efree(message->info.request.URI);
268
269 }
270
271 }
272
273
274
275 /* init */
276
277 http_message_init_type(message, type);
278
279 }
280
281 }
282
283
284
285 PHP_HTTP_API http_message *_http_message_parse_ex(http_message *msg, const char *message, size_t message_length TSRMLS_DC)
286
287 {
288
289 char *body = NULL;
290
291 zend_bool free_msg = msg ? 0 : 1;
292
293
294
295 if (message_length < HTTP_MSG_MIN_SIZE) {
296
297 return NULL;
298
299 }
300
301
302
303 if (!message) {
304
305 return NULL;
306
307 }
308
309
310
311 msg = http_message_init(msg);
312
313
314
315 if (SUCCESS != http_parse_headers_cb(message, &msg->hdrs, 1, http_message_headers_cb, (void **) &msg)) {
316
317 if (free_msg) {
318
319 http_message_free(msg);
320
321 }
322
323 return NULL;
324
325 }
326
327
328
329 /* header parsing stops at CRLF CRLF */
330
331 if (body = strstr(message, HTTP_CRLF HTTP_CRLF)) {
332
333 zval *c;
334
335 const char *continue_at = NULL;
336
337
338
339 body += lenof(HTTP_CRLF HTTP_CRLF);
340
341
342
343 /* message has content-length header */
344
345 if (c = http_message_header(msg, "Content-Length")) {
346
347 long len = atol(Z_STRVAL_P(c));
348
349 phpstr_from_string_ex(PHPSTR(msg), body, len);
350
351 continue_at = body + len;
352
353 } else
354
355
356
357 /* message has chunked transfer encoding */
358
359 if (c = http_message_header(msg, "Transfer-Encoding")) {
360
361 if (!strcasecmp("chunked", Z_STRVAL_P(c))) {
362
363 char *decoded;
364
365 size_t decoded_len;
366
367
368
369 /* decode and replace Transfer-Encoding with Content-Length header */
370
371 if (continue_at = http_chunked_decode(body, message + message_length - body, &decoded, &decoded_len)) {
372
373 phpstr_from_string_ex(PHPSTR(msg), decoded, decoded_len);
374
375 efree(decoded);
376
377 {
378
379 zval *len;
380
381 char *tmp;
382
383
384
385 spprintf(&tmp, 0, "%lu", decoded_len);
386
387 MAKE_STD_ZVAL(len);
388
389 ZVAL_STRING(len, tmp, 0);
390
391
392
393 zend_hash_del(&msg->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"));
394
395 zend_hash_add(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void *) &len, sizeof(zval *), NULL);
396
397 }
398
399 }
400
401 }
402
403 } else
404
405
406
407 /* message has content-range header */
408
409 if (c = http_message_header(msg, "Content-Range")) {
410
411 ulong start = 0, end = 0;
412
413
414
415 sscanf(Z_STRVAL_P(c), "bytes=%lu-%lu", &start, &end);
416
417 if (end > start) {
418
419 phpstr_from_string_ex(PHPSTR(msg), body, (size_t) (end - start));
420
421 continue_at = body + (end - start);
422
423 }
424
425 } else
426
427
428
429 /* no headers that indicate content length */
430
431 if (1) {
432
433 phpstr_from_string_ex(PHPSTR(msg), body, message + message_length - body);
434
435 }
436
437
438
439 /* check for following messages */
440
441 if (continue_at) {
442
443 while (isspace(*continue_at)) ++continue_at;
444
445 if (continue_at < (message + message_length)) {
446
447 http_message *next = NULL, *most = NULL;
448
449
450
451 /* set current message to parent of most parent following messages and return deepest */
452
453 if (most = next = http_message_parse(continue_at, message + message_length - continue_at)) {
454
455 while (most->parent) most = most->parent;
456
457 most->parent = msg;
458
459 msg = next;
460
461 }
462
463 }
464
465 }
466
467 }
468
469
470
471 return msg;
472
473 }
474
475
476
477 PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length)
478
479 {
480
481 phpstr str;
482
483 char *key, *data;
484
485 ulong idx;
486
487 zval **header;
488
489
490
491 phpstr_init_ex(&str, 4096, 0);
492
493
494
495 switch (msg->type)
496
497 {
498
499 case HTTP_MSG_REQUEST:
500
501 phpstr_appendf(&str, "%s %s HTTP/%1.1f" HTTP_CRLF,
502
503 msg->info.request.method,
504
505 msg->info.request.URI,
506
507 msg->info.request.http_version);
508
509 break;
510
511
512
513 case HTTP_MSG_RESPONSE:
514
515 phpstr_appendf(&str, "HTTP/%1.1f %d" HTTP_CRLF,
516
517 msg->info.response.http_version,
518
519 msg->info.response.code);
520
521 break;
522
523
524
525 case HTTP_MSG_NONE:
526
527 default:
528
529 break;
530
531 }
532
533
534
535 FOREACH_HASH_KEYVAL(&msg->hdrs, key, idx, header) {
536
537 if (key) {
538
539 zval **single_header;
540
541
542
543 switch (Z_TYPE_PP(header))
544
545 {
546
547 case IS_STRING:
548
549 phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(header));
550
551 break;
552
553
554
555 case IS_ARRAY:
556
557 FOREACH_VAL(*header, single_header) {
558
559 phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(single_header));
560
561 }
562
563 break;
564
565 }
566
567
568
569 key = NULL;
570
571 }
572
573 }
574
575
576
577 if (PHPSTR_LEN(msg)) {
578
579 phpstr_appends(&str, HTTP_CRLF);
580
581 phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg));
582
583 phpstr_appends(&str, HTTP_CRLF);
584
585 }
586
587
588
589 data = phpstr_data(&str, string, length);
590
591 if (!string) {
592
593 efree(data);
594
595 }
596
597
598
599 phpstr_dtor(&str);
600
601 }
602
603
604
605 PHP_HTTP_API void _http_message_serialize(http_message *message, char **string, size_t *length)
606
607 {
608
609 char *buf;
610
611 size_t len;
612
613 phpstr str;
614
615
616
617 phpstr_init(&str);
618
619
620
621 do {
622
623 http_message_tostring(message, &buf, &len);
624
625 phpstr_prepend(&str, buf, len);
626
627 efree(buf);
628
629 } while (message = message->parent);
630
631
632
633 buf = phpstr_data(&str, string, length);
634
635 if (!string) {
636
637 efree(buf);
638
639 }
640
641
642
643 phpstr_dtor(&str);
644
645 }
646
647
648
649 PHP_HTTP_API STATUS _http_message_send(http_message *message TSRMLS_DC)
650
651 {
652
653 STATUS rs = FAILURE;
654
655
656
657 switch (message->type)
658
659 {
660
661 case HTTP_MSG_RESPONSE:
662
663 {
664
665 char *key;
666
667 ulong idx;
668
669 zval **val;
670
671
672
673 FOREACH_HASH_KEYVAL(&message->hdrs, key, idx, val) {
674
675 if (key) {
676
677 char *header;
678
679 spprintf(&header, 0, "%s: %s", key, Z_STRVAL_PP(val));
680
681 http_send_header(header);
682
683 efree(header);
684
685 key = NULL;
686
687 }
688
689 }
690
691 rs = SUCCESS == http_send_status(message->info.response.code) &&
692
693 SUCCESS == http_send_data(PHPSTR_VAL(message), PHPSTR_LEN(message)) ?
694
695 SUCCESS : FAILURE;
696
697 }
698
699 break;
700
701
702
703 case HTTP_MSG_REQUEST:
704
705 {
706
707 #ifdef HTTP_HAVE_CURL
708
709 char *uri = NULL;
710
711 zval **zhost, options, headers;
712
713
714
715 array_init(&options);
716
717 array_init(&headers);
718
719 zend_hash_copy(Z_ARRVAL(headers), &message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
720
721 add_assoc_zval(&options, "headers", &headers);
722
723
724
725 /* check host header */
726
727 if (SUCCESS == zend_hash_find(&message->hdrs, "Host", sizeof("Host"), (void **) &zhost)) {
728
729 char *colon = NULL, *host = NULL;
730
731 size_t host_len = 0;
732
733 int port = 0;
734
735
736
737 /* check for port */
738
739 if (colon = strchr(Z_STRVAL_PP(zhost), ':')) {
740
741 port = atoi(colon + 1);
742
743 host = estrndup(Z_STRVAL_PP(zhost), host_len = (Z_STRVAL_PP(zhost) - colon - 1));
744
745 } else {
746
747 host = estrndup(Z_STRVAL_PP(zhost), host_len = Z_STRLEN_PP(zhost));
748
749 }
750
751 uri = http_absolute_uri_ex(
752
753 message->info.request.URI, strlen(message->info.request.URI),
754
755 NULL, 0, host, host_len, port);
756
757 efree(host);
758
759 } else {
760
761 uri = http_absolute_uri(message->info.request.URI);
762
763 }
764
765
766
767 if (!strcasecmp("POST", message->info.request.method)) {
768
769 http_request_body body = {HTTP_REQUEST_BODY_CSTRING, PHPSTR_VAL(message), PHPSTR_LEN(message)};
770
771 rs = http_post(uri, &body, Z_ARRVAL(options), NULL, NULL);
772
773 } else
774
775 if (!strcasecmp("GET", message->info.request.method)) {
776
777 rs = http_get(uri, Z_ARRVAL(options), NULL, NULL);
778
779 } else
780
781 if (!strcasecmp("HEAD", message->info.request.method)) {
782
783 rs = http_head(uri, Z_ARRVAL(options), NULL, NULL);
784
785 } else {
786
787 http_error_ex(E_WARNING, HTTP_E_MSG,
788
789 "Cannot send HttpMessage. Request method %s not supported",
790
791 message->info.request.method);
792
793 }
794
795
796
797 efree(uri);
798
799 #else
800
801 http_error(E_WARNING, HTTP_E_MSG, "HTTP requests not supported - ext/http was not linked against libcurl.");
802
803 #endif
804
805 }
806
807 break;
808
809
810
811 case HTTP_MSG_NONE:
812
813 default:
814
815 http_error(E_WARNING, HTTP_E_MSG, "HttpMessage is neither of type HTTP_MSG_REQUEST nor HTTP_MSG_RESPONSE");
816
817 break;
818
819 }
820
821
822
823 return rs;
824
825 }
826
827
828
829 PHP_HTTP_API void _http_message_dtor(http_message *message)
830
831 {
832
833 if (message) {
834
835 zend_hash_destroy(&message->hdrs);
836
837 phpstr_dtor(PHPSTR(message));
838
839 if (HTTP_MSG_TYPE(REQUEST, message)) {
840
841 if (message->info.request.method) {
842
843 efree(message->info.request.method);
844
845 message->info.request.method = NULL;
846
847 }
848
849 if (message->info.request.URI) {
850
851 efree(message->info.request.URI);
852
853 message->info.request.URI = NULL;
854
855 }
856
857 }
858
859 }
860
861 }
862
863
864
865 PHP_HTTP_API void _http_message_free(http_message *message)
866
867 {
868
869 if (message) {
870
871 if (message->parent) {
872
873 http_message_free(message->parent);
874
875 message->parent = NULL;
876
877 }
878
879 http_message_dtor(message);
880
881 efree(message);
882
883 }
884
885 }
886
887
888
889 /*
890
891 * Local variables:
892
893 * tab-width: 4
894
895 * c-basic-offset: 4
896
897 * End:
898
899 * vim600: noet sw=4 ts=4 fdm=marker
900
901 * vim<600: noet sw=4 ts=4
902
903 */
904