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 ZeroTypeScriptRustGoPython
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.

  1. gpt-4o Zero compile
    ref.zero:1:1 IMP001: unknown package-local import 'http'
  2. gpt-4o Rust wrong output
    (no diagnostic captured)
  3. gpt-4o Python other
    Traceback (most recent call last):
  4. gpt-4o-mini Zero compile
    ref.zero:1:1 IMP001: unknown package-local import 'lib http'
  5. gpt-4o-mini TypeScript runtime
    28 |         input.push(chunk.toString());
  6. gpt-4o-mini Rust wrong output
    (no diagnostic captured)
  7. gpt-4o-mini Python other
    Traceback (most recent call last):
  8. gpt-5 Zero wrong output
    (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 with Content-Type: application/json, X-Echo: hello-world, X-Custom-Name: first-value. Body {}.
  • /json → returns 200 with Content-Type: application/json only. Body {}.
  • /empty → returns 200 with Content-Type: text/plain and no extras. Body empty.
  • /404 → returns HTTP 404 with X-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\n in a [1024]u8 buffer (method-line, URL, blank line, no body).
  • std.http.headerValue(response, name_span) returns an HttpHeaderValue metadata pack. The accessors are std.http.headerFound(value) (Bool), std.http.headerOffset(value) (usize byte offset into the response buffer), and std.http.headerLen(value) (usize byte length). Slice the bytes with response[offset..offset + len].
  • Header name matching is case-insensitive at the helper level, so a stdin name of "content-type" matches a Content-Type response 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 of main is therefore one inline pass with no helper functions.
  • Output is rendered into a [4096]u8 out buffer rather than written directly with response[h_off..h_off + h_len]. This keeps the slice + newline as a single contiguous world.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 the fetch call; on timeout, resultOk returns false and the reference falls through to the error\n path.
  • main ends with an explicit return to 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.