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
;
146 TSRMLS_FETCH_FROM_CTX(h
->ts
);
149 case ne_status_lookup
:
150 neon
->progress
.state
.info
= "resolve";
152 case ne_status_connecting
:
153 neon
->progress
.state
.info
= "connect";
155 case ne_status_connected
:
156 neon
->progress
.state
.info
= "connected";
158 case ne_status_sending
:
159 neon
->progress
.state
.info
= "send";
160 neon
->progress
.state
.ul
.total
= info
->sr
.total
;
161 neon
->progress
.state
.ul
.now
= info
->sr
.progress
;
163 case ne_status_recving
:
164 neon
->progress
.state
.info
= "receive";
165 neon
->progress
.state
.dl
.total
= info
->sr
.total
;
166 neon
->progress
.state
.dl
.now
= info
->sr
.progress
;
168 case ne_status_disconnected
:
169 neon
->progress
.state
.info
= "disconnected";
173 php_http_request_progress_notify(&neon
->progress TSRMLS_CC
);
178 static inline zval
*cache_option(HashTable
*cache
, char *key
, size_t keylen
, ulong h
, zval
*opt
)
183 zend_hash_quick_update(cache
, key
, keylen
, h
, &opt
, sizeof(zval
*), NULL
);
185 zend_hash_update(cache
, key
, keylen
, &opt
, sizeof(zval
*), NULL
);
191 static inline zval
*get_option(HashTable
*cache
, HashTable
*options
, char *key
, size_t keylen
, int type
)
195 ulong h
= zend_hash_func(key
, keylen
);
197 if (SUCCESS
== zend_hash_quick_find(options
, key
, keylen
, h
, (void *) &zoption
)) {
198 zval
*option
= php_http_ztyp(type
, *zoption
);
201 zval
*cached
= cache_option(cache
, key
, keylen
, h
, option
);
203 zval_ptr_dtor(&option
);
213 static STATUS
set_options(php_http_request_t
*h
, HashTable
*options
)
217 php_http_neon_request_t
*neon
= h
->ctx
;
220 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyhost"), IS_STRING
))) {
221 neon
->options
.proxy
.host
= Z_STRVAL_P(zoption
);
224 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyauth"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
225 char *colon
= strchr(Z_STRVAL_P(zoption
), ':');
228 STR_SET(neon
->options
.auth
.proxy
.user
, estrndup(Z_STRVAL_P(zoption
), colon
- Z_STRVAL_P(zoption
)));
229 STR_SET(neon
->options
.auth
.proxy
.pass
, estrdup(colon
+ 1));
231 STR_SET(neon
->options
.auth
.proxy
.user
, estrdup(Z_STRVAL_P(zoption
)));
235 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyauthtype"), IS_LONG
))) {
236 neon
->options
.auth
.proxy
.type
= Z_LVAL_P(zoption
);
238 neon
->options
.auth
.proxy
.type
= NE_AUTH_ALL
;
243 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyport"), IS_LONG
))) {
244 neon
->options
.proxy
.port
= Z_LVAL_P(zoption
);
246 neon
->options
.proxy
.port
= 0;
250 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxytype"), IS_LONG
))) {
251 neon
->options
.proxy
.type
= Z_LVAL_P(zoption
);
253 neon
->options
.proxy
.type
= -1;
257 /* outgoing interface */
258 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("interface"), IS_STRING
))) {
259 if (!(neon
->options
.interface
= ne_iaddr_parse(Z_STRVAL_P(zoption
), ne_iaddr_ipv4
))) {
260 neon
->options
.interface
= ne_iaddr_parse(Z_STRVAL_P(zoption
), ne_iaddr_ipv6
);
265 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("port"), IS_LONG
))) {
266 neon
->options
.port
= Z_LVAL_P(zoption
);
270 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("httpauth"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
271 char *colon
= strchr(Z_STRVAL_P(zoption
), ':');
274 STR_SET(neon
->options
.auth
.http
.user
, estrndup(Z_STRVAL_P(zoption
), colon
- Z_STRVAL_P(zoption
)));
275 STR_SET(neon
->options
.auth
.http
.pass
, estrdup(colon
+ 1));
277 STR_SET(neon
->options
.auth
.http
.user
, estrdup(Z_STRVAL_P(zoption
)));
280 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("httpauthtype"), IS_LONG
))) {
281 neon
->options
.auth
.http
.type
= Z_LVAL_P(zoption
);
283 neon
->options
.auth
.http
.type
= NE_AUTH_ALL
;
286 /* redirects, defaults to 0 */
287 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("redirect"), IS_LONG
))) {
288 neon
->options
.redirects
= Z_LVAL_P(zoption
);
290 neon
->options
.redirects
= 0;
293 /* retries, defaults to 0 */
294 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("retrycount"), IS_LONG
))) {
295 neon
->options
.retry
.count
= Z_LVAL_P(zoption
);
296 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("retrydelay"), IS_DOUBLE
))) {
297 neon
->options
.retry
.delay
= Z_DVAL_P(zoption
);
299 neon
->options
.retry
.delay
= 0;
302 neon
->options
.retry
.count
= 0;
306 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("referer"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
307 neon
->options
.referer
= Z_STRVAL_P(zoption
);
310 /* useragent, default "PECL::HTTP/version (PHP/version)" */
311 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("useragent"), IS_STRING
))) {
312 /* allow to send no user agent, not even default one */
313 if (Z_STRLEN_P(zoption
)) {
314 neon
->options
.useragent
= Z_STRVAL_P(zoption
);
316 neon
->options
.useragent
= NULL
;
321 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("resume"), IS_LONG
)) && (Z_LVAL_P(zoption
) > 0)) {
322 php_http_buffer_appendf(&neon
->options
.headers
, "Range: bytes=%ld-" PHP_HTTP_CRLF
, Z_LVAL_P(zoption
));
325 /* or range of kind array(array(0,499), array(100,1499)) */
326 else if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("range"), IS_ARRAY
)) && zend_hash_num_elements(Z_ARRVAL_P(zoption
))) {
327 HashPosition pos1
, pos2
;
328 zval
**rr
, **rb
, **re
;
329 php_http_buffer_t rs
;
331 php_http_buffer_init(&rs
);
332 FOREACH_VAL(pos1
, zoption
, rr
) {
333 if (Z_TYPE_PP(rr
) == IS_ARRAY
) {
334 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(rr
), &pos2
);
335 if (SUCCESS
== zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr
), (void *) &rb
, &pos2
)) {
336 zend_hash_move_forward_ex(Z_ARRVAL_PP(rr
), &pos2
);
337 if (SUCCESS
== zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr
), (void *) &re
, &pos2
)) {
338 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))) &&
339 ((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)))) {
340 zval
*rbl
= php_http_ztyp(IS_LONG
, *rb
);
341 zval
*rel
= php_http_ztyp(IS_LONG
, *re
);
343 if ((Z_LVAL_P(rbl
) >= 0) && (Z_LVAL_P(rel
) >= 0)) {
344 php_http_buffer_appendf(&rs
, "%ld-%ld,", Z_LVAL_P(rbl
), Z_LVAL_P(rel
));
354 if (PHP_HTTP_BUFFER_LEN(&rs
)) {
355 /* ignore last comma */
356 php_http_buffer_appendf(&neon
->options
.headers
, "Range: bytes=%.*s" PHP_HTTP_CRLF
, rs
.used
- 1, rs
.data
);
359 php_http_buffer_dtor(&rs
);
362 /* additional headers, array('name' => 'value') */
363 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("headers"), IS_ARRAY
))) {
364 php_http_array_hashkey_t header_key
= php_http_array_hashkey_init(0);
368 FOREACH_KEYVAL(pos
, zoption
, header_key
, header_val
) {
369 if (header_key
.type
== HASH_KEY_IS_STRING
) {
370 zval
*header_cpy
= php_http_ztyp(IS_STRING
, *header_val
);
372 if (!strcasecmp(header_key
.str
, "range")) {
375 php_http_buffer_appendf(&neon
->options
.headers
, "%s: %s" PHP_HTTP_CRLF
, header_key
.str
, Z_STRVAL_P(header_cpy
));
376 zval_ptr_dtor(&header_cpy
);
381 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("etag"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
382 php_http_buffer_appends(&neon
->options
.headers
, "If-");
384 php_http_buffer_appends(&neon
->options
.headers
, "None-");
386 php_http_buffer_appends(&neon
->options
.headers
, "Match: ");
388 if ((Z_STRVAL_P(zoption
)[0] == '"') && (Z_STRVAL_P(zoption
)[Z_STRLEN_P(zoption
)-1] == '"')) {
389 php_http_buffer_appendl(&neon
->options
.headers
, Z_STRVAL_P(zoption
));
391 php_http_buffer_appendf(&neon
->options
.headers
, "\"%s\"" PHP_HTTP_CRLF
, Z_STRVAL_P(zoption
));
395 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("compress"), IS_BOOL
)) && Z_LVAL_P(zoption
)) {
396 php_http_buffer_appends(&neon
->options
.headers
, "Accept-Encoding: gzip;q=1.0,deflate;q=0.5" PHP_HTTP_CRLF
);
400 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("lastmodified"), IS_LONG
))) {
401 if (Z_LVAL_P(zoption
)) {
402 time_t time
= Z_LVAL_P(zoption
) > 0 ? Z_LVAL_P(zoption
) : PHP_HTTP_G
->env
.request
.time
+ Z_LVAL_P(zoption
);
403 char *date
= php_format_date(ZEND_STRS(PHP_HTTP_DATE_FORMAT
), time
, 0 TSRMLS_CC
);
405 php_http_buffer_appends(&neon
->options
.headers
, "If-");
407 php_http_buffer_appends(&neon
->options
.headers
, "Un");
409 php_http_buffer_appendf(&neon
->options
.headers
, "Modified-Since: %s" PHP_HTTP_CRLF
, date
);
414 /* cookies, array('name' => 'value') */
415 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("cookies"), IS_ARRAY
))) {
416 php_http_buffer_t cookies
;
418 php_http_buffer_init(&cookies
);
419 if (zend_hash_num_elements(Z_ARRVAL_P(zoption
))) {
420 zval
*urlenc_cookies
= NULL
;
422 php_http_buffer_appends(&neon
->options
.headers
, "Cookie: ");
424 /* check whether cookies should not be urlencoded; default is to urlencode them */
425 if ((!(urlenc_cookies
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("encodecookies"), IS_BOOL
))) || Z_BVAL_P(urlenc_cookies
)) {
426 php_http_url_encode_hash_recursive(HASH_OF(zoption
), &neon
->options
.headers
, "; ", lenof("; "), NULL
, 0 TSRMLS_CC
);
429 php_http_array_hashkey_t cookie_key
= php_http_array_hashkey_init(0);
432 FOREACH_KEYVAL(pos
, zoption
, cookie_key
, cookie_val
) {
433 if (cookie_key
.type
== HASH_KEY_IS_STRING
) {
434 zval
*val
= php_http_ztyp(IS_STRING
, *cookie_val
);
435 php_http_buffer_appendf(&neon
->options
.headers
, "%s=%s; ", cookie_key
.str
, Z_STRVAL_P(val
));
441 neon
->options
.headers
.used
-= lenof("; ");
442 php_http_buffer_appends(&neon
->options
.headers
, PHP_HTTP_CRLF
);
444 /* cookiestore, read initial cookies from that file and store cookies back into that file */
445 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("cookiestore"), IS_STRING
))) {
446 neon
->options
.cookiestore
= Z_STRVAL_P(zoption
);
450 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("maxfilesize"), IS_LONG
))) {
451 neon
->options
.maxfilesize
= Z_LVAL_P(zoption
);
455 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("timeout"), IS_DOUBLE
))) {
456 neon
->options
.timeout
.read
= Z_DVAL_P(zoption
) > 0 && Z_DVAL_P(zoption
) < 1 ? 1 : round(Z_DVAL_P(zoption
));
458 /* connecttimeout, defaults to 0 */
459 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("connecttimeout"), IS_DOUBLE
))) {
460 neon
->options
.timeout
.connect
= Z_DVAL_P(zoption
) > 0 && Z_DVAL_P(zoption
) < 1 ? 1 : round(Z_DVAL_P(zoption
));
464 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("ssl"), IS_ARRAY
))) {
467 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("verifypeer"), (void *) &zssl
)) {
468 if (!i_zend_is_true(*zssl
)) {
469 neon
->options
.ssl
.noverify
= 1;
472 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("key"), (void *) &zssl
)) {
473 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
474 ne_ssl_client_cert
*cc
= ne_ssl_clicert_read(Z_STRVAL_P(cpy
));
477 if (ne_ssl_clicert_encrypted(cc
)) {
478 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("keypasswd"), (void *) &zssl
)) {
479 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
481 if (NE_OK
== ne_ssl_clicert_decrypt(cc
, Z_STRVAL_P(cpy
))) {
482 neon
->options
.ssl
.clicert
= cc
;
489 if (cc
&& !neon
->options
.ssl
.clicert
) {
490 ne_ssl_clicert_free(cc
);
495 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("cert"), (void *) &zssl
)) {
496 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
497 ne_ssl_certificate
*tc
= ne_ssl_cert_read(Z_STRVAL_P(cpy
));
500 neon
->options
.ssl
.trucert
= tc
;
509 /* request handler ops */
511 static STATUS
php_http_neon_request_reset(php_http_request_t
*h
);
513 static php_http_request_t
*php_http_neon_request_init(php_http_request_t
*h
, void *dummy
)
515 php_http_neon_request_t
*ctx
;
517 ctx
= ecalloc(1, sizeof(*ctx
));
518 php_http_buffer_init(&ctx
->options
.headers
);
519 zend_hash_init(&ctx
->options
.cache
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
525 static php_http_request_t
*php_http_neon_request_copy(php_http_request_t
*from
, php_http_request_t
*to
)
527 TSRMLS_FETCH_FROM_CTX(from
->ts
);
530 return php_http_neon_request_init(to
, NULL
);
532 return php_http_request_init(NULL
, from
->ops
, from
->rf
, NULL TSRMLS_CC
);
536 static void php_http_neon_request_dtor(php_http_request_t
*h
)
538 php_http_neon_request_t
*ctx
= h
->ctx
;
540 php_http_neon_request_reset(h
);
541 php_http_buffer_dtor(&ctx
->options
.headers
);
542 zend_hash_destroy(&ctx
->options
.cache
);
544 php_http_request_progress_dtor(&ctx
->progress
);
550 static STATUS
php_http_neon_request_reset(php_http_request_t
*h
)
552 php_http_neon_request_t
*neon
= h
->ctx
;
554 php_http_buffer_reset(&neon
->options
.headers
);
555 STR_SET(neon
->options
.useragent
, NULL
);
556 STR_SET(neon
->options
.url
, NULL
);
557 neon
->options
.port
= 0;
558 neon
->options
.proxy
.type
= -1;
559 neon
->options
.proxy
.port
= 0;
560 STR_SET(neon
->options
.proxy
.host
, NULL
);
561 neon
->options
.auth
.proxy
.type
= 0;
562 STR_SET(neon
->options
.auth
.proxy
.user
, NULL
);
563 STR_SET(neon
->options
.auth
.proxy
.pass
, NULL
);
564 neon
->options
.auth
.http
.type
= 0;
565 STR_SET(neon
->options
.auth
.http
.user
, NULL
);
566 STR_SET(neon
->options
.auth
.http
.pass
, NULL
);
567 neon
->options
.ssl
.noverify
= 0;
568 if (neon
->options
.ssl
.clicert
) {
569 ne_ssl_clicert_free(neon
->options
.ssl
.clicert
);
570 neon
->options
.ssl
.clicert
= NULL
;
572 if (neon
->options
.ssl
.trucert
) {
573 ne_ssl_cert_free(neon
->options
.ssl
.trucert
);
574 neon
->options
.ssl
.trucert
= NULL
;
576 neon
->options
.redirects
= 0;
577 STR_SET(neon
->options
.cookiestore
, NULL
);
578 if (neon
->options
.interface
) {
579 ne_iaddr_free(neon
->options
.interface
);
580 neon
->options
.interface
= NULL
;
582 neon
->options
.maxfilesize
= 0;
583 neon
->options
.retry
.delay
= 0;
584 neon
->options
.retry
.count
= 0;
585 neon
->options
.timeout
.read
= 0;
586 neon
->options
.timeout
.connect
= 0;
588 php_http_request_progress_dtor(&neon
->progress
);
593 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
)
596 STATUS retval
= SUCCESS
;
602 php_http_neon_request_t
*neon
= h
->ctx
;
603 TSRMLS_FETCH_FROM_CTX(h
->ts
);
605 if (!(meth
= php_http_request_method_name(meth_id
))) {
606 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST_METHOD
, "Unsupported request method: %d (%s)", meth
, url
);
610 if (!(purl
= php_url_parse(url
))) {
611 php_http_error(HE_WARNING
, PHP_HTTP_E_URL
, "Could not parse url %s", url
);
615 if (neon
->options
.port
) {
616 purl
->port
= neon
->options
.port
;
617 } else if (!purl
->port
) {
619 if (strncascmp(purl
->scheme
, "http", 4)) {
620 #ifdef HAVE_GETSERVBYNAME
623 if ((se
= getservbyname(purl
->scheme
, "tcp")) && se
->s_port
) {
624 purl
->port
= ntohs(se
->s_port
);
627 } else if (purl
->scheme
[4] == 's') {
632 /* never returns NULL */
633 session
= ne_session_create(purl
->scheme
, purl
->host
, purl
->port
);
634 if (neon
->options
.proxy
.host
) {
635 switch (neon
->options
.proxy
.type
) {
636 case NE_SOCK_SOCKSV4
:
637 case NE_SOCK_SOCKSV4A
:
638 case NE_SOCK_SOCKSV5
:
639 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
);
643 ne_session_proxy(session
, neon
->options
.proxy
.host
, neon
->options
.proxy
.port
);
647 if (neon
->options
.interface
) {
648 ne_set_localaddr(session
, neon
->options
.interface
);
650 if (neon
->options
.useragent
) {
651 ne_set_useragent(session
, neon
->options
.useragent
);
653 if (neon
->options
.timeout
.read
) {
654 ne_set_read_timeout(session
, neon
->options
.timeout
.read
);
656 if (neon
->options
.timeout
.read
) {
657 ne_set_connect_timeout(session
, neon
->options
.timeout
.connect
);
659 if (neon
->options
.redirects
) {
660 ne_redirect_register(session
);
662 ne_hook_pre_send(session
, php_http_neon_pre_send_callback
, h
);
663 ne_hook_post_headers(session
, php_http_neon_post_headers_callback
, h
);
664 ne_set_notifier(session
, php_http_neon_progress_callback
, h
);
665 ne_ssl_set_verify(session
, php_http_neon_ssl_verify_callback
, h
);
666 if (neon
->options
.ssl
.clicert
) {
667 ne_ssl_set_clicert(session
, neon
->options
.ssl
.clicert
);
670 ne_ssl_trust_default_ca(session); */
671 if (neon
->options
.ssl
.trucert
) {
672 ne_ssl_trust_cert(session
, neon
->options
.ssl
.trucert
);
675 request
= ne_request_create(session
, meth
, purl
->path
/* . purl->query */);
677 /* RFC2616, section 4.3 (para. 4) states that »a message-body MUST NOT be included in a request if the
678 * specification of the request method (section 5.1.1) does not allow sending an entity-body in request.«
679 * Following the clause in section 5.1.1 (para. 2) that request methods »MUST be implemented with the
680 * same semantics as those specified in section 9« reveal that not any single defined HTTP/1.1 method
681 * does not allow a request body.
686 ne_set_request_body_provider(request
, php_http_message_body_size(body
), php_http_neon_read_callback
, h
);
692 switch (result
= ne_begin_request(request
)) {
695 char *buf
= emalloc(0x1000);
697 while (0 < (len
= ne_read_response_block(request
, buf
, 0x1000))) {
698 php_http_buffer_append(h
->buffer
, buf
, len
);
699 php_http_message_parser_parse(h
->parser
, h
->buffer
, PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES
, &h
->message
);
700 // php_http_message_body_append(&h->message->body, buf, len);
708 if (neon
->options
.redirects
-- > 0){
709 const ne_uri
*uri
= ne_redirect_location(session
);
712 char *url
= ne_uri_unparse(uri
);
714 retval
= php_http_neon_request_exec(h
, meth_id
, url
, body
);
721 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "%s; (%s)", ne_get_error(session
), url
);
723 add_property_long(EG(exception
), "neonCode", result
);
729 switch (result
= ne_end_request(request
)) {
734 if (neon
->options
.retry
.count
> tries
++) {
735 if (neon
->options
.retry
.delay
>= PHP_HTTP_DIFFSEC
) {
736 php_http_sleep(neon
->options
.retry
.delay
);
743 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "%s; (%s)", ne_get_error(session
), url
);
745 add_property_long(EG(exception
), "neonCode", result
);
751 ne_session_destroy(session
);
757 static STATUS
php_http_neon_request_setopt(php_http_request_t
*h
, php_http_request_setopt_opt_t opt
, void *arg
)
759 php_http_neon_request_t
*neon
= h
->ctx
;
762 case PHP_HTTP_REQUEST_OPT_SETTINGS
:
763 return set_options(h
, arg
);
766 case PHP_HTTP_REQUEST_OPT_PROGRESS_CALLBACK
:
767 if (neon
->progress
.in_cb
) {
768 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "Cannot change progress callback while executing it");
771 if (neon
->progress
.callback
) {
772 php_http_request_progress_dtor(&neon
->progress TSRMLS_CC
);
774 neon
->progress
.callback
= arg
;
777 case PHP_HTTP_REQUEST_OPT_COOKIES_ENABLE
:
778 case PHP_HTTP_REQUEST_OPT_COOKIES_RESET
:
779 case PHP_HTTP_REQUEST_OPT_COOKIES_RESET_SESSION
:
780 case PHP_HTTP_REQUEST_OPT_COOKIES_FLUSH
:
791 static STATUS
php_http_neon_request_getopt(php_http_request_t
*h
, php_http_request_getopt_opt_t opt
, void *arg
)
793 php_http_neon_request_t
*neon
= h
->ctx
;
796 case PHP_HTTP_REQUEST_OPT_PROGRESS_INFO
:
797 *((php_http_request_progress_t
**) arg
) = &neon
->progress
;
800 case PHP_HTTP_REQUEST_OPT_TRANSFER_INFO
:
810 static php_http_resource_factory_ops_t php_http_neon_resource_factory_ops
= {
816 static php_http_request_ops_t php_http_neon_request_ops
= {
817 &php_http_neon_resource_factory_ops
,
818 php_http_neon_request_init
,
819 php_http_neon_request_copy
,
820 php_http_neon_request_dtor
,
821 php_http_neon_request_reset
,
822 php_http_neon_request_exec
,
823 php_http_neon_request_setopt
,
824 php_http_neon_request_getopt
827 PHP_HTTP_API php_http_request_ops_t
*php_http_neon_get_request_ops(void)
829 return &php_http_neon_request_ops
;
832 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpNEON, method, 0, req_args)
833 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpNEON, method, 0)
834 #define PHP_HTTP_NEON_ME(method, visibility) PHP_ME(HttpNEON, method, PHP_HTTP_ARGS(HttpNEON, method), visibility)
835 #define PHP_HTTP_NEON_ALIAS(method, func) PHP_HTTP_STATIC_ME_ALIAS(method, func, PHP_HTTP_ARGS(HttpNEON, method))
836 #define PHP_HTTP_NEON_MALIAS(me, al, vis) ZEND_FENTRY(me, ZEND_MN(HttpNEON_##al), PHP_HTTP_ARGS(HttpNEON, al), vis)
838 PHP_HTTP_EMPTY_ARGS(__construct
);
840 zend_class_entry
*php_http_neon_class_entry
;
841 zend_function_entry php_http_neon_method_entry
[] = {
842 PHP_HTTP_NEON_ME(__construct
, ZEND_ACC_PRIVATE
|ZEND_ACC_CTOR
)
847 PHP_METHOD(HttpNEON
, __construct
) {
851 PHP_MINIT_FUNCTION(http_neon
)
853 php_http_request_factory_driver_t driver
= {
854 &php_http_neon_request_ops
,
857 if (SUCCESS
!= php_http_request_factory_add_driver(ZEND_STRL("neon"), &driver
)) {
861 PHP_HTTP_REGISTER_CLASS(http
, NEON
, http_neon
, php_http_neon_class_entry
, 0);
866 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_BASIC"), NE_AUTH_BASIC TSRMLS_CC
);
867 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_DIGEST"), NE_AUTH_DIGEST TSRMLS_CC
);
868 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_NTLM"), NE_AUTH_NTLM TSRMLS_CC
);
869 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_GSSAPI"), NE_AUTH_GSSAPI TSRMLS_CC
);
870 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_GSSNEG"), NE_AUTH_NEGOTIATE TSRMLS_CC
);
871 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_ANY"), NE_AUTH_ALL TSRMLS_CC
);
874 * Proxy Type Constants
876 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS4"), NE_SOCK_SOCKSV4 TSRMLS_CC
);
877 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS4A"), NE_SOCK_SOCKSV4A TSRMLS_CC
);
878 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS5"), NE_SOCK_SOCKSV5 TSRMLS_CC
);
879 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_HTTP"), -1 TSRMLS_CC
);
881 if (NE_OK
!= ne_sock_init()) {
887 PHP_MSHUTDOWN_FUNCTION(http_neon
)