2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/url-file.h"
19 #include "hphp/runtime/base/runtime-error.h"
20 #include "hphp/runtime/ext/pcre/ext_pcre.h"
21 #include "hphp/runtime/ext/stream/ext_stream.h"
22 #include "hphp/runtime/ext/url/ext_url.h"
23 #include "hphp/runtime/base/php-globals.h"
24 #include "hphp/runtime/vm/runtime.h"
25 #include "hphp/runtime/vm/jit/translator-inline.h"
29 ///////////////////////////////////////////////////////////////////////////////
31 const StaticString
s_http_response_header("http_response_header");
32 const StaticString
s_http("http");
33 const StaticString
s_tcp_socket("tcp_socket");
35 ///////////////////////////////////////////////////////////////////////////////
37 UrlFile::UrlFile(const char *method
/* = "GET" */,
38 const Array
& headers
/* = null_array */,
39 const String
& postData
/* = null_string */,
40 int maxRedirect
/* = 20 */,
41 int timeout
/* = -1 */,
42 bool ignoreErrors
/* = false */)
43 : MemFile(s_http
, s_tcp_socket
) {
44 m_get
= (method
== nullptr || strcasecmp(method
, "GET") == 0);
47 m_postData
= postData
;
48 m_maxRedirect
= maxRedirect
;
50 m_ignoreErrors
= ignoreErrors
;
54 void UrlFile::sweep() {
61 s_remove_user_pass_pattern("#://[^@]+@#"),
62 s_remove_user_pass_replace("://");
64 void UrlFile::setProxy(const String
& proxy_host
, int proxy_port
,
65 const String
& proxy_user
, const String
& proxy_pass
) {
66 m_proxyHost
= proxy_host
.c_str();
67 m_proxyPort
= proxy_port
;
68 m_proxyUsername
= proxy_user
.c_str();
69 m_proxyPassword
= proxy_pass
.c_str();
72 bool UrlFile::open(const String
& input_url
, const String
& mode
) {
73 String url
= input_url
;
74 const char* modestr
= mode
.c_str();
75 if (strchr(modestr
, '+') || strchr(modestr
, 'a') || strchr(modestr
, 'w')) {
76 std::string msg
= "cannot open a url stream for write/append operation: ";
81 HttpClient
http(m_timeout
, m_maxRedirect
);
82 auto ctx
= this->getStreamContext();
84 http
.setStreamContextOptions(ctx
->getOptions());
88 if (!m_proxyHost
.empty()) {
89 http
.proxy(m_proxyHost
, m_proxyPort
, m_proxyUsername
, m_proxyPassword
);
92 HeaderMap
*pHeaders
= nullptr;
93 HeaderMap requestHeaders
;
94 if (!m_headers
.empty()) {
95 pHeaders
= &requestHeaders
;
96 for (ArrayIter
iter(m_headers
); iter
; ++iter
) {
97 requestHeaders
[std::string(iter
.first().toString().data())].
98 push_back(iter
.second().toString().data());
102 Variant user
= f_parse_url(url
, k_PHP_URL_USER
);
103 if (user
.isString()) {
104 Variant pass
= f_parse_url(url
, k_PHP_URL_PASS
);
105 http
.auth(user
.toString().c_str(), pass
.toString().c_str());
106 url
= HHVM_FN(preg_replace
)(
107 s_remove_user_pass_pattern
,
108 s_remove_user_pass_replace
,
115 req::vector
<String
> responseHeaders
;
117 code
= http
.get(url
.c_str(), m_response
, pHeaders
, &responseHeaders
);
119 code
= http
.request(m_method
,
120 url
.c_str(), m_postData
.data(), m_postData
.size(),
121 m_response
, pHeaders
, &responseHeaders
);
124 m_responseHeaders
.reset();
125 for (unsigned int i
= 0; i
< responseHeaders
.size(); i
++) {
126 m_responseHeaders
.append(responseHeaders
[i
]);
130 if (fp
->skipFrame()) fp
= g_context
->getPrevVMStateSkipFrame(fp
);
131 auto id
= fp
->func()->lookupVarId(s_http_response_header
.get());
132 if (id
!= kInvalidId
) {
133 auto tvTo
= frame_local(fp
, id
);
134 Variant
varFrom(m_responseHeaders
);
135 const auto tvFrom(varFrom
.asTypedValue());
136 if (tvTo
->m_type
== KindOfRef
) {
137 tvTo
= tvTo
->m_data
.pref
->tv();
139 tvDup(*tvFrom
, *tvTo
);
140 } else if ((fp
->func()->attrs() & AttrMayUseVV
) && fp
->hasVarEnv()) {
141 fp
->getVarEnv()->set(s_http_response_header
.get(),
142 Variant(m_responseHeaders
).asTypedValue());
146 * If code == 0, Curl failed to connect; per PHP5, ignore_errors just means
147 * to not worry if we get an http resonse code that isn't between 200 and 400,
148 * but we shouldn't ignore other errors.
149 * all status codes in the 2xx range are defined by the specification as
151 * all status codes in the 3xx range are for redirection, and so also should
154 if ((code
>= 200 && code
< 400) || (m_ignoreErrors
&& code
!= 0)) {
155 setName(url
.toCppString());
156 m_data
= const_cast<char*>(m_response
.data());
157 m_len
= m_response
.size();
160 m_error
= http
.getLastError().c_str();
165 int64_t UrlFile::writeImpl(const char* /*buffer*/, int64_t /*length*/) {
167 raise_fatal_error((std::string("cannot write a url stream: ") +
171 bool UrlFile::flush() {
173 raise_fatal_error((std::string("cannot flush a url stream: ") +
177 String
UrlFile::getLastError() {
181 ///////////////////////////////////////////////////////////////////////////////