public function onHandshake(Request $request, Response $response) { // During handshakes, you should always check the origin header, otherwise any site will // be able to connect to your endpoint. Websockets are not restricted by the same-origin-policy! $origin = $request->getHeader("origin"); if ($origin !== "http://localhost:1337") { $response->setStatus(403); $response->send("<h1>origin not allowed</h1>"); return null; } // returned values will be passed to onOpen, that way you can pass cookie values or the whole request object. return $request->getConnectionInfo()["client_addr"]; }
public function __construct(Request $request) { $this->request = $request; $config = $request->getLocalVar("aerys.session.config"); assert(\is_array($config), 'No middleware was loaded or Aerys\\Session class instantiated in invalid context'); $this->driver = $config["driver"]; $config += static::CONFIG; $request->setLocalVar("aerys.session.config", $config); $id = $request->getCookie($config["name"]); if (\strlen($id) === self::ID_LENGTH && strspn($id, self::ALLOWED_ID_CHARS) === self::ID_LENGTH) { $this->setId($id); } $this->ttl = $config["ttl"]; $this->maxlife = $config["maxlife"]; }
/** * Route a request * * @param \Aerys\Request $request * @param \Aerys\Response $response */ public function __invoke(Request $request, Response $response) { if (!($preRoute = $request->getLocalVar("aerys.routed"))) { return; } list($isMethodAllowed, $data) = $preRoute; if ($isMethodAllowed) { return $data($request, $response, $request->getLocalVar("aerys.routeArgs")); } else { $allowedMethods = implode(",", $data); $response->setStatus(HTTP_STATUS["METHOD_NOT_ALLOWED"]); $response->setHeader("Allow", $allowedMethods); $response->setHeader("Aerys-Generic-Response", "enable"); $response->end(); } }
/** * @param Request $req * @param array $options available options are: * - size (default: 131072) * - input_vars (default: 200) * - field_len (default: 16384) */ public function __construct(Request $req, array $options = []) { $this->req = $req; $type = $req->getHeader("content-type"); $this->body = $req->getBody($this->totalSize = $this->size = $options["size"] ?? 131072); $this->maxFieldLen = $options["field_len"] ?? 16384; $this->maxInputVars = $options["input_vars"] ?? 200; if ($type !== null && strncmp($type, "application/x-www-form-urlencoded", \strlen("application/x-www-form-urlencoded"))) { if (!preg_match('#^\\s*multipart/(?:form-data|mixed)(?:\\s*;\\s*boundary\\s*=\\s*("?)([^"]*)\\1)?$#', $type, $m)) { $this->req = null; $this->parsing = true; $this->result = new ParsedBody([]); return; } $this->boundary = $m[2]; } $this->body->when(function ($e, $data) { $this->req = null; \Amp\immediately(function () use($e, $data) { if ($e) { if ($e instanceof ClientSizeException) { $e = new ClientException("", 0, $e); } $this->error = $e; } else { $this->result = $this->end($data); } if (!$this->parsing) { $this->parsing = true; foreach ($this->result->getNames() as $field) { foreach ($this->result->getArray($field) as $_) { foreach ($this->watchers as list($cb, $cbData)) { $cb($field, $cbData); } } } } $this->parsing = true; foreach ($this->whens as list($cb, $cbData)) { $cb($this->error, $this->result, $cbData); } $this->whens = $this->watchers = []; }); }); }
private function getJsonRequestBody(AerysRequest $request, AerysResponse $response) : \Generator { if ($request->getHeader('Content-Type') !== 'application/json') { $this->respondWithError($response, 400, 'Type of request body must be application/json'); return null; } $body = $request->getBody(); $bodyText = ''; while ((yield $body->valid())) { $bodyText .= $body->consume(); } try { $json = json_try_decode($bodyText, true); } catch (JSONDecodeErrorException $e) { $this->respondWithError($response, 400, 'Unable to decode request body as application/json'); return null; } if (!is_array($json)) { $this->respondWithError($response, 400, 'Invalid request data structure'); return null; } return $json; }
private function checkPreconditions(Request $request, int $mtime, string $etag) { $ifMatch = $request->getHeader("If-Match"); if ($ifMatch && \stripos($ifMatch, $etag) === false) { return self::PRECOND_FAILED; } $ifNoneMatch = $request->getHeader("If-None-Match"); if ($ifNoneMatch && \stripos($ifNoneMatch, $etag) !== false) { return self::PRECOND_NOT_MODIFIED; } $ifModifiedSince = $request->getHeader("If-Modified-Since"); $ifModifiedSince = $ifModifiedSince ? @\strtotime($ifModifiedSince) : 0; if ($ifModifiedSince && $mtime > $ifModifiedSince) { return self::PRECOND_NOT_MODIFIED; } $ifUnmodifiedSince = $request->getHeader("If-Unmodified-Since"); $ifUnmodifiedSince = $ifUnmodifiedSince ? @\strtotime($ifUnmodifiedSince) : 0; if ($ifUnmodifiedSince && $mtime > $ifUnmodifiedSince) { return self::PRECOND_FAILED; } $ifRange = $request->getHeader("If-Range"); if (!($ifRange || $request->getHeader("Range"))) { return self::PRECOND_OK; } /** * This is a really stupid feature of HTTP but ... * If-Range headers may be either an HTTP timestamp or an Etag: * * If-Range = "If-Range" ":" ( entity-tag | HTTP-date ) * * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.27 */ if ($httpDate = @\strtotime($ifRange)) { return $httpDate > $mtime ? self::PRECOND_IF_RANGE_OK : self::PRECOND_IF_RANGE_FAILED; } // If the If-Range header was not an HTTP date we assume it's an Etag return $etag === $ifRange ? self::PRECOND_IF_RANGE_OK : self::PRECOND_IF_RANGE_FAILED; }
public function doJoin(Request $request, Response $response) { $session = (yield (new Session($request))->open()); parse_str((yield $request->getBody()), $post); $username = isset($post["username"]) && is_string($post["username"]) ? $post["username"] : ""; $avatar = $session->get("auth:identity:avatar"); $provider = $session->get("auth:provider"); if (!$provider) { $response->setStatus(400); $response->setHeader("aerys-generic-response", "enable"); $response->send(""); return; } if (!preg_match("~^[a-z][a-z0-9-]+[a-z0-9]\$~i", $username)) { $template = $this->templateService->load("sign-up.php"); $template->set("hint", $username); $template->set("error", "username must start with a-z and only contain a-z, 0-9 or dashes afterwards"); $response->send($template->render()); return; } $query = (yield $this->db->prepare("INSERT IGNORE INTO user (`name`, `avatar`) VALUES (?, ?)", [$username, $avatar])); if ($query->affectedRows) { (yield $this->db->prepare("INSERT INTO oauth (user_id, provider, identity, label) VALUES (?, ?, ?, ?)", [$query->insertId, $provider, $session->get("auth:identity:id"), $session->get("auth:identity:name")])); $session->set("login", $query->insertId); $session->set("login:name", $username); $session->set("login:avatar", $avatar); $session->set("login:time", time()); $response->setStatus(302); $response->setHeader("location", "/"); $response->send(""); } else { $template = $this->templateService->load("sign-up.php"); $template->set("hint", $username); $template->set("error", "username already taken"); $response->send($template->render()); } (yield $session->save()); }
private function sendPreAppTraceResponse(Request $request, Response $response) { $response->setStatus(HTTP_STATUS["OK"]); $response->setHeader("Content-Type", "message/http"); $response->end($request->getLocalVar('aerys.trace')); }
public function onHandshake(Request $request, Response $response) { return $request->getConnectionInfo()['client_addr']; }
/** * Handles all hooks. * * @param Request $request HTTP request * @param Response $response HTTP response * @param array $args URL args */ public function handle(Request $request, Response $response, array $args) { $response->setHeader("content-type", "text/plain"); $token = $request->getQueryVars()["token"] ?? ""; if (!$token || !is_string($token)) { $response->setStatus(401); $response->send("Failure: No token was provided."); return; } // use @ so we don't have to check for invalid strings manually $token = (string) @hex2bin($token); $hook = (yield $this->hookRepository->get($args["id"])); if (!$hook) { $response->setStatus(404); $response->send("Failure: Hook does not exist."); return; } if (!hash_equals($hook->token, $token)) { $response->setStatus(403); $response->send("Failure: Provided token doesn't match."); return; } $name = $args["service"]; if (!isset($this->services[$name])) { $response->setStatus(404); $response->send("Failure: Unknown service."); return; } $contentType = strtok($request->getHeader("content-type"), ";"); $body = (yield $request->getBody()); switch ($contentType) { case "application/json": $payload = json_decode($body); break; case "application/x-www-form-urlencoded": parse_str($body, $payload); $payload = json_decode(json_encode($payload)); break; default: $response->setStatus(415); $response->send("Failure: Content-type not supported."); return; } $service = $this->services[$name]; $headers = $request->getAllHeaders(); $event = $service->getEventName($headers, $payload); if (!isset($this->schemas[$name][$event])) { $response->setStatus(400); $response->send("Failure: Event not supported."); return; } $schema = $this->schemas[$name][$event]; $this->validator->reset(); $this->validator->check($payload, $schema); if (!$this->validator->isValid()) { $errors = $this->validator->getErrors(); $errors = array_reduce($errors, function (string $carry, array $item) : string { if ($item["property"]) { return $carry . sprintf("\n%s: %s", $item["property"], $item["message"]); } else { return $carry . "\n" . $item["message"]; } }, ""); $response->setStatus(400); $response->send("Failure: Payload validation failed." . $errors); return; } $message = $service->handle($headers, $payload); try { if ($message) { $req = (new HttpRequest())->setMethod("PUT")->setUri($this->config["api"] . "/messages")->setHeader("authorization", "Basic " . base64_encode("{$this->config['user_id']}:{$this->config['token']}"))->setBody(json_encode(["room_id" => $hook->room_id, "text" => $message->getText(), "data" => $message->getData()])); $resp = (yield $this->http->request($req)); if (intval($resp->getStatus() / 100) !== 2) { $message = "API request failed: " . $resp->getStatus(); if ($resp->getBody()) { $message .= "\n" . $resp->getBody(); } throw new Exception($message); } } $response->send("Success: " . ($message ? "Message sent." : "Message skipped.")); } catch (Exception $e) { $response->setStatus(500); $response->send("Failure: Couldn't persist message."); } }
public function __invoke(Request $req, Response $res) { $headers = $req->getAllHeaders(); unset($headers["accept-encoding"]); $connection = $headers["connection"]; unset($headers["connection"]); foreach ($connection as $value) { foreach (explode(",", strtolower($value)) as $type) { $type = trim($type); if ($type == "upgrade") { $headers["connection"][0] = "upgrade"; } else { unset($headers[$type]); } } } if ($this->headers) { if (is_callable($this->headers)) { $headers = ($this->headers)($headers); } else { $headers = $this->headers + $headers; } } $promise = $this->client->request((new \Amp\Artax\Request())->setMethod($req->getMethod())->setUri($this->target . $req->getUri())->setAllHeaders($headers)->setBody((yield $req->getBody()))); // no async sending possible :-( [because of redirects] $promise->watch(function ($update) use($req, $res, &$hasBody, &$status, &$zlib) { list($type, $data) = $update; if ($type == Notify::RESPONSE_HEADERS) { $headers = array_change_key_case($data["headers"], CASE_LOWER); foreach ($data["headers"] as $header => $values) { foreach ($values as $value) { $res->addHeader($header, $value); } } $res->setStatus($status = $data["status"]); $res->setReason($data["reason"]); if (isset($headers["content-encoding"]) && strcasecmp(trim(current($headers["content-encoding"])), 'gzip') === 0) { $zlib = inflate_init(ZLIB_ENCODING_GZIP); } $hasBody = true; } if ($type == Notify::RESPONSE_BODY_DATA) { if ($zlib) { $data = inflate_add($zlib, $data); } $res->stream($data); } if ($type == Notify::RESPONSE) { if (!$hasBody) { foreach ($data->getAllHeaders() as $header => $values) { foreach ($values as $value) { $res->addHeader($header, $value); } } $res->setStatus($status = $data->getStatus()); $res->setReason($data->getReason()); } if ($status == 101) { $req->setLocalVar("aerys.reverse.socket", $update["export_socket"]()); } $res->end($zlib ? inflate_add("", ZLIB_FINISH) : null); } }); (yield $promise); }
public function handle(Request $request, Response $response, array $args) { $endpoint = $request->getLocalVar("chat.api.endpoint"); $user = $request->getLocalVar("chat.api.user"); if (!$endpoint || !$user) { // if this happens, something's really wrong, e.g. wrong order of callables $response->setStatus(500); $response->send(""); } foreach ($args as $key => $arg) { if (is_numeric($arg)) { $args[$key] = (int) $arg; } } foreach ($request->getQueryVars() as $key => $value) { // Don't allow overriding URL parameters if (isset($args[$key])) { continue; } if (is_numeric($value)) { $args[$key] = (int) $value; } else { if (is_string($value)) { $args[$key] = $value; } else { $result = new Error("bad_request", "invalid query parameter types", 400); $this->writeResponse($request, $response, $result); return; } } } $args = $args ? (object) $args : new stdClass(); $body = (yield $request->getBody()); $payload = $body ? json_decode($body) : null; $result = (yield $this->chat->process(new StandardRequest($endpoint, $args, $payload), $user)); $this->writeResponse($request, $response, $result); }
public function onHandshake(Request $request, Response $response) { $origin = $request->getHeader("origin"); if (!isOriginAllowed($origin)) { $response->setStatus(400); $response->setReason("Invalid Origin"); $response->send("<h1>Invalid Origin</h1>"); return null; } if ($this->eventSub->getConnectionState() !== ConnectionState::CONNECTED) { $response->setStatus(503); $response->setReason("Service unavailable"); $response->send(""); return null; } return new Session($request); }