Rename files in runtime/base, part 6
[hiphop-php.git] / hphp / runtime / server / http_protocol.cpp
blob968cf2e769e7a419af03885e2e16952185204336
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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/server/http_protocol.h"
18 #include "hphp/runtime/base/hphp_system.h"
19 #include "hphp/runtime/base/zend-url.h"
20 #include "hphp/runtime/base/zend-string.h"
21 #include "hphp/runtime/base/program-functions.h"
22 #include "hphp/runtime/base/runtime-option.h"
23 #include "hphp/runtime/server/source_root_info.h"
24 #include "hphp/runtime/server/request_uri.h"
25 #include "hphp/runtime/server/transport.h"
26 #include "hphp/util/logger.h"
27 #include "hphp/util/util.h"
28 #include "hphp/runtime/server/upload.h"
29 #include "hphp/runtime/server/replay_transport.h"
30 #include "hphp/runtime/server/virtual_host.h"
31 #include "hphp/runtime/base/http_client.h"
33 #define DEFAULT_POST_CONTENT_TYPE "application/x-www-form-urlencoded"
35 using std::map;
37 namespace HPHP {
38 ///////////////////////////////////////////////////////////////////////////////
39 // helper functions
41 static bool read_all_post_data(Transport *transport,
42 const void *&data, int &size) {
43 if (transport->hasMorePostData()) {
44 data = Util::buffer_duplicate(data, size);
45 do {
46 int delta = 0;
47 const void *extra = transport->getMorePostData(delta);
48 if (size + delta < VirtualHost::GetMaxPostSize()) {
49 data = Util::buffer_append(data, size, extra, delta);
50 size += delta;
52 } while (transport->hasMorePostData());
53 return true;
55 return false;
58 ///////////////////////////////////////////////////////////////////////////////
60 const VirtualHost *HttpProtocol::GetVirtualHost(Transport *transport) {
61 if (!RuntimeOption::VirtualHosts.empty()) {
62 string host = transport->getHeader("Host");
63 for (unsigned int i = 0; i < RuntimeOption::VirtualHosts.size(); i++) {
64 VirtualHostPtr vhost = RuntimeOption::VirtualHosts[i];
65 if (vhost->match(host)) {
66 VirtualHost::SetCurrent(vhost.get());
67 return vhost.get();
71 VirtualHost::SetCurrent(nullptr);
72 return VirtualHost::GetCurrent();
75 const StaticString
76 s_REQUEST_START_TIME("REQUEST_START_TIME"),
77 s_HPHP("HPHP"),
78 s_HHVM("HHVM"),
79 s_HHVM_JIT("HHVM_JIT"),
80 s_HPHP_SERVER("HPHP_SERVER"),
81 s_HPHP_HOTPROFILER("HPHP_HOTPROFILER"),
82 s_HTTP_HOST("HTTP_HOST"),
83 s_CONTENT_TYPE("CONTENT_TYPE"),
84 s_CONTENT_LENGTH("CONTENT_LENGTH"),
85 s_PHP_AUTH_USER("PHP_AUTH_USER"),
86 s_PHP_AUTH_PW("PHP_AUTH_PW"),
87 s_REQUEST_URI("REQUEST_URI"),
88 s_SCRIPT_URL("SCRIPT_URL"),
89 s_SCRIPT_URI("SCRIPT_URI"),
90 s_SCRIPT_NAME("SCRIPT_NAME"),
91 s_PHP_SELF("PHP_SELF"),
92 s_SCRIPT_FILENAME("SCRIPT_FILENAME"),
93 s_PATH_TRANSLATED("PATH_TRANSLATED"),
94 s_PATH_INFO("PATH_INFO"),
95 s_argc("argc"),
96 s_argv("argv"),
97 s__SERVER("_SERVER"),
98 s__GET("_GET"),
99 s__POST("_POST"),
100 s__REQUEST("_REQUEST"),
101 s__ENV("_ENV"),
102 s__COOKIE("_COOKIE"),
103 s_HTTP_RAW_POST_DATA("HTTP_RAW_POST_DATA"),
104 s__FILES("_FILES"),
105 s_GATEWAY_INTERFACE("GATEWAY_INTERFACE"),
106 s_CGI_1_1("CGI/1.1"),
107 s_SERVER_ADDR("SERVER_ADDR"),
108 s_SERVER_NAME("SERVER_NAME"),
109 s_SERVER_PORT("SERVER_PORT"),
110 s_SERVER_SOFTWARE("SERVER_SOFTWARE"),
111 s_SERVER_PROTOCOL("SERVER_PROTOCOL"),
112 s_SERVER_ADMIN("SERVER_ADMIN"),
113 s_SERVER_SIGNATURE("SERVER_SIGNATURE"),
114 s_REQUEST_METHOD("REQUEST_METHOD"),
115 s_GET("GET"),
116 s_HEAD("HEAD"),
117 s_POST("POST"),
118 s_HTTPS("HTTPS"),
119 s_1("1"),
120 s_REQUEST_TIME("REQUEST_TIME"),
121 s_REQUEST_TIME_FLOAT("REQUEST_TIME_FLOAT"),
122 s_QUERY_STRING("QUERY_STRING"),
123 s_REMOTE_ADDR("REMOTE_ADDR"),
124 s_REMOTE_HOST("REMOTE_HOST"),
125 s_REMOTE_PORT("REMOTE_PORT"),
126 s_DOCUMENT_ROOT("DOCUMENT_ROOT"),
127 s_THREAD_TYPE("THREAD_TYPE");
130 * PHP has "EGPCS" processing order of these global variables, and this
131 * order is important in preparing $_REQUEST that needs to know which to
132 * overwrite what when name happens to be the same.
134 void HttpProtocol::PrepareSystemVariables(Transport *transport,
135 const RequestURI &r,
136 const SourceRootInfo &sri) {
137 GlobalVariables *g = get_global_variables();
138 const VirtualHost *vhost = VirtualHost::GetCurrent();
140 Variant& server = g->getRef(s__SERVER);
141 server.set(s_REQUEST_START_TIME, time(nullptr));
143 // $_ENV
144 Variant& env = g->getRef(s__ENV);
145 process_env_variables(env);
146 env.set(s_HPHP, 1);
147 env.set(s_HHVM, 1);
148 if (RuntimeOption::EvalJit) {
149 env.set(s_HHVM_JIT, 1);
152 bool isServer = RuntimeOption::ServerExecutionMode();
153 if (isServer) {
154 env.set(s_HPHP_SERVER, 1);
155 #ifdef HOTPROFILER
156 env.set(s_HPHP_HOTPROFILER, 1);
157 #endif
160 Variant &request = g->getRef(s__REQUEST);
162 // $_GET and $_REQUEST
163 if (!r.queryString().empty()) {
164 Variant &get = g->getRef(s__GET);
165 DecodeParameters(get, r.queryString().data(),
166 r.queryString().size());
167 CopyParams(request, get);
170 string contentType = transport->getHeader("Content-Type");
171 string contentLength = transport->getHeader("Content-Length");
172 // $_POST and $_REQUEST
173 if (transport->getMethod() == Transport::Method::POST) {
174 bool needDelete = false;
175 int size = 0;
176 const void *data = transport->getPostData(size);
177 if (data && size) {
178 assert(((char*)data)[size] == 0); // we need a NULL terminated string
179 string boundary;
180 int content_length = atoi(contentLength.c_str());
181 bool rfc1867Post = IsRfc1867(contentType, boundary);
182 string files;
183 if (rfc1867Post) {
184 if (content_length > VirtualHost::GetMaxPostSize()) {
185 // $_POST and $_FILES are empty
186 Logger::Warning("POST Content-Length of %d bytes exceeds "
187 "the limit of %" PRId64 " bytes",
188 content_length, VirtualHost::GetMaxPostSize());
189 while (transport->hasMorePostData()) {
190 int delta = 0;
191 transport->getMorePostData(delta);
193 } else {
194 if (transport->hasMorePostData()) {
195 needDelete = true;
196 data = Util::buffer_duplicate(data, size);
198 DecodeRfc1867(transport, g->getRef(s__POST), g->getRef(s__FILES),
199 content_length, data, size, boundary);
201 assert(!transport->getFiles(files));
202 } else {
203 needDelete = read_all_post_data(transport, data, size);
205 bool decodeData = strncasecmp(contentType.c_str(),
206 DEFAULT_POST_CONTENT_TYPE,
207 sizeof(DEFAULT_POST_CONTENT_TYPE)-1) == 0;
208 // Always decode data for now. (macvicar)
209 decodeData = true;
211 if (decodeData) {
212 DecodeParameters(g->getRef(s__POST), (const char*)data, size, true);
215 bool ret = transport->getFiles(files);
216 if (ret) {
217 g->getRef(s__FILES) = unserialize_from_string(files);
220 CopyParams(request, g->getRef(s__POST));
221 if (needDelete) {
222 if (RuntimeOption::AlwaysPopulateRawPostData &&
223 uint32_t(size) <= StringData::MaxSize) {
224 g->getRef(s_HTTP_RAW_POST_DATA) =
225 String((char*)data, size, AttachString);
226 } else {
227 free((void *)data);
229 } else {
230 // For literal we disregard RuntimeOption::AlwaysPopulateRawPostData
231 if (uint32_t(size) <= StringData::MaxSize) {
232 g->getRef(s_HTTP_RAW_POST_DATA) =
233 String((char*)data, size, CopyString);
239 // $_COOKIE
240 string cookie_data = transport->getHeader("Cookie");
241 if (!cookie_data.empty()) {
242 StringBuffer sb;
243 sb.append(cookie_data);
244 DecodeCookies(g->getRef(s__COOKIE), (char*)sb.data());
245 CopyParams(request, g->getRef(s__COOKIE));
248 // $_SERVER
250 // HTTP_ headers -- we don't exclude headers we handle elsewhere (e.g.,
251 // Content-Type, Authorization), since the CGI "spec" merely says the server
252 // "may" exclude them; this is not what APE does, but it's harmless.
253 HeaderMap headers;
254 transport->getHeaders(headers);
256 static int bad_request_count = -1;
257 for (HeaderMap::const_iterator iter = headers.begin();
258 iter != headers.end(); ++iter) {
259 const vector<string> &values = iter->second;
261 // Detect suspicious headers. We are about to modify header names
262 // for the SERVER variable. This means that it is possible to
263 // deliberately cause a header collision, which an attacker could
264 // use to sneak a header past a proxy that would either overwrite
265 // or filter it otherwise. Client code should use
266 // apache_request_headers() to retrieve the original headers if
267 // they are security-critical.
268 if (RuntimeOption::LogHeaderMangle != 0) {
269 String key = "HTTP_";
270 key += StringUtil::ToUpper(iter->first).replace("-","_");
271 if (server.asArrRef().exists(key)) {
272 if (!(++bad_request_count % RuntimeOption::LogHeaderMangle)) {
273 Logger::Warning(
274 "HeaderMangle warning: "
275 "The header %s overwrote another header which mapped to the same "
276 "key. This happens because PHP normalises - to _, ie AN_EXAMPLE "
277 "and AN-EXAMPLE are equivalent. You should treat this as "
278 "malicious.",
279 iter->first.c_str());
284 for (unsigned int i = 0; i < values.size(); i++) {
285 String key = "HTTP_";
286 key += StringUtil::ToUpper(iter->first).replace("-", "_");
287 server.set(key, String(values[i]));
291 string host = transport->getHeader("Host");
292 String hostName(VirtualHost::GetCurrent()->serverName(host));
293 string hostHeader(host);
294 if (hostHeader.empty()) {
295 server.set(s_HTTP_HOST, hostName);
296 StackTraceNoHeap::AddExtraLogging("Server", hostName.data());
297 } else {
298 StackTraceNoHeap::AddExtraLogging("Server", hostHeader.c_str());
300 if (hostName.empty() || RuntimeOption::ForceServerNameToHeader) {
301 hostName = hostHeader;
302 // _SERVER['SERVER_NAME'] shouldn't contain the port number
303 int colonPos = hostName.find(':');
304 if (colonPos != String::npos) {
305 hostName = hostName.substr(0, colonPos);
309 // APE sets CONTENT_TYPE and CONTENT_LENGTH without HTTP_
310 if (!contentType.empty()) {
311 server.set(s_CONTENT_TYPE, String(contentType));
313 if (!contentLength.empty()) {
314 server.set(s_CONTENT_LENGTH, String(contentLength));
317 // APE processes Authorization: Basic into PHP_AUTH_USER and PHP_AUTH_PW
318 string authorization = transport->getHeader("Authorization");
319 if (!authorization.empty()) {
320 if (strncmp(authorization.c_str(), "Basic ", 6) == 0) {
321 // it's safe to pass this as a string literal since authorization
322 // outlives decodedAuth; this saves us a superfluous copy.
323 String decodedAuth =
324 StringUtil::Base64Decode(String(authorization.c_str() + 6));
325 int colonPos = decodedAuth.find(':');
326 if (colonPos != String::npos) {
327 server.set(s_PHP_AUTH_USER, decodedAuth.substr(0, colonPos));
328 server.set(s_PHP_AUTH_PW, decodedAuth.substr(colonPos + 1));
333 server.set(s_REQUEST_URI, String(transport->getUrl(), CopyString));
334 server.set(s_SCRIPT_URL, r.originalURL());
335 String prefix(transport->isSSL() ? "https://" : "http://");
336 String port_suffix("");
338 // Need to append port
339 if (!transport->isSSL() && RuntimeOption::ServerPort != 80) {
340 port_suffix = folly::format(":{}", RuntimeOption::ServerPort).str();
342 server.set(s_SCRIPT_URI,
343 String(prefix + (hostHeader.empty() ? hostName + port_suffix :
344 String(hostHeader)) + r.originalURL()));
346 if (r.rewritten()) {
347 // when URL is rewritten, PHP decided to put original URL as SCRIPT_NAME
348 String name = r.originalURL();
349 if (!r.pathInfo().empty()) {
350 int pos = name.find(r.pathInfo());
351 if (pos >= 0) {
352 name = name.substr(0, pos);
355 if (r.defaultDoc()) {
356 if (!name.empty() && name[name.length() - 1] != '/') {
357 name += "/";
359 name += String(RuntimeOption::DefaultDocument);
361 server.set(s_SCRIPT_NAME, name);
362 } else {
363 server.set(s_SCRIPT_NAME, r.resolvedURL());
365 if (r.rewritten()) {
366 server.set(s_PHP_SELF, r.originalURL());
367 } else {
368 server.set(s_PHP_SELF, r.resolvedURL() + r.origPathInfo());
371 server.set(s_SCRIPT_FILENAME, r.absolutePath());
372 if (r.pathInfo().empty()) {
373 server.set(s_PATH_TRANSLATED, r.absolutePath());
374 } else {
375 server.set(s_PATH_TRANSLATED,
376 String(vhost->getDocumentRoot() + r.pathInfo().data()));
377 server.set(s_PATH_INFO, r.pathInfo());
380 server.set(s_argv, CREATE_VECTOR1(r.queryString()));
381 server.set(s_argc, 1);
382 server.set(s_GATEWAY_INTERFACE, s_CGI_1_1);
383 server.set(s_SERVER_ADDR, String(RuntimeOption::ServerPrimaryIP));
384 server.set(s_SERVER_NAME, hostName);
385 server.set(s_SERVER_PORT, RuntimeOption::ServerPort);
386 server.set(s_SERVER_SOFTWARE, s_HPHP);
387 server.set(s_SERVER_PROTOCOL, "HTTP/" + transport->getHTTPVersion());
388 server.set(s_SERVER_ADMIN, empty_string);
389 server.set(s_SERVER_SIGNATURE, empty_string);
390 switch (transport->getMethod()) {
391 case Transport::Method::GET: server.set(s_REQUEST_METHOD, s_GET); break;
392 case Transport::Method::HEAD: server.set(s_REQUEST_METHOD, s_HEAD); break;
393 case Transport::Method::POST:
394 if (transport->getExtendedMethod() == nullptr) {
395 server.set(s_REQUEST_METHOD, s_POST);
396 } else {
397 server.set(s_REQUEST_METHOD, transport->getExtendedMethod());
399 break;
400 default: server.set(s_REQUEST_METHOD, empty_string); break;
402 time_t now;
403 struct timeval tp = {0};
404 double now_double;
405 if (!gettimeofday(&tp, nullptr)) {
406 now_double = (double)(tp.tv_sec + tp.tv_usec / 1000000.00);
407 now = tp.tv_sec;
408 } else {
409 now = time(nullptr);
410 now_double = (double)now;
412 server.set(s_HTTPS, transport->isSSL() ? s_1 : empty_string);
413 server.set(s_REQUEST_TIME, now);
414 server.set(s_REQUEST_TIME_FLOAT, now_double);
415 server.set(s_QUERY_STRING, r.queryString());
417 server.set(s_REMOTE_ADDR, String(transport->getRemoteHost(), CopyString));
418 server.set(s_REMOTE_HOST, empty_string); // I don't think we need to nslookup
419 server.set(s_REMOTE_PORT, transport->getRemotePort());
421 server.set(s_DOCUMENT_ROOT, String(vhost->getDocumentRoot()));
423 for (map<string, string>::const_iterator iter =
424 RuntimeOption::ServerVariables.begin();
425 iter != RuntimeOption::ServerVariables.end(); ++iter) {
426 server.set(String(iter->first), String(iter->second));
428 const map<string, string> &vServerVars = vhost->getServerVars();
429 for (map<string, string>::const_iterator iter =
430 vServerVars.begin();
431 iter != vServerVars.end(); ++iter) {
432 server.set(String(iter->first), String(iter->second));
434 sri.setServerVariables(server);
436 const char *threadType = transport->getThreadTypeName();
437 server.set(s_THREAD_TYPE, threadType);
438 StackTraceNoHeap::AddExtraLogging("ThreadType", threadType);
441 std::string HttpProtocol::RecordRequest(Transport *transport) {
442 char tmpfile[PATH_MAX + 1];
443 if (RuntimeOption::RecordInput) {
444 strcpy(tmpfile, "/tmp/hphp_request_XXXXXX");
445 close(mkstemp(tmpfile));
447 ReplayTransport rt;
448 rt.recordInput(transport, tmpfile);
449 Logger::Info("request recorded in %s", tmpfile);
450 return tmpfile;
452 return "";
455 void HttpProtocol::ClearRecord(bool success, const std::string &tmpfile) {
456 if (success && RuntimeOption::ClearInputOnSuccess && !tmpfile.empty()) {
457 unlink(tmpfile.c_str());
458 Logger::Info("request %s deleted", tmpfile.c_str());
462 ///////////////////////////////////////////////////////////////////////////////
464 void HttpProtocol::CopyParams(Variant &dest, Variant &src) {
465 if (src.isArray()) {
466 Array srcArray = src.toArray();
467 for (ArrayIter iter(srcArray); iter; ++iter) {
468 dest.set(iter.first(), iter.second());
473 void HttpProtocol::DecodeParameters(Variant &variables, const char *data,
474 int size, bool post /* = false */) {
475 if (data == nullptr || size == 0) {
476 return;
479 const char *s = data;
480 const char *e = s + size;
481 const char *p, *val;
483 while (s < e && (p = (const char *)memchr(s, '&', (e - s)))) {
484 last_value:
485 if ((val = (const char *)memchr(s, '=', (p - s)))) {
486 int len = val - s;
487 char *name = url_decode(s, len);
488 String sname(name, len, AttachString);
490 val++;
491 len = p - val;
492 char *value = url_decode(val, len);
493 if (RuntimeOption::EnableMagicQuotesGpc) {
494 char *slashedvalue = string_addslashes(value, len);
495 free(value);
496 value = slashedvalue;
498 String svalue(value, len, AttachString);
500 register_variable(variables, (char*)sname.data(), svalue);
501 } else if (!post) {
502 int len = p - s;
503 char *name = url_decode(s, len);
504 String sname(name, len, AttachString);
505 register_variable(variables, (char*)sname.data(), "");
507 s = p + 1;
509 if (s < e) {
510 p = e;
511 goto last_value;
515 void HttpProtocol::DecodeCookies(Variant &variables, char *data) {
516 assert(data && *data);
518 char *strtok_buf = nullptr;
519 char *var = strtok_r(data, ";", &strtok_buf);
520 while (var) {
521 char *val = strchr(var, '=');
523 // Remove leading spaces from cookie names, needed for multi-cookie
524 // header where ; can be followed by a space */
525 while (isspace(*var)) {
526 var++;
529 if (var != val && *var != '\0') {
530 if (val) { /* have a value */
531 int len = val - var;
532 char *name = url_decode(var, len);
533 String sname(name, len, AttachString);
535 ++val;
536 len = strlen(val);
537 char *value = url_decode(val, len);
538 if (RuntimeOption::EnableMagicQuotesGpc) {
539 char *slashedvalue = string_addslashes(value, len);
540 free(value);
541 value = slashedvalue;
543 String svalue(value, len, AttachString);
545 register_variable(variables, (char*)sname.data(), svalue, false);
546 } else {
547 int len = strlen(var);
548 char *name = url_decode(var, len);
549 String sname(name, len, AttachString);
551 register_variable(variables, (char*)sname.data(), "", false);
555 var = strtok_r(nullptr, ";", &strtok_buf);
559 bool HttpProtocol::IsRfc1867(const string contentType, string &boundary) {
560 if (contentType.empty()) return false;
561 const char *ctstr = contentType.c_str();
562 char *s;
563 char *e;
564 for (s = (char*)ctstr; *s && !(*s == ';' || *s == ',' || *s == ' '); s++) ;
565 if (strncasecmp(ctstr, MULTIPART_CONTENT_TYPE, s - ctstr)) {
566 return false;
568 s = strstr(s, "boundary");
569 if (!s || !(s=strchr(s, '='))) {
570 Logger::Warning("Missing boundary in multipart/form-data POST data");
571 return false;
573 s++;
574 if (s[0] == '"') {
575 s++;
576 e = strchr(s, '"');
577 if (!e) {
578 Logger::Warning("Invalid boundary in multipart/form-data POST data");
579 return false;
581 } else {
582 /* search for the end of the boundary */
583 e = strchr(s, ',');
585 if (e) {
586 e[0] = '\0';
588 boundary = s;
589 return true;
592 void HttpProtocol::DecodeRfc1867(Transport *transport,
593 Variant &post, Variant &files,
594 int contentLength, const void *&data,
595 int &size, string boundary) {
596 rfc1867PostHandler(transport, post, files, contentLength,
597 data, size, boundary);
600 const char *HttpProtocol::GetReasonString(int code) {
601 switch (code) {
602 case 100: return "Continue";
603 case 101: return "Switching Protocols";
604 case 200: return "OK";
605 case 201: return "Created";
606 case 202: return "Accepted";
607 case 203: return "Non-Authoritative Information";
608 case 204: return "No Content";
609 case 205: return "Reset Content";
610 case 206: return "Partial Content";
611 case 207: return "Multi-Status";
612 case 300: return "Multiple Choices";
613 case 301: return "Moved Permanently";
614 case 302: return "Found";
615 case 303: return "See Other";
616 case 304: return "Not Modified";
617 case 305: return "Use Proxy";
618 case 307: return "Temporary Redirect";
619 case 400: return "Bad Request";
620 case 401: return "Unauthorized";
621 case 402: return "Payment Required";
622 case 403: return "Forbidden";
623 case 404: return "Not Found";
624 case 405: return "Method Not Allowed";
625 case 406: return "Not Acceptable";
626 case 407: return "Proxy Authentication Required";
627 case 408: return "Request Time-out";
628 case 409: return "Conflict";
629 case 410: return "Gone";
630 case 411: return "Length Required";
631 case 412: return "Precondition Failed";
632 case 413: return "Request Entity Too Large";
633 case 414: return "Request-URI Too Large";
634 case 415: return "Unsupported Media Type";
635 case 416: return "Requested range not satisfiable";
636 case 417: return "Expectation Failed";
637 case 500: return "Internal Server Error";
638 case 501: return "Not Implemented";
639 case 502: return "Bad Gateway";
640 case 503: return "Service Unavailable";
641 case 504: return "Gateway Time-out";
642 case 505: return "HTTP Version not supported";
644 return "Bad Response";
647 ///////////////////////////////////////////////////////////////////////////////
649 bool HttpProtocol::ProxyRequest(Transport *transport, bool force,
650 const std::string &url,
651 int &code, std::string &error,
652 StringBuffer &response,
653 HeaderMap *extraHeaders /* = NULL */) {
654 assert(transport);
655 if (transport->headersSent()) {
656 raise_warning("Cannot proxy request - headers already sent");
657 return false;
660 HeaderMap requestHeaders;
661 transport->getHeaders(requestHeaders);
662 if (extraHeaders) {
663 for (HeaderMap::const_iterator iter = extraHeaders->begin();
664 iter != extraHeaders->end(); ++iter) {
665 vector<string> &values = requestHeaders[iter->first];
666 values.insert(values.end(), iter->second.begin(), iter->second.end());
670 int size = 0;
671 const char *data = nullptr;
672 if (transport->getMethod() == Transport::Method::POST) {
673 data = (const char *)transport->getPostData(size);
676 code = 0; // HTTP status of curl or 0 for "no server response code"
677 vector<String> responseHeaders;
678 HttpClient http;
679 if (data && size) {
680 code = http.post(url.c_str(), data, size, response, &requestHeaders,
681 &responseHeaders);
682 } else {
683 code = http.get(url.c_str(), response, &requestHeaders, &responseHeaders);
685 if (code == 0) {
686 if (!force) return false; // so we can retry
687 Logger::Error("Unable to proxy %s: %s", url.c_str(),
688 http.getLastError().c_str());
689 error = http.getLastError();
690 return true;
693 for (unsigned int i = 0; i < responseHeaders.size(); i++) {
694 String &header = responseHeaders[i];
695 if (header.find(":") != String::npos &&
696 header.find("Content-Length: ") != 0 &&
697 header.find("Client-Transfer-Encoding: ") != 0 &&
698 header.find("Transfer-Encoding: ") != 0 &&
699 header.find("Connection: ") != 0) {
700 transport->addHeader(header.data());
703 const char* respData = response.data();
704 if (!respData) {
705 respData = "";
707 Logger::Verbose("Response code was %d when proxying %s", code, url.c_str());
708 return true;
711 ///////////////////////////////////////////////////////////////////////////////