2 +--------------------------------------------------------------------+
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 +--------------------------------------------------------------------+
13 #include "php_http_api.h"
15 #if PHP_HTTP_HAVE_NEON
17 #include "php_http_request.h"
19 #include <neon/ne_auth.h>
20 #include <neon/ne_compress.h>
21 #include <neon/ne_session.h>
22 #include <neon/ne_request.h>
23 #include <neon/ne_redirect.h>
25 typedef struct php_http_neon_auth
{
29 } php_http_neon_auth_t
;
31 typedef struct php_http_neon_request
{
32 php_http_message_body_t
*body
;
37 php_http_buffer_t headers
;
50 php_http_neon_auth_t proxy
;
51 php_http_neon_auth_t http
;
56 ne_ssl_client_cert
*clicert
;
57 ne_ssl_certificate
*trucert
;
62 ne_inet_addr
*interface
;
76 php_http_request_progress_t progress
;
78 } php_http_neon_request_t
;
82 static ssize_t
php_http_neon_read_callback(void *ctx
, char *buf
, size_t len
)
84 php_http_request_t
*h
= ctx
;
85 php_http_neon_request_t
*neon
= h
->ctx
;
86 php_http_message_body_t
*body
= neon
->body
;
89 TSRMLS_FETCH_FROM_CTX(body
->ts
);
92 size_t read
= php_stream_read(php_http_message_body_stream(body
), buf
, len
);
94 php_http_buffer_append(h
->buffer
, buf
, read
);
95 php_http_message_parser_parse(h
->parser
, h
->buffer
, 0, &h
->message
);
98 return php_stream_rewind(php_http_message_body_stream(body
));
104 static void php_http_neon_pre_send_callback(ne_request
*req
, void *ctx
, ne_buffer
*header
)
106 php_http_request_t
*h
= ctx
;
107 php_http_neon_request_t
*neon
= h
->ctx
;
109 ne_buffer_append(header
, neon
->options
.headers
.data
, neon
->options
.headers
.used
);
111 php_http_buffer_append(h
->buffer
, header
->data
, header
->used
- 1 /* ne_buffer counts \0 */);
112 php_http_buffer_appends(h
->buffer
, PHP_HTTP_CRLF
);
113 php_http_message_parser_parse(h
->parser
, h
->buffer
, 0, &h
->message
);
116 static void php_http_neon_post_headers_callback(ne_request
*req
, void *ctx
, const ne_status
*status
)
118 php_http_request_t
*h
= ctx
;
119 HashTable
*hdrs
= &h
->message
->hdrs
;
123 const char *name
, *value
;
124 TSRMLS_FETCH_FROM_CTX(h
->ts
);
126 php_http_info_init(&i TSRMLS_CC
);
127 i
.type
= PHP_HTTP_RESPONSE
;
128 php_http_version_init(&i
.http
.version
, status
->major_version
, status
->minor_version TSRMLS_CC
);
129 i
.http
.info
.response
.code
= status
->code
;
130 i
.http
.info
.response
.status
= estrdup(status
->reason_phrase
);
131 php_http_message_info_callback(&h
->message
, &hdrs
, &i TSRMLS_CC
);
132 php_http_info_dtor(&i
);
134 INIT_PZVAL_ARRAY(&tmp
, hdrs
);
135 while ((iter
= ne_response_header_iterate(req
, iter
, &name
, &value
))) {
136 char *key
= php_http_pretty_key(estrdup(name
), strlen(name
), 1, 1);
137 add_assoc_string(&tmp
, key
, estrdup(value
), 0);
140 php_http_message_parser_state_push(h
->parser
, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE
);
143 static int php_http_neon_ssl_verify_callback(void *ctx
, int failures
, const ne_ssl_certificate
*cert
)
145 php_http_request_t
*h
= ctx
;
146 php_http_neon_request_t
*neon
= h
->ctx
;
148 if (neon
->options
.ssl
.noverify
) {
154 static void php_http_neon_progress_callback(void *ctx
, ne_session_status status
, const ne_session_status_info
*info
)
156 php_http_request_t
*h
= ctx
;
157 php_http_neon_request_t
*neon
= h
->ctx
;
158 TSRMLS_FETCH_FROM_CTX(h
->ts
);
161 case ne_status_lookup
:
162 neon
->progress
.state
.info
= "resolve";
164 case ne_status_connecting
:
165 neon
->progress
.state
.info
= "connect";
167 case ne_status_connected
:
168 neon
->progress
.state
.info
= "connected";
170 case ne_status_sending
:
171 neon
->progress
.state
.info
= "send";
172 neon
->progress
.state
.ul
.total
= info
->sr
.total
;
173 neon
->progress
.state
.ul
.now
= info
->sr
.progress
;
175 case ne_status_recving
:
176 neon
->progress
.state
.info
= "receive";
177 neon
->progress
.state
.dl
.total
= info
->sr
.total
;
178 neon
->progress
.state
.dl
.now
= info
->sr
.progress
;
180 case ne_status_disconnected
:
181 neon
->progress
.state
.info
= "disconnected";
185 php_http_request_progress_notify(&neon
->progress TSRMLS_CC
);
190 static inline zval
*cache_option(HashTable
*cache
, char *key
, size_t keylen
, ulong h
, zval
*opt
)
195 zend_hash_quick_update(cache
, key
, keylen
, h
, &opt
, sizeof(zval
*), NULL
);
197 zend_hash_update(cache
, key
, keylen
, &opt
, sizeof(zval
*), NULL
);
203 static inline zval
*get_option(HashTable
*cache
, HashTable
*options
, char *key
, size_t keylen
, int type
)
207 ulong h
= zend_hash_func(key
, keylen
);
209 if (SUCCESS
== zend_hash_quick_find(options
, key
, keylen
, h
, (void *) &zoption
)) {
210 zval
*option
= php_http_ztyp(type
, *zoption
);
213 zval
*cached
= cache_option(cache
, key
, keylen
, h
, option
);
215 zval_ptr_dtor(&option
);
225 static STATUS
set_options(php_http_request_t
*h
, HashTable
*options
)
229 php_http_neon_request_t
*neon
= h
->ctx
;
230 TSRMLS_FETCH_FROM_CTX(h
->ts
);
233 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyhost"), IS_STRING
))) {
234 neon
->options
.proxy
.host
= Z_STRVAL_P(zoption
);
237 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyauth"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
238 char *colon
= strchr(Z_STRVAL_P(zoption
), ':');
241 STR_SET(neon
->options
.auth
.proxy
.user
, estrndup(Z_STRVAL_P(zoption
), colon
- Z_STRVAL_P(zoption
)));
242 STR_SET(neon
->options
.auth
.proxy
.pass
, estrdup(colon
+ 1));
244 STR_SET(neon
->options
.auth
.proxy
.user
, estrdup(Z_STRVAL_P(zoption
)));
248 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyauthtype"), IS_LONG
))) {
249 neon
->options
.auth
.proxy
.type
= Z_LVAL_P(zoption
);
251 neon
->options
.auth
.proxy
.type
= NE_AUTH_ALL
;
256 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyport"), IS_LONG
))) {
257 neon
->options
.proxy
.port
= Z_LVAL_P(zoption
);
259 neon
->options
.proxy
.port
= 0;
263 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxytype"), IS_LONG
))) {
264 neon
->options
.proxy
.type
= Z_LVAL_P(zoption
);
266 neon
->options
.proxy
.type
= -1;
270 /* outgoing interface */
271 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("interface"), IS_STRING
))) {
272 if (!(neon
->options
.interface
= ne_iaddr_parse(Z_STRVAL_P(zoption
), ne_iaddr_ipv4
))) {
273 neon
->options
.interface
= ne_iaddr_parse(Z_STRVAL_P(zoption
), ne_iaddr_ipv6
);
278 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("port"), IS_LONG
))) {
279 neon
->options
.port
= Z_LVAL_P(zoption
);
283 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("httpauth"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
284 char *colon
= strchr(Z_STRVAL_P(zoption
), ':');
287 STR_SET(neon
->options
.auth
.http
.user
, estrndup(Z_STRVAL_P(zoption
), colon
- Z_STRVAL_P(zoption
)));
288 STR_SET(neon
->options
.auth
.http
.pass
, estrdup(colon
+ 1));
290 STR_SET(neon
->options
.auth
.http
.user
, estrdup(Z_STRVAL_P(zoption
)));
293 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("httpauthtype"), IS_LONG
))) {
294 neon
->options
.auth
.http
.type
= Z_LVAL_P(zoption
);
296 neon
->options
.auth
.http
.type
= NE_AUTH_ALL
;
299 /* redirects, defaults to 0 */
300 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("redirect"), IS_LONG
))) {
301 neon
->options
.redirects
= Z_LVAL_P(zoption
);
303 neon
->options
.redirects
= 0;
306 /* retries, defaults to 0 */
307 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("retrycount"), IS_LONG
))) {
308 neon
->options
.retry
.count
= Z_LVAL_P(zoption
);
309 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("retrydelay"), IS_DOUBLE
))) {
310 neon
->options
.retry
.delay
= Z_DVAL_P(zoption
);
312 neon
->options
.retry
.delay
= 0;
315 neon
->options
.retry
.count
= 0;
319 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("referer"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
320 neon
->options
.referer
= Z_STRVAL_P(zoption
);
323 /* useragent, default "PECL::HTTP/version (PHP/version)" */
324 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("useragent"), IS_STRING
))) {
325 /* allow to send no user agent, not even default one */
326 if (Z_STRLEN_P(zoption
)) {
327 neon
->options
.useragent
= Z_STRVAL_P(zoption
);
329 neon
->options
.useragent
= NULL
;
334 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("resume"), IS_LONG
)) && (Z_LVAL_P(zoption
) > 0)) {
335 php_http_buffer_appendf(&neon
->options
.headers
, "Range: bytes=%ld-" PHP_HTTP_CRLF
, Z_LVAL_P(zoption
));
338 /* or range of kind array(array(0,499), array(100,1499)) */
339 else if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("range"), IS_ARRAY
)) && zend_hash_num_elements(Z_ARRVAL_P(zoption
))) {
340 HashPosition pos1
, pos2
;
341 zval
**rr
, **rb
, **re
;
342 php_http_buffer_t rs
;
344 php_http_buffer_init(&rs
);
345 FOREACH_VAL(pos1
, zoption
, rr
) {
346 if (Z_TYPE_PP(rr
) == IS_ARRAY
) {
347 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(rr
), &pos2
);
348 if (SUCCESS
== zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr
), (void *) &rb
, &pos2
)) {
349 zend_hash_move_forward_ex(Z_ARRVAL_PP(rr
), &pos2
);
350 if (SUCCESS
== zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr
), (void *) &re
, &pos2
)) {
351 if ( ((Z_TYPE_PP(rb
) == IS_LONG
) || ((Z_TYPE_PP(rb
) == IS_STRING
) && is_numeric_string(Z_STRVAL_PP(rb
), Z_STRLEN_PP(rb
), NULL
, NULL
, 1))) &&
352 ((Z_TYPE_PP(re
) == IS_LONG
) || ((Z_TYPE_PP(re
) == IS_STRING
) && is_numeric_string(Z_STRVAL_PP(re
), Z_STRLEN_PP(re
), NULL
, NULL
, 1)))) {
353 zval
*rbl
= php_http_ztyp(IS_LONG
, *rb
);
354 zval
*rel
= php_http_ztyp(IS_LONG
, *re
);
356 if ((Z_LVAL_P(rbl
) >= 0) && (Z_LVAL_P(rel
) >= 0)) {
357 php_http_buffer_appendf(&rs
, "%ld-%ld,", Z_LVAL_P(rbl
), Z_LVAL_P(rel
));
367 if (PHP_HTTP_BUFFER_LEN(&rs
)) {
368 /* ignore last comma */
369 int used
= rs
.used
> INT_MAX
? INT_MAX
: rs
.used
;
370 php_http_buffer_appendf(&neon
->options
.headers
, "Range: bytes=%.*s" PHP_HTTP_CRLF
, used
- 1, rs
.data
);
373 php_http_buffer_dtor(&rs
);
376 /* additional headers, array('name' => 'value') */
377 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("headers"), IS_ARRAY
))) {
378 php_http_array_hashkey_t header_key
= php_http_array_hashkey_init(0);
382 FOREACH_KEYVAL(pos
, zoption
, header_key
, header_val
) {
383 if (header_key
.type
== HASH_KEY_IS_STRING
) {
384 zval
*header_cpy
= php_http_ztyp(IS_STRING
, *header_val
);
386 if (!strcasecmp(header_key
.str
, "range")) {
389 php_http_buffer_appendf(&neon
->options
.headers
, "%s: %s" PHP_HTTP_CRLF
, header_key
.str
, Z_STRVAL_P(header_cpy
));
390 zval_ptr_dtor(&header_cpy
);
395 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("etag"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
396 php_http_buffer_appends(&neon
->options
.headers
, "If-");
398 php_http_buffer_appends(&neon
->options
.headers
, "None-");
400 php_http_buffer_appends(&neon
->options
.headers
, "Match: ");
402 if ((Z_STRVAL_P(zoption
)[0] == '"') && (Z_STRVAL_P(zoption
)[Z_STRLEN_P(zoption
)-1] == '"')) {
403 php_http_buffer_appendl(&neon
->options
.headers
, Z_STRVAL_P(zoption
));
405 php_http_buffer_appendf(&neon
->options
.headers
, "\"%s\"" PHP_HTTP_CRLF
, Z_STRVAL_P(zoption
));
409 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("compress"), IS_BOOL
)) && Z_LVAL_P(zoption
)) {
410 php_http_buffer_appends(&neon
->options
.headers
, "Accept-Encoding: gzip;q=1.0,deflate;q=0.5" PHP_HTTP_CRLF
);
414 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("lastmodified"), IS_LONG
))) {
415 if (Z_LVAL_P(zoption
)) {
416 time_t time
= Z_LVAL_P(zoption
) > 0 ? Z_LVAL_P(zoption
) : PHP_HTTP_G
->env
.request
.time
+ Z_LVAL_P(zoption
);
417 char *date
= php_format_date(ZEND_STRS(PHP_HTTP_DATE_FORMAT
), time
, 0 TSRMLS_CC
);
419 php_http_buffer_appends(&neon
->options
.headers
, "If-");
421 php_http_buffer_appends(&neon
->options
.headers
, "Un");
423 php_http_buffer_appendf(&neon
->options
.headers
, "Modified-Since: %s" PHP_HTTP_CRLF
, date
);
428 /* cookies, array('name' => 'value') */
429 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("cookies"), IS_ARRAY
))) {
430 php_http_buffer_t cookies
;
432 php_http_buffer_init(&cookies
);
433 if (zend_hash_num_elements(Z_ARRVAL_P(zoption
))) {
434 zval
*urlenc_cookies
= NULL
;
436 php_http_buffer_appends(&neon
->options
.headers
, "Cookie: ");
438 /* check whether cookies should not be urlencoded; default is to urlencode them */
439 if ((!(urlenc_cookies
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("encodecookies"), IS_BOOL
))) || Z_BVAL_P(urlenc_cookies
)) {
440 php_http_url_encode_hash_ex(HASH_OF(zoption
), &neon
->options
.headers
, ZEND_STRS("; "), ZEND_STRS("="), NULL
, 0 TSRMLS_CC
);
443 php_http_array_hashkey_t cookie_key
= php_http_array_hashkey_init(0);
446 FOREACH_KEYVAL(pos
, zoption
, cookie_key
, cookie_val
) {
447 if (cookie_key
.type
== HASH_KEY_IS_STRING
) {
448 zval
*val
= php_http_ztyp(IS_STRING
, *cookie_val
);
449 php_http_buffer_appendf(&neon
->options
.headers
, "%s=%s; ", cookie_key
.str
, Z_STRVAL_P(val
));
455 neon
->options
.headers
.used
-= lenof("; ");
456 php_http_buffer_appends(&neon
->options
.headers
, PHP_HTTP_CRLF
);
458 /* cookiestore, read initial cookies from that file and store cookies back into that file */
459 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("cookiestore"), IS_STRING
))) {
460 neon
->options
.cookiestore
= Z_STRVAL_P(zoption
);
464 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("maxfilesize"), IS_LONG
))) {
465 neon
->options
.maxfilesize
= Z_LVAL_P(zoption
);
469 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("timeout"), IS_DOUBLE
))) {
470 neon
->options
.timeout
.read
= Z_DVAL_P(zoption
) > 0 && Z_DVAL_P(zoption
) < 1 ? 1 : round(Z_DVAL_P(zoption
));
472 /* connecttimeout, defaults to 0 */
473 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("connecttimeout"), IS_DOUBLE
))) {
474 neon
->options
.timeout
.connect
= Z_DVAL_P(zoption
) > 0 && Z_DVAL_P(zoption
) < 1 ? 1 : round(Z_DVAL_P(zoption
));
478 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("ssl"), IS_ARRAY
))) {
481 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("verifypeer"), (void *) &zssl
)) {
482 if (!i_zend_is_true(*zssl
)) {
483 neon
->options
.ssl
.noverify
= 1;
486 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("key"), (void *) &zssl
)) {
487 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
488 ne_ssl_client_cert
*cc
= ne_ssl_clicert_read(Z_STRVAL_P(cpy
));
491 if (ne_ssl_clicert_encrypted(cc
)) {
492 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("keypasswd"), (void *) &zssl
)) {
493 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
495 if (NE_OK
== ne_ssl_clicert_decrypt(cc
, Z_STRVAL_P(cpy
))) {
496 neon
->options
.ssl
.clicert
= cc
;
503 if (cc
&& !neon
->options
.ssl
.clicert
) {
504 ne_ssl_clicert_free(cc
);
509 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("cert"), (void *) &zssl
)) {
510 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
511 ne_ssl_certificate
*tc
= ne_ssl_cert_read(Z_STRVAL_P(cpy
));
514 neon
->options
.ssl
.trucert
= tc
;
523 /* request handler ops */
525 static STATUS
php_http_neon_request_reset(php_http_request_t
*h
);
527 static php_http_request_t
*php_http_neon_request_init(php_http_request_t
*h
, void *dummy
)
529 php_http_neon_request_t
*ctx
;
531 ctx
= ecalloc(1, sizeof(*ctx
));
532 php_http_buffer_init(&ctx
->options
.headers
);
533 zend_hash_init(&ctx
->options
.cache
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
539 static php_http_request_t
*php_http_neon_request_copy(php_http_request_t
*from
, php_http_request_t
*to
)
541 TSRMLS_FETCH_FROM_CTX(from
->ts
);
544 return php_http_neon_request_init(to
, NULL
);
546 return php_http_request_init(NULL
, from
->ops
, from
->rf
, NULL TSRMLS_CC
);
550 static void php_http_neon_request_dtor(php_http_request_t
*h
)
552 php_http_neon_request_t
*ctx
= h
->ctx
;
553 TSRMLS_FETCH_FROM_CTX(h
->ts
);
555 php_http_neon_request_reset(h
);
556 php_http_buffer_dtor(&ctx
->options
.headers
);
557 zend_hash_destroy(&ctx
->options
.cache
);
559 php_http_request_progress_dtor(&ctx
->progress TSRMLS_CC
);
565 static STATUS
php_http_neon_request_reset(php_http_request_t
*h
)
567 php_http_neon_request_t
*neon
= h
->ctx
;
568 TSRMLS_FETCH_FROM_CTX(h
->ts
);
570 php_http_buffer_reset(&neon
->options
.headers
);
571 STR_SET(neon
->options
.useragent
, NULL
);
572 STR_SET(neon
->options
.url
, NULL
);
573 neon
->options
.port
= 0;
574 neon
->options
.proxy
.type
= -1;
575 neon
->options
.proxy
.port
= 0;
576 STR_SET(neon
->options
.proxy
.host
, NULL
);
577 neon
->options
.auth
.proxy
.type
= 0;
578 STR_SET(neon
->options
.auth
.proxy
.user
, NULL
);
579 STR_SET(neon
->options
.auth
.proxy
.pass
, NULL
);
580 neon
->options
.auth
.http
.type
= 0;
581 STR_SET(neon
->options
.auth
.http
.user
, NULL
);
582 STR_SET(neon
->options
.auth
.http
.pass
, NULL
);
583 neon
->options
.ssl
.noverify
= 0;
584 if (neon
->options
.ssl
.clicert
) {
585 ne_ssl_clicert_free(neon
->options
.ssl
.clicert
);
586 neon
->options
.ssl
.clicert
= NULL
;
588 if (neon
->options
.ssl
.trucert
) {
589 ne_ssl_cert_free(neon
->options
.ssl
.trucert
);
590 neon
->options
.ssl
.trucert
= NULL
;
592 neon
->options
.redirects
= 0;
593 STR_SET(neon
->options
.cookiestore
, NULL
);
594 if (neon
->options
.interface
) {
595 ne_iaddr_free(neon
->options
.interface
);
596 neon
->options
.interface
= NULL
;
598 neon
->options
.maxfilesize
= 0;
599 neon
->options
.retry
.delay
= 0;
600 neon
->options
.retry
.count
= 0;
601 neon
->options
.timeout
.read
= 0;
602 neon
->options
.timeout
.connect
= 0;
604 php_http_request_progress_dtor(&neon
->progress TSRMLS_CC
);
609 static STATUS
php_http_neon_request_exec(php_http_request_t
*h
, php_http_request_method_t meth_id
, const char *url
, php_http_message_body_t
*body
)
612 STATUS retval
= SUCCESS
;
618 php_http_neon_request_t
*neon
= h
->ctx
;
619 TSRMLS_FETCH_FROM_CTX(h
->ts
);
621 if (!(meth
= php_http_request_method_name(meth_id TSRMLS_CC
))) {
622 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST_METHOD
, "Unsupported request method: %d (%s)", meth
, url
);
626 if (!(purl
= php_url_parse(url
))) {
627 php_http_error(HE_WARNING
, PHP_HTTP_E_URL
, "Could not parse url %s", url
);
631 if (neon
->options
.port
) {
632 purl
->port
= neon
->options
.port
;
633 } else if (!purl
->port
) {
635 if (strncasecmp(purl
->scheme
, "http", 4)) {
636 #ifdef HAVE_GETSERVBYNAME
639 if ((se
= getservbyname(purl
->scheme
, "tcp")) && se
->s_port
) {
640 purl
->port
= ntohs(se
->s_port
);
643 } else if (purl
->scheme
[4] == 's') {
648 /* never returns NULL */
649 session
= ne_session_create(purl
->scheme
, purl
->host
, purl
->port
);
650 if (neon
->options
.proxy
.host
) {
651 switch (neon
->options
.proxy
.type
) {
652 case NE_SOCK_SOCKSV4
:
653 case NE_SOCK_SOCKSV4A
:
654 case NE_SOCK_SOCKSV5
:
655 ne_session_socks_proxy(session
, neon
->options
.proxy
.type
, neon
->options
.proxy
.host
, neon
->options
.proxy
.port
, neon
->options
.auth
.proxy
.user
, neon
->options
.auth
.proxy
.pass
);
659 ne_session_proxy(session
, neon
->options
.proxy
.host
, neon
->options
.proxy
.port
);
663 if (neon
->options
.interface
) {
664 ne_set_localaddr(session
, neon
->options
.interface
);
666 if (neon
->options
.useragent
) {
667 ne_set_useragent(session
, neon
->options
.useragent
);
669 if (neon
->options
.timeout
.read
) {
670 ne_set_read_timeout(session
, neon
->options
.timeout
.read
);
672 if (neon
->options
.timeout
.read
) {
673 ne_set_connect_timeout(session
, neon
->options
.timeout
.connect
);
675 if (neon
->options
.redirects
) {
676 ne_redirect_register(session
);
678 ne_hook_pre_send(session
, php_http_neon_pre_send_callback
, h
);
679 ne_hook_post_headers(session
, php_http_neon_post_headers_callback
, h
);
680 ne_set_notifier(session
, php_http_neon_progress_callback
, h
);
681 ne_ssl_set_verify(session
, php_http_neon_ssl_verify_callback
, h
);
682 if (neon
->options
.ssl
.clicert
) {
683 ne_ssl_set_clicert(session
, neon
->options
.ssl
.clicert
);
686 ne_ssl_trust_default_ca(session); */
687 if (neon
->options
.ssl
.trucert
) {
688 ne_ssl_trust_cert(session
, neon
->options
.ssl
.trucert
);
691 request
= ne_request_create(session
, meth
, purl
->path
/* . purl->query */);
693 /* RFC2616, section 4.3 (para. 4) states that »a message-body MUST NOT be included in a request if the
694 * specification of the request method (section 5.1.1) does not allow sending an entity-body in request.«
695 * Following the clause in section 5.1.1 (para. 2) that request methods »MUST be implemented with the
696 * same semantics as those specified in section 9« reveal that not any single defined HTTP/1.1 method
697 * does not allow a request body.
702 ne_set_request_body_provider(request
, php_http_message_body_size(body
), php_http_neon_read_callback
, h
);
708 switch (result
= ne_begin_request(request
)) {
711 char *buf
= emalloc(0x1000);
713 while (0 < (len
= ne_read_response_block(request
, buf
, 0x1000))) {
714 php_http_buffer_append(h
->buffer
, buf
, len
);
715 php_http_message_parser_parse(h
->parser
, h
->buffer
, PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES
, &h
->message
);
716 // php_http_message_body_append(&h->message->body, buf, len);
724 if (neon
->options
.redirects
-- > 0){
725 const ne_uri
*uri
= ne_redirect_location(session
);
728 char *url
= ne_uri_unparse(uri
);
730 retval
= php_http_neon_request_exec(h
, meth_id
, url
, body
);
737 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "%s; (%s)", ne_get_error(session
), url
);
739 add_property_long(EG(exception
), "neonCode", result
);
745 switch (result
= ne_end_request(request
)) {
750 if (neon
->options
.retry
.count
> tries
++) {
751 if (neon
->options
.retry
.delay
>= PHP_HTTP_DIFFSEC
) {
752 php_http_sleep(neon
->options
.retry
.delay
);
759 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "%s; (%s)", ne_get_error(session
), url
);
761 add_property_long(EG(exception
), "neonCode", result
);
767 ne_session_destroy(session
);
773 static STATUS
php_http_neon_request_setopt(php_http_request_t
*h
, php_http_request_setopt_opt_t opt
, void *arg
)
775 php_http_neon_request_t
*neon
= h
->ctx
;
776 TSRMLS_FETCH_FROM_CTX(h
->ts
);
779 case PHP_HTTP_REQUEST_OPT_SETTINGS
:
780 return set_options(h
, arg
);
783 case PHP_HTTP_REQUEST_OPT_PROGRESS_CALLBACK
:
784 if (neon
->progress
.in_cb
) {
785 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "Cannot change progress callback while executing it");
788 if (neon
->progress
.callback
) {
789 php_http_request_progress_dtor(&neon
->progress TSRMLS_CC
);
791 neon
->progress
.callback
= arg
;
794 case PHP_HTTP_REQUEST_OPT_COOKIES_ENABLE
:
795 case PHP_HTTP_REQUEST_OPT_COOKIES_RESET
:
796 case PHP_HTTP_REQUEST_OPT_COOKIES_RESET_SESSION
:
797 case PHP_HTTP_REQUEST_OPT_COOKIES_FLUSH
:
808 static STATUS
php_http_neon_request_getopt(php_http_request_t
*h
, php_http_request_getopt_opt_t opt
, void *arg
)
810 php_http_neon_request_t
*neon
= h
->ctx
;
813 case PHP_HTTP_REQUEST_OPT_PROGRESS_INFO
:
814 *((php_http_request_progress_t
**) arg
) = &neon
->progress
;
817 case PHP_HTTP_REQUEST_OPT_TRANSFER_INFO
:
827 static php_http_resource_factory_ops_t php_http_neon_resource_factory_ops
= {
833 static php_http_request_ops_t php_http_neon_request_ops
= {
834 &php_http_neon_resource_factory_ops
,
835 php_http_neon_request_init
,
836 php_http_neon_request_copy
,
837 php_http_neon_request_dtor
,
838 php_http_neon_request_reset
,
839 php_http_neon_request_exec
,
840 php_http_neon_request_setopt
,
841 php_http_neon_request_getopt
844 PHP_HTTP_API php_http_request_ops_t
*php_http_neon_get_request_ops(void)
846 return &php_http_neon_request_ops
;
849 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpNEON, method, 0, req_args)
850 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpNEON, method, 0)
851 #define PHP_HTTP_NEON_ME(method, visibility) PHP_ME(HttpNEON, method, PHP_HTTP_ARGS(HttpNEON, method), visibility)
852 #define PHP_HTTP_NEON_ALIAS(method, func) PHP_HTTP_STATIC_ME_ALIAS(method, func, PHP_HTTP_ARGS(HttpNEON, method))
853 #define PHP_HTTP_NEON_MALIAS(me, al, vis) ZEND_FENTRY(me, ZEND_MN(HttpNEON_##al), PHP_HTTP_ARGS(HttpNEON, al), vis)
855 PHP_HTTP_EMPTY_ARGS(__construct
);
857 zend_class_entry
*php_http_neon_class_entry
;
858 zend_function_entry php_http_neon_method_entry
[] = {
859 PHP_HTTP_NEON_ME(__construct
, ZEND_ACC_PRIVATE
|ZEND_ACC_CTOR
)
864 PHP_METHOD(HttpNEON
, __construct
) {
868 PHP_MINIT_FUNCTION(http_neon
)
870 php_http_request_factory_driver_t driver
= {
871 &php_http_neon_request_ops
,
874 if (SUCCESS
!= php_http_request_factory_add_driver(ZEND_STRL("neon"), &driver
)) {
878 PHP_HTTP_REGISTER_CLASS(http
, NEON
, http_neon
, php_http_neon_class_entry
, 0);
883 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_BASIC"), NE_AUTH_BASIC TSRMLS_CC
);
884 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_DIGEST"), NE_AUTH_DIGEST TSRMLS_CC
);
885 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_NTLM"), NE_AUTH_NTLM TSRMLS_CC
);
886 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_GSSAPI"), NE_AUTH_GSSAPI TSRMLS_CC
);
887 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_GSSNEG"), NE_AUTH_NEGOTIATE TSRMLS_CC
);
888 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_ANY"), NE_AUTH_ALL TSRMLS_CC
);
891 * Proxy Type Constants
893 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS4"), NE_SOCK_SOCKSV4 TSRMLS_CC
);
894 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS4A"), NE_SOCK_SOCKSV4A TSRMLS_CC
);
895 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS5"), NE_SOCK_SOCKSV5 TSRMLS_CC
);
896 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_HTTP"), -1 TSRMLS_CC
);
898 if (NE_OK
!= ne_sock_init()) {
904 PHP_MSHUTDOWN_FUNCTION(http_neon
)
917 * vim600: noet sw=4 ts=4 fdm=marker
918 * vim<600: noet sw=4 ts=4