Skip to content

Commit

Permalink
example: http-echo (wasmerio#43)
Browse files Browse the repository at this point in the history
* examples: Add http-echo example

* build: Update flake.lock
  • Loading branch information
theduke authored Jan 17, 2024
1 parent 2dd61a7 commit b2f25f8
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 6 deletions.
20 changes: 20 additions & 0 deletions examples/http-echo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# http-echo-plain

A simple http echo server that responds with information about the incoming
request.

The response format can be customized with:

* The `Accept` header (`application/json` or `text/html`)
* The `?format` query param
- `json`
- `html`
- `echo` (returns request headers and body unmodified)

## Running locally

```bash
wasmer run wasmer/winterjs --net --mapdir=src:src -- src/index.js --watch
```

This example is also deployed to Wasmer Edge at: https://httpinfo-winterjs.wasmer.app .
6 changes: 6 additions & 0 deletions examples/http-echo/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
kind: wasmer.io/App.v0
name: httpinfo-winterjs
package: wasmer-examples/[email protected]
debug: false
app_id: da_p2qMIbt8UvX2
188 changes: 188 additions & 0 deletions examples/http-echo/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@

async function handleRequest(req) {
const accept = req.headers.get('accept') ?? '';
const url = new URL(req.url);
const queryFormat = url.searchParams.get('format');

let outputFormat = 'html';

if (queryFormat) {
switch (queryFormat) {
case 'json':
outputFormat = 'json';
break;
case 'html':
outputFormat = 'html';
break;
case 'echo':
outputFormat = 'echo';
break;
}
} else if (accept) {
if (accept.startsWith('application/json')) {
outputFormat = 'json';
} else if (accept.search('text/html') !== -1) {
outputFormat = 'html';
}
}

switch (outputFormat) {
case 'json':
return buildResponseJson(req);
case 'html':
return buildResponseHtml(req);
case 'echo':
return buildResponseEcho(req);
}
}

async function requestBodyToString(req) {
try {
const body = await req.text();
return !body ? '<no body>' : body;
} catch (e) {
console.warn("Could not decode request body", e);
return "<non-utf8 body>";
}
}


addEventListener("fetch", (ev) => {
ev.respondWith(handleRequest(ev.request));
});

async function buildResponseJson(req) {
const reqBody = await requestBodyToString(req);

const data = {
url: req.url,
method: req.method,
headers: Object.fromEntries(req.headers),
body: reqBody,
};
const body = JSON.stringify(data, null, 2);
return new Response(body, {
headers: { "content-type": "application/json" },
});
}

async function buildResponseHtml(req) {

let headers = '';

for (const [key, value] of req.headers.entries()) {
headers += `
<tr>
<th>${key}</th>
<td>${value}</td>
</tr>`;
}

const url = new URL(req.url);
url.searchParams.set('format', 'json');
const jsonUrl = url.pathname + url.search;

const reqBody = await requestBodyToString(req);

let html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css">
<title>HTTP-Info - Analyze HTTP requests</title>
</head>
<body>
<section class="section">
<div class="container">
<h1 class="title">
HTTP-Info
</h1>
<div class="mb-4">
<a class="button" href="${jsonUrl}">JSON</a>
</div>
<table class="table">
<thead>
</thead>
<tbody>
<tr>
<th>URL</th>
<td>${req.url}</td>
</tr>
<tr>
<th>Method</th>
<td>${req.method}</td>
</tr>
<tr>
<th>Headers</th>
<td>
<table>
<tbody>
${headers}
</tbody>
</table>
</tr>
<tr>
<th>Body</th>
<td>${reqBody}</td>
</tr>
</tbody>
</table>
<div class="message is-info">
<div class="message-header">
<p>Info</p>
</div>
<div class="message-body content">
<p>This service provides information about the incoming HTTP request.
It is useful for debugging and analyzing HTTP clients.</p>
<p>You can control the output format by:</p>
<ul>
<li>Setting the <code>Accept</code> header to <code>application/json</code> or <code>text/html</code></li>
<li>
Setting the <code>?format=XXX</code> query parameter to
<code>json</code>, <code>html</code> or <code>echo</code></li>.
</ul>
<p>
By default the output format is <code>html</code>.<br/>
If the format is <code>echo</code>, the response will contain
the request headers and body unchanged.
</p>
</div>
</div>
<article class="message is-link">
<div class="message-header">
<p>Wasmer Edge</p>
</div>
<div class="message-body">
This site is hosted on <a href="https://wasmer.io/products/edge">Wasmer Edge</a>.
</div>
</article>
</div>
</section>
</body>
</html>
`;

return new Response(html, {
headers: { "content-type": "text/html" },
});
}

function buildResponseEcho(req) {
return new Response(req.body, {
headers: req.headers,
});
}
18 changes: 18 additions & 0 deletions examples/http-echo/wasmer.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "wasmer-examples/http-echo-winterjs"
version = "0.1.0"
description = "A simple HTTP echo server using WinterJS - responds with request information."

[dependencies]
"wasmer/winterjs" = "*"

[fs]
"/src" = "./src"

[[command]]
name = "script"
module = "wasmer/winterjs:wasmer-winter"
runner = "https://webc.org/runner/wasi"

[command.annotations.wasi]
main-args = ["/src/index.js"]
12 changes: 6 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b2f25f8

Please sign in to comment.