3 #include "php_http_request.h"
5 #include <ext/date/php_date.h>
7 #include <neon/ne_auth.h>
8 #include <neon/ne_compress.h>
9 #include <neon/ne_session.h>
10 #include <neon/ne_request.h>
11 #include <neon/ne_redirect.h>
13 typedef struct php_http_neon_auth
{
17 } php_http_neon_auth_t
;
19 typedef struct php_http_neon_request
{
20 php_http_message_body_t
*body
;
25 php_http_buffer_t headers
;
38 php_http_neon_auth_t proxy
;
39 php_http_neon_auth_t http
;
44 ne_ssl_client_cert
*clicert
;
45 ne_ssl_certificate
*trucert
;
50 ne_inet_addr
*interface
;
64 php_http_request_progress_t progress
;
66 } php_http_neon_request_t
;
70 static ssize_t
php_http_neon_read_callback(void *ctx
, char *buf
, size_t len
)
72 php_http_request_t
*h
= ctx
;
73 php_http_neon_request_t
*neon
= h
->ctx
;
74 php_http_message_body_t
*body
= neon
->body
;
77 TSRMLS_FETCH_FROM_CTX(body
->ts
);
80 size_t read
= php_stream_read(php_http_message_body_stream(body
), buf
, len
);
82 php_http_buffer_append(h
->buffer
, buf
, read
);
83 php_http_message_parser_parse(h
->parser
, h
->buffer
, 0, &h
->message
);
86 return php_stream_rewind(php_http_message_body_stream(body
));
92 static void php_http_neon_pre_send_callback(ne_request
*req
, void *ctx
, ne_buffer
*header
)
94 php_http_request_t
*h
= ctx
;
95 php_http_neon_request_t
*neon
= h
->ctx
;
97 ne_buffer_append(header
, neon
->options
.headers
.data
, neon
->options
.headers
.used
);
99 php_http_buffer_append(h
->buffer
, header
->data
, header
->used
- 1 /* ne_buffer counts \0 */);
100 php_http_buffer_appends(h
->buffer
, PHP_HTTP_CRLF
);
101 php_http_message_parser_parse(h
->parser
, h
->buffer
, 0, &h
->message
);
104 static void php_http_neon_post_headers_callback(ne_request
*req
, void *ctx
, const ne_status
*status
)
106 php_http_request_t
*h
= ctx
;
107 HashTable
*hdrs
= &h
->message
->hdrs
;
111 const char *name
, *value
;
112 TSRMLS_FETCH_FROM_CTX(h
->ts
);
114 php_http_info_init(&i TSRMLS_CC
);
115 i
.type
= PHP_HTTP_RESPONSE
;
116 php_http_version_init(&i
.http
.version
, status
->major_version
, status
->minor_version TSRMLS_CC
);
117 i
.http
.info
.response
.code
= status
->code
;
118 i
.http
.info
.response
.status
= estrdup(status
->reason_phrase
);
119 php_http_message_info_callback(&h
->message
, &hdrs
, &i TSRMLS_CC
);
120 php_http_info_dtor(&i
);
122 INIT_PZVAL_ARRAY(&tmp
, hdrs
);
123 while ((iter
= ne_response_header_iterate(req
, iter
, &name
, &value
))) {
124 char *key
= php_http_pretty_key(estrdup(name
), strlen(name
), 1, 1);
125 add_assoc_string(&tmp
, key
, estrdup(value
), 0);
128 php_http_message_parser_state_push(h
->parser
, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE
);
131 static int php_http_neon_ssl_verify_callback(void *ctx
, int failures
, const ne_ssl_certificate
*cert
)
133 php_http_request_t
*h
= ctx
;
134 php_http_neon_request_t
*neon
= h
->ctx
;
136 if (neon
->options
.ssl
.noverify
) {
142 static void php_http_neon_progress_callback(void *ctx
, ne_session_status status
, const ne_session_status_info
*info
)
144 php_http_request_t
*h
= ctx
;
145 php_http_neon_request_t
*neon
= h
->ctx
;
148 case ne_status_lookup
:
150 case ne_status_connecting
:
152 case ne_status_connected
:
154 case ne_status_sending
:
155 neon
->progress
.state
.ul
.total
= info
->sr
.total
;
156 neon
->progress
.state
.ul
.now
= info
->sr
.progress
;
158 case ne_status_recving
:
159 neon
->progress
.state
.dl
.total
= info
->sr
.total
;
160 neon
->progress
.state
.dl
.now
= info
->sr
.progress
;
162 case ne_status_disconnected
:
166 if (neon
->progress
.callback
) {
168 TSRMLS_FETCH_FROM_CTX(h
->ts
);
173 with_error_handling(EH_NORMAL
, NULL
) {
174 if (neon
->progress
.pass_state
) {
177 MAKE_STD_ZVAL(param
);
179 add_assoc_double(param
, "dltotal", neon
->progress
.state
.dl
.total
);
180 add_assoc_double(param
, "dlnow", neon
->progress
.state
.dl
.now
);
181 add_assoc_double(param
, "ultotal", neon
->progress
.state
.ul
.total
);
182 add_assoc_double(param
, "ulnow", neon
->progress
.state
.ul
.now
);
184 neon
->progress
.in_cb
= 1;
185 call_user_function(EG(function_table
), NULL
, neon
->progress
.callback
, &retval
, 1, ¶m TSRMLS_CC
);
186 neon
->progress
.in_cb
= 0;
188 zval_ptr_dtor(¶m
);
190 neon
->progress
.in_cb
= 1;
191 call_user_function(EG(function_table
), NULL
, neon
->progress
.callback
, &retval
, 0, NULL TSRMLS_CC
);
192 neon
->progress
.in_cb
= 0;
194 } end_error_handling();
202 static inline zval
*cache_option(HashTable
*cache
, char *key
, size_t keylen
, ulong h
, zval
*opt
)
207 zend_hash_quick_update(cache
, key
, keylen
, h
, &opt
, sizeof(zval
*), NULL
);
209 zend_hash_update(cache
, key
, keylen
, &opt
, sizeof(zval
*), NULL
);
215 static inline zval
*get_option(HashTable
*cache
, HashTable
*options
, char *key
, size_t keylen
, int type
)
219 ulong h
= zend_hash_func(key
, keylen
);
221 if (SUCCESS
== zend_hash_quick_find(options
, key
, keylen
, h
, (void *) &zoption
)) {
222 zval
*option
= php_http_zsep(type
, *zoption
);
225 zval
*cached
= cache_option(cache
, key
, keylen
, h
, option
);
227 zval_ptr_dtor(&option
);
237 static STATUS
set_options(php_http_request_t
*h
, HashTable
*options
)
241 php_http_neon_request_t
*neon
= h
->ctx
;
244 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyhost"), IS_STRING
))) {
245 neon
->options
.proxy
.host
= Z_STRVAL_P(zoption
);
248 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyauth"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
249 char *colon
= strchr(Z_STRVAL_P(zoption
), ':');
252 STR_SET(neon
->options
.auth
.proxy
.user
, estrndup(Z_STRVAL_P(zoption
), colon
- Z_STRVAL_P(zoption
)));
253 STR_SET(neon
->options
.auth
.proxy
.pass
, estrdup(colon
+ 1));
255 STR_SET(neon
->options
.auth
.proxy
.user
, estrdup(Z_STRVAL_P(zoption
)));
259 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyauthtype"), IS_LONG
))) {
260 neon
->options
.auth
.proxy
.type
= Z_LVAL_P(zoption
);
262 neon
->options
.auth
.proxy
.type
= NE_AUTH_ALL
;
267 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyport"), IS_LONG
))) {
268 neon
->options
.proxy
.port
= Z_LVAL_P(zoption
);
270 neon
->options
.proxy
.port
= 0;
274 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxytype"), IS_LONG
))) {
275 neon
->options
.proxy
.type
= Z_LVAL_P(zoption
);
277 neon
->options
.proxy
.type
= -1;
281 /* outgoing interface */
282 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("interface"), IS_STRING
))) {
283 if (!(neon
->options
.interface
= ne_iaddr_parse(Z_STRVAL_P(zoption
), ne_iaddr_ipv4
))) {
284 neon
->options
.interface
= ne_iaddr_parse(Z_STRVAL_P(zoption
), ne_iaddr_ipv6
);
289 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("port"), IS_LONG
))) {
290 neon
->options
.port
= Z_LVAL_P(zoption
);
294 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("httpauth"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
295 char *colon
= strchr(Z_STRVAL_P(zoption
), ':');
298 STR_SET(neon
->options
.auth
.http
.user
, estrndup(Z_STRVAL_P(zoption
), colon
- Z_STRVAL_P(zoption
)));
299 STR_SET(neon
->options
.auth
.http
.pass
, estrdup(colon
+ 1));
301 STR_SET(neon
->options
.auth
.http
.user
, estrdup(Z_STRVAL_P(zoption
)));
304 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("httpauthtype"), IS_LONG
))) {
305 neon
->options
.auth
.http
.type
= Z_LVAL_P(zoption
);
307 neon
->options
.auth
.http
.type
= NE_AUTH_ALL
;
310 /* redirects, defaults to 0 */
311 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("redirect"), IS_LONG
))) {
312 neon
->options
.redirects
= Z_LVAL_P(zoption
);
314 neon
->options
.redirects
= 0;
317 /* retries, defaults to 0 */
318 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("retrycount"), IS_LONG
))) {
319 neon
->options
.retry
.count
= Z_LVAL_P(zoption
);
320 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("retrydelay"), IS_DOUBLE
))) {
321 neon
->options
.retry
.delay
= Z_DVAL_P(zoption
);
323 neon
->options
.retry
.delay
= 0;
326 neon
->options
.retry
.count
= 0;
330 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("referer"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
331 neon
->options
.referer
= Z_STRVAL_P(zoption
);
334 /* useragent, default "PECL::HTTP/version (PHP/version)" */
335 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("useragent"), IS_STRING
))) {
336 /* allow to send no user agent, not even default one */
337 if (Z_STRLEN_P(zoption
)) {
338 neon
->options
.useragent
= Z_STRVAL_P(zoption
);
340 neon
->options
.useragent
= NULL
;
345 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("resume"), IS_LONG
)) && (Z_LVAL_P(zoption
) > 0)) {
346 php_http_buffer_appendf(&neon
->options
.headers
, "Range: bytes=%ld-" PHP_HTTP_CRLF
, Z_LVAL_P(zoption
));
349 /* or range of kind array(array(0,499), array(100,1499)) */
350 else if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("range"), IS_ARRAY
)) && zend_hash_num_elements(Z_ARRVAL_P(zoption
))) {
351 HashPosition pos1
, pos2
;
352 zval
**rr
, **rb
, **re
;
353 php_http_buffer_t rs
;
355 php_http_buffer_init(&rs
);
356 FOREACH_VAL(pos1
, zoption
, rr
) {
357 if (Z_TYPE_PP(rr
) == IS_ARRAY
) {
358 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(rr
), &pos2
);
359 if (SUCCESS
== zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr
), (void *) &rb
, &pos2
)) {
360 zend_hash_move_forward_ex(Z_ARRVAL_PP(rr
), &pos2
);
361 if (SUCCESS
== zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr
), (void *) &re
, &pos2
)) {
362 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))) &&
363 ((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)))) {
364 zval
*rbl
= php_http_zsep(IS_LONG
, *rb
);
365 zval
*rel
= php_http_zsep(IS_LONG
, *re
);
367 if ((Z_LVAL_P(rbl
) >= 0) && (Z_LVAL_P(rel
) >= 0)) {
368 php_http_buffer_appendf(&rs
, "%ld-%ld,", Z_LVAL_P(rbl
), Z_LVAL_P(rel
));
378 if (PHP_HTTP_BUFFER_LEN(&rs
)) {
379 /* ignore last comma */
380 php_http_buffer_appendf(&neon
->options
.headers
, "Range: bytes=%.*s" PHP_HTTP_CRLF
, rs
.used
- 1, rs
.data
);
383 php_http_buffer_dtor(&rs
);
386 /* additional headers, array('name' => 'value') */
387 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("headers"), IS_ARRAY
))) {
388 php_http_array_hashkey_t header_key
= php_http_array_hashkey_init(0);
392 FOREACH_KEYVAL(pos
, zoption
, header_key
, header_val
) {
393 if (header_key
.type
== HASH_KEY_IS_STRING
) {
394 zval
*header_cpy
= php_http_zsep(IS_STRING
, *header_val
);
396 if (!strcasecmp(header_key
.str
, "range")) {
399 php_http_buffer_appendf(&neon
->options
.headers
, "%s: %s" PHP_HTTP_CRLF
, header_key
.str
, Z_STRVAL_P(header_cpy
));
400 zval_ptr_dtor(&header_cpy
);
405 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("etag"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
406 php_http_buffer_appends(&neon
->options
.headers
, "If-");
408 php_http_buffer_appends(&neon
->options
.headers
, "None-");
410 php_http_buffer_appends(&neon
->options
.headers
, "Match: ");
412 if ((Z_STRVAL_P(zoption
)[0] == '"') && (Z_STRVAL_P(zoption
)[Z_STRLEN_P(zoption
)-1] == '"')) {
413 php_http_buffer_appendl(&neon
->options
.headers
, Z_STRVAL_P(zoption
));
415 php_http_buffer_appendf(&neon
->options
.headers
, "\"%s\"" PHP_HTTP_CRLF
, Z_STRVAL_P(zoption
));
419 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("compress"), IS_BOOL
)) && Z_LVAL_P(zoption
)) {
420 php_http_buffer_appends(&neon
->options
.headers
, "Accept-Encoding: gzip;q=1.0,deflate;q=0.5" PHP_HTTP_CRLF
);
424 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("lastmodified"), IS_LONG
))) {
425 if (Z_LVAL_P(zoption
)) {
426 time_t time
= Z_LVAL_P(zoption
) > 0 ? Z_LVAL_P(zoption
) : PHP_HTTP_G
->env
.request
.time
+ Z_LVAL_P(zoption
);
427 char *date
= php_format_date(ZEND_STRS(PHP_HTTP_DATE_FORMAT
), time
, 0 TSRMLS_CC
);
429 php_http_buffer_appends(&neon
->options
.headers
, "If-");
431 php_http_buffer_appends(&neon
->options
.headers
, "Un");
433 php_http_buffer_appendf(&neon
->options
.headers
, "Modified-Since: %s" PHP_HTTP_CRLF
, date
);
438 /* cookies, array('name' => 'value') */
439 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("cookies"), IS_ARRAY
))) {
440 php_http_buffer_t cookies
;
442 php_http_buffer_init(&cookies
);
443 if (zend_hash_num_elements(Z_ARRVAL_P(zoption
))) {
444 zval
*urlenc_cookies
= NULL
;
446 php_http_buffer_appends(&neon
->options
.headers
, "Cookie: ");
448 /* check whether cookies should not be urlencoded; default is to urlencode them */
449 if ((!(urlenc_cookies
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("encodecookies"), IS_BOOL
))) || Z_BVAL_P(urlenc_cookies
)) {
450 php_http_url_encode_hash_recursive(HASH_OF(zoption
), &neon
->options
.headers
, "; ", lenof("; "), NULL
, 0 TSRMLS_CC
);
453 php_http_array_hashkey_t cookie_key
= php_http_array_hashkey_init(0);
456 FOREACH_KEYVAL(pos
, zoption
, cookie_key
, cookie_val
) {
457 if (cookie_key
.type
== HASH_KEY_IS_STRING
) {
458 zval
*val
= php_http_zsep(IS_STRING
, *cookie_val
);
459 php_http_buffer_appendf(&neon
->options
.headers
, "%s=%s; ", cookie_key
.str
, Z_STRVAL_P(val
));
465 neon
->options
.headers
.used
-= lenof("; ");
466 php_http_buffer_appends(&neon
->options
.headers
, PHP_HTTP_CRLF
);
468 /* cookiestore, read initial cookies from that file and store cookies back into that file */
469 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("cookiestore"), IS_STRING
))) {
470 neon
->options
.cookiestore
= Z_STRVAL_P(zoption
);
474 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("maxfilesize"), IS_LONG
))) {
475 neon
->options
.maxfilesize
= Z_LVAL_P(zoption
);
479 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("timeout"), IS_DOUBLE
))) {
480 neon
->options
.timeout
.read
= Z_DVAL_P(zoption
) > 0 && Z_DVAL_P(zoption
) < 1 ? 1 : round(Z_DVAL_P(zoption
));
482 /* connecttimeout, defaults to 0 */
483 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("connecttimeout"), IS_DOUBLE
))) {
484 neon
->options
.timeout
.connect
= Z_DVAL_P(zoption
) > 0 && Z_DVAL_P(zoption
) < 1 ? 1 : round(Z_DVAL_P(zoption
));
488 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("ssl"), IS_ARRAY
))) {
491 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("verifypeer"), (void *) &zssl
)) {
492 if (!i_zend_is_true(*zssl
)) {
493 neon
->options
.ssl
.noverify
= 1;
496 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("key"), (void *) &zssl
)) {
497 zval
*cpy
= php_http_zsep(IS_STRING
, *zssl
);
498 ne_ssl_client_cert
*cc
= ne_ssl_clicert_read(Z_STRVAL_P(cpy
));
501 if (ne_ssl_clicert_encrypted(cc
)) {
502 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("keypasswd"), (void *) &zssl
)) {
503 zval
*cpy
= php_http_zsep(IS_STRING
, *zssl
);
505 if (NE_OK
== ne_ssl_clicert_decrypt(cc
, Z_STRVAL_P(cpy
))) {
506 neon
->options
.ssl
.clicert
= cc
;
513 if (cc
&& !neon
->options
.ssl
.clicert
) {
514 ne_ssl_clicert_free(cc
);
519 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("cert"), (void *) &zssl
)) {
520 zval
*cpy
= php_http_zsep(IS_STRING
, *zssl
);
521 ne_ssl_certificate
*tc
= ne_ssl_cert_read(Z_STRVAL_P(cpy
));
524 neon
->options
.ssl
.trucert
= tc
;
533 /* request handler ops */
535 static STATUS
php_http_neon_request_reset(php_http_request_t
*h
);
537 static php_http_request_t
*php_http_neon_request_init(php_http_request_t
*h
, void *dummy
)
539 php_http_neon_request_t
*ctx
;
541 ctx
= ecalloc(1, sizeof(*ctx
));
542 php_http_buffer_init(&ctx
->options
.headers
);
543 zend_hash_init(&ctx
->options
.cache
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
549 static php_http_request_t
*php_http_neon_request_copy(php_http_request_t
*from
, php_http_request_t
*to
)
551 TSRMLS_FETCH_FROM_CTX(from
->ts
);
554 return php_http_neon_request_init(to
, NULL
);
556 return php_http_request_init(NULL
, from
->ops
, NULL TSRMLS_CC
);
560 static void php_http_neon_request_dtor(php_http_request_t
*h
)
562 php_http_neon_request_t
*ctx
= h
->ctx
;
564 php_http_neon_request_reset(h
);
565 php_http_buffer_dtor(&ctx
->options
.headers
);
566 zend_hash_destroy(&ctx
->options
.cache
);
572 static STATUS
php_http_neon_request_reset(php_http_request_t
*h
)
574 php_http_neon_request_t
*neon
= h
->ctx
;
576 php_http_buffer_reset(&neon
->options
.headers
);
577 STR_SET(neon
->options
.useragent
, NULL
);
578 STR_SET(neon
->options
.url
, NULL
);
579 neon
->options
.port
= 0;
580 neon
->options
.proxy
.type
= -1;
581 neon
->options
.proxy
.port
= 0;
582 STR_SET(neon
->options
.proxy
.host
, NULL
);
583 neon
->options
.auth
.proxy
.type
= 0;
584 STR_SET(neon
->options
.auth
.proxy
.user
, NULL
);
585 STR_SET(neon
->options
.auth
.proxy
.pass
, NULL
);
586 neon
->options
.auth
.http
.type
= 0;
587 STR_SET(neon
->options
.auth
.http
.user
, NULL
);
588 STR_SET(neon
->options
.auth
.http
.pass
, NULL
);
589 neon
->options
.ssl
.noverify
= 0;
590 if (neon
->options
.ssl
.clicert
) {
591 ne_ssl_clicert_free(neon
->options
.ssl
.clicert
);
592 neon
->options
.ssl
.clicert
= NULL
;
594 if (neon
->options
.ssl
.trucert
) {
595 ne_ssl_cert_free(neon
->options
.ssl
.trucert
);
596 neon
->options
.ssl
.trucert
= NULL
;
598 neon
->options
.redirects
= 0;
599 STR_SET(neon
->options
.cookiestore
, NULL
);
600 if (neon
->options
.interface
) {
601 ne_iaddr_free(neon
->options
.interface
);
602 neon
->options
.interface
= NULL
;
604 neon
->options
.maxfilesize
= 0;
605 neon
->options
.retry
.delay
= 0;
606 neon
->options
.retry
.count
= 0;
607 neon
->options
.timeout
.read
= 0;
608 neon
->options
.timeout
.connect
= 0;
610 if (neon
->progress
.callback
) {
611 zval_ptr_dtor(&neon
->progress
.callback
);
612 neon
->progress
.callback
= NULL
;
614 neon
->progress
.pass_state
= 0;
615 neon
->progress
.state
.dl
.now
= 0;
616 neon
->progress
.state
.dl
.total
= 0;
617 neon
->progress
.state
.ul
.now
= 0;
618 neon
->progress
.state
.ul
.total
= 0;
623 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
)
626 STATUS retval
= SUCCESS
;
632 php_http_neon_request_t
*neon
= h
->ctx
;
633 TSRMLS_FETCH_FROM_CTX(h
->ts
);
635 if (!(meth
= php_http_request_method_name(meth_id
))) {
636 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST_METHOD
, "Unsupported request method: %d (%s)", meth
, url
);
640 if (!(purl
= php_url_parse(url
))) {
641 php_http_error(HE_WARNING
, PHP_HTTP_E_URL
, "Could not parse url %s", url
);
645 if (neon
->options
.port
) {
646 purl
->port
= neon
->options
.port
;
647 } else if (!purl
->port
) {
648 #ifdef HAVE_GETSERVBYNAME
651 if ((se
= getservbyname(purl
->scheme
, "tcp")) && se
->s_port
) {
652 purl
->port
= ntohs(se
->s_port
);
655 if (!strcasecmp(purl
->scheme
, "https")) {
662 session
= ne_session_create(purl
->scheme
, purl
->host
, purl
->port
);
663 if (neon
->options
.proxy
.host
) {
664 switch (neon
->options
.proxy
.type
) {
665 case NE_SOCK_SOCKSV4
:
666 case NE_SOCK_SOCKSV4A
:
667 case NE_SOCK_SOCKSV5
:
668 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
);
672 ne_session_proxy(session
, neon
->options
.proxy
.host
, neon
->options
.proxy
.port
);
676 if (neon
->options
.interface
) {
677 ne_set_localaddr(session
, neon
->options
.interface
);
679 if (neon
->options
.useragent
) {
680 ne_set_useragent(session
, neon
->options
.useragent
);
682 if (neon
->options
.timeout
.read
) {
683 ne_set_read_timeout(session
, neon
->options
.timeout
.read
);
685 if (neon
->options
.timeout
.read
) {
686 ne_set_connect_timeout(session
, neon
->options
.timeout
.connect
);
688 if (neon
->options
.redirects
) {
689 ne_redirect_register(session
);
691 ne_hook_pre_send(session
, php_http_neon_pre_send_callback
, h
);
692 ne_hook_post_headers(session
, php_http_neon_post_headers_callback
, h
);
693 ne_set_notifier(session
, php_http_neon_progress_callback
, h
);
694 ne_ssl_set_verify(session
, php_http_neon_ssl_verify_callback
, h
);
695 if (neon
->options
.ssl
.clicert
) {
696 ne_ssl_set_clicert(session
, neon
->options
.ssl
.clicert
);
699 ne_ssl_trust_default_ca(session); */
700 if (neon
->options
.ssl
.trucert
) {
701 ne_ssl_trust_cert(session
, neon
->options
.ssl
.trucert
);
704 request
= ne_request_create(session
, meth
, purl
->path
/* . purl->query */);
706 /* RFC2616, section 4.3 (para. 4) states that »a message-body MUST NOT be included in a request if the
707 * specification of the request method (section 5.1.1) does not allow sending an entity-body in request.«
708 * Following the clause in section 5.1.1 (para. 2) that request methods »MUST be implemented with the
709 * same semantics as those specified in section 9« reveal that not any single defined HTTP/1.1 method
710 * does not allow a request body.
715 ne_set_request_body_provider(request
, php_http_message_body_size(body
), php_http_neon_read_callback
, h
);
721 switch (result
= ne_begin_request(request
)) {
724 char *buf
= emalloc(0x1000);
726 while (0 < (len
= ne_read_response_block(request
, buf
, 0x1000))) {
727 php_http_buffer_append(h
->buffer
, buf
, len
);
728 php_http_message_parser_parse(h
->parser
, h
->buffer
, PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES
, &h
->message
);
729 // php_http_message_body_append(&h->message->body, buf, len);
737 if (neon
->options
.redirects
-- > 0){
738 const ne_uri
*uri
= ne_redirect_location(session
);
741 char *url
= ne_uri_unparse(uri
);
743 retval
= php_http_neon_request_exec(h
, meth_id
, url
, body
);
750 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "%s; (%s)", ne_get_error(session
), url
);
752 add_property_long(EG(exception
), "neonCode", result
);
758 switch (result
= ne_end_request(request
)) {
763 if (neon
->options
.retry
.count
> tries
++) {
764 if (neon
->options
.retry
.delay
>= PHP_HTTP_DIFFSEC
) {
765 php_http_sleep(neon
->options
.retry
.delay
);
772 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "%s; (%s)", ne_get_error(session
), url
);
774 add_property_long(EG(exception
), "neonCode", result
);
780 ne_session_destroy(session
);
786 static STATUS
php_http_neon_request_setopt(php_http_request_t
*h
, php_http_request_setopt_opt_t opt
, void *arg
)
788 php_http_neon_request_t
*neon
= h
->ctx
;
791 case PHP_HTTP_REQUEST_OPT_SETTINGS
:
792 return set_options(h
, arg
);
795 case PHP_HTTP_REQUEST_OPT_PROGRESS_CALLBACK
:
796 if (neon
->progress
.callback
) {
797 zval_ptr_dtor(&neon
->progress
.callback
);
799 if ((neon
->progress
.callback
= arg
)) {
800 Z_ADDREF_P(neon
->progress
.callback
);
804 case PHP_HTTP_REQUEST_OPT_PROGRESS_CALLBACK_WANTS_STATE
:
805 neon
->progress
.pass_state
= *((int *)arg
);
808 case PHP_HTTP_REQUEST_OPT_COOKIES_ENABLE
:
809 case PHP_HTTP_REQUEST_OPT_COOKIES_RESET
:
810 case PHP_HTTP_REQUEST_OPT_COOKIES_RESET_SESSION
:
811 case PHP_HTTP_REQUEST_OPT_COOKIES_FLUSH
:
822 static STATUS
php_http_neon_request_getopt(php_http_request_t
*h
, php_http_request_getopt_opt_t opt
, void *arg
)
824 php_http_neon_request_t
*neon
= h
->ctx
;
827 case PHP_HTTP_REQUEST_OPT_PROGRESS_INFO
:
828 memcpy(arg
, &neon
->progress
, sizeof(neon
->progress
));
831 case PHP_HTTP_REQUEST_OPT_TRANSFER_INFO
:
841 static php_http_request_ops_t php_http_neon_request_ops
= {
842 php_http_neon_request_init
,
843 php_http_neon_request_copy
,
844 php_http_neon_request_dtor
,
845 php_http_neon_request_reset
,
846 php_http_neon_request_exec
,
847 php_http_neon_request_setopt
,
848 php_http_neon_request_getopt
851 PHP_HTTP_API php_http_request_ops_t
*php_http_neon_get_request_ops(void)
853 return &php_http_neon_request_ops
;
856 PHP_MINIT_FUNCTION(http_neon
)
858 php_http_request_factory_driver_t driver
= {
859 &php_http_neon_request_ops
,
862 if (SUCCESS
!= php_http_request_factory_add_driver(ZEND_STRL("neon"), &driver
)) {
869 zend_declare_class_constant_long(php_http_request_class_entry
, ZEND_STRL("AUTH_BASIC"), NE_AUTH_BASIC TSRMLS_CC
);
870 zend_declare_class_constant_long(php_http_request_class_entry
, ZEND_STRL("AUTH_DIGEST"), NE_AUTH_DIGEST TSRMLS_CC
);
871 zend_declare_class_constant_long(php_http_request_class_entry
, ZEND_STRL("AUTH_NTLM"), NE_AUTH_NTLM TSRMLS_CC
);
872 zend_declare_class_constant_long(php_http_request_class_entry
, ZEND_STRL("AUTH_GSSNEG"), NE_AUTH_GSSAPI TSRMLS_CC
);
873 zend_declare_class_constant_long(php_http_request_class_entry
, ZEND_STRL("AUTH_ANY"), NE_AUTH_ALL TSRMLS_CC
);
876 * Proxy Type Constants
878 zend_declare_class_constant_long(php_http_request_class_entry
, ZEND_STRL("PROXY_SOCKS4"), NE_SOCK_SOCKSV4 TSRMLS_CC
);
879 zend_declare_class_constant_long(php_http_request_class_entry
, ZEND_STRL("PROXY_SOCKS4A"), NE_SOCK_SOCKSV4A TSRMLS_CC
);
880 zend_declare_class_constant_long(php_http_request_class_entry
, ZEND_STRL("PROXY_SOCKS5"), NE_SOCK_SOCKSV5 TSRMLS_CC
);
881 zend_declare_class_constant_long(php_http_request_class_entry
, ZEND_STRL("PROXY_HTTP"), -1 TSRMLS_CC
);
883 if (NE_OK
!= ne_sock_init()) {
889 PHP_MSHUTDOWN_FUNCTION(http_neon
)