server.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. <?php
  2. require __DIR__ . '/vendor/autoload.php';
  3. use Amp\Cluster\Cluster;
  4. use Amp\Coroutine;
  5. use Amp\Http\Server\Request;
  6. use Amp\Http\Server\RequestHandler;
  7. use Amp\Http\Server\Response;
  8. use Amp\Http\Server\Router;
  9. use Amp\Http\Server\Options;
  10. use Amp\Http\Server\Server;
  11. use Amp\Promise;
  12. use Amp\Success;
  13. use Psr\Log\NullLogger;
  14. Amp\Loop::run(function () {
  15. $sockets = yield [
  16. Cluster::listen("0.0.0.0:8080"),
  17. Cluster::listen("[::]:8080"),
  18. ];
  19. $router = new Router;
  20. // Case 1 - JSON
  21. $router->addRoute("GET", "/json", new class implements RequestHandler {
  22. public function handleRequest(Request $request): Promise {
  23. return new Success(new Response(200, [
  24. "content-type" => "application/json",
  25. "server" => "amphp/http-server",
  26. ], \json_encode([
  27. "message" => "Hello, World!",
  28. ])));
  29. }
  30. });
  31. // Case 2 - Single Query
  32. $router->addRoute("GET", "/db", new class implements RequestHandler {
  33. private $mysql;
  34. public function __construct() {
  35. $this->mysql = Amp\Mysql\pool("host=tfb-database user=benchmarkdbuser password=benchmarkdbpass db=hello_world");
  36. }
  37. public function handleRequest(Request $request): Promise {
  38. return new Coroutine($this->doHandleRequest($request));
  39. }
  40. private function doHandleRequest($request) {
  41. $statement = yield $this->mysql->prepare("SELECT * FROM World WHERE id = ?");
  42. $result = yield $statement->execute([mt_rand(1, 10000)]);
  43. if (yield $result->advance()) {
  44. $item = $result->getCurrent();
  45. } else {
  46. $item = null;
  47. }
  48. return new Response(200, [
  49. "content-type" => "application/json",
  50. "server" => "amphp/http-server",
  51. ], \json_encode($item));
  52. }
  53. });
  54. // Case 3 - Multiple Queries
  55. $router->addRoute("GET", "/queries", new class implements RequestHandler {
  56. private $mysql;
  57. public function __construct() {
  58. $this->mysql = Amp\Mysql\pool("host=tfb-database user=benchmarkdbuser password=benchmarkdbpass db=hello_world");
  59. }
  60. public function handleRequest(Request $request): Promise {
  61. return new Coroutine($this->doHandleRequest($request));
  62. }
  63. private function doHandleRequest($request) {
  64. $query = $request->getUri()->getQuery();
  65. \parse_str($query, $queryParams);
  66. $queries = (int) ($queryParams["queries"] ?? 1);
  67. if ($queries < 1) {
  68. $queries = 1;
  69. } elseif ($queries > 500) {
  70. $queries = 500;
  71. }
  72. $items = [];
  73. $statement = yield $this->mysql->prepare("SELECT * FROM World WHERE id = ?");
  74. for ($i = 0; $i < $queries; $i++) {
  75. $items[] = new Coroutine($this->execute($statement));
  76. }
  77. return new Response(200, [
  78. "content-type" => "application/json",
  79. "server" => "amphp/http-server",
  80. ], \json_encode(yield $items));
  81. }
  82. private function execute($statement) {
  83. $result = yield $statement->execute([mt_rand(1, 10000)]);
  84. yield $result->advance();
  85. return $result->getCurrent();
  86. }
  87. });
  88. // Case 4 - Fortunes
  89. $router->addRoute("GET", "/fortunes", new class implements RequestHandler {
  90. private $mysql;
  91. public function __construct() {
  92. $this->mysql = Amp\Mysql\pool("host=tfb-database user=benchmarkdbuser password=benchmarkdbpass db=hello_world");
  93. }
  94. public function handleRequest(Request $request): Promise {
  95. return new Coroutine($this->doHandleRequest($request));
  96. }
  97. private function doHandleRequest($request) {
  98. $result = yield $this->mysql->query("SELECT * FROM Fortune");
  99. $items = [];
  100. while (yield $result->advance()) {
  101. $item = $result->getCurrent();
  102. $items[$item["id"]] = $item["message"];
  103. }
  104. $items[0] = "Additional fortune added at request time.";
  105. \asort($items);
  106. \ob_start();
  107. require __DIR__ . '/fortunes.php';
  108. return new Response(200, [
  109. "content-type" => "text/html; charset=utf-8",
  110. "server" => "amphp/http-server",
  111. ], \ob_get_clean());
  112. }
  113. private function execute($statement) {
  114. $result = yield $statement->execute([mt_rand(1, 10000)]);
  115. yield $result->advance();
  116. return $result->getCurrent();
  117. }
  118. });
  119. // Case 6 - Plaintext
  120. $router->addRoute("GET", "/plaintext", new class implements RequestHandler {
  121. public function handleRequest(Request $request): Promise {
  122. return new Success(new Response(200, [
  123. "content-type" => "text/plain",
  124. "server" => "amphp/http-server",
  125. ], "Hello, World!"));
  126. }
  127. });
  128. $logger = new Monolog\Logger("server");
  129. $logger->pushHandler(Amp\Cluster\createLogHandler());
  130. $logger->info("Using " . get_class(Amp\Loop::get()));
  131. $options = (new Options)
  132. ->withoutCompression()
  133. ->withConnectionLimit(16384)
  134. ->withConnectionsPerIpLimit(16384);
  135. $server = new Server($sockets, $router, $logger, $options);
  136. yield $server->start();
  137. Amp\Loop::onSignal(SIGINT, function (string $watcherId) use ($server) {
  138. Amp\Loop::cancel($watcherId);
  139. yield $server->stop();
  140. });
  141. });