2000-10-13 00:09:31 +00:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
2014-09-19 16:33:14 +00:00
|
|
|
| PHP Version 7 |
|
2000-10-13 00:09:31 +00:00
|
|
|
+----------------------------------------------------------------------+
|
2014-01-03 03:08:10 +00:00
|
|
|
| Copyright (c) 1997-2014 The PHP Group |
|
2000-10-13 00:09:31 +00:00
|
|
|
+----------------------------------------------------------------------+
|
2006-01-01 12:51:34 +00:00
|
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
2000-10-13 00:09:31 +00:00
|
|
|
| that is bundled with this package in the file LICENSE, and is |
|
2003-06-10 20:04:29 +00:00
|
|
|
| available through the world-wide-web at the following url: |
|
2006-01-01 12:51:34 +00:00
|
|
|
| http://www.php.net/license/3_01.txt |
|
2000-10-13 00:09:31 +00:00
|
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
|
|
+----------------------------------------------------------------------+
|
2002-02-28 08:29:35 +00:00
|
|
|
| Authors: Rasmus Lerdorf <rasmus@php.net> |
|
2000-10-13 00:09:31 +00:00
|
|
|
| Jim Winstead <jimw@php.net> |
|
|
|
|
| Hartmut Holzgraefe <hholzgra@php.net> |
|
2002-03-15 21:03:08 +00:00
|
|
|
| Wez Furlong <wez@thebrainroom.com> |
|
2004-09-07 19:27:11 +00:00
|
|
|
| Sara Golemon <pollita@php.net> |
|
2000-10-13 00:09:31 +00:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
2002-09-07 15:55:04 +00:00
|
|
|
/* $Id$ */
|
2000-10-13 00:09:31 +00:00
|
|
|
|
|
|
|
#include "php.h"
|
|
|
|
#include "php_globals.h"
|
2002-03-15 21:03:08 +00:00
|
|
|
#include "php_streams.h"
|
2000-10-13 09:13:01 +00:00
|
|
|
#include "php_network.h"
|
2002-09-07 15:55:04 +00:00
|
|
|
#include "php_ini.h"
|
|
|
|
#include "ext/standard/basic_functions.h"
|
2014-09-20 20:42:02 +00:00
|
|
|
#include "zend_smart_str.h"
|
2000-10-13 00:09:31 +00:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#ifdef PHP_WIN32
|
|
|
|
#define O_RDONLY _O_RDONLY
|
|
|
|
#include "win32/param.h"
|
|
|
|
#else
|
|
|
|
#include <sys/param.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "php_standard.h"
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#if HAVE_SYS_SOCKET_H
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PHP_WIN32
|
2003-02-16 03:48:49 +00:00
|
|
|
#include <winsock2.h>
|
2002-09-05 14:25:07 +00:00
|
|
|
#elif defined(NETWARE) && defined(USE_WINSOCK)
|
|
|
|
#include <novsock2.h>
|
2000-10-13 00:09:31 +00:00
|
|
|
#else
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netdb.h>
|
2001-01-21 17:29:15 +00:00
|
|
|
#if HAVE_ARPA_INET_H
|
2000-10-13 00:09:31 +00:00
|
|
|
#include <arpa/inet.h>
|
|
|
|
#endif
|
2001-01-21 17:29:15 +00:00
|
|
|
#endif
|
2000-10-13 00:09:31 +00:00
|
|
|
|
2002-09-05 14:25:07 +00:00
|
|
|
#if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
|
2000-10-13 00:09:31 +00:00
|
|
|
#undef AF_UNIX
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(AF_UNIX)
|
|
|
|
#include <sys/un.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "php_fopen_wrappers.h"
|
|
|
|
|
2002-03-15 21:03:08 +00:00
|
|
|
#define HTTP_HEADER_BLOCK_SIZE 1024
|
2003-03-04 16:04:05 +00:00
|
|
|
#define PHP_URL_REDIRECT_MAX 20
|
2003-04-07 21:02:04 +00:00
|
|
|
#define HTTP_HEADER_USER_AGENT 1
|
|
|
|
#define HTTP_HEADER_HOST 2
|
|
|
|
#define HTTP_HEADER_AUTH 4
|
|
|
|
#define HTTP_HEADER_FROM 8
|
2004-09-07 19:27:11 +00:00
|
|
|
#define HTTP_HEADER_CONTENT_LENGTH 16
|
2004-03-19 15:41:26 +00:00
|
|
|
#define HTTP_HEADER_TYPE 32
|
Request non-keep-alive connections by default in HTTP 1.1 requests.
As noted in FR #65634, at present we don't send a Connection request header
when the protocol version is set to 1.1, which means that RFC-compliant Web
servers should respond with keep-alive connections. Since there's no way of
reusing the HTTP connection at present, this simply means that PHP will appear
to hang until the remote server hits its connection timeout, which may be quite
some time.
This commit sends a "Connection: close" header by default when HTTP 1.1 (or
later) is requested by the user via the context options. It can be overridden
by specifying a Connection header in the context options. It isn't possible to
disable sending of the Connection header, but given "Connection: keep-alive" is
the same as the default HTTP 1.1 behaviour, I don't see this as a significant
issue — users who want to opt in for that still can.
As a note, although I've removed an efree(protocol_version), this doesn't
result in a memory leak: protocol_version is freed in the out: block at the end
of the function anyway, and there are no returns between the removed efree()
and the later call. Yes, I ran the tests with valgrind to check that. ☺
Implements FR #65634 (HTTP wrapper is very slow with protocol_version 1.1).
2013-09-11 21:11:29 +00:00
|
|
|
#define HTTP_HEADER_CONNECTION 64
|
2003-03-04 16:04:05 +00:00
|
|
|
|
2008-07-29 13:57:43 +00:00
|
|
|
#define HTTP_WRAPPER_HEADER_INIT 1
|
|
|
|
#define HTTP_WRAPPER_REDIRECTED 2
|
|
|
|
|
2013-10-01 09:07:55 +00:00
|
|
|
static inline void strip_header(char *header_bag, char *lc_header_bag,
|
|
|
|
const char *lc_header_name)
|
|
|
|
{
|
|
|
|
char *lc_header_start = strstr(lc_header_bag, lc_header_name);
|
|
|
|
char *header_start = header_bag + (lc_header_start - lc_header_bag);
|
|
|
|
|
|
|
|
if (lc_header_start
|
|
|
|
&& (lc_header_start == lc_header_bag || *(lc_header_start-1) == '\n')
|
|
|
|
) {
|
|
|
|
char *lc_eol = strchr(lc_header_start, '\n');
|
|
|
|
char *eol = header_start + (lc_eol - lc_header_start);
|
|
|
|
|
|
|
|
if (lc_eol) {
|
|
|
|
size_t eollen = strlen(lc_eol);
|
|
|
|
|
|
|
|
memmove(lc_header_start, lc_eol+1, eollen);
|
|
|
|
memmove(header_start, eol+1, eollen);
|
|
|
|
} else {
|
|
|
|
*lc_header_start = '\0';
|
|
|
|
*header_start = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-01 09:11:48 +00:00
|
|
|
php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
|
|
|
|
const char *path, const char *mode, int options, char **opened_path,
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_context *context, int redirect_max, int flags STREAMS_DC) /* {{{ */
|
2002-03-15 21:03:08 +00:00
|
|
|
{
|
2002-03-16 01:34:52 +00:00
|
|
|
php_stream *stream = NULL;
|
|
|
|
php_url *resource = NULL;
|
2002-03-15 21:03:08 +00:00
|
|
|
int use_ssl;
|
2005-09-21 15:04:26 +00:00
|
|
|
int use_proxy = 0;
|
2002-03-16 01:34:52 +00:00
|
|
|
char *scratch = NULL;
|
|
|
|
char *tmp = NULL;
|
2002-09-07 15:45:29 +00:00
|
|
|
char *ua_str = NULL;
|
2014-07-14 07:44:53 +00:00
|
|
|
zval *ua_zval = NULL, *tmpzval = NULL, ssl_proxy_peer_name;
|
2014-10-23 09:36:34 +00:00
|
|
|
size_t scratch_len = 0;
|
2002-03-15 21:03:08 +00:00
|
|
|
int body = 0;
|
|
|
|
char location[HTTP_HEADER_BLOCK_SIZE];
|
2005-12-12 05:04:15 +00:00
|
|
|
zval *response_header = NULL;
|
2002-03-15 21:03:08 +00:00
|
|
|
int reqok = 0;
|
2002-03-16 01:34:52 +00:00
|
|
|
char *http_header_line = NULL;
|
2002-03-15 21:03:08 +00:00
|
|
|
char tmp_line[128];
|
2002-04-19 07:56:41 +00:00
|
|
|
size_t chunk_size = 0, file_size = 0;
|
2003-08-28 16:28:33 +00:00
|
|
|
int eol_detect = 0;
|
2014-08-13 10:51:48 +00:00
|
|
|
char *transport_string;
|
|
|
|
zend_string *errstr = NULL;
|
2014-10-23 09:36:34 +00:00
|
|
|
size_t transport_len;
|
|
|
|
int have_header = 0;
|
|
|
|
zend_bool request_fulluri = 0, ignore_errors = 0;
|
2004-09-07 19:27:11 +00:00
|
|
|
char *protocol_version = NULL;
|
|
|
|
int protocol_version_len = 3; /* Default: "1.0" */
|
2007-01-19 00:02:13 +00:00
|
|
|
struct timeval timeout;
|
2008-04-06 15:21:24 +00:00
|
|
|
char *user_headers = NULL;
|
2008-07-29 13:57:43 +00:00
|
|
|
int header_init = ((flags & HTTP_WRAPPER_HEADER_INIT) != 0);
|
|
|
|
int redirected = ((flags & HTTP_WRAPPER_REDIRECTED) != 0);
|
2014-10-23 09:36:34 +00:00
|
|
|
zend_bool follow_location = 1;
|
2009-04-16 10:16:27 +00:00
|
|
|
php_stream_filter *transfer_encoding = NULL;
|
2013-01-29 08:24:23 +00:00
|
|
|
int response_code;
|
2014-07-04 14:03:45 +00:00
|
|
|
zend_array *symbol_table;
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2005-06-06 12:39:57 +00:00
|
|
|
tmp_line[0] = '\0';
|
|
|
|
|
2003-03-04 16:04:05 +00:00
|
|
|
if (redirect_max < 1) {
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_wrapper_log_error(wrapper, options, "Redirection limit reached, aborting");
|
2003-03-04 16:04:05 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-03-15 21:03:08 +00:00
|
|
|
resource = php_url_parse(path);
|
2003-03-04 16:04:05 +00:00
|
|
|
if (resource == NULL) {
|
2002-03-15 21:03:08 +00:00
|
|
|
return NULL;
|
2003-03-04 16:04:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strncasecmp(resource->scheme, "http", sizeof("http")) && strncasecmp(resource->scheme, "https", sizeof("https"))) {
|
2004-07-24 04:01:48 +00:00
|
|
|
if (!context ||
|
2014-02-13 13:54:23 +00:00
|
|
|
(tmpzval = php_stream_context_get_option(context, wrapper->wops->label, "proxy")) == NULL ||
|
|
|
|
Z_TYPE_P(tmpzval) != IS_STRING ||
|
2014-08-25 17:24:55 +00:00
|
|
|
Z_STRLEN_P(tmpzval) <= 0) {
|
2004-07-24 04:01:48 +00:00
|
|
|
php_url_free(resource);
|
2010-04-26 23:53:30 +00:00
|
|
|
return php_stream_open_wrapper_ex(path, mode, REPORT_ERRORS, NULL, context);
|
2004-07-24 04:01:48 +00:00
|
|
|
}
|
|
|
|
/* Called from a non-http wrapper with http proxying requested (i.e. ftp) */
|
|
|
|
request_fulluri = 1;
|
|
|
|
use_ssl = 0;
|
2005-09-21 15:04:26 +00:00
|
|
|
use_proxy = 1;
|
2004-07-24 04:01:48 +00:00
|
|
|
|
2014-08-25 17:24:55 +00:00
|
|
|
transport_len = Z_STRLEN_P(tmpzval);
|
|
|
|
transport_string = estrndup(Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval));
|
2003-12-03 05:30:16 +00:00
|
|
|
} else {
|
2004-07-24 04:01:48 +00:00
|
|
|
/* Normal http request (possibly with proxy) */
|
|
|
|
|
|
|
|
if (strpbrk(mode, "awx+")) {
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper does not support writeable connections");
|
2007-04-23 16:37:28 +00:00
|
|
|
php_url_free(resource);
|
2004-07-24 04:01:48 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
use_ssl = resource->scheme && (strlen(resource->scheme) > 4) && resource->scheme[4] == 's';
|
|
|
|
/* choose default ports */
|
|
|
|
if (use_ssl && resource->port == 0)
|
|
|
|
resource->port = 443;
|
|
|
|
else if (resource->port == 0)
|
|
|
|
resource->port = 80;
|
|
|
|
|
2005-09-21 15:04:26 +00:00
|
|
|
if (context &&
|
2014-02-13 13:54:23 +00:00
|
|
|
(tmpzval = php_stream_context_get_option(context, wrapper->wops->label, "proxy")) != NULL &&
|
|
|
|
Z_TYPE_P(tmpzval) == IS_STRING &&
|
2014-08-25 17:24:55 +00:00
|
|
|
Z_STRLEN_P(tmpzval) > 0) {
|
2005-09-21 15:04:26 +00:00
|
|
|
use_proxy = 1;
|
2014-08-25 17:24:55 +00:00
|
|
|
transport_len = Z_STRLEN_P(tmpzval);
|
|
|
|
transport_string = estrndup(Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval));
|
2004-07-24 04:01:48 +00:00
|
|
|
} else {
|
|
|
|
transport_len = spprintf(&transport_string, 0, "%s://%s:%d", use_ssl ? "ssl" : "tcp", resource->host, resource->port);
|
|
|
|
}
|
2003-12-03 05:30:16 +00:00
|
|
|
}
|
2003-02-27 17:43:38 +00:00
|
|
|
|
2014-02-13 13:54:23 +00:00
|
|
|
if (context && (tmpzval = php_stream_context_get_option(context, wrapper->wops->label, "timeout")) != NULL) {
|
2014-04-25 12:41:12 +00:00
|
|
|
double d = zval_get_double(tmpzval);
|
2014-10-23 09:36:34 +00:00
|
|
|
#ifndef PHP_WIN32
|
2014-04-25 12:41:12 +00:00
|
|
|
timeout.tv_sec = (time_t) d;
|
|
|
|
timeout.tv_usec = (size_t) ((d - timeout.tv_sec) * 1000000);
|
2014-10-23 09:36:34 +00:00
|
|
|
#else
|
|
|
|
timeout.tv_sec = (long) d;
|
|
|
|
timeout.tv_usec = (long) ((d - timeout.tv_sec) * 1000000);
|
|
|
|
#endif
|
2007-01-19 00:02:13 +00:00
|
|
|
} else {
|
2014-10-23 09:36:34 +00:00
|
|
|
#ifndef PHP_WIN32
|
2007-01-19 00:02:13 +00:00
|
|
|
timeout.tv_sec = FG(default_socket_timeout);
|
2014-10-23 09:36:34 +00:00
|
|
|
#else
|
|
|
|
timeout.tv_sec = (long)FG(default_socket_timeout);
|
|
|
|
#endif
|
2007-01-19 00:02:13 +00:00
|
|
|
timeout.tv_usec = 0;
|
|
|
|
}
|
|
|
|
|
2003-02-27 17:43:38 +00:00
|
|
|
stream = php_stream_xport_create(transport_string, transport_len, options,
|
|
|
|
STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
|
2007-01-19 00:02:13 +00:00
|
|
|
NULL, &timeout, context, &errstr, NULL);
|
|
|
|
|
|
|
|
if (stream) {
|
|
|
|
php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &timeout);
|
|
|
|
}
|
2003-02-27 17:43:38 +00:00
|
|
|
|
|
|
|
if (errstr) {
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_wrapper_log_error(wrapper, options, "%s", errstr->val);
|
2014-08-25 17:24:55 +00:00
|
|
|
zend_string_release(errstr);
|
2003-02-27 17:43:38 +00:00
|
|
|
errstr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
efree(transport_string);
|
|
|
|
|
2005-09-21 15:04:26 +00:00
|
|
|
if (stream && use_proxy && use_ssl) {
|
2014-07-15 11:50:42 +00:00
|
|
|
smart_str header = {0};
|
|
|
|
|
2014-07-12 14:49:42 +00:00
|
|
|
/* Set peer_name or name verification will try to use the proxy server name */
|
2014-07-14 07:44:53 +00:00
|
|
|
if (!context || (tmpzval = php_stream_context_get_option(context, "ssl", "peer_name")) == NULL) {
|
|
|
|
ZVAL_STRING(&ssl_proxy_peer_name, resource->host);
|
2014-08-07 11:55:37 +00:00
|
|
|
php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_name", &ssl_proxy_peer_name);
|
2014-07-12 14:49:42 +00:00
|
|
|
}
|
|
|
|
|
2005-09-21 15:04:26 +00:00
|
|
|
smart_str_appendl(&header, "CONNECT ", sizeof("CONNECT ")-1);
|
|
|
|
smart_str_appends(&header, resource->host);
|
|
|
|
smart_str_appendc(&header, ':');
|
|
|
|
smart_str_append_unsigned(&header, resource->port);
|
2011-01-28 10:33:47 +00:00
|
|
|
smart_str_appendl(&header, " HTTP/1.0\r\n", sizeof(" HTTP/1.0\r\n")-1);
|
|
|
|
|
|
|
|
/* check if we have Proxy-Authorization header */
|
2014-02-13 13:54:23 +00:00
|
|
|
if (context && (tmpzval = php_stream_context_get_option(context, "http", "header")) != NULL) {
|
2011-01-28 10:33:47 +00:00
|
|
|
char *s, *p;
|
|
|
|
|
2014-02-13 13:54:23 +00:00
|
|
|
if (Z_TYPE_P(tmpzval) == IS_ARRAY) {
|
|
|
|
zval *tmpheader = NULL;
|
2011-01-28 10:33:47 +00:00
|
|
|
|
2014-04-18 15:18:11 +00:00
|
|
|
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(tmpzval), tmpheader) {
|
2014-02-13 13:54:23 +00:00
|
|
|
if (Z_TYPE_P(tmpheader) == IS_STRING) {
|
|
|
|
s = Z_STRVAL_P(tmpheader);
|
2011-01-28 10:33:47 +00:00
|
|
|
do {
|
|
|
|
while (*s == ' ' || *s == '\t') s++;
|
|
|
|
p = s;
|
|
|
|
while (*p != 0 && *p != ':' && *p != '\r' && *p !='\n') p++;
|
|
|
|
if (*p == ':') {
|
|
|
|
p++;
|
|
|
|
if (p - s == sizeof("Proxy-Authorization:") - 1 &&
|
|
|
|
zend_binary_strcasecmp(s, sizeof("Proxy-Authorization:") - 1,
|
|
|
|
"Proxy-Authorization:", sizeof("Proxy-Authorization:") - 1) == 0) {
|
|
|
|
while (*p != 0 && *p != '\r' && *p !='\n') p++;
|
|
|
|
smart_str_appendl(&header, s, p - s);
|
|
|
|
smart_str_appendl(&header, "\r\n", sizeof("\r\n")-1);
|
|
|
|
goto finish;
|
|
|
|
} else {
|
|
|
|
while (*p != 0 && *p != '\r' && *p !='\n') p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s = p;
|
|
|
|
while (*s == '\r' || *s == '\n') s++;
|
|
|
|
} while (*s != 0);
|
|
|
|
}
|
2014-04-18 15:18:11 +00:00
|
|
|
} ZEND_HASH_FOREACH_END();
|
2014-08-25 17:24:55 +00:00
|
|
|
} else if (Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval)) {
|
2014-02-13 13:54:23 +00:00
|
|
|
s = Z_STRVAL_P(tmpzval);
|
2011-01-28 10:33:47 +00:00
|
|
|
do {
|
|
|
|
while (*s == ' ' || *s == '\t') s++;
|
|
|
|
p = s;
|
|
|
|
while (*p != 0 && *p != ':' && *p != '\r' && *p !='\n') p++;
|
|
|
|
if (*p == ':') {
|
|
|
|
p++;
|
|
|
|
if (p - s == sizeof("Proxy-Authorization:") - 1 &&
|
|
|
|
zend_binary_strcasecmp(s, sizeof("Proxy-Authorization:") - 1,
|
|
|
|
"Proxy-Authorization:", sizeof("Proxy-Authorization:") - 1) == 0) {
|
|
|
|
while (*p != 0 && *p != '\r' && *p !='\n') p++;
|
|
|
|
smart_str_appendl(&header, s, p - s);
|
|
|
|
smart_str_appendl(&header, "\r\n", sizeof("\r\n")-1);
|
|
|
|
goto finish;
|
|
|
|
} else {
|
|
|
|
while (*p != 0 && *p != '\r' && *p !='\n') p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s = p;
|
|
|
|
while (*s == '\r' || *s == '\n') s++;
|
|
|
|
} while (*s != 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
finish:
|
|
|
|
smart_str_appendl(&header, "\r\n", sizeof("\r\n")-1);
|
|
|
|
|
2014-02-18 09:42:46 +00:00
|
|
|
if (php_stream_write(stream, header.s->val, header.s->len) != header.s->len) {
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy");
|
2005-09-21 15:04:26 +00:00
|
|
|
php_stream_close(stream);
|
|
|
|
stream = NULL;
|
|
|
|
}
|
|
|
|
smart_str_free(&header);
|
|
|
|
|
|
|
|
if (stream) {
|
|
|
|
char header_line[HTTP_HEADER_BLOCK_SIZE];
|
|
|
|
|
|
|
|
/* get response header */
|
2010-08-16 10:29:01 +00:00
|
|
|
while (php_stream_gets(stream, header_line, HTTP_HEADER_BLOCK_SIZE-1) != NULL) {
|
2005-09-21 15:04:26 +00:00
|
|
|
if (header_line[0] == '\n' ||
|
|
|
|
header_line[0] == '\r' ||
|
|
|
|
header_line[0] == '\0') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable SSL transport layer */
|
|
|
|
if (stream) {
|
2014-12-13 22:06:14 +00:00
|
|
|
if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_ANY_CLIENT, NULL) < 0 ||
|
|
|
|
php_stream_xport_crypto_enable(stream, 1) < 0) {
|
|
|
|
php_stream_wrapper_log_error(wrapper, options, "Cannot connect to HTTPS server through proxy");
|
2005-09-21 15:04:26 +00:00
|
|
|
php_stream_close(stream);
|
|
|
|
stream = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-16 10:29:01 +00:00
|
|
|
if (stream == NULL)
|
2002-03-15 21:03:08 +00:00
|
|
|
goto out;
|
|
|
|
|
2002-04-10 22:42:32 +00:00
|
|
|
/* avoid buffering issues while reading header */
|
2003-02-13 13:38:20 +00:00
|
|
|
if (options & STREAM_WILL_CAST)
|
|
|
|
chunk_size = php_stream_set_chunk_size(stream, 1);
|
2002-04-10 22:42:32 +00:00
|
|
|
|
2003-02-13 13:38:20 +00:00
|
|
|
/* avoid problems with auto-detecting when reading the headers -> the headers
|
|
|
|
* are always in canonical \r\n format */
|
|
|
|
eol_detect = stream->flags & (PHP_STREAM_FLAG_DETECT_EOL | PHP_STREAM_FLAG_EOL_MAC);
|
|
|
|
stream->flags &= ~(PHP_STREAM_FLAG_DETECT_EOL | PHP_STREAM_FLAG_EOL_MAC);
|
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_context_set(stream, context);
|
2002-04-10 22:42:32 +00:00
|
|
|
|
|
|
|
php_stream_notify_info(context, PHP_STREAM_NOTIFY_CONNECT, NULL, 0);
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2014-02-13 13:54:23 +00:00
|
|
|
if (header_init && context && (tmpzval = php_stream_context_get_option(context, "http", "max_redirects")) != NULL) {
|
2014-10-23 09:36:34 +00:00
|
|
|
redirect_max = (int)zval_get_long(tmpzval);
|
2005-05-06 02:31:07 +00:00
|
|
|
}
|
|
|
|
|
2014-02-13 13:54:23 +00:00
|
|
|
if (context && (tmpzval = php_stream_context_get_option(context, "http", "method")) != NULL) {
|
2014-08-25 17:24:55 +00:00
|
|
|
if (Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval) > 0) {
|
2008-07-28 19:08:02 +00:00
|
|
|
/* As per the RFC, automatically redirected requests MUST NOT use other methods than
|
|
|
|
* GET and HEAD unless it can be confirmed by the user */
|
2008-07-29 13:57:43 +00:00
|
|
|
if (!redirected
|
2014-08-25 17:24:55 +00:00
|
|
|
|| (Z_STRLEN_P(tmpzval) == 3 && memcmp("GET", Z_STRVAL_P(tmpzval), 3) == 0)
|
|
|
|
|| (Z_STRLEN_P(tmpzval) == 4 && memcmp("HEAD",Z_STRVAL_P(tmpzval), 4) == 0)
|
2008-07-28 19:08:02 +00:00
|
|
|
) {
|
2014-08-25 17:24:55 +00:00
|
|
|
scratch_len = strlen(path) + 29 + Z_STRLEN_P(tmpzval);
|
2008-07-28 19:08:02 +00:00
|
|
|
scratch = emalloc(scratch_len);
|
2014-08-25 17:24:55 +00:00
|
|
|
strlcpy(scratch, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval) + 1);
|
2011-08-09 12:16:58 +00:00
|
|
|
strncat(scratch, " ", 1);
|
2008-07-28 19:08:02 +00:00
|
|
|
}
|
2003-06-13 19:27:26 +00:00
|
|
|
}
|
|
|
|
}
|
2007-01-19 00:02:13 +00:00
|
|
|
|
2014-02-13 13:54:23 +00:00
|
|
|
if (context && (tmpzval = php_stream_context_get_option(context, "http", "protocol_version")) != NULL) {
|
2014-10-23 09:36:34 +00:00
|
|
|
protocol_version_len = (int)spprintf(&protocol_version, 0, "%.1F", zval_get_double(tmpzval));
|
2004-09-07 19:27:11 +00:00
|
|
|
}
|
|
|
|
|
2003-06-13 19:27:26 +00:00
|
|
|
if (!scratch) {
|
2004-09-07 19:27:11 +00:00
|
|
|
scratch_len = strlen(path) + 29 + protocol_version_len;
|
2003-06-13 19:27:26 +00:00
|
|
|
scratch = emalloc(scratch_len);
|
2011-08-09 12:16:58 +00:00
|
|
|
strncpy(scratch, "GET ", scratch_len);
|
2003-04-07 21:02:04 +00:00
|
|
|
}
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2004-01-30 00:24:18 +00:00
|
|
|
/* Should we send the entire path in the request line, default to no. */
|
2014-04-25 12:41:12 +00:00
|
|
|
if (!request_fulluri && context &&
|
2014-02-13 13:54:23 +00:00
|
|
|
(tmpzval = php_stream_context_get_option(context, "http", "request_fulluri")) != NULL) {
|
2014-12-13 22:06:14 +00:00
|
|
|
request_fulluri = zend_is_true(tmpzval);
|
2004-01-30 00:24:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (request_fulluri) {
|
|
|
|
/* Ask for everything */
|
|
|
|
strcat(scratch, path);
|
|
|
|
} else {
|
|
|
|
/* Send the traditional /path/to/file?query_string */
|
2003-04-07 21:02:04 +00:00
|
|
|
|
2004-01-30 00:24:18 +00:00
|
|
|
/* file */
|
|
|
|
if (resource->path && *resource->path) {
|
|
|
|
strlcat(scratch, resource->path, scratch_len);
|
|
|
|
} else {
|
|
|
|
strlcat(scratch, "/", scratch_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* query string */
|
2010-08-16 10:29:01 +00:00
|
|
|
if (resource->query) {
|
2004-01-30 00:24:18 +00:00
|
|
|
strlcat(scratch, "?", scratch_len);
|
|
|
|
strlcat(scratch, resource->query, scratch_len);
|
|
|
|
}
|
2002-03-15 21:03:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* protocol version we are speaking */
|
2004-09-07 19:27:11 +00:00
|
|
|
if (protocol_version) {
|
|
|
|
strlcat(scratch, " HTTP/", scratch_len);
|
|
|
|
strlcat(scratch, protocol_version, scratch_len);
|
|
|
|
strlcat(scratch, "\r\n", scratch_len);
|
|
|
|
} else {
|
|
|
|
strlcat(scratch, " HTTP/1.0\r\n", scratch_len);
|
|
|
|
}
|
|
|
|
|
2002-03-15 21:03:08 +00:00
|
|
|
/* send it */
|
|
|
|
php_stream_write(stream, scratch, strlen(scratch));
|
|
|
|
|
2014-02-13 13:54:23 +00:00
|
|
|
if (context && (tmpzval = php_stream_context_get_option(context, "http", "header")) != NULL) {
|
2009-05-05 00:31:56 +00:00
|
|
|
tmp = NULL;
|
|
|
|
|
2014-02-13 13:54:23 +00:00
|
|
|
if (Z_TYPE_P(tmpzval) == IS_ARRAY) {
|
|
|
|
zval *tmpheader = NULL;
|
2009-05-05 00:31:56 +00:00
|
|
|
smart_str tmpstr = {0};
|
|
|
|
|
2014-04-18 15:18:11 +00:00
|
|
|
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(tmpzval), tmpheader) {
|
2014-02-13 13:54:23 +00:00
|
|
|
if (Z_TYPE_P(tmpheader) == IS_STRING) {
|
2014-09-21 18:47:07 +00:00
|
|
|
smart_str_append(&tmpstr, Z_STR_P(tmpheader));
|
2009-05-05 00:31:56 +00:00
|
|
|
smart_str_appendl(&tmpstr, "\r\n", sizeof("\r\n") - 1);
|
|
|
|
}
|
2014-04-18 15:18:11 +00:00
|
|
|
} ZEND_HASH_FOREACH_END();
|
2009-05-05 00:31:56 +00:00
|
|
|
smart_str_0(&tmpstr);
|
2009-07-20 10:54:37 +00:00
|
|
|
/* Remove newlines and spaces from start and end. there's at least one extra \r\n at the end that needs to go. */
|
2014-02-18 09:42:46 +00:00
|
|
|
if (tmpstr.s) {
|
2014-12-13 22:06:14 +00:00
|
|
|
tmp = php_trim(tmpstr.s->val, tmpstr.s->len, NULL, 0, NULL, 3);
|
2009-08-04 09:24:48 +00:00
|
|
|
smart_str_free(&tmpstr);
|
|
|
|
}
|
2009-05-05 00:31:56 +00:00
|
|
|
}
|
2014-08-25 17:24:55 +00:00
|
|
|
if (Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval)) {
|
2009-05-05 00:31:56 +00:00
|
|
|
/* Remove newlines and spaces from start and end php_trim will estrndup() */
|
2014-12-13 22:06:14 +00:00
|
|
|
tmp = php_trim(Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval), NULL, 0, NULL, 3);
|
2009-05-05 00:31:56 +00:00
|
|
|
}
|
2014-09-13 18:12:21 +00:00
|
|
|
if (tmp && tmp[0] != '\0') {
|
2011-01-28 10:33:47 +00:00
|
|
|
char *s;
|
|
|
|
|
2008-04-06 15:21:24 +00:00
|
|
|
user_headers = estrdup(tmp);
|
2003-04-07 21:02:04 +00:00
|
|
|
|
|
|
|
/* Make lowercase for easy comparison against 'standard' headers */
|
|
|
|
php_strtolower(tmp, strlen(tmp));
|
2013-10-01 09:07:55 +00:00
|
|
|
|
|
|
|
if (!header_init) {
|
|
|
|
/* strip POST headers on redirect */
|
|
|
|
strip_header(user_headers, tmp, "content-length:");
|
|
|
|
strip_header(user_headers, tmp, "content-type:");
|
|
|
|
}
|
|
|
|
|
2011-01-28 10:33:47 +00:00
|
|
|
if ((s = strstr(tmp, "user-agent:")) &&
|
|
|
|
(s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
|
|
|
|
*(s-1) == '\t' || *(s-1) == ' ')) {
|
2003-04-07 21:02:04 +00:00
|
|
|
have_header |= HTTP_HEADER_USER_AGENT;
|
|
|
|
}
|
2011-01-28 10:33:47 +00:00
|
|
|
if ((s = strstr(tmp, "host:")) &&
|
|
|
|
(s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
|
|
|
|
*(s-1) == '\t' || *(s-1) == ' ')) {
|
2003-04-07 21:02:04 +00:00
|
|
|
have_header |= HTTP_HEADER_HOST;
|
|
|
|
}
|
2011-01-28 10:33:47 +00:00
|
|
|
if ((s = strstr(tmp, "from:")) &&
|
|
|
|
(s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
|
|
|
|
*(s-1) == '\t' || *(s-1) == ' ')) {
|
2003-04-07 21:02:04 +00:00
|
|
|
have_header |= HTTP_HEADER_FROM;
|
|
|
|
}
|
2011-01-28 10:33:47 +00:00
|
|
|
if ((s = strstr(tmp, "authorization:")) &&
|
|
|
|
(s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
|
|
|
|
*(s-1) == '\t' || *(s-1) == ' ')) {
|
2003-04-07 21:02:04 +00:00
|
|
|
have_header |= HTTP_HEADER_AUTH;
|
|
|
|
}
|
2011-01-28 10:33:47 +00:00
|
|
|
if ((s = strstr(tmp, "content-length:")) &&
|
|
|
|
(s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
|
|
|
|
*(s-1) == '\t' || *(s-1) == ' ')) {
|
2004-03-19 15:41:26 +00:00
|
|
|
have_header |= HTTP_HEADER_CONTENT_LENGTH;
|
|
|
|
}
|
2011-01-28 10:33:47 +00:00
|
|
|
if ((s = strstr(tmp, "content-type:")) &&
|
|
|
|
(s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
|
|
|
|
*(s-1) == '\t' || *(s-1) == ' ')) {
|
2004-03-19 15:41:26 +00:00
|
|
|
have_header |= HTTP_HEADER_TYPE;
|
|
|
|
}
|
Request non-keep-alive connections by default in HTTP 1.1 requests.
As noted in FR #65634, at present we don't send a Connection request header
when the protocol version is set to 1.1, which means that RFC-compliant Web
servers should respond with keep-alive connections. Since there's no way of
reusing the HTTP connection at present, this simply means that PHP will appear
to hang until the remote server hits its connection timeout, which may be quite
some time.
This commit sends a "Connection: close" header by default when HTTP 1.1 (or
later) is requested by the user via the context options. It can be overridden
by specifying a Connection header in the context options. It isn't possible to
disable sending of the Connection header, but given "Connection: keep-alive" is
the same as the default HTTP 1.1 behaviour, I don't see this as a significant
issue — users who want to opt in for that still can.
As a note, although I've removed an efree(protocol_version), this doesn't
result in a memory leak: protocol_version is freed in the out: block at the end
of the function anyway, and there are no returns between the removed efree()
and the later call. Yes, I ran the tests with valgrind to check that. ☺
Implements FR #65634 (HTTP wrapper is very slow with protocol_version 1.1).
2013-09-11 21:11:29 +00:00
|
|
|
if ((s = strstr(tmp, "connection:")) &&
|
|
|
|
(s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
|
|
|
|
*(s-1) == '\t' || *(s-1) == ' ')) {
|
|
|
|
have_header |= HTTP_HEADER_CONNECTION;
|
|
|
|
}
|
2011-01-28 10:33:47 +00:00
|
|
|
/* remove Proxy-Authorization header */
|
|
|
|
if (use_proxy && use_ssl && (s = strstr(tmp, "proxy-authorization:")) &&
|
|
|
|
(s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
|
|
|
|
*(s-1) == '\t' || *(s-1) == ' ')) {
|
|
|
|
char *p = s + sizeof("proxy-authorization:") - 1;
|
|
|
|
|
|
|
|
while (s > tmp && (*(s-1) == ' ' || *(s-1) == '\t')) s--;
|
|
|
|
while (*p != 0 && *p != '\r' && *p != '\n') p++;
|
|
|
|
while (*p == '\r' || *p == '\n') p++;
|
|
|
|
if (*p == 0) {
|
|
|
|
if (s == tmp) {
|
|
|
|
efree(user_headers);
|
|
|
|
user_headers = NULL;
|
|
|
|
} else {
|
|
|
|
while (s > tmp && (*(s-1) == '\r' || *(s-1) == '\n')) s--;
|
|
|
|
user_headers[s - tmp] = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
memmove(user_headers + (s - tmp), user_headers + (p - tmp), strlen(p) + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-04-07 21:02:04 +00:00
|
|
|
}
|
2009-05-14 13:36:56 +00:00
|
|
|
if (tmp) {
|
|
|
|
efree(tmp);
|
|
|
|
}
|
2003-04-07 21:02:04 +00:00
|
|
|
}
|
|
|
|
|
2002-09-14 12:09:47 +00:00
|
|
|
/* auth header if it was specified */
|
2010-01-25 16:28:13 +00:00
|
|
|
if (((have_header & HTTP_HEADER_AUTH) == 0) && resource->user) {
|
2014-02-24 10:48:22 +00:00
|
|
|
zend_string *stmp;
|
2002-07-11 02:35:45 +00:00
|
|
|
/* decode the strings first */
|
|
|
|
php_url_decode(resource->user, strlen(resource->user));
|
|
|
|
|
2002-03-15 21:03:08 +00:00
|
|
|
/* scratch is large enough, since it was made large enough for the whole URL */
|
|
|
|
strcpy(scratch, resource->user);
|
|
|
|
strcat(scratch, ":");
|
2010-01-25 16:28:13 +00:00
|
|
|
|
|
|
|
/* Note: password is optional! */
|
|
|
|
if (resource->pass) {
|
|
|
|
php_url_decode(resource->pass, strlen(resource->pass));
|
|
|
|
strcat(scratch, resource->pass);
|
|
|
|
}
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2014-02-24 10:48:22 +00:00
|
|
|
stmp = php_base64_encode((unsigned char*)scratch, strlen(scratch));
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2014-02-24 10:48:22 +00:00
|
|
|
if (snprintf(scratch, scratch_len, "Authorization: Basic %s\r\n", stmp->val) > 0) {
|
2002-03-15 21:03:08 +00:00
|
|
|
php_stream_write(stream, scratch, strlen(scratch));
|
2002-04-10 22:42:32 +00:00
|
|
|
php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_REQUIRED, NULL, 0);
|
|
|
|
}
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2014-08-25 17:24:55 +00:00
|
|
|
zend_string_free(stmp);
|
2002-03-15 21:03:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* if the user has configured who they are, send a From: line */
|
2010-10-29 15:29:15 +00:00
|
|
|
if (((have_header & HTTP_HEADER_FROM) == 0) && FG(from_address)) {
|
|
|
|
if (snprintf(scratch, scratch_len, "From: %s\r\n", FG(from_address)) > 0)
|
2002-03-15 21:03:08 +00:00
|
|
|
php_stream_write(stream, scratch, strlen(scratch));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send Host: header so name-based virtual hosts work */
|
2003-04-07 21:02:04 +00:00
|
|
|
if ((have_header & HTTP_HEADER_HOST) == 0) {
|
2004-07-24 04:01:48 +00:00
|
|
|
if ((use_ssl && resource->port != 443 && resource->port != 0) ||
|
2010-08-16 10:29:01 +00:00
|
|
|
(!use_ssl && resource->port != 80 && resource->port != 0)) {
|
2003-04-07 21:02:04 +00:00
|
|
|
if (snprintf(scratch, scratch_len, "Host: %s:%i\r\n", resource->host, resource->port) > 0)
|
|
|
|
php_stream_write(stream, scratch, strlen(scratch));
|
|
|
|
} else {
|
|
|
|
if (snprintf(scratch, scratch_len, "Host: %s\r\n", resource->host) > 0) {
|
|
|
|
php_stream_write(stream, scratch, strlen(scratch));
|
|
|
|
}
|
|
|
|
}
|
2002-03-15 21:03:08 +00:00
|
|
|
}
|
|
|
|
|
Request non-keep-alive connections by default in HTTP 1.1 requests.
As noted in FR #65634, at present we don't send a Connection request header
when the protocol version is set to 1.1, which means that RFC-compliant Web
servers should respond with keep-alive connections. Since there's no way of
reusing the HTTP connection at present, this simply means that PHP will appear
to hang until the remote server hits its connection timeout, which may be quite
some time.
This commit sends a "Connection: close" header by default when HTTP 1.1 (or
later) is requested by the user via the context options. It can be overridden
by specifying a Connection header in the context options. It isn't possible to
disable sending of the Connection header, but given "Connection: keep-alive" is
the same as the default HTTP 1.1 behaviour, I don't see this as a significant
issue — users who want to opt in for that still can.
As a note, although I've removed an efree(protocol_version), this doesn't
result in a memory leak: protocol_version is freed in the out: block at the end
of the function anyway, and there are no returns between the removed efree()
and the later call. Yes, I ran the tests with valgrind to check that. ☺
Implements FR #65634 (HTTP wrapper is very slow with protocol_version 1.1).
2013-09-11 21:11:29 +00:00
|
|
|
/* Send a Connection: close header when using HTTP 1.1 or later to avoid
|
|
|
|
* hanging when the server interprets the RFC literally and establishes a
|
|
|
|
* keep-alive connection, unless the user specifically requests something
|
|
|
|
* else by specifying a Connection header in the context options. */
|
|
|
|
if (protocol_version &&
|
|
|
|
((have_header & HTTP_HEADER_CONNECTION) == 0) &&
|
|
|
|
(strncmp(protocol_version, "1.0", MIN(protocol_version_len, 3)) > 0)) {
|
|
|
|
php_stream_write_string(stream, "Connection: close\r\n");
|
|
|
|
}
|
|
|
|
|
2002-09-07 15:45:29 +00:00
|
|
|
if (context &&
|
2014-02-13 13:54:23 +00:00
|
|
|
(ua_zval = php_stream_context_get_option(context, "http", "user_agent")) != NULL &&
|
|
|
|
Z_TYPE_P(ua_zval) == IS_STRING) {
|
|
|
|
ua_str = Z_STRVAL_P(ua_zval);
|
2002-09-23 18:12:39 +00:00
|
|
|
} else if (FG(user_agent)) {
|
|
|
|
ua_str = FG(user_agent);
|
2002-09-07 15:45:29 +00:00
|
|
|
}
|
|
|
|
|
2003-04-07 21:02:04 +00:00
|
|
|
if (((have_header & HTTP_HEADER_USER_AGENT) == 0) && ua_str) {
|
2002-09-07 15:45:29 +00:00
|
|
|
#define _UA_HEADER "User-Agent: %s\r\n"
|
|
|
|
char *ua;
|
|
|
|
size_t ua_len;
|
2002-09-07 21:57:11 +00:00
|
|
|
|
2002-09-07 15:45:29 +00:00
|
|
|
ua_len = sizeof(_UA_HEADER) + strlen(ua_str);
|
2002-09-07 21:57:11 +00:00
|
|
|
|
|
|
|
/* ensure the header is only sent if user_agent is not blank */
|
|
|
|
if (ua_len > sizeof(_UA_HEADER)) {
|
|
|
|
ua = emalloc(ua_len + 1);
|
2007-02-27 03:28:17 +00:00
|
|
|
if ((ua_len = slprintf(ua, ua_len, _UA_HEADER, ua_str)) > 0) {
|
2002-09-07 21:57:11 +00:00
|
|
|
ua[ua_len] = 0;
|
|
|
|
php_stream_write(stream, ua, ua_len);
|
|
|
|
} else {
|
2014-12-13 22:06:14 +00:00
|
|
|
php_error_docref(NULL, E_WARNING, "Cannot construct User-agent header");
|
2002-09-07 21:57:11 +00:00
|
|
|
}
|
2002-09-07 15:45:29 +00:00
|
|
|
|
2002-09-07 21:57:11 +00:00
|
|
|
if (ua) {
|
|
|
|
efree(ua);
|
|
|
|
}
|
|
|
|
}
|
2002-09-07 15:45:29 +00:00
|
|
|
}
|
|
|
|
|
2008-04-06 15:21:24 +00:00
|
|
|
if (user_headers) {
|
|
|
|
/* A bit weird, but some servers require that Content-Length be sent prior to Content-Type for POST
|
|
|
|
* see bug #44603 for details. Since Content-Type maybe part of user's headers we need to do this check first.
|
|
|
|
*/
|
|
|
|
if (
|
|
|
|
header_init &&
|
|
|
|
context &&
|
|
|
|
!(have_header & HTTP_HEADER_CONTENT_LENGTH) &&
|
2014-02-13 13:54:23 +00:00
|
|
|
(tmpzval = php_stream_context_get_option(context, "http", "content")) != NULL &&
|
2014-08-25 17:24:55 +00:00
|
|
|
Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval) > 0
|
2008-04-06 15:21:24 +00:00
|
|
|
) {
|
2014-08-25 17:24:55 +00:00
|
|
|
scratch_len = slprintf(scratch, scratch_len, "Content-Length: %d\r\n", Z_STRLEN_P(tmpzval));
|
2008-04-06 15:21:24 +00:00
|
|
|
php_stream_write(stream, scratch, scratch_len);
|
|
|
|
have_header |= HTTP_HEADER_CONTENT_LENGTH;
|
|
|
|
}
|
|
|
|
|
|
|
|
php_stream_write(stream, user_headers, strlen(user_headers));
|
|
|
|
php_stream_write(stream, "\r\n", sizeof("\r\n")-1);
|
|
|
|
efree(user_headers);
|
|
|
|
}
|
|
|
|
|
2003-04-07 21:02:04 +00:00
|
|
|
/* Request content, such as for POST requests */
|
2005-07-14 14:59:16 +00:00
|
|
|
if (header_init && context &&
|
2014-02-13 13:54:23 +00:00
|
|
|
(tmpzval = php_stream_context_get_option(context, "http", "content")) != NULL &&
|
2014-08-25 17:24:55 +00:00
|
|
|
Z_TYPE_P(tmpzval) == IS_STRING && Z_STRLEN_P(tmpzval) > 0) {
|
2004-03-19 15:41:26 +00:00
|
|
|
if (!(have_header & HTTP_HEADER_CONTENT_LENGTH)) {
|
2014-08-25 17:24:55 +00:00
|
|
|
scratch_len = slprintf(scratch, scratch_len, "Content-Length: %d\r\n", Z_STRLEN_P(tmpzval));
|
2004-03-19 15:41:26 +00:00
|
|
|
php_stream_write(stream, scratch, scratch_len);
|
|
|
|
}
|
|
|
|
if (!(have_header & HTTP_HEADER_TYPE)) {
|
|
|
|
php_stream_write(stream, "Content-Type: application/x-www-form-urlencoded\r\n",
|
|
|
|
sizeof("Content-Type: application/x-www-form-urlencoded\r\n") - 1);
|
2014-12-13 22:06:14 +00:00
|
|
|
php_error_docref(NULL, E_NOTICE, "Content-type not specified assuming application/x-www-form-urlencoded");
|
2004-03-19 15:41:26 +00:00
|
|
|
}
|
|
|
|
php_stream_write(stream, "\r\n", sizeof("\r\n")-1);
|
2014-08-25 17:24:55 +00:00
|
|
|
php_stream_write(stream, Z_STRVAL_P(tmpzval), Z_STRLEN_P(tmpzval));
|
2004-03-19 15:41:26 +00:00
|
|
|
} else {
|
|
|
|
php_stream_write(stream, "\r\n", sizeof("\r\n")-1);
|
2003-04-07 21:02:04 +00:00
|
|
|
}
|
|
|
|
|
2002-03-15 21:03:08 +00:00
|
|
|
location[0] = '\0';
|
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
symbol_table = zend_rebuild_symbol_table();
|
2008-04-29 08:15:20 +00:00
|
|
|
|
2005-12-12 05:04:15 +00:00
|
|
|
if (header_init) {
|
2014-02-13 13:54:23 +00:00
|
|
|
zval ztmp;
|
|
|
|
array_init(&ztmp);
|
2014-12-13 22:06:14 +00:00
|
|
|
zend_set_local_var_str("http_response_header", sizeof("http_response_header")-1, &ztmp, 0);
|
2005-12-12 05:04:15 +00:00
|
|
|
}
|
|
|
|
|
2014-07-04 14:03:45 +00:00
|
|
|
response_header = zend_hash_str_find_ind(&symbol_table->ht, "http_response_header", sizeof("http_response_header")-1);
|
2003-04-14 13:55:34 +00:00
|
|
|
|
2004-02-24 21:52:40 +00:00
|
|
|
if (!php_stream_eof(stream)) {
|
|
|
|
size_t tmp_line_len;
|
2002-03-15 21:03:08 +00:00
|
|
|
/* get response header */
|
|
|
|
|
2005-12-09 18:53:09 +00:00
|
|
|
if (php_stream_get_line(stream, tmp_line, sizeof(tmp_line) - 1, &tmp_line_len) != NULL) {
|
2014-02-13 13:54:23 +00:00
|
|
|
zval http_response;
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2004-02-24 21:52:40 +00:00
|
|
|
if (tmp_line_len > 9) {
|
|
|
|
response_code = atoi(tmp_line + 9);
|
|
|
|
} else {
|
|
|
|
response_code = 0;
|
|
|
|
}
|
2014-02-13 13:54:23 +00:00
|
|
|
if (context && NULL != (tmpzval = php_stream_context_get_option(context, "http", "ignore_errors"))) {
|
2014-12-13 22:06:14 +00:00
|
|
|
ignore_errors = zend_is_true(tmpzval);
|
2009-05-16 20:34:48 +00:00
|
|
|
}
|
2006-01-20 01:40:32 +00:00
|
|
|
/* when we request only the header, don't fail even on error codes */
|
2009-05-16 20:34:48 +00:00
|
|
|
if ((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) {
|
2006-01-20 01:40:32 +00:00
|
|
|
reqok = 1;
|
|
|
|
}
|
2008-07-25 08:27:37 +00:00
|
|
|
/* all status codes in the 2xx range are defined by the specification as successful;
|
|
|
|
* all status codes in the 3xx range are for redirection, and so also should never
|
|
|
|
* fail */
|
|
|
|
if (response_code >= 200 && response_code < 400) {
|
|
|
|
reqok = 1;
|
|
|
|
} else {
|
|
|
|
switch(response_code) {
|
|
|
|
case 403:
|
|
|
|
php_stream_notify_error(context, PHP_STREAM_NOTIFY_AUTH_RESULT,
|
|
|
|
tmp_line, response_code);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* safety net in the event tmp_line == NULL */
|
|
|
|
if (!tmp_line_len) {
|
|
|
|
tmp_line[0] = '\0';
|
|
|
|
}
|
|
|
|
php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE,
|
|
|
|
tmp_line, response_code);
|
|
|
|
}
|
2002-04-10 22:42:32 +00:00
|
|
|
}
|
2005-11-15 14:46:34 +00:00
|
|
|
if (tmp_line[tmp_line_len - 1] == '\n') {
|
|
|
|
--tmp_line_len;
|
|
|
|
if (tmp_line[tmp_line_len - 1] == '\r') {
|
|
|
|
--tmp_line_len;
|
2002-03-15 21:03:08 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-13 13:54:23 +00:00
|
|
|
ZVAL_STRINGL(&http_response, tmp_line, tmp_line_len);
|
|
|
|
zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_response);
|
2002-03-15 21:03:08 +00:00
|
|
|
}
|
2004-02-26 00:02:27 +00:00
|
|
|
} else {
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_wrapper_log_error(wrapper, options, "HTTP request failed, unexpected end of socket!");
|
2004-02-26 00:02:27 +00:00
|
|
|
goto out;
|
2002-03-15 21:03:08 +00:00
|
|
|
}
|
|
|
|
|
2003-04-14 13:55:34 +00:00
|
|
|
/* read past HTTP headers */
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2003-04-14 13:55:34 +00:00
|
|
|
http_header_line = emalloc(HTTP_HEADER_BLOCK_SIZE);
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2005-12-08 02:53:08 +00:00
|
|
|
while (!body && !php_stream_eof(stream)) {
|
|
|
|
size_t http_header_line_length;
|
2005-12-09 18:53:09 +00:00
|
|
|
if (php_stream_get_line(stream, http_header_line, HTTP_HEADER_BLOCK_SIZE, &http_header_line_length) && *http_header_line != '\n' && *http_header_line != '\r') {
|
2005-12-08 02:53:08 +00:00
|
|
|
char *e = http_header_line + http_header_line_length - 1;
|
2009-12-13 15:44:22 +00:00
|
|
|
if (*e != '\n') {
|
|
|
|
do { /* partial header */
|
2011-11-08 14:12:37 +00:00
|
|
|
if (php_stream_get_line(stream, http_header_line, HTTP_HEADER_BLOCK_SIZE, &http_header_line_length) == NULL) {
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_wrapper_log_error(wrapper, options, "Failed to read HTTP headers");
|
2011-11-08 14:12:37 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2009-12-13 15:44:22 +00:00
|
|
|
e = http_header_line + http_header_line_length - 1;
|
|
|
|
} while (*e != '\n');
|
2009-12-10 03:23:05 +00:00
|
|
|
continue;
|
|
|
|
}
|
2005-12-08 02:53:08 +00:00
|
|
|
while (*e == '\n' || *e == '\r') {
|
|
|
|
e--;
|
2003-04-14 13:55:34 +00:00
|
|
|
}
|
2005-12-08 02:53:08 +00:00
|
|
|
http_header_line_length = e - http_header_line + 1;
|
|
|
|
http_header_line[http_header_line_length] = '\0';
|
|
|
|
|
2003-04-14 13:55:34 +00:00
|
|
|
if (!strncasecmp(http_header_line, "Location: ", 10)) {
|
2014-02-13 13:54:23 +00:00
|
|
|
if (context && (tmpzval = php_stream_context_get_option(context, "http", "follow_location")) != NULL) {
|
2014-10-23 09:36:34 +00:00
|
|
|
follow_location = zval_is_true(tmpzval);
|
2014-06-13 01:12:53 +00:00
|
|
|
} else if (!(response_code >= 300 && response_code < 304 || 307 == response_code || 308 == response_code)) {
|
2013-03-15 16:54:18 +00:00
|
|
|
/* we shouldn't redirect automatically
|
|
|
|
if follow_location isn't set and response_code not in (300, 301, 302, 303 and 307)
|
2014-06-13 01:12:53 +00:00
|
|
|
see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.1
|
|
|
|
RFC 7238 defines 308: http://tools.ietf.org/html/rfc7238 */
|
2013-03-15 16:54:18 +00:00
|
|
|
follow_location = 0;
|
2010-08-23 16:54:57 +00:00
|
|
|
}
|
2003-04-14 13:55:34 +00:00
|
|
|
strlcpy(location, http_header_line + 10, sizeof(location));
|
|
|
|
} else if (!strncasecmp(http_header_line, "Content-Type: ", 14)) {
|
|
|
|
php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, http_header_line + 14, 0);
|
|
|
|
} else if (!strncasecmp(http_header_line, "Content-Length: ", 16)) {
|
|
|
|
file_size = atoi(http_header_line + 16);
|
|
|
|
php_stream_notify_file_size(context, file_size, http_header_line, 0);
|
2009-04-16 10:16:27 +00:00
|
|
|
} else if (!strncasecmp(http_header_line, "Transfer-Encoding: chunked", sizeof("Transfer-Encoding: chunked"))) {
|
|
|
|
|
|
|
|
/* create filter to decode response body */
|
|
|
|
if (!(options & STREAM_ONLY_GET_HEADERS)) {
|
2014-08-25 17:24:55 +00:00
|
|
|
zend_long decode = 1;
|
2009-04-16 10:16:27 +00:00
|
|
|
|
2014-02-13 13:54:23 +00:00
|
|
|
if (context && (tmpzval = php_stream_context_get_option(context, "http", "auto_decode")) != NULL) {
|
2014-12-13 22:06:14 +00:00
|
|
|
decode = zend_is_true(tmpzval);
|
2009-04-16 10:16:27 +00:00
|
|
|
}
|
|
|
|
if (decode) {
|
2014-12-13 22:06:14 +00:00
|
|
|
transfer_encoding = php_stream_filter_create("dechunk", NULL, php_stream_is_persistent(stream));
|
2009-04-16 10:16:27 +00:00
|
|
|
if (transfer_encoding) {
|
|
|
|
/* don't store transfer-encodeing header */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-04-14 13:55:34 +00:00
|
|
|
}
|
2002-09-26 10:14:41 +00:00
|
|
|
|
2003-04-14 13:55:34 +00:00
|
|
|
if (http_header_line[0] == '\0') {
|
|
|
|
body = 1;
|
|
|
|
} else {
|
2014-02-13 13:54:23 +00:00
|
|
|
zval http_header;
|
2002-09-07 15:45:29 +00:00
|
|
|
|
2014-02-13 13:54:23 +00:00
|
|
|
ZVAL_STRINGL(&http_header, http_header_line, http_header_line_length);
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2014-02-13 13:54:23 +00:00
|
|
|
zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_header);
|
2003-01-03 17:12:35 +00:00
|
|
|
}
|
2003-04-14 13:55:34 +00:00
|
|
|
} else {
|
|
|
|
break;
|
2002-03-15 21:03:08 +00:00
|
|
|
}
|
2003-04-14 13:55:34 +00:00
|
|
|
}
|
2010-08-23 16:54:57 +00:00
|
|
|
|
|
|
|
if (!reqok || (location[0] != '\0' && follow_location)) {
|
2010-08-23 18:05:05 +00:00
|
|
|
if (!follow_location || (((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) && redirect_max <= 1)) {
|
2007-01-19 00:02:13 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2002-04-10 22:42:32 +00:00
|
|
|
if (location[0] != '\0')
|
|
|
|
php_stream_notify_info(context, PHP_STREAM_NOTIFY_REDIRECTED, location, 0);
|
|
|
|
|
2002-03-15 21:03:08 +00:00
|
|
|
php_stream_close(stream);
|
|
|
|
stream = NULL;
|
|
|
|
|
2010-08-16 10:29:01 +00:00
|
|
|
if (location[0] != '\0') {
|
2002-03-15 21:03:08 +00:00
|
|
|
|
|
|
|
char new_path[HTTP_HEADER_BLOCK_SIZE];
|
2002-12-29 20:01:33 +00:00
|
|
|
char loc_path[HTTP_HEADER_BLOCK_SIZE];
|
2002-03-15 21:03:08 +00:00
|
|
|
|
|
|
|
*new_path='\0';
|
2003-03-04 16:04:05 +00:00
|
|
|
if (strlen(location)<8 || (strncasecmp(location, "http://", sizeof("http://")-1) &&
|
|
|
|
strncasecmp(location, "https://", sizeof("https://")-1) &&
|
|
|
|
strncasecmp(location, "ftp://", sizeof("ftp://")-1) &&
|
|
|
|
strncasecmp(location, "ftps://", sizeof("ftps://")-1)))
|
|
|
|
{
|
2002-03-15 21:03:08 +00:00
|
|
|
if (*location != '/') {
|
2010-08-16 10:29:01 +00:00
|
|
|
if (*(location+1) != '\0' && resource->path) {
|
2003-02-19 00:49:31 +00:00
|
|
|
char *s = strrchr(resource->path, '/');
|
|
|
|
if (!s) {
|
|
|
|
s = resource->path;
|
2004-05-28 13:38:06 +00:00
|
|
|
if (!s[0]) {
|
|
|
|
efree(s);
|
|
|
|
s = resource->path = estrdup("/");
|
|
|
|
} else {
|
|
|
|
*s = '/';
|
|
|
|
}
|
2003-02-19 00:49:31 +00:00
|
|
|
}
|
2003-02-20 00:34:03 +00:00
|
|
|
s[1] = '\0';
|
2003-01-03 17:05:16 +00:00
|
|
|
if (resource->path && *(resource->path) == '/' && *(resource->path + 1) == '\0') {
|
|
|
|
snprintf(loc_path, sizeof(loc_path) - 1, "%s%s", resource->path, location);
|
|
|
|
} else {
|
|
|
|
snprintf(loc_path, sizeof(loc_path) - 1, "%s/%s", resource->path, location);
|
|
|
|
}
|
2002-12-29 20:01:33 +00:00
|
|
|
} else {
|
|
|
|
snprintf(loc_path, sizeof(loc_path) - 1, "/%s", location);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
strlcpy(loc_path, location, sizeof(loc_path));
|
|
|
|
}
|
|
|
|
if ((use_ssl && resource->port != 443) || (!use_ssl && resource->port != 80)) {
|
|
|
|
snprintf(new_path, sizeof(new_path) - 1, "%s://%s:%d%s", resource->scheme, resource->host, resource->port, loc_path);
|
|
|
|
} else {
|
|
|
|
snprintf(new_path, sizeof(new_path) - 1, "%s://%s%s", resource->scheme, resource->host, loc_path);
|
2002-03-15 21:03:08 +00:00
|
|
|
}
|
2003-01-03 17:05:16 +00:00
|
|
|
} else {
|
2002-03-15 21:03:08 +00:00
|
|
|
strlcpy(new_path, location, sizeof(new_path));
|
|
|
|
}
|
2005-05-06 02:13:46 +00:00
|
|
|
|
|
|
|
php_url_free(resource);
|
|
|
|
/* check for invalid redirection URLs */
|
|
|
|
if ((resource = php_url_parse(new_path)) == NULL) {
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path);
|
2005-05-06 02:13:46 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-08-16 10:29:01 +00:00
|
|
|
#define CHECK_FOR_CNTRL_CHARS(val) { \
|
|
|
|
if (val) { \
|
|
|
|
unsigned char *s, *e; \
|
2014-10-23 09:36:34 +00:00
|
|
|
size_t l; \
|
2010-08-16 10:29:01 +00:00
|
|
|
l = php_url_decode(val, strlen(val)); \
|
|
|
|
s = (unsigned char*)val; e = s + l; \
|
|
|
|
while (s < e) { \
|
|
|
|
if (iscntrl(*s)) { \
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); \
|
2010-08-16 10:29:01 +00:00
|
|
|
goto out; \
|
|
|
|
} \
|
|
|
|
s++; \
|
|
|
|
} \
|
|
|
|
} \
|
2010-01-25 16:28:13 +00:00
|
|
|
}
|
2005-05-06 02:13:46 +00:00
|
|
|
/* check for control characters in login, password & path */
|
2006-04-16 17:40:33 +00:00
|
|
|
if (strncasecmp(new_path, "http://", sizeof("http://") - 1) || strncasecmp(new_path, "https://", sizeof("https://") - 1)) {
|
2006-04-16 16:54:49 +00:00
|
|
|
CHECK_FOR_CNTRL_CHARS(resource->user)
|
|
|
|
CHECK_FOR_CNTRL_CHARS(resource->pass)
|
|
|
|
CHECK_FOR_CNTRL_CHARS(resource->path)
|
|
|
|
}
|
2014-12-13 22:06:14 +00:00
|
|
|
stream = php_stream_url_wrap_http_ex(wrapper, new_path, mode, options, opened_path, context, --redirect_max, HTTP_WRAPPER_REDIRECTED STREAMS_CC);
|
2002-04-10 22:42:32 +00:00
|
|
|
} else {
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_wrapper_log_error(wrapper, options, "HTTP request failed! %s", tmp_line);
|
2002-03-15 21:03:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
out:
|
2004-09-07 19:27:11 +00:00
|
|
|
if (protocol_version) {
|
|
|
|
efree(protocol_version);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (http_header_line) {
|
2002-03-15 21:03:08 +00:00
|
|
|
efree(http_header_line);
|
2004-09-07 19:27:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (scratch) {
|
2002-03-15 21:03:08 +00:00
|
|
|
efree(scratch);
|
2004-09-07 19:27:11 +00:00
|
|
|
}
|
|
|
|
|
2005-05-06 02:20:00 +00:00
|
|
|
if (resource) {
|
|
|
|
php_url_free(resource);
|
|
|
|
}
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2003-04-14 13:55:34 +00:00
|
|
|
if (stream) {
|
|
|
|
if (header_init) {
|
2014-02-13 13:54:23 +00:00
|
|
|
ZVAL_COPY(&stream->wrapperdata, response_header);
|
2003-04-14 13:55:34 +00:00
|
|
|
}
|
2002-04-10 22:42:32 +00:00
|
|
|
php_stream_notify_progress_init(context, 0, file_size);
|
2009-08-01 03:17:31 +00:00
|
|
|
|
2003-02-07 23:44:58 +00:00
|
|
|
/* Restore original chunk size now that we're done with headers */
|
2003-02-13 13:38:20 +00:00
|
|
|
if (options & STREAM_WILL_CAST)
|
2014-10-23 09:36:34 +00:00
|
|
|
php_stream_set_chunk_size(stream, (int)chunk_size);
|
2003-02-13 13:38:20 +00:00
|
|
|
|
|
|
|
/* restore the users auto-detect-line-endings setting */
|
|
|
|
stream->flags |= eol_detect;
|
|
|
|
|
2002-09-23 01:47:04 +00:00
|
|
|
/* as far as streams are concerned, we are now at the start of
|
|
|
|
* the stream */
|
|
|
|
stream->position = 0;
|
2004-09-07 19:27:11 +00:00
|
|
|
|
2009-08-01 03:17:31 +00:00
|
|
|
/* restore mode */
|
|
|
|
strlcpy(stream->mode, mode, sizeof(stream->mode));
|
|
|
|
|
2009-04-16 10:16:27 +00:00
|
|
|
if (transfer_encoding) {
|
|
|
|
php_stream_filter_append(&stream->readfilters, transfer_encoding);
|
|
|
|
}
|
|
|
|
} else if (transfer_encoding) {
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream_filter_free(transfer_encoding);
|
2002-04-10 22:42:32 +00:00
|
|
|
}
|
2002-03-15 21:03:08 +00:00
|
|
|
|
|
|
|
return stream;
|
|
|
|
}
|
2009-05-05 00:16:21 +00:00
|
|
|
/* }}} */
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
php_stream *php_stream_url_wrap_http(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC) /* {{{ */
|
2003-03-04 16:04:05 +00:00
|
|
|
{
|
2014-12-13 22:06:14 +00:00
|
|
|
return php_stream_url_wrap_http_ex(wrapper, path, mode, options, opened_path, context, PHP_URL_REDIRECT_MAX, HTTP_WRAPPER_HEADER_INIT STREAMS_CC);
|
2003-03-04 16:04:05 +00:00
|
|
|
}
|
2009-05-05 00:16:21 +00:00
|
|
|
/* }}} */
|
2003-03-04 16:04:05 +00:00
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
static int php_stream_http_stream_stat(php_stream_wrapper *wrapper, php_stream *stream, php_stream_statbuf *ssb) /* {{{ */
|
2002-03-28 00:49:00 +00:00
|
|
|
{
|
|
|
|
/* one day, we could fill in the details based on Date: and Content-Length:
|
|
|
|
* headers. For now, we return with a failure code to prevent the underlying
|
|
|
|
* file's details from being used instead. */
|
|
|
|
return -1;
|
|
|
|
}
|
2009-05-05 00:16:21 +00:00
|
|
|
/* }}} */
|
2002-03-28 00:49:00 +00:00
|
|
|
|
2002-03-24 18:05:49 +00:00
|
|
|
static php_stream_wrapper_ops http_stream_wops = {
|
2002-03-15 21:03:08 +00:00
|
|
|
php_stream_url_wrap_http,
|
2002-08-11 14:29:01 +00:00
|
|
|
NULL, /* stream_close */
|
2002-03-28 00:49:00 +00:00
|
|
|
php_stream_http_stream_stat,
|
2002-08-11 14:29:01 +00:00
|
|
|
NULL, /* stat_url */
|
2002-09-26 10:14:41 +00:00
|
|
|
NULL, /* opendir */
|
2004-07-24 04:01:48 +00:00
|
|
|
"http",
|
2003-12-12 23:06:42 +00:00
|
|
|
NULL, /* unlink */
|
|
|
|
NULL, /* rename */
|
2003-12-13 04:07:18 +00:00
|
|
|
NULL, /* mkdir */
|
|
|
|
NULL /* rmdir */
|
2002-03-15 21:03:08 +00:00
|
|
|
};
|
|
|
|
|
2010-08-16 10:29:01 +00:00
|
|
|
PHPAPI php_stream_wrapper php_stream_http_wrapper = {
|
2002-03-24 18:05:49 +00:00
|
|
|
&http_stream_wops,
|
2002-04-16 22:14:27 +00:00
|
|
|
NULL,
|
|
|
|
1 /* is_url */
|
2002-03-24 18:05:49 +00:00
|
|
|
};
|
2002-03-15 21:03:08 +00:00
|
|
|
|
2001-06-05 13:12:10 +00:00
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* tab-width: 4
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* End:
|
2001-09-09 13:29:31 +00:00
|
|
|
* vim600: sw=4 ts=4 fdm=marker
|
|
|
|
* vim<600: sw=4 ts=4
|
2001-06-05 13:12:10 +00:00
|
|
|
*/
|