2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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"
18 #include "hphp/compiler/parser/parser.h"
19 #include "hphp/compiler/builtin_symbols.h"
20 #include "hphp/compiler/code_generator.h"
21 #include "hphp/compiler/analysis/analysis_result.h"
22 #include "hphp/util/util.h"
23 #include "hphp/util/process.h"
24 #include "hphp/compiler/option.h"
25 #include "hphp/util/async-func.h"
26 #include "hphp/runtime/ext/curl/ext_curl.h"
27 #include "hphp/runtime/ext/ext_options.h"
28 #include "hphp/runtime/server/http-request-handler.h"
29 #include "hphp/runtime/base/http-client.h"
30 #include "hphp/runtime/base/runtime-option.h"
31 #include "hphp/runtime/server/server.h"
38 #define PORT_MAX 65535
40 ///////////////////////////////////////////////////////////////////////////////
42 TestServer::TestServer(const std::string serverType
)
43 : m_serverType(serverType
) { }
45 static int s_server_port
= 0;
46 static int s_admin_port
= 0;
47 static int s_rpc_port
= 0;
48 static int inherit_fd
= -1;
49 static std::unique_ptr
<AsyncFunc
<TestServer
>> s_func
;
50 static char s_pidfile
[MAXPATHLEN
];
51 static char s_repoFile
[MAXPATHLEN
];
52 static char s_filename
[MAXPATHLEN
];
53 static int k_timeout
= 30;
55 bool TestServer::VerifyServerResponse(const char *input
, const char **outputs
,
56 const char **urls
, int nUrls
,
58 const char *header
, const char *postdata
,
60 const char *file
/* = "" */,
64 if (port
== 0) port
= s_server_port
;
66 if (!CleanUp()) return false;
67 string fullPath
= "runtime/tmp/string";
68 std::ofstream
f(fullPath
.c_str());
70 printf("Unable to open %s for write. Run this test from hphp/.\n",
78 AsyncFunc
<TestServer
> func(this, &TestServer::RunServer
);
82 s_func
->waitForEnd(k_timeout
);
90 for (url
= 0; url
< nUrls
; url
++) {
91 String server
= "http://";
92 server
+= f_php_uname("n");
93 server
+= ":" + lexical_cast
<string
>(port
) + "/";
95 actual
= "<No response from server>";
97 for (int i
= 0; i
< 10; i
++) {
98 Variant c
= HHVM_FN(curl_init
)();
99 HHVM_FN(curl_setopt
)(c
.toResource(), k_CURLOPT_URL
, server
);
100 HHVM_FN(curl_setopt
)(c
.toResource(), k_CURLOPT_RETURNTRANSFER
, true);
102 HHVM_FN(curl_setopt
)(c
.toResource(), k_CURLOPT_POSTFIELDS
, postdata
);
103 HHVM_FN(curl_setopt
)(c
.toResource(), k_CURLOPT_POST
, true);
106 HHVM_FN(curl_setopt
)(c
.toResource(), k_CURLOPT_HTTPHEADER
,
107 make_packed_array(header
));
109 if (responseHeader
) {
110 HHVM_FN(curl_setopt
)(c
.toResource(), k_CURLOPT_HEADER
, 1);
113 Variant res
= HHVM_FN(curl_exec
)(c
.toResource());
114 if (!same(res
, false)) {
115 actual
= (std::string
) res
.toString();
118 sleep(1); // wait until HTTP server is up and running
120 if (actual
!= outputs
[url
]) {
121 if (!responseHeader
||
122 actual
.find(outputs
[url
]) == string::npos
) {
129 AsyncFunc
<TestServer
>(this, &TestServer::StopServer
).run();
133 printf("%s:%d\nParsing: [%s] (req %d)\nBet %d:\n"
134 "--------------------------------------\n"
136 "--------------------------------------\n"
138 "--------------------------------------\n"
140 "--------------------------------------\n",
141 file
, line
, input
, url
+ 1, (int)strlen(outputs
[url
]), outputs
[url
],
142 (int)actual
.length(), actual
.c_str());
148 void TestServer::RunServer() {
150 string portConfig
= "-vServer.Port=" + lexical_cast
<string
>(s_server_port
);
151 string adminConfig
= "-vAdminServer.Port=" +
152 lexical_cast
<string
>(s_admin_port
);
153 string rpcConfig
= "-vSatellites.rpc.Port=" +
154 lexical_cast
<string
>(s_rpc_port
);
155 string fd
= lexical_cast
<string
>(inherit_fd
);
156 string option
= (inherit_fd
>= 0) ? (string("--port-fd=") + fd
) :
157 (string("-vServer.TakeoverFilename=") + string(s_filename
));
158 string serverType
= string("-vServer.Type=") + m_serverType
;
159 string pidFile
= string("-vPidFile=") + string(s_pidfile
);
160 string repoFile
= string("-vRepo.Central.Path=") + string(s_repoFile
);
162 const char *argv
[] = {
163 "__HHVM__", "--mode=server", "--config=test/ext/config-server.hdf",
164 portConfig
.c_str(), adminConfig
.c_str(), rpcConfig
.c_str(),
165 option
.c_str(), serverType
.c_str(), pidFile
.c_str(),
170 if (Option::EnableEval
< Option::FullEval
) {
171 argv
[0] = "runtime/tmp/TestServer/test";
176 Process::Exec(argv
[0], argv
, nullptr, out
, &err
);
179 void TestServer::StopServer() {
180 for (int i
= 0; i
< 10; i
++) {
181 Variant c
= HHVM_FN(curl_init
)();
182 String url
= "http://";
183 url
+= f_php_uname("n");
184 url
+= ":" + lexical_cast
<string
>(s_admin_port
) + "/stop";
185 HHVM_FN(curl_setopt
)(c
.toResource(), k_CURLOPT_URL
, url
);
186 HHVM_FN(curl_setopt
)(c
.toResource(), k_CURLOPT_RETURNTRANSFER
, true);
187 HHVM_FN(curl_setopt
)(c
.toResource(), k_CURLOPT_TIMEOUT
, 1);
188 HHVM_FN(curl_setopt
)(c
.toResource(), k_CURLOPT_CONNECTTIMEOUT
, 1);
189 Variant res
= HHVM_FN(curl_exec
)(c
.toResource());
190 if (!same(res
, false)) {
193 sleep(1); // wait until HTTP server is up and running
196 // Getting more aggresive
198 int fd
= open(s_pidfile
, O_RDONLY
);
199 int ret
= read(fd
, buf
, sizeof(buf
) - 1);
201 printf("Can't read pid from pid file %s\n", s_pidfile
);
206 const char *argv
[] = {"kill", buf
, nullptr};
207 for (int i
= 0; i
< 10; i
++) {
208 auto ret
= Process::Exec(argv
[0], argv
, nullptr, out
, &err
);
215 const char *argv9
[] = {"kill", "-9", buf
, nullptr};
216 for (int i
= 0; i
< 10; i
++) {
217 auto ret
= Process::Exec(argv9
[0], argv9
, nullptr, out
, &err
);
223 printf("Can't kill pid %s read from pid file %s\n", buf
, s_pidfile
);
226 ///////////////////////////////////////////////////////////////////////////////
228 class TestServerRequestHandler
: public RequestHandler
{
230 explicit TestServerRequestHandler(int timeout
) : RequestHandler(timeout
) {}
231 // implementing RequestHandler
232 virtual void handleRequest(Transport
*transport
) {
235 virtual void abortRequest(Transport
*transport
) {
240 static int find_server_port(const std::string
&serverType
) {
241 for (int tries
= 0; true; tries
++) {
242 auto port
= (rand() % (PORT_MAX
- PORT_MIN
)) + PORT_MIN
;
244 ServerPtr server
= ServerFactoryRegistry::createServer(
245 serverType
, "127.0.0.1", port
, 50);
246 server
->setRequestHandlerFactory
<TestServerRequestHandler
>(k_timeout
);
249 server
->waitForEnd();
251 } catch (const FailedToListenException
& e
) {
252 if (tries
>= 100) throw;
257 bool TestServer::RunTests(const std::string
&which
) {
261 s_server_port
= find_server_port(m_serverType
);
262 s_admin_port
= find_server_port(m_serverType
);
263 s_rpc_port
= find_server_port(m_serverType
);
264 snprintf(s_pidfile
, MAXPATHLEN
, "/tmp/pid_XXXXXX");
265 int tmpfd
= mkstemp(s_pidfile
);
267 snprintf(s_repoFile
, MAXPATHLEN
, "/tmp/test_server.hhvm.hhbc_XXXXXX");
268 tmpfd
= mkstemp(s_repoFile
);
271 RUN_TEST(TestInheritFdServer
);
272 RUN_TEST(TestTakeoverServer
);
273 RUN_TEST(TestSanity
);
274 RUN_TEST(TestServerVariables
);
275 RUN_TEST(TestInteraction
);
278 RUN_TEST(TestCookie
);
279 RUN_TEST(TestResponseHeader
);
280 RUN_TEST(TestSetCookie
);
281 //RUN_TEST(TestRequestHandling);
282 RUN_TEST(TestHttpClient
);
283 RUN_TEST(TestRPCServer
);
284 RUN_TEST(TestXboxServer
);
285 RUN_TEST(TestPageletServer
);
290 ///////////////////////////////////////////////////////////////////////////////
292 bool TestServer::TestSanity() {
293 VSR("<?php print 'Hello, World!';",
298 bool TestServer::TestServerVariables() {
299 VSR("<?php var_dump($_POST, $_GET);",
300 "array(0) {\n}\narray(0) {\n}\n");
302 VSR("<?php print $_SERVER['REQUEST_URI'];",
306 "var_dump($_SERVER['PATH_INFO']);"
307 "var_dump(clean($_SERVER['PATH_TRANSLATED']));"
308 "var_dump($_SERVER['SCRIPT_NAME']);"
309 "var_dump($_SERVER['REQUEST_URI']);"
310 "var_dump(clean($_SERVER['SCRIPT_FILENAME']));"
311 "var_dump($_SERVER['QUERY_STRING']);"
312 "function clean($x) { return str_replace(getcwd(),'',$x); }",
314 "string(13) \"/path/subpath\"\n"
315 "string(20) \"/string/path/subpath\"\n"
316 "string(7) \"/string\"\n"
317 "string(28) \"/string/path/subpath?a=1&b=2\"\n"
318 "string(7) \"/string\"\n"
319 "string(7) \"a=1&b=2\"\n",
321 "string/path/subpath?a=1&b=2");
324 "var_dump($_SERVER['PATH_INFO']);"
325 "var_dump(clean($_SERVER['PATH_TRANSLATED']));"
326 "var_dump($_SERVER['SCRIPT_NAME']);"
327 "var_dump($_SERVER['REQUEST_URI']);"
328 "var_dump(clean($_SERVER['SCRIPT_FILENAME']));"
329 "var_dump($_SERVER['QUERY_STRING']);"
330 "var_dump(isset($_ENV['HPHP_RPC']));"
331 "function clean($x) { return str_replace(getcwd(),'',$x); }",
334 "string(7) \"/string\"\n"
335 "string(7) \"/string\"\n"
336 "string(15) \"/string?a=1&b=2\"\n"
337 "string(7) \"/string\"\n"
338 "string(7) \"a=1&b=2\"\n"
346 bool TestServer::TestInteraction() {
347 // run this twice to test lvalBlackHole
349 "$a[] = new stdclass;"
350 "var_dump(count(array_combine($a, $a)));",
356 bool TestServer::TestGet() {
357 VSGET("<?php var_dump($_GET['name']);",
358 "string(0) \"\"\n", "string?name");
360 VSGET("<?php var_dump($_GET['name'], $_GET['id']);",
361 "string(0) \"\"\nstring(1) \"1\"\n", "string?name&id=1");
363 VSGET("<?php print $_GET['name'];",
364 "value", "string?name=value");
366 VSGET("<?php var_dump($_GET['names']);",
369 " string(3) \"foo\"\n"
371 " string(3) \"bar\"\n"
373 "string?names[1]=foo&names[2]=bar");
375 VSGET("<?php var_dump($_GET['names']);",
378 " string(3) \"foo\"\n"
380 " string(3) \"bar\"\n"
382 "string?names[]=foo&names[]=bar");
384 VSGET("<?php print $_REQUEST['name'];",
385 "value", "string?name=value");
390 bool TestServer::TestPost() {
391 const char *params
= "name=value";
393 VSPOST("<?php print $_POST['name'];",
394 "value", "string", params
);
396 VSPOST("<?php print $_REQUEST['name'];",
397 "value", "string", params
);
399 VSPOST("<?php print $HTTP_RAW_POST_DATA;",
400 "name=value", "string", params
);
405 bool TestServer::TestCookie() {
406 VSRX("<?php print $_COOKIE['name'];",
407 "value", "string", "GET", "Cookie: name=value;", nullptr);
409 VSRX("<?php print $_COOKIE['name2'];",
410 "value2", "string", "GET", "Cookie: n=v;name2=value2;n3=v3", nullptr);
415 bool TestServer::TestResponseHeader() {
416 VSR("<?php header('Set-Cookie: name=value'); var_dump(headers_list());",
419 " string(22) \"Set-Cookie: name=value\"\n"
422 VSRES("<?php header('Set-Cookie: name=value');",
423 "Set-Cookie: name=value");
425 VSRES("<?php header('Location: new/url');",
428 VSRES("<?php header(\"Test-Header: x\ry\"); echo 'done';",
434 bool TestServer::TestSetCookie() {
435 VSR("<?php setcookie('name', 'value'); var_dump(headers_list());",
438 " string(22) \"Set-Cookie: name=value\"\n"
442 VSRES("<?php setcookie('name', 'value');",
443 "Set-Cookie: name=value");
448 ///////////////////////////////////////////////////////////////////////////////
450 class TestTransport
: public Transport
{
452 TestTransport() : m_code(0) {}
455 std::string m_response
;
458 * Implementing HttpTransport...
460 virtual const char *getUrl() { return "/string";}
461 virtual const char *getRemoteHost() { return "remote";}
462 virtual const void *getPostData(int &size
) { size
= 0; return nullptr;}
463 virtual uint16_t getRemotePort() { return 0; }
464 virtual Method
getMethod() { return Transport::Method::GET
;}
465 virtual std::string
getHeader(const char *name
) { return "";}
466 virtual void getHeaders(HeaderMap
&headers
) {}
467 virtual void addHeaderImpl(const char *name
, const char *value
) {}
468 virtual void removeHeaderImpl(const char *name
) {}
470 virtual void sendImpl(const void *data
, int size
, int code
, bool chunked
) {
472 m_response
.append((const char *)data
, size
);
477 HttpRequestHandler
handler(0);
478 for (unsigned int i
= 0; i
< 100; i
++) {
479 handler
.handleRequest(this);
484 typedef std::shared_ptr
<TestTransport
> TestTransportPtr
;
485 typedef std::vector
<TestTransportPtr
> TestTransportPtrVec
;
486 typedef AsyncFunc
<TestTransport
> TestTransportAsyncFunc
;
487 typedef std::shared_ptr
<TestTransportAsyncFunc
> TestTransportAsyncFuncPtr
;
488 typedef std::vector
<TestTransportAsyncFuncPtr
> TestTransportAsyncFuncPtrVec
;
490 #define TEST_SIZE 100
493 * Start processing TEST_SIZE number of requests at the same time with
494 * that many threads. This is mainly testing global variables to make sure
495 * all handling are thread-safe.
497 bool TestServer::TestRequestHandling() {
498 RuntimeOption::AllowedFiles
.insert("/string");
499 TestTransportPtrVec
transports(TEST_SIZE
);
500 TestTransportAsyncFuncPtrVec
funcs(TEST_SIZE
);
501 for (unsigned int i
= 0; i
< TEST_SIZE
; i
++) {
502 TestTransport
*transport
= new TestTransport();
503 transports
[i
] = TestTransportPtr(transport
);
504 funcs
[i
] = TestTransportAsyncFuncPtr
505 (new TestTransportAsyncFunc(transport
, &TestTransport::process
));
508 for (unsigned int i
= 0; i
< TEST_SIZE
; i
++) {
511 for (unsigned int i
= 0; i
< TEST_SIZE
; i
++) {
512 funcs
[i
]->waitForEnd();
514 for (unsigned int i
= 0; i
< TEST_SIZE
; i
++) {
515 VS(transports
[i
]->m_code
, 200);
516 VS(String(transports
[i
]->m_response
), "Hello, world!");
521 static bool PreBindSocketHelper(struct addrinfo
*info
) {
522 if (info
->ai_family
!= AF_INET
&& info
->ai_family
!= AF_INET6
) {
523 printf("No IPV4/6 interface found.\n");
527 int fd
= socket(info
->ai_family
, SOCK_STREAM
, IPPROTO_TCP
);
529 printf("Error creating socket: %s\n", strerror(errno
));
533 int ret
= ::bind(fd
, info
->ai_addr
, info
->ai_addrlen
);
535 printf("Error binding socket to port %d: %s\n", s_server_port
,
544 bool TestServer::PreBindSocket() {
545 struct addrinfo hints
, *res
, *res0
;
546 std::memset(&hints
, 0, sizeof(hints
));
547 hints
.ai_family
= AF_UNSPEC
;
548 hints
.ai_socktype
= SOCK_STREAM
;
549 hints
.ai_flags
= AI_PASSIVE
| AI_ADDRCONFIG
;
551 if (getaddrinfo(nullptr, lexical_cast
<string
>(s_server_port
).c_str(),
552 &hints
, &res0
) < 0) {
553 printf("Error in getaddrinfo(): %s\n", strerror(errno
));
557 for (res
= res0
; res
; res
= res
->ai_next
) {
558 if (res
->ai_family
== AF_INET6
|| res
->ai_next
== nullptr) {
563 bool ret
= PreBindSocketHelper(res
);
568 void TestServer::CleanupPreBoundSocket() {
573 bool TestServer::TestInheritFdServer() {
574 WITH_PREBOUND_SOCKET(VSR("<?php print 'Hello, World!';",
579 bool TestServer::TestTakeoverServer() {
581 snprintf(s_filename
, MAXPATHLEN
, "/tmp/hphp_takeover_XXXXXX");
582 int tmpfd
= mkstemp(s_filename
);
585 s_func
.reset(new AsyncFunc
<TestServer
>(this, &TestServer::RunServer
));
588 // Wait for the server to actually start
590 StringBuffer response
;
591 vector
<String
> responseHeaders
;
592 string url
= "http://127.0.0.1:" + lexical_cast
<string
>(s_server_port
) +
595 for (int i
= 0; i
< 10; i
++) {
596 int code
= http
.get(url
.c_str(), response
, &headers
, &responseHeaders
);
603 // will start a second server, which should takeover
604 VSR("<?php print 'Hello, World!';",
611 ///////////////////////////////////////////////////////////////////////////////
613 class EchoHandler
: public RequestHandler
{
615 explicit EchoHandler(int timeout
) : RequestHandler(timeout
) {}
616 // implementing RequestHandler
617 virtual void handleRequest(Transport
*transport
) {
619 transport
->getHeaders(headers
);
622 response
= "\nGET param: name = ";
623 response
+= transport
->getParam("name");
625 if (transport
->getMethod() == Transport::Method::POST
) {
627 const char *data
= (const char *)transport
->getPostData(size
);
628 response
+= "\nPOST data: ";
629 response
+= string(data
, size
);
632 for (HeaderMap::const_iterator iter
= headers
.begin();
633 iter
!= headers
.end(); ++iter
) {
634 response
+= "\nHeader: ";
635 response
+= iter
->first
;
636 for (unsigned int i
= 0; i
< iter
->second
.size(); i
++) {
638 response
+= lexical_cast
<string
>(i
);
640 response
+= iter
->second
[i
];
644 transport
->addHeader("Custom", "blah");
645 transport
->sendString(response
);
647 virtual void abortRequest(Transport
*transport
) {
648 transport
->sendString("Service Unavailable", 503);
652 bool TestServer::TestHttpClient() {
654 for (s_server_port
= PORT_MIN
; s_server_port
<= PORT_MAX
; s_server_port
++) {
656 server
= ServerFactoryRegistry::createServer(
657 m_serverType
, "127.0.0.1", s_server_port
, 50);
658 server
->setRequestHandlerFactory
<EchoHandler
>(0);
661 } catch (const FailedToListenException
& e
) {
662 if (s_server_port
== PORT_MAX
) throw;
667 headers
["Cookie"].push_back("c1=v1;c2=v2;");
668 headers
["Cookie"].push_back("c3=v3;c4=v4;");
669 string url
= "http://127.0.0.1:" + lexical_cast
<string
>(s_server_port
) +
672 static const StaticString
s_Custom_colon_blah("Custom: blah");
674 for (int i
= 0; i
< 10; i
++) {
676 StringBuffer response
;
677 vector
<String
> responseHeaders
;
678 int code
= http
.get(url
.c_str(), response
, &headers
, &responseHeaders
);
681 ("\nGET param: name = value"
688 "\n0: 127.0.0.1:" + lexical_cast
<string
>(s_server_port
)).c_str());
691 for (unsigned int i
= 0; i
< responseHeaders
.size(); i
++) {
692 if (responseHeaders
[i
] == s_Custom_colon_blah
) {
698 for (int i
= 0; i
< 10; i
++) {
700 StringBuffer response
;
701 vector
<String
> responseHeaders
;
702 int code
= http
.post(url
.c_str(), "postdata", 8, response
, &headers
,
706 ("\nGET param: name = value"
707 "\nPOST data: postdata"
708 "\nHeader: Content-Type"
709 "\n0: application/x-www-form-urlencoded"
710 "\nHeader: Content-Length"
718 "\n0: 127.0.0.1:" + lexical_cast
<string
>(s_server_port
)).c_str());
721 for (unsigned int i
= 0; i
< responseHeaders
.size(); i
++) {
722 if (responseHeaders
[i
] == s_Custom_colon_blah
) {
730 server
->waitForEnd();
734 bool TestServer::TestRPCServer() {
737 "function f() { return 100; }\n",
744 "function f($a) { return array(1, 2, 3, $a); }\n",
746 "f?auth=test&p=\"hello\"",
751 "function f($a, $b) { return array_merge($a, $b); }\n",
752 "{\"a\":1,\"0\":2,\"1\":1,\"2\":2}",
753 "f?auth=test&p={\"a\":1,\"1\":2}&p=[1,2]",
756 // builtin function and static method
758 "class A { static function f($a) { return $a; } }\n",
760 "call_user_func?auth=test&p=\"A::f\"&p=100",
763 // invoking a file, with NO json encoding
764 // "int(100)" is printed twice, one from warmup, and the other from include
769 "?include=string&output=1&auth=test",
773 "var_dump(isset($_ENV['HPHP_RPC']));\n",
776 "?include=string&output=1&auth=test",
782 bool TestServer::TestXboxServer() {
784 "if (array_key_exists('main', $_GET)) {\n"
785 " $t = xbox_task_start('1');\n"
786 " xbox_task_result($t, 0, $r);\n"
788 " $t = xbox_task_start('2');\n"
789 " xbox_task_result($t, 0, $r);\n"
790 " $t = xbox_task_start('1');\n"
791 " xbox_task_result($t, 0, $r);\n"
794 " $t = xbox_task_start('3');\n"
795 " xbox_task_result($t, 0, $r);\n"
798 " $t = xbox_task_start('4');\n"
799 " xbox_task_result($t, 0, $r);\n"
800 " $t = xbox_task_start('3');\n"
801 " xbox_task_result($t, 0, $r);\n"
804 " $t = xbox_task_start('3');\n"
805 " xbox_task_result($t, 0, $r);\n"
808 " function xbox_process_message($msg) {\n"
809 " if ($msg == '1') return xbox_get_thread_timeout();\n"
810 " else if ($msg == '2') xbox_set_thread_timeout(5);\n"
811 " else if ($msg == '3') return xbox_get_thread_time();\n"
812 " else xbox_schedule_thread_reset();\n"
825 bool TestServer::TestPageletServer() {
827 "if (array_key_exists('pagelet', $_GET)) {\n"
828 " echo 'Hello from the pagelet!';\n"
830 " $h = array('Host: ' . $_SERVER['HTTP_HOST']);\n"
831 " $t = pagelet_server_task_start('/string?pagelet=1', $h, '');\n"
833 " $r = pagelet_server_task_result($t, $h, $c);\n"
836 "First! Hello from the pagelet!",
841 "if (array_key_exists('pagelet', $_GET)) {\n"
842 " echo $_SERVER['REQUEST_METHOD'];\n"
844 " $h = array('Host: ' . $_SERVER['HTTP_HOST']);\n"
845 " $t = pagelet_server_task_start('/string?pagelet=1', $h, '');\n"
847 " $r = pagelet_server_task_result($t, $h, $c);\n"
854 "if ($_SERVER['THREAD_TYPE'] == 'Pagelet Thread') {\n"
856 " pagelet_server_flush();\n"
859 " pagelet_server_flush();\n"
862 " $h = array('Host: ' . $_SERVER['HTTP_HOST']);\n"
863 " $t = pagelet_server_task_start('/string', $h, '');\n"
864 " for ($i = 0; ; $i++) {\n"
865 " while (($s = pagelet_server_task_status($t)) == \n"
866 " PAGELET_NOT_READY) { sleep(1); }\n"
867 " echo \"Step $i:\\n\";\n"
868 " $r = pagelet_server_task_result($t, $h, $c);\n"
869 " echo $r . \"\\n\";\n"
870 " if ($s == PAGELET_DONE) break;\n"