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
;
218 TSRMLS_FETCH_FROM_CTX(h
->ts
);
221 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyhost"), IS_STRING
))) {
222 neon
->options
.proxy
.host
= Z_STRVAL_P(zoption
);
225 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyauth"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
226 char *colon
= strchr(Z_STRVAL_P(zoption
), ':');
229 STR_SET(neon
->options
.auth
.proxy
.user
, estrndup(Z_STRVAL_P(zoption
), colon
- Z_STRVAL_P(zoption
)));
230 STR_SET(neon
->options
.auth
.proxy
.pass
, estrdup(colon
+ 1));
232 STR_SET(neon
->options
.auth
.proxy
.user
, estrdup(Z_STRVAL_P(zoption
)));
236 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyauthtype"), IS_LONG
))) {
237 neon
->options
.auth
.proxy
.type
= Z_LVAL_P(zoption
);
239 neon
->options
.auth
.proxy
.type
= NE_AUTH_ALL
;
244 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxyport"), IS_LONG
))) {
245 neon
->options
.proxy
.port
= Z_LVAL_P(zoption
);
247 neon
->options
.proxy
.port
= 0;
251 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("proxytype"), IS_LONG
))) {
252 neon
->options
.proxy
.type
= Z_LVAL_P(zoption
);
254 neon
->options
.proxy
.type
= -1;
258 /* outgoing interface */
259 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("interface"), IS_STRING
))) {
260 if (!(neon
->options
.interface
= ne_iaddr_parse(Z_STRVAL_P(zoption
), ne_iaddr_ipv4
))) {
261 neon
->options
.interface
= ne_iaddr_parse(Z_STRVAL_P(zoption
), ne_iaddr_ipv6
);
266 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("port"), IS_LONG
))) {
267 neon
->options
.port
= Z_LVAL_P(zoption
);
271 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("httpauth"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
272 char *colon
= strchr(Z_STRVAL_P(zoption
), ':');
275 STR_SET(neon
->options
.auth
.http
.user
, estrndup(Z_STRVAL_P(zoption
), colon
- Z_STRVAL_P(zoption
)));
276 STR_SET(neon
->options
.auth
.http
.pass
, estrdup(colon
+ 1));
278 STR_SET(neon
->options
.auth
.http
.user
, estrdup(Z_STRVAL_P(zoption
)));
281 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("httpauthtype"), IS_LONG
))) {
282 neon
->options
.auth
.http
.type
= Z_LVAL_P(zoption
);
284 neon
->options
.auth
.http
.type
= NE_AUTH_ALL
;
287 /* redirects, defaults to 0 */
288 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("redirect"), IS_LONG
))) {
289 neon
->options
.redirects
= Z_LVAL_P(zoption
);
291 neon
->options
.redirects
= 0;
294 /* retries, defaults to 0 */
295 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("retrycount"), IS_LONG
))) {
296 neon
->options
.retry
.count
= Z_LVAL_P(zoption
);
297 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("retrydelay"), IS_DOUBLE
))) {
298 neon
->options
.retry
.delay
= Z_DVAL_P(zoption
);
300 neon
->options
.retry
.delay
= 0;
303 neon
->options
.retry
.count
= 0;
307 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("referer"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
308 neon
->options
.referer
= Z_STRVAL_P(zoption
);
311 /* useragent, default "PECL::HTTP/version (PHP/version)" */
312 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("useragent"), IS_STRING
))) {
313 /* allow to send no user agent, not even default one */
314 if (Z_STRLEN_P(zoption
)) {
315 neon
->options
.useragent
= Z_STRVAL_P(zoption
);
317 neon
->options
.useragent
= NULL
;
322 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("resume"), IS_LONG
)) && (Z_LVAL_P(zoption
) > 0)) {
323 php_http_buffer_appendf(&neon
->options
.headers
, "Range: bytes=%ld-" PHP_HTTP_CRLF
, Z_LVAL_P(zoption
));
326 /* or range of kind array(array(0,499), array(100,1499)) */
327 else if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("range"), IS_ARRAY
)) && zend_hash_num_elements(Z_ARRVAL_P(zoption
))) {
328 HashPosition pos1
, pos2
;
329 zval
**rr
, **rb
, **re
;
330 php_http_buffer_t rs
;
332 php_http_buffer_init(&rs
);
333 FOREACH_VAL(pos1
, zoption
, rr
) {
334 if (Z_TYPE_PP(rr
) == IS_ARRAY
) {
335 zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(rr
), &pos2
);
336 if (SUCCESS
== zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr
), (void *) &rb
, &pos2
)) {
337 zend_hash_move_forward_ex(Z_ARRVAL_PP(rr
), &pos2
);
338 if (SUCCESS
== zend_hash_get_current_data_ex(Z_ARRVAL_PP(rr
), (void *) &re
, &pos2
)) {
339 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))) &&
340 ((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)))) {
341 zval
*rbl
= php_http_ztyp(IS_LONG
, *rb
);
342 zval
*rel
= php_http_ztyp(IS_LONG
, *re
);
344 if ((Z_LVAL_P(rbl
) >= 0) && (Z_LVAL_P(rel
) >= 0)) {
345 php_http_buffer_appendf(&rs
, "%ld-%ld,", Z_LVAL_P(rbl
), Z_LVAL_P(rel
));
355 if (PHP_HTTP_BUFFER_LEN(&rs
)) {
356 /* ignore last comma */
357 int used
= rs
.used
> INT_MAX
? INT_MAX
: rs
.used
;
358 php_http_buffer_appendf(&neon
->options
.headers
, "Range: bytes=%.*s" PHP_HTTP_CRLF
, used
- 1, rs
.data
);
361 php_http_buffer_dtor(&rs
);
364 /* additional headers, array('name' => 'value') */
365 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("headers"), IS_ARRAY
))) {
366 php_http_array_hashkey_t header_key
= php_http_array_hashkey_init(0);
370 FOREACH_KEYVAL(pos
, zoption
, header_key
, header_val
) {
371 if (header_key
.type
== HASH_KEY_IS_STRING
) {
372 zval
*header_cpy
= php_http_ztyp(IS_STRING
, *header_val
);
374 if (!strcasecmp(header_key
.str
, "range")) {
377 php_http_buffer_appendf(&neon
->options
.headers
, "%s: %s" PHP_HTTP_CRLF
, header_key
.str
, Z_STRVAL_P(header_cpy
));
378 zval_ptr_dtor(&header_cpy
);
383 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("etag"), IS_STRING
)) && Z_STRLEN_P(zoption
)) {
384 php_http_buffer_appends(&neon
->options
.headers
, "If-");
386 php_http_buffer_appends(&neon
->options
.headers
, "None-");
388 php_http_buffer_appends(&neon
->options
.headers
, "Match: ");
390 if ((Z_STRVAL_P(zoption
)[0] == '"') && (Z_STRVAL_P(zoption
)[Z_STRLEN_P(zoption
)-1] == '"')) {
391 php_http_buffer_appendl(&neon
->options
.headers
, Z_STRVAL_P(zoption
));
393 php_http_buffer_appendf(&neon
->options
.headers
, "\"%s\"" PHP_HTTP_CRLF
, Z_STRVAL_P(zoption
));
397 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("compress"), IS_BOOL
)) && Z_LVAL_P(zoption
)) {
398 php_http_buffer_appends(&neon
->options
.headers
, "Accept-Encoding: gzip;q=1.0,deflate;q=0.5" PHP_HTTP_CRLF
);
402 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("lastmodified"), IS_LONG
))) {
403 if (Z_LVAL_P(zoption
)) {
404 time_t time
= Z_LVAL_P(zoption
) > 0 ? Z_LVAL_P(zoption
) : PHP_HTTP_G
->env
.request
.time
+ Z_LVAL_P(zoption
);
405 char *date
= php_format_date(ZEND_STRS(PHP_HTTP_DATE_FORMAT
), time
, 0 TSRMLS_CC
);
407 php_http_buffer_appends(&neon
->options
.headers
, "If-");
409 php_http_buffer_appends(&neon
->options
.headers
, "Un");
411 php_http_buffer_appendf(&neon
->options
.headers
, "Modified-Since: %s" PHP_HTTP_CRLF
, date
);
416 /* cookies, array('name' => 'value') */
417 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("cookies"), IS_ARRAY
))) {
418 php_http_buffer_t cookies
;
420 php_http_buffer_init(&cookies
);
421 if (zend_hash_num_elements(Z_ARRVAL_P(zoption
))) {
422 zval
*urlenc_cookies
= NULL
;
424 php_http_buffer_appends(&neon
->options
.headers
, "Cookie: ");
426 /* check whether cookies should not be urlencoded; default is to urlencode them */
427 if ((!(urlenc_cookies
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("encodecookies"), IS_BOOL
))) || Z_BVAL_P(urlenc_cookies
)) {
428 php_http_url_encode_hash_ex(HASH_OF(zoption
), &neon
->options
.headers
, ZEND_STRS("; "), ZEND_STRS("="), NULL
, 0 TSRMLS_CC
);
431 php_http_array_hashkey_t cookie_key
= php_http_array_hashkey_init(0);
434 FOREACH_KEYVAL(pos
, zoption
, cookie_key
, cookie_val
) {
435 if (cookie_key
.type
== HASH_KEY_IS_STRING
) {
436 zval
*val
= php_http_ztyp(IS_STRING
, *cookie_val
);
437 php_http_buffer_appendf(&neon
->options
.headers
, "%s=%s; ", cookie_key
.str
, Z_STRVAL_P(val
));
443 neon
->options
.headers
.used
-= lenof("; ");
444 php_http_buffer_appends(&neon
->options
.headers
, PHP_HTTP_CRLF
);
446 /* cookiestore, read initial cookies from that file and store cookies back into that file */
447 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("cookiestore"), IS_STRING
))) {
448 neon
->options
.cookiestore
= Z_STRVAL_P(zoption
);
452 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("maxfilesize"), IS_LONG
))) {
453 neon
->options
.maxfilesize
= Z_LVAL_P(zoption
);
457 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("timeout"), IS_DOUBLE
))) {
458 neon
->options
.timeout
.read
= Z_DVAL_P(zoption
) > 0 && Z_DVAL_P(zoption
) < 1 ? 1 : round(Z_DVAL_P(zoption
));
460 /* connecttimeout, defaults to 0 */
461 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("connecttimeout"), IS_DOUBLE
))) {
462 neon
->options
.timeout
.connect
= Z_DVAL_P(zoption
) > 0 && Z_DVAL_P(zoption
) < 1 ? 1 : round(Z_DVAL_P(zoption
));
466 if ((zoption
= get_option(&neon
->options
.cache
, options
, ZEND_STRS("ssl"), IS_ARRAY
))) {
469 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("verifypeer"), (void *) &zssl
)) {
470 if (!i_zend_is_true(*zssl
)) {
471 neon
->options
.ssl
.noverify
= 1;
474 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("key"), (void *) &zssl
)) {
475 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
476 ne_ssl_client_cert
*cc
= ne_ssl_clicert_read(Z_STRVAL_P(cpy
));
479 if (ne_ssl_clicert_encrypted(cc
)) {
480 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("keypasswd"), (void *) &zssl
)) {
481 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
483 if (NE_OK
== ne_ssl_clicert_decrypt(cc
, Z_STRVAL_P(cpy
))) {
484 neon
->options
.ssl
.clicert
= cc
;
491 if (cc
&& !neon
->options
.ssl
.clicert
) {
492 ne_ssl_clicert_free(cc
);
497 if (SUCCESS
== zend_hash_find(Z_ARRVAL_P(zoption
), ZEND_STRS("cert"), (void *) &zssl
)) {
498 zval
*cpy
= php_http_ztyp(IS_STRING
, *zssl
);
499 ne_ssl_certificate
*tc
= ne_ssl_cert_read(Z_STRVAL_P(cpy
));
502 neon
->options
.ssl
.trucert
= tc
;
511 /* request handler ops */
513 static STATUS
php_http_neon_request_reset(php_http_request_t
*h
);
515 static php_http_request_t
*php_http_neon_request_init(php_http_request_t
*h
, void *dummy
)
517 php_http_neon_request_t
*ctx
;
519 ctx
= ecalloc(1, sizeof(*ctx
));
520 php_http_buffer_init(&ctx
->options
.headers
);
521 zend_hash_init(&ctx
->options
.cache
, 0, NULL
, ZVAL_PTR_DTOR
, 0);
527 static php_http_request_t
*php_http_neon_request_copy(php_http_request_t
*from
, php_http_request_t
*to
)
529 TSRMLS_FETCH_FROM_CTX(from
->ts
);
532 return php_http_neon_request_init(to
, NULL
);
534 return php_http_request_init(NULL
, from
->ops
, from
->rf
, NULL TSRMLS_CC
);
538 static void php_http_neon_request_dtor(php_http_request_t
*h
)
540 php_http_neon_request_t
*ctx
= h
->ctx
;
541 TSRMLS_FETCH_FROM_CTX(h
->ts
);
543 php_http_neon_request_reset(h
);
544 php_http_buffer_dtor(&ctx
->options
.headers
);
545 zend_hash_destroy(&ctx
->options
.cache
);
547 php_http_request_progress_dtor(&ctx
->progress TSRMLS_CC
);
553 static STATUS
php_http_neon_request_reset(php_http_request_t
*h
)
555 php_http_neon_request_t
*neon
= h
->ctx
;
556 TSRMLS_FETCH_FROM_CTX(h
->ts
);
558 php_http_buffer_reset(&neon
->options
.headers
);
559 STR_SET(neon
->options
.useragent
, NULL
);
560 STR_SET(neon
->options
.url
, NULL
);
561 neon
->options
.port
= 0;
562 neon
->options
.proxy
.type
= -1;
563 neon
->options
.proxy
.port
= 0;
564 STR_SET(neon
->options
.proxy
.host
, NULL
);
565 neon
->options
.auth
.proxy
.type
= 0;
566 STR_SET(neon
->options
.auth
.proxy
.user
, NULL
);
567 STR_SET(neon
->options
.auth
.proxy
.pass
, NULL
);
568 neon
->options
.auth
.http
.type
= 0;
569 STR_SET(neon
->options
.auth
.http
.user
, NULL
);
570 STR_SET(neon
->options
.auth
.http
.pass
, NULL
);
571 neon
->options
.ssl
.noverify
= 0;
572 if (neon
->options
.ssl
.clicert
) {
573 ne_ssl_clicert_free(neon
->options
.ssl
.clicert
);
574 neon
->options
.ssl
.clicert
= NULL
;
576 if (neon
->options
.ssl
.trucert
) {
577 ne_ssl_cert_free(neon
->options
.ssl
.trucert
);
578 neon
->options
.ssl
.trucert
= NULL
;
580 neon
->options
.redirects
= 0;
581 STR_SET(neon
->options
.cookiestore
, NULL
);
582 if (neon
->options
.interface
) {
583 ne_iaddr_free(neon
->options
.interface
);
584 neon
->options
.interface
= NULL
;
586 neon
->options
.maxfilesize
= 0;
587 neon
->options
.retry
.delay
= 0;
588 neon
->options
.retry
.count
= 0;
589 neon
->options
.timeout
.read
= 0;
590 neon
->options
.timeout
.connect
= 0;
592 php_http_request_progress_dtor(&neon
->progress TSRMLS_CC
);
597 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
)
600 STATUS retval
= SUCCESS
;
606 php_http_neon_request_t
*neon
= h
->ctx
;
607 TSRMLS_FETCH_FROM_CTX(h
->ts
);
609 if (!(meth
= php_http_request_method_name(meth_id TSRMLS_CC
))) {
610 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST_METHOD
, "Unsupported request method: %d (%s)", meth
, url
);
614 if (!(purl
= php_url_parse(url
))) {
615 php_http_error(HE_WARNING
, PHP_HTTP_E_URL
, "Could not parse url %s", url
);
619 if (neon
->options
.port
) {
620 purl
->port
= neon
->options
.port
;
621 } else if (!purl
->port
) {
623 if (strncasecmp(purl
->scheme
, "http", 4)) {
624 #ifdef HAVE_GETSERVBYNAME
627 if ((se
= getservbyname(purl
->scheme
, "tcp")) && se
->s_port
) {
628 purl
->port
= ntohs(se
->s_port
);
631 } else if (purl
->scheme
[4] == 's') {
636 /* never returns NULL */
637 session
= ne_session_create(purl
->scheme
, purl
->host
, purl
->port
);
638 if (neon
->options
.proxy
.host
) {
639 switch (neon
->options
.proxy
.type
) {
640 case NE_SOCK_SOCKSV4
:
641 case NE_SOCK_SOCKSV4A
:
642 case NE_SOCK_SOCKSV5
:
643 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
);
647 ne_session_proxy(session
, neon
->options
.proxy
.host
, neon
->options
.proxy
.port
);
651 if (neon
->options
.interface
) {
652 ne_set_localaddr(session
, neon
->options
.interface
);
654 if (neon
->options
.useragent
) {
655 ne_set_useragent(session
, neon
->options
.useragent
);
657 if (neon
->options
.timeout
.read
) {
658 ne_set_read_timeout(session
, neon
->options
.timeout
.read
);
660 if (neon
->options
.timeout
.read
) {
661 ne_set_connect_timeout(session
, neon
->options
.timeout
.connect
);
663 if (neon
->options
.redirects
) {
664 ne_redirect_register(session
);
666 ne_hook_pre_send(session
, php_http_neon_pre_send_callback
, h
);
667 ne_hook_post_headers(session
, php_http_neon_post_headers_callback
, h
);
668 ne_set_notifier(session
, php_http_neon_progress_callback
, h
);
669 ne_ssl_set_verify(session
, php_http_neon_ssl_verify_callback
, h
);
670 if (neon
->options
.ssl
.clicert
) {
671 ne_ssl_set_clicert(session
, neon
->options
.ssl
.clicert
);
674 ne_ssl_trust_default_ca(session); */
675 if (neon
->options
.ssl
.trucert
) {
676 ne_ssl_trust_cert(session
, neon
->options
.ssl
.trucert
);
679 request
= ne_request_create(session
, meth
, purl
->path
/* . purl->query */);
681 /* RFC2616, section 4.3 (para. 4) states that »a message-body MUST NOT be included in a request if the
682 * specification of the request method (section 5.1.1) does not allow sending an entity-body in request.«
683 * Following the clause in section 5.1.1 (para. 2) that request methods »MUST be implemented with the
684 * same semantics as those specified in section 9« reveal that not any single defined HTTP/1.1 method
685 * does not allow a request body.
690 ne_set_request_body_provider(request
, php_http_message_body_size(body
), php_http_neon_read_callback
, h
);
696 switch (result
= ne_begin_request(request
)) {
699 char *buf
= emalloc(0x1000);
701 while (0 < (len
= ne_read_response_block(request
, buf
, 0x1000))) {
702 php_http_buffer_append(h
->buffer
, buf
, len
);
703 php_http_message_parser_parse(h
->parser
, h
->buffer
, PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES
, &h
->message
);
704 // php_http_message_body_append(&h->message->body, buf, len);
712 if (neon
->options
.redirects
-- > 0){
713 const ne_uri
*uri
= ne_redirect_location(session
);
716 char *url
= ne_uri_unparse(uri
);
718 retval
= php_http_neon_request_exec(h
, meth_id
, url
, body
);
725 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "%s; (%s)", ne_get_error(session
), url
);
727 add_property_long(EG(exception
), "neonCode", result
);
733 switch (result
= ne_end_request(request
)) {
738 if (neon
->options
.retry
.count
> tries
++) {
739 if (neon
->options
.retry
.delay
>= PHP_HTTP_DIFFSEC
) {
740 php_http_sleep(neon
->options
.retry
.delay
);
747 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "%s; (%s)", ne_get_error(session
), url
);
749 add_property_long(EG(exception
), "neonCode", result
);
755 ne_session_destroy(session
);
761 static STATUS
php_http_neon_request_setopt(php_http_request_t
*h
, php_http_request_setopt_opt_t opt
, void *arg
)
763 php_http_neon_request_t
*neon
= h
->ctx
;
764 TSRMLS_FETCH_FROM_CTX(h
->ts
);
767 case PHP_HTTP_REQUEST_OPT_SETTINGS
:
768 return set_options(h
, arg
);
771 case PHP_HTTP_REQUEST_OPT_PROGRESS_CALLBACK
:
772 if (neon
->progress
.in_cb
) {
773 php_http_error(HE_WARNING
, PHP_HTTP_E_REQUEST
, "Cannot change progress callback while executing it");
776 if (neon
->progress
.callback
) {
777 php_http_request_progress_dtor(&neon
->progress TSRMLS_CC
);
779 neon
->progress
.callback
= arg
;
782 case PHP_HTTP_REQUEST_OPT_COOKIES_ENABLE
:
783 case PHP_HTTP_REQUEST_OPT_COOKIES_RESET
:
784 case PHP_HTTP_REQUEST_OPT_COOKIES_RESET_SESSION
:
785 case PHP_HTTP_REQUEST_OPT_COOKIES_FLUSH
:
796 static STATUS
php_http_neon_request_getopt(php_http_request_t
*h
, php_http_request_getopt_opt_t opt
, void *arg
)
798 php_http_neon_request_t
*neon
= h
->ctx
;
801 case PHP_HTTP_REQUEST_OPT_PROGRESS_INFO
:
802 *((php_http_request_progress_t
**) arg
) = &neon
->progress
;
805 case PHP_HTTP_REQUEST_OPT_TRANSFER_INFO
:
815 static php_http_resource_factory_ops_t php_http_neon_resource_factory_ops
= {
821 static php_http_request_ops_t php_http_neon_request_ops
= {
822 &php_http_neon_resource_factory_ops
,
823 php_http_neon_request_init
,
824 php_http_neon_request_copy
,
825 php_http_neon_request_dtor
,
826 php_http_neon_request_reset
,
827 php_http_neon_request_exec
,
828 php_http_neon_request_setopt
,
829 php_http_neon_request_getopt
832 PHP_HTTP_API php_http_request_ops_t
*php_http_neon_get_request_ops(void)
834 return &php_http_neon_request_ops
;
837 #define PHP_HTTP_BEGIN_ARGS(method, req_args) PHP_HTTP_BEGIN_ARGS_EX(HttpNEON, method, 0, req_args)
838 #define PHP_HTTP_EMPTY_ARGS(method) PHP_HTTP_EMPTY_ARGS_EX(HttpNEON, method, 0)
839 #define PHP_HTTP_NEON_ME(method, visibility) PHP_ME(HttpNEON, method, PHP_HTTP_ARGS(HttpNEON, method), visibility)
840 #define PHP_HTTP_NEON_ALIAS(method, func) PHP_HTTP_STATIC_ME_ALIAS(method, func, PHP_HTTP_ARGS(HttpNEON, method))
841 #define PHP_HTTP_NEON_MALIAS(me, al, vis) ZEND_FENTRY(me, ZEND_MN(HttpNEON_##al), PHP_HTTP_ARGS(HttpNEON, al), vis)
843 PHP_HTTP_EMPTY_ARGS(__construct
);
845 zend_class_entry
*php_http_neon_class_entry
;
846 zend_function_entry php_http_neon_method_entry
[] = {
847 PHP_HTTP_NEON_ME(__construct
, ZEND_ACC_PRIVATE
|ZEND_ACC_CTOR
)
852 PHP_METHOD(HttpNEON
, __construct
) {
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
)) {
866 PHP_HTTP_REGISTER_CLASS(http
, NEON
, http_neon
, php_http_neon_class_entry
, 0);
871 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_BASIC"), NE_AUTH_BASIC TSRMLS_CC
);
872 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_DIGEST"), NE_AUTH_DIGEST TSRMLS_CC
);
873 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_NTLM"), NE_AUTH_NTLM TSRMLS_CC
);
874 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_GSSAPI"), NE_AUTH_GSSAPI TSRMLS_CC
);
875 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_GSSNEG"), NE_AUTH_NEGOTIATE TSRMLS_CC
);
876 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("AUTH_ANY"), NE_AUTH_ALL TSRMLS_CC
);
879 * Proxy Type Constants
881 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS4"), NE_SOCK_SOCKSV4 TSRMLS_CC
);
882 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS4A"), NE_SOCK_SOCKSV4A TSRMLS_CC
);
883 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_SOCKS5"), NE_SOCK_SOCKSV5 TSRMLS_CC
);
884 zend_declare_class_constant_long(php_http_neon_class_entry
, ZEND_STRL("PROXY_HTTP"), -1 TSRMLS_CC
);
886 if (NE_OK
!= ne_sock_init()) {
892 PHP_MSHUTDOWN_FUNCTION(http_neon
)