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 +--------------------------------------------------------------------+
15 #if PHP_HTTP_HAVE_NEON
17 #include "php_http_request.h"
19 #include <ext/date/php_date.h>
21 #include <neon/ne_auth.h>
22 #include <neon/ne_compress.h>
23 #include <neon/ne_session.h>
24 #include <neon/ne_request.h>
25 #include <neon/ne_redirect.h>
27 typedef struct php_http_neon_auth
{
31 } php_http_neon_auth_t
;
33 typedef struct php_http_neon_request
{
34 php_http_message_body_t
*body
;
39 php_http_buffer_t headers
;
52 php_http_neon_auth_t proxy
;
53 php_http_neon_auth_t http
;
58 ne_ssl_client_cert
*clicert
;
59 ne_ssl_certificate
*trucert
;
64 ne_inet_addr
*interface
;
78 php_http_request_progress_t progress
;
80 } php_http_neon_request_t
;
84 static ssize_t
php_http_neon_read_callback(void *ctx
, char *buf
, size_t len
)
86 php_http_request_t
*h
= ctx
;
87 php_http_neon_request_t
*neon
= h
->ctx
;
88 php_http_message_body_t
*body
= neon
->body
;
91 TSRMLS_FETCH_FROM_CTX(body
->ts
);
94 size_t read
= php_stream_read(php_http_message_body_stream(body
), buf
, len
);
96 php_http_buffer_append(h
->buffer
, buf
, read
);
97 php_http_message_parser_parse(h
->parser
, h
->buffer
, 0, &h
->message
);
100 return php_stream_rewind(php_http_message_body_stream(body
));
106 static void php_http_neon_pre_send_callback(ne_request
*req
, void *ctx
, ne_buffer
*header
)
108 php_http_request_t
*h
= ctx
;
109 php_http_neon_request_t
*neon
= h
->ctx
;
111 ne_buffer_append(header
, neon
->options
.headers
.data
, neon
->options
.headers
.used
);
113 php_http_buffer_append(h
->buffer
, header
->data
, header
->used
- 1 /* ne_buffer counts \0 */);
114 php_http_buffer_appends(h
->buffer
, PHP_HTTP_CRLF
);
115 php_http_message_parser_parse(h
->parser
, h
->buffer
, 0, &h
->message
);
118 static void php_http_neon_post_headers_callback(ne_request
*req
, void *ctx
, const ne_status
*status
)
120 php_http_request_t
*h
= ctx
;
121 HashTable
*hdrs
= &h
->message
->hdrs
;
125 const char *name
, *value
;
126 TSRMLS_FETCH_FROM_CTX(h
->ts
);
128 php_http_info_init(&i TSRMLS_CC
);
129 i
.type
= PHP_HTTP_RESPONSE
;
130 php_http_version_init(&i
.http
.version
, status
->major_version
, status
->minor_version TSRMLS_CC
);
131 i
.http
.info
.response
.code
= status
->code
;
132 i
.http
.info
.response
.status
= estrdup(status
->reason_phrase
);
133 php_http_message_info_callback(&h
->message
, &hdrs
, &i TSRMLS_CC
);
134 php_http_info_dtor(&i
);
136 INIT_PZVAL_ARRAY(&tmp
, hdrs
);
137 while ((iter
= ne_response_header_iterate(req
, iter
, &name
, &value
))) {
138 char *key
= php_http_pretty_key(estrdup(name
), strlen(name
), 1, 1);
139 add_assoc_string(&tmp
, key
, estrdup(value
), 0);
142 php_http_message_parser_state_push(h
->parser
, 1, PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE
);
145 static int php_http_neon_ssl_verify_callback(void *ctx
, int failures
, const ne_ssl_certificate
*cert
)
147 php_http_request_t
*h
= ctx
;
148 php_http_neon_request_t
*neon
= h
->ctx
;
150 if (neon
->options
.ssl
.noverify
) {
156 static void php_http_neon_progress_callback(void *ctx
, ne_session_status status
, const ne_session_status_info
*info
)
158 php_http_request_t
*h
= ctx
;
159 php_http_neon_request_t
*neon
= h
->ctx
;
160 TSRMLS_FETCH_FROM_CTX(h
->ts
);
163 case ne_status_lookup
:
164 neon
->progress
.state
.info
= "resolve";
166 case ne_status_connecting
:
167 neon
->progress
.state
.info
= "connect";
169 case ne_status_connected
:
170 neon
->progress
.state
.info
= "connected";
172 case ne_status_sending
:
173 neon
->progress
.state
.info
= "send";
174 neon
->progress
.state
.ul
.total
= info
->sr
.total
;
175 neon
->progress
.state
.ul
.now
= info
->sr
.progress
;
177 case ne_status_recving
:
178 neon
->progress
.state
.info
= "receive";
179 neon
->progress
.state
.dl
.total
= info
->sr
.total
;
180 neon
->progress
.state
.dl
.now
= info
->sr
.progress
;
182 case ne_status_disconnected
:
183 neon
->progress
.state
.info
= "disconnected";
187 php_http_request_progress_notify(&neon
->progress TSRMLS_CC
);
192 static inline zval
*cache_option(HashTable
*cache
, char *key
, size_t keylen
, ulong h
, zval
*opt
)
197 zend_hash_quick_update(cache
, key
, keylen
, h
, &opt
, sizeof(zval
*), NULL
);
199 zend_hash_update(cache
, key
, keylen
, &opt
, sizeof(zval
*), NULL
);
205 static inline zval
*get_option(HashTable
*cache
, HashTable
*options
, char *key
, size_t keylen
, int type
)
209 ulong h
= zend_hash_func(key
, keylen
);
211 if (SUCCESS
== zend_hash_quick_find(options
, key
, keylen
, h
, (void *) &zoption
)) {
212 zval
*option
= php_http_ztyp(type
, *zoption
);
215 zval
*cached
= cache_option(cache
, key
, keylen
, h
, option
);
217 zval_ptr_dtor(&option
);
227 static STATUS
set_options(php_http_request_t
*h
, HashTable
*options
)
231 php_http_neon_request_t
*neon
= h
->ctx
;
232 TSRMLS_FETCH_FROM_CTX(h
->ts
);
235 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyhost"), IS_STRING
))) {
236 neon
->options
.proxy
.host
= Z_STRVAL_P(zoption
);
239 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyauth"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
240 char *colon
= strchr(Z_STRVAL_P(zoption
), ':');
243 STR_SET(neon
->options
.auth
.proxy
.user
, estrndup(Z_STRVAL_P(zoption
), colon
- Z_STRVAL_P(zoption
)));
244 STR_SET(neon
->options
.auth
.proxy
.pass
, estrdup(colon
+ 1));
246 STR_SET(neon
->options
.auth
.proxy
.user
, estrdup(Z_STRVAL_P(zoption
)));
250 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyauthtype"), IS_LONG
))) {
251 neon
->options
.auth
.proxy
.type
= Z_LVAL_P(zoption
);
253 neon
->options
.auth
.proxy
.type
= NE_AUTH_ALL
;
258 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyport"), IS_LONG
))) {
259 neon
->options
.proxy
.port
= Z_LVAL_P(zoption
);
261 neon
->options
.proxy
.port
= 0;
265 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxytype"), IS_LONG
))) {
266 neon
->options
.proxy
.type
= Z_LVAL_P(zoption
);
268 neon
->options
.proxy
.type
= -1;
272 /* outgoing interface */
273 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("interface"), IS_STRING
))) {
274 if (!(neon
->options
.interface
= ne_iaddr_parse(Z_STRVAL_P(zoption
), ne_iaddr_ipv4
))) {
275 neon
->options
.interface
= ne_iaddr_parse(Z_STRVAL_P(zoption
), ne_iaddr_ipv6
);
280 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("port"), IS_LONG
))) {
281 neon
->options
.port
= Z_LVAL_P(zoption
);
285 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("httpauth"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
286 char *colon
= strchr(Z_STRVAL_P(zoption
), ':');
289 STR_SET(neon
->options
.auth
.http
.user
, estrndup(Z_STRVAL_P(zoption
), colon
- Z_STRVAL_P(zoption
)));
290 STR_SET(neon
->options
.auth
.http
.pass
, estrdup(colon
+ 1));
292 STR_SET(neon
->options
.auth
.http
.user
, estrdup(Z_STRVAL_P(zoption
)));
295 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("httpauthtype"), IS_LONG
))) {
296 neon
->options
.auth
.http
.type
= Z_LVAL_P(zoption
);
298 neon
->options
.auth
.http
.type
= NE_AUTH_ALL
;
301 /* redirects, defaults to 0 */
302 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("redirect"), IS_LONG
))) {
303 neon
->options
.redirects
= Z_LVAL_P(zoption
);
305 neon
->options
.redirects
= 0;
308 /* retries, defaults to 0 */
309 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("retrycount"), IS_LONG
))) {
310 neon
->options
.retry
.count
= Z_LVAL_P(zoption
);
311 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("retrydelay"), IS_DOUBLE
))) {
312 neon
->options
.retry
.delay
= Z_DVAL_P(zoption
);
314 neon
->options
.retry
.delay
= 0;
317 neon
->options
.retry
.count
= 0;
321 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("referer"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
322 neon
->options
.referer
= Z_STRVAL_P(zoption
);
325 /* useragent, default "PECL::HTTP/version (PHP/version)" */
326 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("useragent"), IS_STRING
))) {
327 /* allow to send no user agent, not even default one */
328 if (Z_STRLEN_P(zoption
)) {
329 neon
->options
.useragent
= Z_STRVAL_P(zoption
);
331 neon
->options
.useragent
= NULL
;
336 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("resume"), IS_LONG
)) && (Z_LVAL_P(zoption
) > 0)) {
337 php_http_buffer_appendf(&neon
->options
.headers
, "Range: bytes=%ld-" PHP_HTTP_CRLF
, Z_LVAL_P(zoption
));
340 /* or range of kind array(array(0,499), array(100,1499)) */
341 else if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("range"), IS_ARRAY
)) && zend_hash_num_elements(Z_ARRVAL_P(zoption
))) {
342 HashPosition pos1
, pos2
;
343 zval
**rr
, **rb
, **re
;
344 php_http_buffer_t rs
;
346 php_http_buffer_init(&rs
);
347 FOREACH_VAL(pos1
, zoption
, rr
) {
348 if (Z_TYPE_PP(rr
) == IS_ARRAY
) {
349 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(rr
), &pos2
);
350 if (SUCCESS
== zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr
), (void *) &rb
, &pos2
)) {
351 zend_hash_move_forward_ex(Z_ARRVAL_PP(rr
), &pos2
);
352 if (SUCCESS
== zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr
), (void *) &re
, &pos2
)) {
353 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))) &&
354 ((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)))) {
355 zval
*rbl
= php_http_ztyp(IS_LONG
, *rb
);
356 zval
*rel
= php_http_ztyp(IS_LONG
, *re
);
358 if ((Z_LVAL_P(rbl
) >= 0) && (Z_LVAL_P(rel
) >= 0)) {
359 php_http_buffer_appendf(&rs
, "%ld-%ld,", Z_LVAL_P(rbl
), Z_LVAL_P(rel
));
369 if (PHP_HTTP_BUFFER_LEN(&rs
)) {
370 /* ignore last comma */
371 int used
= rs
.used
> INT_MAX
? INT_MAX
: rs
.used
;
372 php_http_buffer_appendf(&neon
->options
.headers
, "Range: bytes=%.*s" PHP_HTTP_CRLF
, used
- 1, rs
.data
);
375 php_http_buffer_dtor(&rs
);
378 /* additional headers, array('name' => 'value') */
379 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("headers"), IS_ARRAY
))) {
380 php_http_array_hashkey_t header_key
= php_http_array_hashkey_init(0);
384 FOREACH_KEYVAL(pos
, zoption
, header_key
, header_val
) {
385 if (header_key
.type
== HASH_KEY_IS_STRING
) {
386 zval
*header_cpy
= php_http_ztyp(IS_STRING
, *header_val
);
388 if (!strcasecmp(header_key
.str
, "range")) {
391 php_http_buffer_appendf(&neon
->options
.headers
, "%s: %s" PHP_HTTP_CRLF
, header_key
.str
, Z_STRVAL_P(header_cpy
));
392 zval_ptr_dtor(&header_cpy
);
397 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("etag"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
398 php_http_buffer_appends(&neon
->options
.headers
, "If-");
400 php_http_buffer_appends(&neon
->options
.headers
, "None-");
402 php_http_buffer_appends(&neon
->options
.headers
, "Match: ");
404 if ((Z_STRVAL_P(zoption
)[0] == '"') && (Z_STRVAL_P(zoption
)[Z_STRLEN_P(zoption
)-1] == '"')) {
405 php_http_buffer_appendl(&neon
->options
.headers
, Z_STRVAL_P(zoption
));
407 php_http_buffer_appendf(&neon
->options
.headers
, "\"%s\"" PHP_HTTP_CRLF
, Z_STRVAL_P(zoption
));
411 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("compress"), IS_BOOL
)) && Z_LVAL_P(zoption
)) {
412 php_http_buffer_appends(&neon
->options
.headers
, "Accept-Encoding: gzip;q=1.0,deflate;q=0.5" PHP_HTTP_CRLF
);
416 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("lastmodified"), IS_LONG
))) {
417 if (Z_LVAL_P(zoption
)) {
418 time_t time
= Z_LVAL_P(zoption
) > 0 ? Z_LVAL_P(zoption
) : PHP_HTTP_G
->env
.request
.time
+ Z_LVAL_P(zoption
);
419 char *date
= php_format_date(ZEND_STRS(PHP_HTTP_DATE_FORMAT
), time
, 0 TSRMLS_CC
);
421 php_http_buffer_appends(&neon
->options
.headers
, "If-");
423 php_http_buffer_appends(&neon
->options
.headers
, "Un");
425 php_http_buffer_appendf(&neon
->options
.headers
, "Modified-Since: %s" PHP_HTTP_CRLF
, date
);
430 /* cookies, array('name' => 'value') */
431 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("cookies"), IS_ARRAY
))) {
432 php_http_buffer_t cookies
;
434 php_http_buffer_init(&cookies
);
435 if (zend_hash_num_elements(Z_ARRVAL_P(zoption
))) {
436 zval
*urlenc_cookies
= NULL
;
438 php_http_buffer_appends(&neon
->options
.headers
, "Cookie: ");
440 /* check whether cookies should not be urlencoded; default is to urlencode them */
441 if ((!(urlenc_cookies
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("encodecookies"), IS_BOOL
))) || Z_BVAL_P(urlenc_cookies
)) {
442 php_http_url_encode_hash_ex(HASH_OF(zoption
), &neon
->options
.headers
, ZEND_STRS("; "), ZEND_STRS("="), NULL
, 0 TSRMLS_CC
);
445 php_http_array_hashkey_t cookie_key
= php_http_array_hashkey_init(0);
448 FOREACH_KEYVAL(pos
, zoption
, cookie_key
, cookie_val
) {
449 if (cookie_key
.type
== HASH_KEY_IS_STRING
) {
450 zval
*val
= php_http_ztyp(IS_STRING
, *cookie_val
);
451 php_http_buffer_appendf(&neon
->options
.headers
, "%s=%s; ", cookie_key
.str
, Z_STRVAL_P(val
));
457 neon
->options
.headers
.used
-= lenof("; ");
458 php_http_buffer_appends(&neon
->options
.headers
, PHP_HTTP_CRLF
);
460 /* cookiestore, read initial cookies from that file and store cookies back into that file */
461 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("cookiestore"), IS_STRING
))) {
462 neon
->options
.cookiestore
= Z_STRVAL_P(zoption
);
466 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("maxfilesize"), IS_LONG
))) {
467 neon
->options
.maxfilesize
= Z_LVAL_P(zoption
);
471 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("timeout"), IS_DOUBLE
))) {
472 neon
->options
.timeout
.read
= Z_DVAL_P(zoption
) > 0 && Z_DVAL_P(zoption
) < 1 ? 1 : round(Z_DVAL_P(zoption
));
474 /* connecttimeout, defaults to 0 */
475 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("connecttimeout"), IS_DOUBLE
))) {
476 neon
->options
.timeout
.connect
= Z_DVAL_P(zoption
) > 0 && Z_DVAL_P(zoption
) < 1 ? 1 : round(Z_DVAL_P(zoption
));
480 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("ssl"), IS_ARRAY
))) {
483 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("verifypeer"), (void *) &zssl
)) {
484 if (!i_zend_is_true(*zssl
)) {
485 neon
->options
.ssl
.noverify
= 1;
488 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("key"), (void *) &zssl
)) {
489 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
490 ne_ssl_client_cert
*cc
= ne_ssl_clicert_read(Z_STRVAL_P(cpy
));
493 if (ne_ssl_clicert_encrypted(cc
)) {
494 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("keypasswd"), (void *) &zssl
)) {
495 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
497 if (NE_OK
== ne_ssl_clicert_decrypt(cc
, Z_STRVAL_P(cpy
))) {
498 neon
->options
.ssl
.clicert
= cc
;
505 if (cc
&& !neon
->options
.ssl
.clicert
) {
506 ne_ssl_clicert_free(cc
);
511 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("cert"), (void *) &zssl
)) {
512 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
513 ne_ssl_certificate
*tc
= ne_ssl_cert_read(Z_STRVAL_P(cpy
));
516 neon
->options
.ssl
.trucert
= tc
;
525 /* request handler ops */
527 static STATUS
php_http_neon_request_reset(php_http_request_t
*h
);
529 static php_http_request_t
*php_http_neon_request_init(php_http_request_t
*h
, void *dummy
)
531 php_http_neon_request_t
*ctx
;
533 ctx
= ecalloc(1, sizeof(*ctx
));
534 php_http_buffer_init(&ctx
->options
.headers
);
535 zend_hash_init(&ctx
->options
.cache
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
541 static php_http_request_t
*php_http_neon_request_copy(php_http_request_t
*from
, php_http_request_t
*to
)
543 TSRMLS_FETCH_FROM_CTX(from
->ts
);
546 return php_http_neon_request_init(to
, NULL
);
548 return php_http_request_init(NULL
, from
->ops
, from
->rf
, NULL TSRMLS_CC
);
552 static void php_http_neon_request_dtor(php_http_request_t
*h
)
554 php_http_neon_request_t
*ctx
= h
->ctx
;
555 TSRMLS_FETCH_FROM_CTX(h
->ts
);
557 php_http_neon_request_reset(h
);
558 php_http_buffer_dtor(&ctx
->options
.headers
);
559 zend_hash_destroy(&ctx
->options
.cache
);
561 php_http_request_progress_dtor(&ctx
->progress TSRMLS_CC
);
567 static STATUS
php_http_neon_request_reset(php_http_request_t
*h
)
569 php_http_neon_request_t
*neon
= h
->ctx
;
570 TSRMLS_FETCH_FROM_CTX(h
->ts
);
572 php_http_buffer_reset(&neon
->options
.headers
);
573 STR_SET(neon
->options
.useragent
, NULL
);
574 STR_SET(neon
->options
.url
, NULL
);
575 neon
->options
.port
= 0;
576 neon
->options
.proxy
.type
= -1;
577 neon
->options
.proxy
.port
= 0;
578 STR_SET(neon
->options
.proxy
.host
, NULL
);
579 neon
->options
.auth
.proxy
.type
= 0;
580 STR_SET(neon
->options
.auth
.proxy
.user
, NULL
);
581 STR_SET(neon
->options
.auth
.proxy
.pass
, NULL
);
582 neon
->options
.auth
.http
.type
= 0;
583 STR_SET(neon
->options
.auth
.http
.user
, NULL
);
584 STR_SET(neon
->options
.auth
.http
.pass
, NULL
);
585 neon
->options
.ssl
.noverify
= 0;
586 if (neon
->options
.ssl
.clicert
) {
587 ne_ssl_clicert_free(neon
->options
.ssl
.clicert
);
588 neon
->options
.ssl
.clicert
= NULL
;
590 if (neon
->options
.ssl
.trucert
) {
591 ne_ssl_cert_free(neon
->options
.ssl
.trucert
);
592 neon
->options
.ssl
.trucert
= NULL
;
594 neon
->options
.redirects
= 0;
595 STR_SET(neon
->options
.cookiestore
, NULL
);
596 if (neon
->options
.interface
) {
597 ne_iaddr_free(neon
->options
.interface
);
598 neon
->options
.interface
= NULL
;
600 neon
->options
.maxfilesize
= 0;
601 neon
->options
.retry
.delay
= 0;
602 neon
->options
.retry
.count
= 0;
603 neon
->options
.timeout
.read
= 0;
604 neon
->options
.timeout
.connect
= 0;
606 php_http_request_progress_dtor(&neon
->progress TSRMLS_CC
);
611 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
)
614 STATUS retval
= SUCCESS
;
620 php_http_neon_request_t
*neon
= h
->ctx
;
621 TSRMLS_FETCH_FROM_CTX(h
->ts
);
623 if (!(meth
= php_http_request_method_name(meth_id TSRMLS_CC
))) {
624 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST_METHOD
, "Unsupported request method: %d (%s)", meth
, url
);
628 if (!(purl
= php_url_parse(url
))) {
629 php_http_error(HE_WARNING
, PHP_HTTP_E_URL
, "Could not parse url %s", url
);
633 if (neon
->options
.port
) {
634 purl
->port
= neon
->options
.port
;
635 } else if (!purl
->port
) {
637 if (strncasecmp(purl
->scheme
, "http", 4)) {
638 #ifdef HAVE_GETSERVBYNAME
641 if ((se
= getservbyname(purl
->scheme
, "tcp")) && se
->s_port
) {
642 purl
->port
= ntohs(se
->s_port
);
645 } else if (purl
->scheme
[4] == 's') {
650 /* never returns NULL */
651 session
= ne_session_create(purl
->scheme
, purl
->host
, purl
->port
);
652 if (neon
->options
.proxy
.host
) {
653 switch (neon
->options
.proxy
.type
) {
654 case NE_SOCK_SOCKSV4
:
655 case NE_SOCK_SOCKSV4A
:
656 case NE_SOCK_SOCKSV5
:
657 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
);
661 ne_session_proxy(session
, neon
->options
.proxy
.host
, neon
->options
.proxy
.port
);
665 if (neon
->options
.interface
) {
666 ne_set_localaddr(session
, neon
->options
.interface
);
668 if (neon
->options
.useragent
) {
669 ne_set_useragent(session
, neon
->options
.useragent
);
671 if (neon
->options
.timeout
.read
) {
672 ne_set_read_timeout(session
, neon
->options
.timeout
.read
);
674 if (neon
->options
.timeout
.read
) {
675 ne_set_connect_timeout(session
, neon
->options
.timeout
.connect
);
677 if (neon
->options
.redirects
) {
678 ne_redirect_register(session
);
680 ne_hook_pre_send(session
, php_http_neon_pre_send_callback
, h
);
681 ne_hook_post_headers(session
, php_http_neon_post_headers_callback
, h
);
682 ne_set_notifier(session
, php_http_neon_progress_callback
, h
);
683 ne_ssl_set_verify(session
, php_http_neon_ssl_verify_callback
, h
);
684 if (neon
->options
.ssl
.clicert
) {
685 ne_ssl_set_clicert(session
, neon
->options
.ssl
.clicert
);
688 ne_ssl_trust_default_ca(session); */
689 if (neon
->options
.ssl
.trucert
) {
690 ne_ssl_trust_cert(session
, neon
->options
.ssl
.trucert
);
693 request
= ne_request_create(session
, meth
, purl
->path
/* . purl->query */);
695 /* RFC2616, section 4.3 (para. 4) states that »a message-body MUST NOT be included in a request if the
696 * specification of the request method (section 5.1.1) does not allow sending an entity-body in request.«
697 * Following the clause in section 5.1.1 (para. 2) that request methods »MUST be implemented with the
698 * same semantics as those specified in section 9« reveal that not any single defined HTTP/1.1 method
699 * does not allow a request body.
704 ne_set_request_body_provider(request
, php_http_message_body_size(body
), php_http_neon_read_callback
, h
);
710 switch (result
= ne_begin_request(request
)) {
713 char *buf
= emalloc(0x1000);
715 while (0 < (len
= ne_read_response_block(request
, buf
, 0x1000))) {
716 php_http_buffer_append(h
->buffer
, buf
, len
);
717 php_http_message_parser_parse(h
->parser
, h
->buffer
, PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES
, &h
->message
);
718 // php_http_message_body_append(&h->message->body, buf, len);
726 if (neon
->options
.redirects
-- > 0){
727 const ne_uri
*uri
= ne_redirect_location(session
);
730 char *url
= ne_uri_unparse(uri
);
732 retval
= php_http_neon_request_exec(h
, meth_id
, url
, body
);
739 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "%s; (%s)", ne_get_error(session
), url
);
741 add_property_long(EG(exception
), "neonCode", result
);
747 switch (result
= ne_end_request(request
)) {
752 if (neon
->options
.retry
.count
> tries
++) {
753 if (neon
->options
.retry
.delay
>= PHP_HTTP_DIFFSEC
) {
754 php_http_sleep(neon
->options
.retry
.delay
);
761 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "%s; (%s)", ne_get_error(session
), url
);
763 add_property_long(EG(exception
), "neonCode", result
);
769 ne_session_destroy(session
);
775 static STATUS
php_http_neon_request_setopt(php_http_request_t
*h
, php_http_request_setopt_opt_t opt
, void *arg
)
777 php_http_neon_request_t
*neon
= h
->ctx
;
778 TSRMLS_FETCH_FROM_CTX(h
->ts
);
781 case PHP_HTTP_REQUEST_OPT_SETTINGS
:
782 return set_options(h
, arg
);
785 case PHP_HTTP_REQUEST_OPT_PROGRESS_CALLBACK
:
786 if (neon
->progress
.in_cb
) {
787 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "Cannot change progress callback while executing it");
790 if (neon
->progress
.callback
) {
791 php_http_request_progress_dtor(&neon
->progress TSRMLS_CC
);
793 neon
->progress
.callback
= arg
;
796 case PHP_HTTP_REQUEST_OPT_COOKIES_ENABLE
:
797 case PHP_HTTP_REQUEST_OPT_COOKIES_RESET
:
798 case PHP_HTTP_REQUEST_OPT_COOKIES_RESET_SESSION
:
799 case PHP_HTTP_REQUEST_OPT_COOKIES_FLUSH
:
810 static STATUS
php_http_neon_request_getopt(php_http_request_t
*h
, php_http_request_getopt_opt_t opt
, void *arg
)
812 php_http_neon_request_t
*neon
= h
->ctx
;
815 case PHP_HTTP_REQUEST_OPT_PROGRESS_INFO
:
816 *((php_http_request_progress_t
**) arg
) = &neon
->progress
;
819 case PHP_HTTP_REQUEST_OPT_TRANSFER_INFO
:
829 static php_http_resource_factory_ops_t php_http_neon_resource_factory_ops
= {
835 static php_http_request_ops_t php_http_neon_request_ops
= {
836 &php_http_neon_resource_factory_ops
,
837 php_http_neon_request_init
,
838 php_http_neon_request_copy
,
839 php_http_neon_request_dtor
,
840 php_http_neon_request_reset
,
841 php_http_neon_request_exec
,
842 php_http_neon_request_setopt
,
843 php_http_neon_request_getopt
846 PHP_HTTP_API php_http_request_ops_t
*php_http_neon_get_request_ops(void)
848 return &php_http_neon_request_ops
;
851 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpNEON, method, 0, req_args)
852 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpNEON, method, 0)
853 #define PHP_HTTP_NEON_ME(method, visibility) PHP_ME(HttpNEON, method, PHP_HTTP_ARGS(HttpNEON, method), visibility)
854 #define PHP_HTTP_NEON_ALIAS(method, func) PHP_HTTP_STATIC_ME_ALIAS(method, func, PHP_HTTP_ARGS(HttpNEON, method))
855 #define PHP_HTTP_NEON_MALIAS(me, al, vis) ZEND_FENTRY(me, ZEND_MN(HttpNEON_##al), PHP_HTTP_ARGS(HttpNEON, al), vis)
857 PHP_HTTP_EMPTY_ARGS(__construct
);
859 zend_class_entry
*php_http_neon_class_entry
;
860 zend_function_entry php_http_neon_method_entry
[] = {
861 PHP_HTTP_NEON_ME(__construct
, ZEND_ACC_PRIVATE
|ZEND_ACC_CTOR
)
866 PHP_METHOD(HttpNEON
, __construct
) {
870 PHP_MINIT_FUNCTION(http_neon
)
872 php_http_request_factory_driver_t driver
= {
873 &php_http_neon_request_ops
,
876 if (SUCCESS
!= php_http_request_factory_add_driver(ZEND_STRL("neon"), &driver
)) {
880 PHP_HTTP_REGISTER_CLASS(http
, NEON
, http_neon
, php_http_neon_class_entry
, 0);
885 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_BASIC"), NE_AUTH_BASIC TSRMLS_CC
);
886 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_DIGEST"), NE_AUTH_DIGEST TSRMLS_CC
);
887 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_NTLM"), NE_AUTH_NTLM TSRMLS_CC
);
888 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_GSSAPI"), NE_AUTH_GSSAPI TSRMLS_CC
);
889 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_GSSNEG"), NE_AUTH_NEGOTIATE TSRMLS_CC
);
890 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_ANY"), NE_AUTH_ALL TSRMLS_CC
);
893 * Proxy Type Constants
895 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS4"), NE_SOCK_SOCKSV4 TSRMLS_CC
);
896 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS4A"), NE_SOCK_SOCKSV4A TSRMLS_CC
);
897 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS5"), NE_SOCK_SOCKSV5 TSRMLS_CC
);
898 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_HTTP"), -1 TSRMLS_CC
);
900 if (NE_OK
!= ne_sock_init()) {
906 PHP_MSHUTDOWN_FUNCTION(http_neon
)
919 * vim600: noet sw=4 ts=4 fdm=marker
920 * vim<600: noet sw=4 ts=4