agentlang-index · task medium
GET a URL and echo a named response header
014-http-header-echo. Read two newline-terminated lines from standard input:
Prompt
This is the natural-language brief given to every model, verbatim. The harness prefixes a language-specific calling-convention block and suffixes a "return only the source code" instruction. Nothing else.
# 014-http-header-echo
Read two newline-terminated lines from standard input:
1. A URL — the endpoint to GET.
2. A header name to echo.
Issue an HTTP GET request to the URL with a 5000 millisecond
transport timeout.
On a transport-successful HTTP 200 response, locate the named
response header (case-insensitive on the name) and write its
value followed by a newline to standard output (e.g.
`application/json\n`).
On any failure — transport error (DNS, connect, TLS, timeout,
invalid URL, unsupported protocol, provider unavailable, I/O),
non-200 status, or the named header is not present — write the
literal string `error\n` to standard output instead. Do not
write to standard error. Exit with status 0 in every case.
Trailing whitespace on either input line should be trimmed
before use. Header name comparison is ASCII case-insensitive.
The echoed value bytes are exactly what the platform helper
returns; HTTP value trimming (leading/trailing whitespace inside
the value) is delegated to the helper.
## Examples
Input (stdin, two lines):
```
http://127.0.0.1:18014/headers
X-Echo
```
Output (stdout):
```
hello-world
```
Input (stdin):
```
http://127.0.0.1:18014/headers
content-type
```
Output (stdout):
```
application/json
```
Input (stdin):
```
http://127.0.0.1:18014/404
X-Echo
```
Output (stdout):
```
error
```
Input (stdin):
```
http://this-host-does-not-resolve.invalid/headers
X-Echo
```
Output (stdout):
```
error
```
## Acceptance
- stdout matches the expected bytes exactly per test case
- stderr is empty
- exit code is 0
- the run completes within 10 seconds wall time
## Input convention by language
- **TypeScript / Rust / Go / Python**: read two lines from stdin.
- **Zero**: take URL from `argv[1]`, header name from `argv[2]`
because Zero 0.1.2 does not expose a standard-input
capability. The byte semantics are the same.
## Verifier fixture
The verifier starts a local Python HTTP fixture server on port
18014 before running the references and tears it down after.
- `GET /headers` — returns HTTP 200 with headers
`Content-Type: application/json`, `X-Echo: hello-world`, and
`X-Custom-Name: first-value`. Body is `{}`.
- `GET /json` — returns HTTP 200 with `Content-Type: application/json`
only. Body is `{}`.
- `GET /empty` — returns HTTP 200 with `Content-Type: text/plain`
and no extra headers. Body is empty.
- `GET /404` — returns HTTP 404 with `X-Echo: shouldnotappear`.
Body is empty.
References target `http://127.0.0.1:18014/<path>` for all
fixture-based cases. The transport-failure case points at a
name that does not resolve.
Acceptance
A task counts as passed only when every public and hidden test case agrees on these fields. No fuzzy matching, no "off by one trailing newline is fine."
| stdout (byte-exact, per case) | true |
|---|---|
| stderr (exact bytes) | "" |
| exit code | 0 |
| wall time max (ms) | 10000 |
| tags | networking, http, headers, stdlib-breadth |
Results
Each cell is one attempt. Pass means stdout matched byte-exact on every test case, stderr empty, exit zero. Hover a failure to see the captured first line of the diagnostic.
| Model | Zero | TypeScript | Rust | Go | Python |
|---|---|---|---|---|---|
| gpt-4o | compile | ✓ | wrong output | ✓ | other |
| gpt-4o-mini | compile | runtime | wrong output | ✓ | other |
| gpt-5 | wrong output | ✓ | ✓ | ✓ | ✓ |
Failure excerpts
8 of 15 attempts failed. Each card is one attempt, with the captured first line of the diagnostic.
-
ref.zero:1:1 IMP001: unknown package-local import 'http' -
(no diagnostic captured) -
Traceback (most recent call last): -
ref.zero:1:1 IMP001: unknown package-local import 'lib http' -
28 | input.push(chunk.toString()); -
(no diagnostic captured) -
Traceback (most recent call last): -
(no diagnostic captured)
Reference implementations
The hand-written reference each language ships with. Every reference passes the same public and hidden test suite under the pinned toolchain before any model touches the task.
Click a language to expand
Zero 107 lines
// HTTP header echo reference for AgentLang Index.
//
// Zero 0.1.2 has no exposed stdin, so URL/header-name come from
// argv[1..2]. std.http.fetch is invoked with a GET envelope
// (method-line + URL + blank line). On HTTP 200 we look up the
// named header with std.http.headerValue and echo its bytes
// + newline. On any transport, status, or header-miss failure
// we write `error\n`.
//
// The direct backend MVP forbids user functions that take Span<u8>,
// MutSpan<u8>, or return shape locals, so all logic is inline.
pub fun main(world: World) -> Void raises {
let maybe_url = std.args.get(1)
let maybe_name = std.args.get(2)
if maybe_url.has == false {
check world.out.write("error\n")
return
}
if maybe_name.has == false {
check world.out.write("error\n")
return
}
let url_s = std.mem.span(maybe_url.value)
let name_s = std.mem.span(maybe_name.value)
// ---- Build GET envelope: GET <url>\n\n ----
let mut envelope: [1024]u8 = [0_u8; 1024]
let mut env_n: usize = 0
let prefix = std.mem.span("GET ")
let pre_n = std.mem.len(prefix)
let mut pi: usize = 0
while pi < pre_n {
envelope[env_n] = prefix[pi]
env_n = env_n + 1
pi = pi + 1
}
let url_n = std.mem.len(url_s)
let mut ui: usize = 0
let mut url_flag: Bool = true
while url_flag {
if ui >= url_n {
url_flag = false
} else {
let c = url_s[ui]
if c == 10_u8 {
url_flag = false
} else {
if c == 13_u8 {
url_flag = false
} else {
envelope[env_n] = c
env_n = env_n + 1
ui = ui + 1
}
}
}
}
envelope[env_n] = 10_u8
env_n = env_n + 1
envelope[env_n] = 10_u8
env_n = env_n + 1
// ---- Fetch ----
let net = std.net.host()
let client = std.http.client(net)
let mut response: [8192]u8 = [0_u8; 8192]
let result = std.http.fetch(client, envelope[0..env_n], response, std.time.ms(5000))
if std.http.resultOk(result) == false {
check world.out.write("error\n")
return
}
if std.http.resultStatus(result) != 200 {
check world.out.write("error\n")
return
}
// ---- Header lookup ----
let header_val = std.http.headerValue(response, name_s)
if std.http.headerFound(header_val) == false {
check world.out.write("error\n")
return
}
let h_off = std.http.headerOffset(header_val)
let h_len = std.http.headerLen(header_val)
// ---- Render header value + newline ----
// Inline the write rather than reach for a helper so we obey
// the seventh codegen-quirk constraint about returning slices.
let mut out: [4096]u8 = [0_u8; 4096]
let mut out_n: usize = 0
let mut wi: usize = 0
while wi < h_len {
out[out_n] = response[h_off + wi]
out_n = out_n + 1
wi = wi + 1
}
out[out_n] = 10_u8
out_n = out_n + 1
check world.out.write(out[0..out_n])
return
}
TypeScript 46 lines
#!/usr/bin/env -S tsx
// Reference implementation for 014-http-header-echo (TypeScript).
import { readFileSync } from "node:fs";
async function main(): Promise<void> {
let raw: string;
try {
raw = readFileSync(0, "utf8");
} catch {
process.stdout.write("error\n");
return;
}
const lines = raw.split("\n");
if (lines.length < 2) {
process.stdout.write("error\n");
return;
}
const url = lines[0].trim();
const name = lines[1].trim();
if (!url || !name) {
process.stdout.write("error\n");
return;
}
const ctl = new AbortController();
const timer = setTimeout(() => ctl.abort(), 5000);
try {
const resp = await fetch(url, { method: "GET", signal: ctl.signal });
clearTimeout(timer);
if (resp.status !== 200) {
process.stdout.write("error\n");
return;
}
const value = resp.headers.get(name);
if (value === null) {
process.stdout.write("error\n");
return;
}
process.stdout.write(value + "\n");
} catch {
clearTimeout(timer);
process.stdout.write("error\n");
}
}
main();
Rust 46 lines
// HTTP header echo reference for AgentLang Index.
use std::io::{self, Read};
use std::time::Duration;
fn fail() {
print!("error\n");
}
fn run() -> Option<String> {
let mut input = String::new();
io::stdin().read_to_string(&mut input).ok()?;
let mut lines = input.lines();
let url = lines.next()?.trim().to_string();
let name = lines.next()?.trim().to_string();
if url.is_empty() || name.is_empty() {
return None;
}
let agent = ureq::Agent::config_builder()
.timeout_global(Some(Duration::from_secs(5)))
.build()
.new_agent();
let response = agent.get(&url).call();
let response = match response {
Ok(r) => r,
Err(_) => return None,
};
if response.status().as_u16() != 200 {
return None;
}
let headers = response.headers();
let value_opt = headers.get(&name);
let v = value_opt?;
v.to_str().ok().map(|s| s.to_string())
}
fn main() {
match run() {
Some(s) => print!("{}\n", s),
None => fail(),
}
}
Go 67 lines
package main
import (
"bufio"
"context"
"fmt"
"net/http"
"os"
"strings"
"time"
)
func main() {
reader := bufio.NewReader(os.Stdin)
url, err := reader.ReadString('\n')
if err != nil && url == "" {
fmt.Print("error\n")
return
}
name, err2 := reader.ReadString('\n')
if err2 != nil && name == "" {
fmt.Print("error\n")
return
}
url = strings.TrimRight(url, "\r\n \t")
url = strings.TrimSpace(url)
name = strings.TrimSpace(name)
if url == "" || name == "" {
fmt.Print("error\n")
return
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
fmt.Print("error\n")
return
}
client := &http.Client{
CheckRedirect: func(*http.Request, []*http.Request) error {
return http.ErrUseLastResponse
},
}
resp, err := client.Do(req)
if err != nil {
fmt.Print("error\n")
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
fmt.Print("error\n")
return
}
value := resp.Header.Get(name)
if value == "" {
// Distinguish "missing" from "present and empty" — Go's
// canonical Get returns "" for both. Check Values() instead.
values := resp.Header.Values(name)
if len(values) == 0 {
fmt.Print("error\n")
return
}
value = values[0]
}
fmt.Print(value + "\n")
}
Python 41 lines
#!/usr/bin/env python3
"""Reference implementation for 014-http-header-echo (Python)."""
import sys
import urllib.request
import urllib.error
def main() -> int:
try:
lines = sys.stdin.read().splitlines()
if len(lines) < 2:
sys.stdout.write("error\n")
return 0
url = lines[0].strip()
name = lines[1].strip()
if not url or not name:
sys.stdout.write("error\n")
return 0
try:
req = urllib.request.Request(url, method="GET")
with urllib.request.urlopen(req, timeout=5.0) as resp:
if resp.status != 200:
sys.stdout.write("error\n")
return 0
value = resp.headers.get(name)
if value is None:
sys.stdout.write("error\n")
return 0
sys.stdout.write(value + "\n")
return 0
except Exception:
sys.stdout.write("error\n")
return 0
except Exception:
sys.stdout.write("error\n")
return 0
if __name__ == "__main__":
sys.exit(main())
Design notes
Algorithm, failure modes, cross-language parity, and where Zero needed a workaround. From corpus/014-http-header-echo/notes.md.
Algorithm
Read two inputs (URL, header name). GET the URL with a 5-second
transport timeout. On HTTP 200, look up the named header
(case-insensitive on the name) and write its value followed by
\n. On any transport error, non-200 status, or missing header,
write error\n. Process exit is 0 either way.
Fixture
A local Python HTTP server listens on 127.0.0.1:18014 while
verify.sh runs the references. Routes are all GET:
/headers→ returns 200 withContent-Type: application/json,X-Echo: hello-world,X-Custom-Name: first-value. Body{}./json→ returns 200 withContent-Type: application/jsononly. Body{}./empty→ returns 200 withContent-Type: text/plainand no extras. Body empty./404→ returns HTTP 404 withX-Echo: shouldnotappear(proves that non-200 results suppress header echoing).
verify.sh starts the fixture in the background, waits for ready
on stdout, runs all references against six cases (three public,
three hidden) covering the four routes, and kills the fixture on
exit.
Edge cases
- Header name comparison is ASCII case-insensitive on the name; matches whatever the underlying HTTP library does.
- Value bytes are echoed exactly as the platform helper returns them (no extra trimming beyond what the HTTP layer already performs on header value whitespace).
- Inputs are trimmed for leading/trailing whitespace on the stdin path (and trusted clean on the Zero argv path).
- Redirects are not followed in any reference.
- Five second wall-clock timeout on every fetch.
- A header that is found but has an empty value writes
\n(the value bytes plus newline). This is a structural choice — the spec defines failure as "missing header", not "empty-value header".
Zero-specific notes
- argv[1..2] carry URL and header name because Zero 0.1.2 has no exposed stdin.
- The GET envelope shape is
GET <url>\n\nin a[1024]u8buffer (method-line, URL, blank line, no body). std.http.headerValue(response, name_span)returns anHttpHeaderValuemetadata pack. The accessors arestd.http.headerFound(value)(Bool),std.http.headerOffset(value)(usize byte offset into the response buffer), andstd.http.headerLen(value)(usize byte length). Slice the bytes withresponse[offset..offset + len].- Header name matching is case-insensitive at the helper level,
so a stdin name of "content-type" matches a
Content-Typeresponse header without any manual normalization. - The direct backend ELF64 MVP forbids user functions that take
or return
Span<u8>/MutSpan<u8>/ shape values (the "seventh codegen quirk" surfaced in task 013). The body ofmainis therefore one inline pass with no helper functions. - Output is rendered into a
[4096]u8outbuffer rather than written directly withresponse[h_off..h_off + h_len]. This keeps the slice + newline as a single contiguousworld.out.write, which is byte-exact equivalent to two writes but reads cleaner and avoids any chance of buffering quirks across the boundary. - The 5-second timeout is
std.time.ms(5000)on thefetchcall; on timeout,resultOkreturns false and the reference falls through to theerror\npath. - main ends with an explicit
returnto dodge the trailing-write byte-count-as-exit-code codegen quirk surfaced in task 012.
Cross-implementation parity
All five references issue exactly one GET, observe one HTTP
transaction (no redirects), and surface either the named
response header's value bytes (case-insensitive on the name) or
the literal error\n for any failure. Byte-exact agreement on
every case.
The Go reference disambiguates "header present with empty value"
from "header missing" via Header.Values(name) (since Get
returns "" for both). Rust uses headers().get(name); Python
uses resp.headers.get(name) (which returns None for missing);
TypeScript uses headers.get(name) (which returns null for
missing). Zero uses std.http.headerFound(value).
Cost
| Model | Prompt tokens | Completion tokens | API ms |
|---|---|---|---|
| gpt-4o | 4,680 | 1,151 | 12,594 |
| gpt-4o-mini | 4,680 | 1,073 | 18,697 |
| gpt-5 | 4,675 | 26,112 | 221,951 |
Tokens and API ms are summed across the five languages this model attempted for this task.
Compare
Model deep-dives: gpt-4o · gpt-4o-mini · gpt-5 . Back to the leaderboard and methodology.