- highly experimental stream wrapper
authorMichael Wallner <mike@php.net>
Fri, 16 Dec 2005 16:42:30 +0000 (16:42 +0000)
committerMichael Wallner <mike@php.net>
Fri, 16 Dec 2005 16:42:30 +0000 (16:42 +0000)
config.m4
config.w32
http.c
http_wrapper_api.c [new file with mode: 0644]
php_http_request_api.h
php_http_wrapper_api.h [new file with mode: 0644]

index bf19c24..457821b 100644 (file)
--- 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
index 7db93da..2847a2c 100644 (file)
@@ -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 (file)
--- 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 (file)
index 0000000..981d833
--- /dev/null
@@ -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 <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+/* $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
+ */
+
+
index 2c2fc6b..318b696 100644 (file)
@@ -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 (file)
index 0000000..fc69af5
--- /dev/null
@@ -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 <mike@php.net>            |
+    +--------------------------------------------------------------------+
+*/
+
+/* $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
+ */
+