Request and response
http_request, form and JSON bodies, headers, cookies and isolation.
Once $app.listen starts listening, the page no longer runs once but on every request:
request
↓
the file runs from the top (from the AST cache; reparsed if the file changed)
↓
$req = new http_request → that request's object
↓
the document is built
↓
SSR → minify → gzip → responseuse http_request
$req = new http_request
if $req.method == http_request.types.POST then
out($req.form_data.name)
end ifWhat can be read#
| Call | Returns |
|---|---|
$req.method | "GET" / "POST" / … |
$req.path | the request path |
$req.content_type | e.g. "application/json" (parameters stripped) |
$req.form_data | form fields object |
$req.json | the decoded JSON body, or null |
$req.data | POST data regardless of body format |
$req.query | query parameters object |
$req.headers | headers object (names lowercased) |
$req.body | the raw body |
$req.form("name") | the field, or null |
$req.get("q") | the query parameter, or null |
$req.header("accept") | the header, or null |
$req.is("POST") | case-insensitive comparison |
The http_request.types table: GET POST PUT DELETE PATCH HEAD OPTIONS.
Object or helper#
$req.form_data.name raises an error when the field is absent, so that a typo does not pass silently. For optional fields use $req.form("name"), which returns null.
Body formats#
Content-Type | Where it lands |
|---|---|
application/x-www-form-urlencoded | form_data and data |
multipart/form-data | form_data and data (text fields) |
application/json | json and data |
Parameters such as ; charset=utf-8 are ignored. Malformed JSON does not crash the page; json becomes null.
JSON is converted into the language's own values: object → object, array → array, number → int/float, true/false → bool, null → null.
$j = $req.json
out($j.get("user").get("name"))
out($j.get("tags").get(0))Escaping#
Form data is not raw HTML; it is escaped on the way into the document. Sending name=<script> does not open a tag.
Isolation#
Every request lives in its own run; one request's variables do not leak into another. Concurrent requests do not affect each other. Pages included with from_file see the current request too.
new http_request returns an empty GET request when there is no request context, so a page can also be run from the console.
Accepted methods are GET HEAD POST PUT DELETE PATCH in dynamic mode, and GET HEAD only in static mode.
File uploads#
<form method="POST" enctype="multipart/form-data">
<input type="file" name="document">
</form>$file = $req.files("document")
if $file != null then
$file.save(path.pwd + "/uploads/" + $file.uuid + "." + $file.extension)
end if| Field / call | Meaning |
|---|---|
$file.name | the submitted name — the path part is stripped |
$file.safe_name | a name suitable for writing to disk |
$file.uuid | a unique identifier per upload |
$file.size | bytes |
$file.mime_type | the type — determined from the content |
$file.extension | the extension, without the dot |
$file.image | {type, width, height} for images, otherwise null |
$file.content | binary content |
$file.save($path) | writes to disk, creating the directory if needed |
Do not use the submitted name when writing to disk. uuid also stops two users submitting the same name from overwriting each other's files. The original name remains available as $file.name and can be kept as metadata.
The type is read from the content, not from the header the client declared; that header comes from the user and can be changed. The submitted name is untrusted (../../etc/passwd); only the last segment is taken. The body limit is 10 MB.
Image processing#
$image = fs.image($file.content)
$image.resize({width: 800}).to_jpeg({quality: 80}).save($path)Formats read and written: JPEG, PNG, GIF. to_webp(...) does not exist yet.