167 lines
7.7 KiB
HTML
167 lines
7.7 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>CoAP Hello — UDP Reference</title>
|
|
<style>
|
|
:root {
|
|
color-scheme: light dark;
|
|
--bg: #fbfbfa; --fg: #1a1a19; --muted: #6b6b68;
|
|
--card: #ffffff; --border: #e4e4e1; --accent: #b05730;
|
|
--ok: #2f7d4f; --err: #b3402f; --code-bg: #f4f4f2;
|
|
}
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--bg: #1a1a19; --fg: #edecea; --muted: #9a9a96;
|
|
--card: #232322; --border: #353533; --accent: #e08a5f;
|
|
--ok: #6bbd8a; --err: #e8806c; --code-bg: #2b2b29;
|
|
}
|
|
}
|
|
* { box-sizing: border-box; }
|
|
body {
|
|
margin: 0; padding: 2rem 1.25rem; background: var(--bg); color: var(--fg);
|
|
font: 15px/1.55 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
}
|
|
main { max-width: 680px; margin: 0 auto; }
|
|
h1 { font-size: 1.4rem; margin: 0 0 .35rem; letter-spacing: -.01em; }
|
|
.sub { color: var(--muted); margin: 0 0 1.75rem; font-size: .9rem; }
|
|
.card {
|
|
background: var(--card); border: 1px solid var(--border);
|
|
border-radius: 10px; padding: 1.25rem; margin-bottom: 1rem;
|
|
}
|
|
h2 { font-size: .82rem; text-transform: uppercase; letter-spacing: .06em;
|
|
color: var(--muted); margin: 0 0 .7rem; }
|
|
label { display: block; font-weight: 600; font-size: .82rem; margin-bottom: .4rem; }
|
|
input {
|
|
width: 100%; padding: .6rem .7rem; font: inherit; border-radius: 7px;
|
|
border: 1px solid var(--border); background: var(--bg); color: var(--fg);
|
|
}
|
|
input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
.hint { color: var(--muted); font-size: .82rem; margin: .8rem 0 0; }
|
|
.cmd { display: flex; gap: .5rem; align-items: stretch; margin-top: .55rem; }
|
|
.cmd pre { flex: 1; margin: 0; }
|
|
pre {
|
|
background: var(--code-bg); border: 1px solid var(--border); border-radius: 7px;
|
|
padding: .7rem .8rem; overflow-x: auto; margin: 0;
|
|
font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
}
|
|
button {
|
|
font: inherit; font-size: .8rem; font-weight: 600; padding: .35rem .7rem;
|
|
border-radius: 7px; border: 1px solid var(--border);
|
|
background: var(--bg); color: var(--muted); cursor: pointer; white-space: nowrap;
|
|
}
|
|
button:hover { border-color: var(--accent); color: var(--accent); }
|
|
button.copied { color: var(--ok); border-color: var(--ok); }
|
|
.what { font-size: .8rem; color: var(--muted); margin: 1rem 0 .1rem; }
|
|
.what:first-of-type { margin-top: 0; }
|
|
table { width: 100%; border-collapse: collapse; font-size: .87rem; }
|
|
th, td { text-align: left; padding: .4rem .5rem; border-bottom: 1px solid var(--border); }
|
|
th { font-size: .74rem; text-transform: uppercase; letter-spacing: .05em;
|
|
color: var(--muted); font-weight: 600; }
|
|
tr:last-child td { border-bottom: none; }
|
|
td.code { font: 13px ui-monospace, SFMono-Regular, Menlo, monospace; white-space: nowrap; }
|
|
.ok { color: var(--ok); font-weight: 600; }
|
|
.err { color: var(--err); font-weight: 600; }
|
|
code { background: var(--code-bg); padding: .1em .35em; border-radius: 4px;
|
|
font: 13px ui-monospace, SFMono-Regular, Menlo, monospace; }
|
|
a { color: var(--accent); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main>
|
|
<h1>CoAP Hello — UDP Reference</h1>
|
|
<p class="sub">This server speaks CoAP over UDP only, so there is nothing here
|
|
for a browser to call. Type a name and copy the command for the client you
|
|
use — the page is static and works straight off disk.</p>
|
|
|
|
<div class="card">
|
|
<label for="name">Name</label>
|
|
<input id="name" value="Imran" autocomplete="off" spellcheck="false"
|
|
placeholder="leave blank for "World"">
|
|
|
|
<p class="what">Bundled client — <code>cargo build --release</code> first</p>
|
|
<div class="cmd"><pre id="c-get"></pre><button data-copy="c-get">Copy</button></div>
|
|
<div class="cmd"><pre id="c-post"></pre><button data-copy="c-post">Copy</button></div>
|
|
<div class="cmd"><pre id="c-json"></pre><button data-copy="c-json">Copy</button></div>
|
|
<div class="cmd"><pre id="c-health"></pre><button data-copy="c-health">Copy</button></div>
|
|
|
|
<p class="what">Any other CoAP client, e.g. <a href="https://www.npmjs.com/package/coap-cli">coap-cli</a></p>
|
|
<div class="cmd"><pre id="c-cli"></pre><button data-copy="c-cli">Copy</button></div>
|
|
|
|
<p class="hint">Set <code>COAP_HOST</code> / <code>COAP_PORT</code> to point at
|
|
another server; both default to <code>127.0.0.1:5683</code>.</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Routes</h2>
|
|
<table>
|
|
<thead><tr><th>Request</th><th>Code</th><th>Response</th></tr></thead>
|
|
<tbody>
|
|
<tr><td class="code">GET /hello</td><td class="code ok">2.05</td><td class="code">Hello, World!</td></tr>
|
|
<tr><td class="code">GET /hello?name=…</td><td class="code ok">2.05</td><td class="code">Hello, …!</td></tr>
|
|
<tr><td class="code">POST /hello</td><td class="code ok">2.01</td><td class="code">Hello, <payload>!</td></tr>
|
|
<tr><td class="code">GET /health</td><td class="code ok">2.05</td><td class="code">{"status":"ok","uptime":12}</td></tr>
|
|
<tr><td class="code">POST /hello (empty)</td><td class="code err">4.00</td><td class="code">Bad Request</td></tr>
|
|
<tr><td class="code">GET /nope</td><td class="code err">4.04</td><td class="code">Not Found</td></tr>
|
|
<tr><td class="code">PUT /hello</td><td class="code err">4.05</td><td class="code">Method Not Allowed</td></tr>
|
|
</tbody>
|
|
</table>
|
|
<p class="hint">CoAP writes response codes as <code>class.detail</code>:
|
|
<code>2.05</code> is HTTP's 200, <code>4.04</code> its 404.
|
|
<code>POST /hello</code> takes raw text or <code>{"name":"…"}</code>.</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h2>Scripts</h2>
|
|
<p class="what">Full request/response suite against a running server</p>
|
|
<div class="cmd"><pre id="c-test">./test-coap.sh</pre><button data-copy="c-test">Copy</button></div>
|
|
<p class="what">Liveness check — exit 0 healthy, 1 not</p>
|
|
<div class="cmd"><pre id="c-health-sh">./healthcheck.sh -v</pre><button data-copy="c-health-sh">Copy</button></div>
|
|
<p class="what">Start the server</p>
|
|
<div class="cmd"><pre id="c-run">cargo run --release</pre><button data-copy="c-run">Copy</button></div>
|
|
</div>
|
|
</main>
|
|
|
|
<script>
|
|
const $ = (id) => document.getElementById(id);
|
|
const shell = (s) => "'" + String(s).replace(/'/g, `'\\''`) + "'";
|
|
|
|
function render() {
|
|
const name = $('name').value.trim();
|
|
const q = name ? '/hello?name=' + encodeURIComponent(name) : '/hello';
|
|
const who = name || 'Imran';
|
|
|
|
$('c-get').textContent = `./target/release/client get ${shell(q)}`;
|
|
$('c-post').textContent = `./target/release/client post /hello ${shell(who)}`;
|
|
$('c-json').textContent = `./target/release/client post /hello ${shell(JSON.stringify({ name: who }))}`;
|
|
$('c-health').textContent = `./target/release/client get /health`;
|
|
$('c-cli').textContent = `coap get ${shell('coap://localhost:5683' + q)}`;
|
|
}
|
|
|
|
document.querySelectorAll('button[data-copy]').forEach((btn) => {
|
|
btn.addEventListener('click', async () => {
|
|
const text = $(btn.dataset.copy).textContent;
|
|
try {
|
|
await navigator.clipboard.writeText(text);
|
|
} catch {
|
|
// file:// and non-secure origins deny the clipboard API; fall back.
|
|
const ta = document.createElement('textarea');
|
|
ta.value = text;
|
|
document.body.appendChild(ta);
|
|
ta.select();
|
|
document.execCommand('copy');
|
|
ta.remove();
|
|
}
|
|
btn.textContent = 'Copied';
|
|
btn.classList.add('copied');
|
|
setTimeout(() => { btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 1200);
|
|
});
|
|
});
|
|
|
|
$('name').addEventListener('input', render);
|
|
render();
|
|
</script>
|
|
</body>
|
|
</html>
|