Free code.prof after retranslate-all completes
[hiphop-php.git] / hphp / test / ext / test_server.cpp
blob7ae355490782dd539980e8fa6b890b1f8eb7b713
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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/test/ext/test_server.h"
19 #include "hphp/compiler/option.h"
21 #include "hphp/util/async-func.h"
22 #include "hphp/util/process-exec.h"
24 #include "hphp/runtime/ext/curl/ext_curl.h"
25 #include "hphp/runtime/ext/std/ext_std_options.h"
26 #include "hphp/runtime/server/http-request-handler.h"
27 #include "hphp/runtime/base/array-init.h"
28 #include "hphp/runtime/base/comparisons.h"
29 #include "hphp/runtime/base/http-client.h"
30 #include "hphp/runtime/base/program-functions.h"
31 #include "hphp/runtime/base/runtime-option.h"
32 #include "hphp/runtime/server/server.h"
34 #include <sys/types.h>
36 #include <fstream>
37 #include <memory>
38 #include <sys/param.h>
39 #include <vector>
41 #include <folly/Conv.h>
42 #include <folly/portability/Sockets.h>
44 using namespace HPHP;
46 #define PORT_MIN 1024
47 #define PORT_MAX 65535
49 ///////////////////////////////////////////////////////////////////////////////
51 TestServer::TestServer(const std::string serverType)
52 : m_serverType(serverType) { }
54 static int s_server_port = 0;
55 static int s_admin_port = 0;
56 static int s_rpc_port = 0;
57 static int inherit_fd = -1;
58 static std::unique_ptr<AsyncFunc<TestServer>> s_func;
59 static char s_pidfile[PATH_MAX];
60 static char s_repoFile[PATH_MAX];
61 static char s_logFile[PATH_MAX];
62 static char s_filename[PATH_MAX];
63 static int k_timeout = 30;
65 bool TestServer::VerifyServerResponse(const char* input, const char** outputs,
66 const char** urls, int nUrls,
67 const char* /*method*/,
68 const char* header, const char* postdata,
69 bool responseHeader,
70 const char* file /* = "" */,
71 int line /* = 0 */, int port /* = 0 */) {
72 assert(input);
73 if (port == 0) port = s_server_port;
75 std::string fullPath = "runtime/tmp/string";
76 std::ofstream f(fullPath.c_str());
77 if (!f) {
78 printf("Unable to open %s for write. Run this test from hphp/.\n",
79 fullPath.c_str());
80 return false;
83 f << input;
84 f.close();
86 AsyncFunc<TestServer> func(this, &TestServer::RunServer);
87 func.start();
89 bool passed = true;
90 if (s_func) {
91 if (!s_func->waitForEnd(k_timeout)) {
92 // Takeover didn't complete in 30s, stop the old server
93 fprintf(stderr, "stopping HHVM\n");
94 AsyncFunc<TestServer> stopFunc(this, &TestServer::KillServer);
95 stopFunc.run();
96 fprintf(stderr, "Waiting for stop\n");
97 stopFunc.waitForEnd();
98 fprintf(stderr, "Waiting for old HHVM\n");
99 s_func->waitForEnd();
100 // Mark this test a failure
101 fprintf(stderr, "Proceeding to test\n");
102 passed = false;
104 s_func.reset();
107 std::string actual;
109 int url = 0;
110 for (url = 0; url < nUrls; url++) {
111 String server = "http://";
112 server += HHVM_FN(php_uname)("n").toString();
113 server += ":" + folly::to<std::string>(port) + "/";
114 server += urls[url];
115 actual = "<No response from server>";
116 std::string err;
117 for (int i = 0; i < 50; i++) {
118 Variant c = HHVM_FN(curl_init)();
119 HHVM_FN(curl_setopt)(c.toResource(), CURLOPT_URL, server);
120 HHVM_FN(curl_setopt)(c.toResource(), CURLOPT_RETURNTRANSFER, true);
121 if (postdata) {
122 HHVM_FN(curl_setopt)(c.toResource(), CURLOPT_POSTFIELDS, postdata);
123 HHVM_FN(curl_setopt)(c.toResource(), CURLOPT_POST, true);
125 if (header) {
126 HHVM_FN(curl_setopt)(c.toResource(), CURLOPT_HTTPHEADER,
127 make_varray(header));
129 if (responseHeader) {
130 HHVM_FN(curl_setopt)(c.toResource(), CURLOPT_HEADER, 1);
133 Variant res = HHVM_FN(curl_exec)(c.toResource());
134 if (!same(res, false)) {
135 actual = res.toString().toCppString();
136 break;
138 sleep(1); // wait until HTTP server is up and running
140 if (actual != outputs[url]) {
141 if (!responseHeader ||
142 actual.find(outputs[url]) == std::string::npos) {
143 passed = false;
144 break;
149 AsyncFunc<TestServer>(this, &TestServer::StopServer).run();
150 func.waitForEnd();
152 if (!passed) {
153 printf("%s:%d\nParsing: [%s] (req %d)\nBet %d:\n"
154 "--------------------------------------\n"
155 "%s"
156 "--------------------------------------\n"
157 "Got %d:\n"
158 "--------------------------------------\n"
159 "%s"
160 "--------------------------------------\n",
161 file, line, input, url + 1, (int)strlen(outputs[url]), outputs[url],
162 (int)actual.length(), actual.c_str());
163 return false;
165 return true;
168 void TestServer::RunServer() {
169 std::string out, err;
170 auto const portConfig = "-vServer.Port=" +
171 folly::to<std::string>(s_server_port);
172 auto const adminConfig = "-vAdminServer.Port=" +
173 folly::to<std::string>(s_admin_port);
174 auto const rpcConfig = "-vSatellites.rpc.Port=" +
175 folly::to<std::string>(s_rpc_port);
176 auto const fd = folly::to<std::string>(inherit_fd);
177 auto option = inherit_fd >= 0
178 ? "--port-fd=" + fd
179 : "-vServer.TakeoverFilename=" + std::string(s_filename);
180 auto serverType = std::string("-vServer.Type=") + m_serverType;
181 auto pidFile = std::string("-vPidFile=") + s_pidfile;
182 auto repoFile = std::string("-vRepo.Central.Path=") + s_repoFile;
183 auto logFile = std::string("-vLog.File=") + s_logFile;
185 const char *argv[] = {
186 "__HHVM__", "--mode=server", "--config=test/ext/config-server.hdf",
187 portConfig.c_str(), adminConfig.c_str(), rpcConfig.c_str(),
188 option.c_str(), serverType.c_str(), pidFile.c_str(), repoFile.c_str(),
189 logFile.c_str(),
190 nullptr
193 #ifdef HHVM_PATH
194 // replace __HHVM__
195 argv[0] = HHVM_PATH;
196 #endif
198 proc::exec(argv[0], argv, nullptr, out, &err);
201 void TestServer::StopServer() {
202 for (int i = 0; i < 10; i++) {
203 Variant c = HHVM_FN(curl_init)();
204 String url = "http://";
205 url += HHVM_FN(php_uname)("n").toString();
206 url += ":" + folly::to<std::string>(s_admin_port) + "/stop";
207 HHVM_FN(curl_setopt)(c.toResource(), CURLOPT_URL, url);
208 HHVM_FN(curl_setopt)(c.toResource(), CURLOPT_RETURNTRANSFER, true);
209 HHVM_FN(curl_setopt)(c.toResource(), CURLOPT_TIMEOUT, 1);
210 HHVM_FN(curl_setopt)(c.toResource(), CURLOPT_CONNECTTIMEOUT, 1);
211 Variant res = HHVM_FN(curl_exec)(c.toResource());
212 if (!same(res, false)) {
213 return;
215 sleep(1); // wait until HTTP server is up and running
217 KillServer();
220 void TestServer::KillServer() {
221 fprintf(stderr, "Have to kill HHVM\n");
222 // Getting more aggresive
223 char buf[1024];
224 int fd = open(s_pidfile, O_RDONLY);
225 int ret = read(fd, buf, sizeof(buf) - 1);
226 if (ret <= 0) {
227 printf("Can't read pid from pid file %s\n", s_pidfile);
228 return;
230 buf[ret] = 0;
231 std::string out, err;
232 const char *argv[] = {"kill", buf, nullptr};
233 for (int i = 0; i < 10; i++) {
234 auto ret = proc::exec(argv[0], argv, nullptr, out, &err);
235 if (ret) {
236 return;
240 // Last resort
241 const char *argv9[] = {"kill", "-9", buf, nullptr};
242 for (int i = 0; i < 10; i++) {
243 auto ret = proc::exec(argv9[0], argv9, nullptr, out, &err);
244 if (ret) {
245 return;
249 printf("Can't kill pid %s read from pid file %s\n", buf, s_pidfile);
252 ///////////////////////////////////////////////////////////////////////////////
254 struct TestServerRequestHandler : RequestHandler {
255 explicit TestServerRequestHandler(int timeout) : RequestHandler(timeout) {}
256 // implementing RequestHandler
257 void handleRequest(Transport* /*transport*/) override {
258 // do nothing
260 void abortRequest(Transport* /*transport*/) override {
261 // do nothing
265 static int find_server_port(const std::string &serverType) {
266 for (int tries = 0; true; tries++) {
267 auto port = (rand() % (PORT_MAX - PORT_MIN)) + PORT_MIN;
268 try {
269 ServerPtr server = ServerFactoryRegistry::createServer(
270 serverType, "127.0.0.1", port, 50);
271 server->setRequestHandlerFactory<TestServerRequestHandler>(k_timeout);
272 server->start();
273 server->stop();
274 server->waitForEnd();
275 return port;
276 } catch (const FailedToListenException& e) {
277 if (tries >= 100) throw;
282 bool TestServer::RunTests(const std::string &which) {
283 bool ret = true;
285 srand(time(0));
286 s_server_port = find_server_port(m_serverType);
287 s_admin_port = find_server_port(m_serverType);
288 s_rpc_port = find_server_port(m_serverType);
289 snprintf(s_pidfile, MAXPATHLEN, "/tmp/test_server.hhvm.pid_XXXXXX");
290 int tmpfd = mkstemp(s_pidfile);
291 close(tmpfd);
292 snprintf(s_repoFile, MAXPATHLEN, "/tmp/test_server.hhvm.hhbc_XXXXXX");
293 tmpfd = mkstemp(s_repoFile);
294 close(tmpfd);
295 snprintf(s_logFile, MAXPATHLEN, "/tmp/test_server.hhvm.log_XXXXXX");
296 tmpfd = mkstemp(s_logFile);
297 close(tmpfd);
299 RUN_TEST(TestInheritFdServer);
300 RUN_TEST(TestTakeoverServer);
301 RUN_TEST(TestSanity);
302 RUN_TEST(TestServerVariables);
303 RUN_TEST(TestInteraction);
304 RUN_TEST(TestGet);
305 RUN_TEST(TestPost);
306 RUN_TEST(TestExpectContinue);
307 RUN_TEST(TestCookie);
308 RUN_TEST(TestResponseHeader);
309 RUN_TEST(TestSetCookie);
310 //RUN_TEST(TestRequestHandling);
311 RUN_TEST(TestHttpClient);
312 RUN_TEST(TestRPCServer);
313 RUN_TEST(TestXboxServer);
314 RUN_TEST(TestPageletServer);
316 return ret;
319 ///////////////////////////////////////////////////////////////////////////////
321 bool TestServer::TestSanity() {
322 VSR("<?php print 'Hello, World!';",
323 "Hello, World!");
324 return true;
327 bool TestServer::TestServerVariables() {
328 VSR("<?php var_dump($_POST, $_GET);",
329 "array(0) {\n}\narray(0) {\n}\n");
331 VSR("<?php print $_SERVER['REQUEST_URI'];",
332 "/string");
334 VSGET("<?php "
335 "var_dump($_SERVER['PATH_INFO']);"
336 "var_dump(clean($_SERVER['PATH_TRANSLATED']));"
337 "var_dump($_SERVER['SCRIPT_NAME']);"
338 "var_dump($_SERVER['REQUEST_URI']);"
339 "var_dump(clean($_SERVER['SCRIPT_FILENAME']));"
340 "var_dump($_SERVER['QUERY_STRING']);"
341 "function clean($x) { return str_replace(getcwd(),'',$x); }",
343 "string(13) \"/path/subpath\"\n"
344 "string(20) \"/string/path/subpath\"\n"
345 "string(7) \"/string\"\n"
346 "string(28) \"/string/path/subpath?a=1&b=2\"\n"
347 "string(7) \"/string\"\n"
348 "string(7) \"a=1&b=2\"\n",
350 "string/path/subpath?a=1&b=2");
352 VSGET("<?php "
353 "var_dump($_SERVER['PATH_INFO']);"
354 "var_dump(clean($_SERVER['PATH_TRANSLATED']));"
355 "var_dump($_SERVER['SCRIPT_NAME']);"
356 "var_dump($_SERVER['REQUEST_URI']);"
357 "var_dump(clean($_SERVER['SCRIPT_FILENAME']));"
358 "var_dump($_SERVER['QUERY_STRING']);"
359 "var_dump(isset($_ENV['HPHP_RPC']));"
360 "function clean($x) { return str_replace(getcwd(),'',$x); }",
362 "NULL\n"
363 "string(7) \"/string\"\n"
364 "string(7) \"/string\"\n"
365 "string(15) \"/string?a=1&b=2\"\n"
366 "string(7) \"/string\"\n"
367 "string(7) \"a=1&b=2\"\n"
368 "bool(false)\n",
370 "string?a=1&b=2");
372 return true;
375 bool TestServer::TestInteraction() {
376 // run this twice to test lvalBlackHole
377 VSR2("<?php "
378 "$a[] = new stdclass;"
379 "var_dump(count(array_combine($a, $a)));",
380 "");
382 return true;
385 bool TestServer::TestGet() {
386 VSGET("<?php var_dump($_GET['name']);",
387 "string(0) \"\"\n", "string?name");
389 VSGET("<?php var_dump($_GET['name'], $_GET['id']);",
390 "string(0) \"\"\nstring(1) \"1\"\n", "string?name&id=1");
392 VSGET("<?php print $_GET['name'];",
393 "value", "string?name=value");
395 VSGET("<?php var_dump($_GET['names']);",
396 "array(2) {\n"
397 " [1]=>\n"
398 " string(3) \"foo\"\n"
399 " [2]=>\n"
400 " string(3) \"bar\"\n"
401 "}\n",
402 "string?names[1]=foo&names[2]=bar");
404 VSGET("<?php var_dump($_GET['names']);",
405 "array(2) {\n"
406 " [0]=>\n"
407 " string(3) \"foo\"\n"
408 " [1]=>\n"
409 " string(3) \"bar\"\n"
410 "}\n",
411 "string?names[]=foo&names[]=bar");
413 VSGET("<?php print $_REQUEST['name'];",
414 "value", "string?name=value");
416 return true;
419 bool TestServer::TestPost() {
420 const char *params = "name=value";
422 VSPOST("<?php print $_POST['name'];",
423 "value", "string", params);
425 VSPOST("<?php print $_REQUEST['name'];",
426 "value", "string", params);
428 VSPOST("<?php print $HTTP_RAW_POST_DATA;",
429 "name=value", "string", params);
431 return true;
434 bool TestServer::TestExpectContinue() {
435 const char *params = "name=value";
437 VSRX("<?php print $_POST['name'];",
438 "value", "string", "POST", "Expect: 100-continue", params);
440 return true;
443 bool TestServer::TestCookie() {
444 VSRX("<?php print $_COOKIE['name'];",
445 "value", "string", "GET", "Cookie: name=value;", nullptr);
447 VSRX("<?php print $_COOKIE['name2'];",
448 "value2", "string", "GET", "Cookie: n=v;name2=value2;n3=v3", nullptr);
450 return true;
453 bool TestServer::TestResponseHeader() {
454 VSR("<?php header('Set-Cookie: name=value'); var_dump(headers_list());",
455 "array(1) {\n"
456 " [0]=>\n"
457 " string(22) \"Set-Cookie: name=value\"\n"
458 "}\n");
460 VSRES("<?php header('Set-Cookie: name=value');",
461 "Set-Cookie: name=value");
463 VSRES("<?php header('Location: new/url');",
464 "302");
466 VSRES("<?php header(\"Test-Header: x\ry\"); echo 'done';",
467 "done");
469 return true;
472 bool TestServer::TestSetCookie() {
473 VSR("<?php setcookie('name', 'value'); var_dump(headers_list());",
474 "array(1) {\n"
475 " [0]=>\n"
476 " string(22) \"Set-Cookie: name=value\"\n"
477 "}\n");
478 return true;
480 VSRES("<?php setcookie('name', 'value');",
481 "Set-Cookie: name=value");
483 return true;
486 ///////////////////////////////////////////////////////////////////////////////
488 const StaticString s_test("test");
490 struct TestTransport final : Transport {
491 TestTransport() : m_code(0) {}
493 int m_code;
494 std::string m_response;
497 * Implementing HttpTransport...
499 const char *getUrl() override { return "/string"; }
500 const char *getRemoteHost() override { return "remote"; }
501 const void *getPostData(size_t &size) override { size = 0; return nullptr; }
502 uint16_t getRemotePort() override { return 0; }
503 Method getMethod() override { return Transport::Method::GET; }
504 std::string getHeader(const char* /*name*/) override { return ""; }
505 const HeaderMap& getHeaders() override {
506 static const HeaderMap emptyMap{};
507 return emptyMap;
509 void addHeaderImpl(const char* /*name*/, const char* /*value*/) override {}
510 void removeHeaderImpl(const char* /*name*/) override {}
513 * Get a description of the type of transport.
515 String describe() const override {
516 return s_test;
519 void sendImpl(const void* data, int size, int code, bool /*chunked*/,
520 bool /*eom*/) override {
521 m_response.clear();
522 m_response.append((const char *)data, size);
523 m_code = code;
526 void process() {
527 HttpRequestHandler handler(0);
528 for (unsigned int i = 0; i < 100; i++) {
529 handler.run(this);
534 typedef std::shared_ptr<TestTransport> TestTransportPtr;
535 typedef std::vector<TestTransportPtr> TestTransportPtrVec;
536 typedef AsyncFunc<TestTransport> TestTransportAsyncFunc;
537 typedef std::shared_ptr<TestTransportAsyncFunc> TestTransportAsyncFuncPtr;
538 typedef std::vector<TestTransportAsyncFuncPtr> TestTransportAsyncFuncPtrVec;
540 #define TEST_SIZE 100
543 * Start processing TEST_SIZE number of requests at the same time with
544 * that many threads. This is mainly testing global variables to make sure
545 * all handling are thread-safe.
547 bool TestServer::TestRequestHandling() {
548 RuntimeOption::AllowedFiles.insert("/string");
549 TestTransportPtrVec transports(TEST_SIZE);
550 TestTransportAsyncFuncPtrVec funcs(TEST_SIZE);
551 for (unsigned int i = 0; i < TEST_SIZE; i++) {
552 TestTransport *transport = new TestTransport();
553 transports[i] = TestTransportPtr(transport);
554 funcs[i] = TestTransportAsyncFuncPtr
555 (new TestTransportAsyncFunc(transport, &TestTransport::process));
558 for (unsigned int i = 0; i < TEST_SIZE; i++) {
559 funcs[i]->start();
561 for (unsigned int i = 0; i < TEST_SIZE; i++) {
562 funcs[i]->waitForEnd();
564 for (unsigned int i = 0; i < TEST_SIZE; i++) {
565 VS(transports[i]->m_code, 200);
566 VS(String(transports[i]->m_response), "Hello, world!");
568 return Count(true);
571 static bool PreBindSocketHelper(struct addrinfo *info) {
572 if (info->ai_family != AF_INET && info->ai_family != AF_INET6) {
573 printf("No IPV4/6 interface found.\n");
574 return false;
577 int fd = socket(info->ai_family, SOCK_STREAM, IPPROTO_TCP);
578 if (fd < 0) {
579 printf("Error creating socket: %s\n", strerror(errno));
580 return false;
583 int ret = ::bind(fd, info->ai_addr, info->ai_addrlen);
584 if (ret < 0) {
585 printf("Error binding socket to port %d: %s\n", s_server_port,
586 strerror(errno));
587 return false;
590 inherit_fd = fd;
591 return true;
594 bool TestServer::PreBindSocket() {
595 struct addrinfo hints, *res, *res0;
596 std::memset(&hints, 0, sizeof(hints));
597 hints.ai_family = AF_UNSPEC;
598 hints.ai_socktype = SOCK_STREAM;
599 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
601 if (getaddrinfo(nullptr, folly::to<std::string>(s_server_port).c_str(),
602 &hints, &res0) < 0) {
603 printf("Error in getaddrinfo(): %s\n", strerror(errno));
604 return false;
607 for (res = res0; res; res = res->ai_next) {
608 if (res->ai_family == AF_INET6 || res->ai_next == nullptr) {
609 break;
613 bool ret = PreBindSocketHelper(res);
614 freeaddrinfo(res0);
615 return ret;
618 void TestServer::CleanupPreBoundSocket() {
619 close(inherit_fd);
620 inherit_fd = -1;
623 bool TestServer::TestInheritFdServer() {
624 WITH_PREBOUND_SOCKET(VSR("<?php print 'Hello, World!';",
625 "Hello, World!"));
626 return true;
629 bool TestServer::TestTakeoverServer() {
630 // start a server
631 snprintf(s_filename, MAXPATHLEN, "/tmp/hphp_takeover_XXXXXX");
632 auto const tmpfd = mkstemp(s_filename);
633 close(tmpfd);
635 s_func.reset(new AsyncFunc<TestServer>(this, &TestServer::RunServer));
636 s_func->start();
638 // Wait for the server to actually start
639 HttpClient http;
640 StringBuffer response;
641 req::vector<String> responseHeaders;
642 auto url = "http://127.0.0.1:" + folly::to<std::string>(s_server_port) +
643 "/status.php";
644 HeaderMap headers;
645 for (int i = 0; i < 10; i++) {
646 int code = http.get(url.c_str(), response, &headers, &responseHeaders);
647 if (code > 0) {
648 break;
650 sleep(1);
653 // will start a second server, which should takeover
654 VSR("<?php print 'Hello, World!';",
655 "Hello, World!");
656 unlink(s_filename);
657 s_filename[0] = 0;
658 return true;
661 ///////////////////////////////////////////////////////////////////////////////
663 struct EchoHandler final : RequestHandler {
664 explicit EchoHandler(int timeout) : RequestHandler(timeout) {}
665 // implementing RequestHandler
666 void handleRequest(Transport *transport) override {
667 g_context.getCheck();
668 const HeaderMap& headers = transport->getHeaders();
670 std::string response;
671 response = "\nGET param: name = ";
672 response += transport->getParam("name");
674 if (transport->getMethod() == Transport::Method::POST) {
675 size_t size = 0;
676 auto const data = (const char *)transport->getPostData(size);
677 response += "\nPOST data: ";
678 response += std::string(data, size);
681 for (HeaderMap::const_iterator iter = headers.begin();
682 iter != headers.end(); ++iter) {
683 response += "\nHeader: ";
684 response += iter->first;
685 for (unsigned int i = 0; i < iter->second.size(); i++) {
686 response += "\n";
687 response += folly::to<std::string>(i);
688 response += ": ";
689 response += iter->second[i];
693 transport->addHeader("Custom", "blah");
694 transport->sendString(response);
695 hphp_memory_cleanup();
697 void abortRequest(Transport *transport) override {
698 transport->sendString("Service Unavailable", 503);
702 bool TestServer::TestHttpClient() {
703 ServerPtr server;
704 for (s_server_port = PORT_MIN; s_server_port <= PORT_MAX; s_server_port++) {
705 try {
706 server = ServerFactoryRegistry::createServer(
707 m_serverType, "127.0.0.1", s_server_port, 50);
708 server->setRequestHandlerFactory<EchoHandler>(0);
709 server->start();
710 break;
711 } catch (const FailedToListenException& e) {
712 if (s_server_port == PORT_MAX) throw;
716 HeaderMap headers;
717 headers["Cookie"].push_back("c1=v1;c2=v2;");
718 headers["Cookie"].push_back("c3=v3;c4=v4;");
719 auto url = "http://127.0.0.1:" + folly::to<std::string>(s_server_port) +
720 "/echo?name=value";
722 static const StaticString s_Custom_colon_blah("Custom: blah");
724 for (int i = 0; i < 10; i++) {
725 HttpClient http;
726 StringBuffer response;
727 req::vector<String> responseHeaders;
728 int code = http.get(url.c_str(), response, &headers, &responseHeaders);
729 VS(code, 200);
730 VS(response.data(),
731 ("\nGET param: name = value"
732 "\nHeader: Cookie"
733 "\n0: c1=v1;c2=v2;"
734 "\n1: c3=v3;c4=v4;"
735 "\nHeader: Accept"
736 "\n0: */*"
737 "\nHeader: Host"
738 "\n0: 127.0.0.1:" + folly::to<std::string>(s_server_port)).c_str());
740 bool found = false;
741 for (unsigned int i2 = 0; i2 < responseHeaders.size(); i2++) {
742 if (responseHeaders[i2] == s_Custom_colon_blah) {
743 found = true;
746 VERIFY(found);
748 for (int i = 0; i < 10; i++) {
749 HttpClient http;
750 StringBuffer response;
751 req::vector<String> responseHeaders;
752 int code = http.post(url.c_str(), "postdata", 8, response, &headers,
753 &responseHeaders);
754 VS(code, 200);
755 VS(response.data(),
756 ("\nGET param: name = value"
757 "\nPOST data: postdata"
758 "\nHeader: Content-Type"
759 "\n0: application/x-www-form-urlencoded"
760 "\nHeader: Cookie"
761 "\n0: c1=v1;c2=v2;"
762 "\n1: c3=v3;c4=v4;"
763 "\nHeader: Accept"
764 "\n0: */*"
765 "\nHeader: Content-Length"
766 "\n0: 8"
767 "\nHeader: Host"
768 "\n0: 127.0.0.1:" + folly::to<std::string>(s_server_port)).c_str());
770 bool found = false;
771 for (unsigned int i2 = 0; i2 < responseHeaders.size(); i2++) {
772 if (responseHeaders[i2] == s_Custom_colon_blah) {
773 found = true;
776 VERIFY(found);
779 server->stop();
780 server->waitForEnd();
781 return Count(true);
784 bool TestServer::TestRPCServer() {
785 // the simplest case
786 VSGETP("<?php\n"
787 "function f() { return 100; }\n",
788 "100",
789 "f?auth=test",
790 s_rpc_port);
792 // array output
793 VSGETP("<?php\n"
794 "function f($a) { return array(1, 2, 3, $a); }\n",
795 "[1,2,3,\"hello\"]",
796 "f?auth=test&p=\"hello\"",
797 s_rpc_port);
799 // associate arrays
800 VSGETP("<?php\n"
801 "function f($a, $b) { return array_merge($a, $b); }\n",
802 "{\"a\":1,\"0\":2,\"1\":1,\"2\":2}",
803 "f?auth=test&p={\"a\":1,\"1\":2}&p=[1,2]",
804 s_rpc_port);
806 // builtin function and static method
807 VSGETP("<?php\n"
808 "class A { static function f($a) { return $a; } }\n",
809 "100",
810 "call_user_func?auth=test&p=\"A::f\"&p=100",
811 s_rpc_port);
813 // invoking a file, with NO json encoding
814 // "int(100)" is printed twice, one from warmup, and the other from include
815 VSGETP("<?php\n"
816 "var_dump(100);\n",
817 "int(100)\n"
818 "int(100)\n",
819 "?include=string&output=1&auth=test",
820 s_rpc_port);
822 VSGETP("<?php\n"
823 "var_dump(isset($_ENV['HPHP_RPC']));\n",
824 "bool(true)\n"
825 "bool(true)\n",
826 "?include=string&output=1&auth=test",
827 s_rpc_port);
829 return true;
832 bool TestServer::TestXboxServer() {
833 VSGET("<?php\n"
834 "if (array_key_exists('main', $_GET)) {\n"
835 " $t = xbox_task_start('1');\n"
836 " xbox_task_result($t, 0, $r);\n"
837 " var_dump($r);\n"
838 " $t = xbox_task_start('2');\n"
839 " xbox_task_result($t, 0, $r);\n"
840 " $t = xbox_task_start('1');\n"
841 " xbox_task_result($t, 0, $r);\n"
842 " var_dump($r);\n"
843 " sleep(7);\n"
844 " $t = xbox_task_start('3');\n"
845 " xbox_task_result($t, 0, $r);\n"
846 " var_dump($r);\n"
847 " sleep(2);\n"
848 " $t = xbox_task_start('4');\n"
849 " xbox_task_result($t, 0, $r);\n"
850 " $t = xbox_task_start('3');\n"
851 " xbox_task_result($t, 0, $r);\n"
852 " var_dump($r);\n"
853 " sleep(2);\n"
854 " $t = xbox_task_start('3');\n"
855 " xbox_task_result($t, 0, $r);\n"
856 " var_dump($r);\n"
857 "} else {\n"
858 " function xbox_process_message($msg) {\n"
859 " if ($msg == '1') return xbox_get_thread_timeout();\n"
860 " else if ($msg == '2') xbox_set_thread_timeout(5);\n"
861 " else if ($msg == '3') return xbox_get_thread_time();\n"
862 " else xbox_schedule_thread_reset();\n"
863 " }\n"
864 "}\n",
865 "int(10)\n"
866 "int(5)\n"
867 "int(0)\n"
868 "int(0)\n"
869 "int(0)\n",
870 "string?main=1");
872 return true;
875 bool TestServer::TestPageletServer() {
876 VSGET("<?php\n"
877 "if (array_key_exists('pagelet', $_GET)) {\n"
878 " echo 'Hello from the pagelet!';\n"
879 "} else {\n"
880 " $h = array('Host: ' . $_SERVER['HTTP_HOST']);\n"
881 " $t = pagelet_server_task_start('/string?pagelet=1', $h, '');\n"
882 " echo 'First! ';\n"
883 " $r = pagelet_server_task_result($t, $h, $c);\n"
884 " echo $r;\n"
885 "}\n",
886 "First! Hello from the pagelet!",
887 "string");
889 // POST vs GET
890 VSGET("<?php\n"
891 "if (array_key_exists('pagelet', $_GET)) {\n"
892 " echo $_SERVER['REQUEST_METHOD'];\n"
893 "} else {\n"
894 " $h = array('Host: ' . $_SERVER['HTTP_HOST']);\n"
895 " $t = pagelet_server_task_start('/string?pagelet=1', $h, '');\n"
896 " echo 'First! ';\n"
897 " $r = pagelet_server_task_result($t, $h, $c);\n"
898 " echo $r;\n"
899 "}\n",
900 "First! GET",
901 "string");
903 VSGET("<?php\n"
904 "if ($_SERVER['THREAD_TYPE'] == 'Pagelet Thread') {\n"
905 " echo 'hello';\n"
906 " pagelet_server_flush();\n"
907 " ob_start();\n"
908 " echo 'world';\n"
909 " pagelet_server_flush();\n"
910 " echo 'what';\n"
911 "} else {\n"
912 " $h = array('Host: ' . $_SERVER['HTTP_HOST']);\n"
913 " $t = pagelet_server_task_start('/string', $h, '');\n"
914 " for ($i = 0; ; $i++) {\n"
915 " while (($s = pagelet_server_task_status($t)) == \n"
916 " PAGELET_NOT_READY) { sleep(1); }\n"
917 " echo \"Step $i:\\n\";\n"
918 " $r = pagelet_server_task_result($t, $h, $c);\n"
919 " echo $r . \"\\n\";\n"
920 " if ($s == PAGELET_DONE) break;\n"
921 " }\n"
922 "}\n",
923 "Step 0:\n"
924 "hello\n"
925 "Step 1:\n"
926 "world\n"
927 "Step 2:\n"
928 "what\n",
929 "string");
931 return true;