From: Michael Wallner Date: Fri, 16 Dec 2005 16:42:30 +0000 (+0000) Subject: - highly experimental stream wrapper X-Git-Tag: RELEASE_0_21_0~67 X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=commitdiff_plain;h=16dba799dae126258bbb09e2f699d5d7954fac17 - highly experimental stream wrapper --- diff --git a/config.m4 b/config.m4 index bf19c24..457821b 100644 --- a/config.m4 +++ b/config.m4 @@ -4,6 +4,9 @@ dnl vim: noet ts=1 sw=1 PHP_ARG_ENABLE([http], [whether to enable extended HTTP support], [ --enable-http Enable extended HTTP support]) +PHP_ARG_ENABLE([http-stream-wrapper], [whether to enable experimental stream wrapper], +[ --enable-http-stream-wrapper + Enable experimental stream wrapper support], no, no) PHP_ARG_WITH([http-curl-requests], [whether to enable cURL HTTP requests], [ --with-http-curl-requests[=CURLDIR] With cURL HTTP request support]) @@ -225,7 +228,7 @@ dnl ---- http_api.c http_cache_api.c http_request_api.c http_date_api.c \ http_headers_api.c http_message_api.c http_send_api.c http_url_api.c \ http_info_api.c http_request_method_api.c http_encoding_api.c \ - http_filter_api.c http_request_body_api.c" + http_filter_api.c http_request_body_api.c http_wrapper_api.h" PHP_NEW_EXTENSION([http], $PHP_HTTP_SOURCES, $ext_shared) PHP_ADD_BUILD_DIR($ext_builddir/phpstr, 1) PHP_SUBST([HTTP_SHARED_LIBADD]) @@ -235,7 +238,8 @@ dnl ---- php_http_request_api.h php_http_request_method_api.h php_http_send_api.h php_http_url_api.h \ php_http_encoding_api.h phpstr/phpstr.h missing.h php_http_request_body_api.h \ php_http_exception_object.h php_http_message_object.h php_http_request_object.h \ - php_http_requestpool_object.h php_http_response_object.h php_http_util_object.h" + php_http_requestpool_object.h php_http_response_object.h php_http_util_object.h \ + php_http_wrapper_api.h" ifdef([PHP_INSTALL_HEADERS], [ PHP_INSTALL_HEADERS(ext/http, $PHP_HTTP_HEADERS) ], [ @@ -244,4 +248,8 @@ dnl ---- ]) AC_DEFINE([HAVE_HTTP], [1], [Have extended HTTP support]) + + if test "$PHP_HTTP_STREAM_WRAPPER" = "yes"; then + AC_DEFINE([HTTP_HAVE_WRAPPER], [1], [Have experimental HTTP stream wrapper]) + fi fi diff --git a/config.w32 b/config.w32 index 7db93da..2847a2c 100644 --- a/config.w32 +++ b/config.w32 @@ -2,6 +2,7 @@ // $Id$ ARG_ENABLE("http", "whether to enable extended HTTP support", "no"); +ARG_ENABLE("http-stream-wrapper", "whether to enable experimental stream wrapper", "no", "no"); function check_for_main_ext(ext, header) { @@ -54,12 +55,16 @@ if (PHP_HTTP != "no") { "http_request_api.c http_date_api.c http_headers_api.c "+ "http_message_api.c http_send_api.c http_url_api.c "+ "http_info_api.c http_request_method_api.c http_encoding_api.c "+ - "http_filter_api.c http_request_body_api.c", + "http_filter_api.c http_request_body_api.c http_wrapper_api.c", null, "/I\"" + configure_module_dirname + "/phpstr\""); ADD_SOURCES(configure_module_dirname + "/phpstr", "phpstr.c", "http"); AC_DEFINE("HAVE_HTTP", 1, "Have extended HTTP support"); + if (PHP_HTTP_STREAM_WRAPPER == "yes") { + AC_DEFINE("HTTP_HAVE_WRAPPER", 1, "Have experimental HTTP stream wrapper"); + } + if (PHP_DEBUG != "no") { ADD_FLAG("CFLAGS_HTTP", "/W3"); } diff --git a/http.c b/http.c index eb8f5e9..2beb4af 100644 --- a/http.c +++ b/http.c @@ -34,6 +34,9 @@ #include "php_http_request_method_api.h" #ifdef HTTP_HAVE_CURL # include "php_http_request_api.h" +# ifdef HTTP_HAVE_WRAPPER +# include "php_http_wrapper_api.h" +# endif #endif #ifdef ZEND_ENGINE_2 @@ -223,6 +226,9 @@ PHP_MINIT_FUNCTION(http) (SUCCESS != PHP_MINIT_CALL(http_headers)) || #ifdef HTTP_HAVE_CURL (SUCCESS != PHP_MINIT_CALL(http_request)) || +# ifdef HTTP_HAVE_WRAPPER + (SUCCESS != PHP_MINIT_CALL(http_wrapper)) || +# endif #endif /* HTTP_HAVE_CURL */ (SUCCESS != PHP_MINIT_CALL(http_request_method))) { return FAILURE; diff --git a/http_wrapper_api.c b/http_wrapper_api.c new file mode 100644 index 0000000..981d833 --- /dev/null +++ b/http_wrapper_api.c @@ -0,0 +1,353 @@ +/* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2005, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define HTTP_WANT_CURL +#include "php_http.h" + +#if defined(HTTP_HAVE_CURL) && defined(HTTP_HAVE_WRAPPER) + +#include "php_streams.h" + +#include "php_http_api.h" +#include "php_http_wrapper_api.h" +#include "php_http_request_api.h" +#include "php_http_headers_api.h" +#include "php_http_message_api.h" +#include "php_http_url_api.h" + +ZEND_EXTERN_MODULE_GLOBALS(http); + +typedef struct { + phpstr b; + size_t p; +} http_wrapper_t; + +static php_stream_ops http_stream_ops; + +PHP_MINIT_FUNCTION(http_wrapper) +{ + php_unregister_url_stream_wrapper("http" TSRMLS_CC); + return php_register_url_stream_wrapper("http", &http_wrapper TSRMLS_CC); +} + +php_stream *http_wrapper_ex(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) +{ + zval opt_array, **opt_elem, *ssl_opt; + http_request request; + php_stream *stream; + + if (strpbrk(mode, "awx+")) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "HTTP wrapper does not support writeable connections."); + return NULL; + } + + INIT_PZVAL(&opt_array); + array_init(&opt_array); + + http_request_init(&request); + request.url = estrdup(path); + + /* + HTTP options + */ + + /* request method */ + if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "method", &opt_elem)) { + switch (Z_TYPE_PP(opt_elem)) + { + case IS_LONG: + if (http_request_method_exists(0, Z_LVAL_PP(opt_elem), NULL)) { + request.meth = Z_LVAL_PP(opt_elem); + } + break; + default: + { + ulong method; + zval *orig = *opt_elem; + convert_to_string_ex(opt_elem); + if ((method = http_request_method_exists(1, 0, Z_STRVAL_PP(opt_elem)))) { + request.meth = method; + } + if (orig != *opt_elem) zval_ptr_dtor(opt_elem); + } + break; + } + } + + /* header */ + if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "header", &opt_elem)) { + if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) { + zval *headers; + + MAKE_STD_ZVAL(headers); + array_init(headers); + if (SUCCESS == http_parse_headers(Z_STRVAL_PP(opt_elem), headers)) { + add_assoc_zval(&opt_array, "headers", headers); + } else { + zval_ptr_dtor(&headers); + } + } + } + + /* user_agent */ + if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "user_agent", &opt_elem)) { + if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) { + add_assoc_stringl(&opt_array, "useragent", Z_STRVAL_PP(opt_elem), Z_STRLEN_PP(opt_elem), 1); + } + } + + /* proxy */ + if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "proxy", &opt_elem)) { + if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) { + if (strstr(Z_STRVAL_PP(opt_elem), "://")) { + if (!strncasecmp(Z_STRVAL_PP(opt_elem), "tcp://", lenof("tcp://"))) { + phpstr proxy; + + phpstr_init(&proxy); + phpstr_appends(&proxy, "http://"); + phpstr_append(&proxy, Z_STRVAL_PP(opt_elem)+lenof("tcp://"), Z_STRLEN_PP(opt_elem)-lenof("tcp://")); + phpstr_fix(&proxy); + + add_assoc_stringl(&opt_array, "proxy", PHPSTR_VAL(&proxy), PHPSTR_LEN(&proxy), 0); + } + } else { + ZVAL_ADDREF(*opt_elem); + add_assoc_zval(&opt_array, "proxy", *opt_elem); + } + } + } + + /* redirect */ + if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "max_redirects", &opt_elem)) { + zval *orig = *opt_elem; + + convert_to_long_ex(opt_elem); + if (0 < Z_LVAL_PP(opt_elem)) { + add_assoc_long(&opt_array, "redirect", Z_LVAL_PP(opt_elem)-1); + } + if (orig != *opt_elem) { + zval_ptr_dtor(opt_elem); + } + } + + /* request body */ + if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "content", &opt_elem)) { + if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) { + request.body = http_request_body_init_ex(request.body, HTTP_REQUEST_BODY_CSTRING, + estrndup(Z_STRVAL_PP(opt_elem), Z_STRLEN_PP(opt_elem)), Z_STRLEN_PP(opt_elem), 1); + } + } + + /* + SSL options + */ + + MAKE_STD_ZVAL(ssl_opt); + array_init(ssl_opt); + + /* verify_peer */ + if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "verify_peer", &opt_elem)) { + if (zval_is_true(*opt_elem)) { + add_assoc_bool(ssl_opt, "verifypeer", 1); + } + } + + /* cafile */ + if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "cafile", &opt_elem)) { + if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) { + ZVAL_ADDREF(*opt_elem); + add_assoc_zval(ssl_opt, "cainfo", *opt_elem); + } + } + + /* capath */ + if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "capath", &opt_elem)) { + if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) { + ZVAL_ADDREF(*opt_elem); + add_assoc_zval(ssl_opt, "capath", *opt_elem); + } + } + + /* local_cert */ + if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "local_cert", &opt_elem)) { + if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) { + zval **pass = NULL; + + ZVAL_ADDREF(*opt_elem); + add_assoc_zval(ssl_opt, "cert", *opt_elem); + + if (SUCCESS == php_stream_context_get_option(context, wrapper->wops->label, "passphrase", &opt_elem)) { + if (Z_TYPE_PP(opt_elem) == IS_STRING && Z_STRLEN_PP(opt_elem)) { + ZVAL_ADDREF(*opt_elem); + add_assoc_zval(ssl_opt, "certpasswd", *opt_elem); + } + } + } + } + + add_assoc_zval(&opt_array, "ssl", ssl_opt); + + if (SUCCESS == http_request_prepare(&request, Z_ARRVAL(opt_array))) { + http_message *msg; + + http_request_exec(&request); + + if (opened_path) { + char *url; + if (CURLE_OK == curl_easy_getinfo(request.ch, CURLINFO_EFFECTIVE_URL, &url) && url) { + *opened_path = estrdup(url); + } else { + *opened_path = NULL; + } + } + + if ((msg = http_message_parse(PHPSTR_VAL(&request.conv.response), PHPSTR_LEN(&request.conv.response)))) { + http_wrapper_t *w = emalloc(sizeof(http_wrapper_t)); + + w->p = 0; + phpstr_init(&w->b); + phpstr_append(&w->b, PHPSTR_VAL(&msg->body), PHPSTR_LEN(&msg->body)); + + stream = php_stream_alloc(&http_stream_ops, w, NULL, mode); + + http_message_free(&msg); + } + } + + http_request_dtor(&request); + + zval_dtor(&opt_array); + return stream; +} + +static size_t http_wrapper_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) +{ + return 0; +} + +static size_t http_wrapper_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) +{ + http_wrapper_t *w = (http_wrapper_t *) stream->abstract; + size_t len = MIN(count, w->b.used - w->p); + + if (len) { + memcpy(buf, w->b.data + w->p, len); + w->p += len; + } + + return len; +} + +static int http_wrapper_close(php_stream *stream, int close_handle TSRMLS_DC) +{ + phpstr_dtor(&((http_wrapper_t *)stream->abstract)->b); + efree(stream->abstract); + stream->abstract = NULL; + return 0; +} + +static int http_wrapper_flush(php_stream *stream TSRMLS_DC) +{ + return 0; +} + +static int http_wrapper_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) +{ + http_wrapper_t *w = (http_wrapper_t *) stream->abstract; + off_t o; + + *newoffset = w->p; + + switch (whence) + { + case SEEK_SET: + if (offset < 0 || offset > w->b.used) { + return -1; + } + w->p = offset; + break; + + case SEEK_END: + o = w->b.used + offset; + if (o < 0 || o > w->b.used) { + return -1; + } + w->p = o; + break; + + case SEEK_CUR: + o = w->p + offset; + if (o < 0 || o > w->b.used) { + return -1; + } + w->p = o; + break; + + default: + return -1; + break; + } + + *newoffset = w->p; + + return 0; +} + +static php_stream_ops http_stream_ops = { + http_wrapper_write, + http_wrapper_read, + http_wrapper_close, + http_wrapper_flush, + "http", + http_wrapper_seek, + NULL, + NULL, + NULL, +}; + +static php_stream_wrapper_ops http_wrapper_ops = { + http_wrapper_ex, + NULL, + NULL, + NULL, + NULL, + "http", + NULL, + NULL, + NULL, + NULL +}; + +PHP_HTTP_API php_stream_wrapper http_wrapper = { + &http_wrapper_ops, + NULL, + 1 +}; + +#endif /* HTTP_HAVE_CURL */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + + diff --git a/php_http_request_api.h b/php_http_request_api.h index 2c2fc6b..318b696 100644 --- a/php_http_request_api.h +++ b/php_http_request_api.h @@ -50,6 +50,7 @@ typedef struct { } http_request; +#define http_request_new() _http_request_init_ex(NULL, NULL, 0, NULL ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) #define http_request_init(r) _http_request_init_ex((r), NULL, 0, NULL ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) #define http_request_init_ex(r, c, m, u) _http_request_init_ex((r), (c), (m), (u) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC TSRMLS_CC) PHP_HTTP_API http_request *_http_request_init_ex(http_request *request, CURL *ch, http_request_method meth, const char *url ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC TSRMLS_DC); diff --git a/php_http_wrapper_api.h b/php_http_wrapper_api.h new file mode 100644 index 0000000..fc69af5 --- /dev/null +++ b/php_http_wrapper_api.h @@ -0,0 +1,36 @@ +/* + +--------------------------------------------------------------------+ + | PECL :: http | + +--------------------------------------------------------------------+ + | Redistribution and use in source and binary forms, with or without | + | modification, are permitted provided that the conditions mentioned | + | in the accompanying LICENSE file are met. | + +--------------------------------------------------------------------+ + | Copyright (c) 2004-2005, Michael Wallner | + +--------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_HTTP_WRAPPER_API_H +#define PHP_HTTP_WRAPPER_API_H + +#if defined(HTTP_HAVE_CURL) && defined(HTTP_HAVE_WRAPPER) + +extern PHP_MINIT_FUNCTION(http_wrapper); +extern php_stream *http_wrapper_ex(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC); + +PHP_HTTP_API php_stream_wrapper http_wrapper; + +#endif +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ +