# Request Echo — Product Requirements (v1.0)

Request Echo is an HTTP request and response service for API testing. Its endpoints reflect the arguments, headers, body, form fields and cookies they receive, or produce a requested status code, delay, redirect chain, cookie or authentication challenge. A playground builds requests from method, path, query and header grids and several body types, and keeps saved requests per browser tab.

**Problem.** People who test HTTP clients need a service that answers exactly what was sent or exactly what was asked for. If an echo drops a value, a status is ignored or an auth check is lax, tests built on it give wrong confidence.

## Users
- **API tester** — Send requests and trust that the response reflects them faithfully.
- **Client developer** — Get predictable statuses, redirects, delays, cookies and auth challenges to exercise error handling.

## Goals
- Every echo endpoint reflects what was sent without loss or change.
- Status, delay, redirect, cookie and auth endpoints behave exactly as requested.
- The playground builds the same request the user sees in its grids.
- Invalid input is caught before sending where possible.

## Echo endpoints (ECH)

Method endpoints and /anything reflect the request.

- **FR-ECH-01** (Must) GET /get answers 200 with a JSON body that reflects the query arguments, the URL and the request headers.
  - GET /get?keval=run-4821 returns 200 with Content-Type application/json and args.keval equal to "run-4821".
  - url ends with /api/echo/get?keval=run-4821 and headers contains Host.
- **FR-ECH-02** (Must) A query key that appears more than once is echoed as a list of all its values in order.
  - /get?keval=demo-123&tag=a&tag=b returns args.keval "demo-123" and args.tag ["a","b"].
- **FR-ECH-03** (Must) A JSON body is echoed parsed in "json" and raw in "data", with its method and headers.
  - POST {"item":"Notebook","qty":3,"gift":false} returns json equal to that object.
  - The X-Request-Id header sent is echoed in headers; headers["Content-Type"] is application/json; method "POST".
- **FR-ECH-04** (Should) Urlencoded form bodies arrive in "form" and leave "json" null.
  - PUT with city=Lyon and zip=69001 returns form {"city":"Lyon","zip":"69001"}, json null, method "PUT" and Content-Type application/x-www-form-urlencoded.
- **FR-ECH-05** (Should) Each method endpoint and /anything echo the method, body, arguments and URL; an endpoint that accepts only one method answers 404 to others.
  - DELETE /delete with {"id":7} returns 200, json {"id":7} and method "DELETE".
  - PATCH /anything/a/b?x=1 with text "hi" returns data "hi", args {"x":"1"} and a url ending /anything/a/b?x=1.
  - GET /post returns 404.
  - GET /base64/UmVxdWVzdCBFY2hvIHJvY2tz returns 200 with the text "Request Echo rocks".

## Status, redirects and delay (STA)

Endpoints produce a chosen status code, a redirect chain or a delayed answer.

- **FR-STA-01** (Must) The status endpoint answers exactly the requested code, and an invalid code is refused.
  - /status/201 shows "201 Created"; /status/404 shows "404 Not Found" with an empty body.
  - /status/418 shows "418 I'm a teapot" with ASCII art containing "teapot"; /status/503 shows "503 Service Unavailable".
  - /status/abc returns 400 with the message "Invalid status code".
- **FR-STA-02** (Should) A redirect chain is followed to /get and the final response reports that it was redirected.
  - "Redirect ×3" ends 200 OK with "Redirected → /api/echo/get" and a body url ending /api/echo/get.
- **FR-STA-03** (Should) The delay endpoint waits the requested time before answering, and the client shows it is waiting.
  - The send button shows "Sending…" for about 2 seconds on a 2 second delay.
  - The answer is 200 OK and the displayed time is at least 2000 ms.

## Authentication challenges (AUT)

Basic and Bearer endpoints check the credentials sent.

- **FR-AUT-01** (Must) The Basic auth endpoint accepts only the user and password named in its path; a wrong password or a missing Authorization header gives 401 with an empty body.
  - Correct credentials return 200 {"authenticated":true,"user":<the path user>}.
  - A wrong password returns 401 Unauthorized with an empty body.
  - Removing the Authorization header returns 401 Unauthorized.
- **FR-AUT-02** (Should) The Bearer endpoint echoes the token of a Bearer authorization and refuses any other scheme with 401.
  - Authorization "Bearer <token>" returns 200 {"authenticated":true,"token":<that token>}.
  - Authorization "Token abc" returns 401 Unauthorized.

## Cookies and response headers (COK)

Endpoints set and delete cookies and set response headers from the query.

- **FR-COK-01** (Should) Cookies set through the cookie endpoints persist in the browser and can be deleted by name.
  - /cookies/set?flavor=oat&size=xl answers 200 after a redirect with cookies {"flavor":"oat","size":"xl"}.
  - /cookies still lists flavor and size.
  - /cookies/delete?flavor leaves cookies {"size":"xl"}.
- **FR-COK-02** (Could) Response headers requested in the query are returned and visible in the response Headers tab.
  - /response-headers?x-trace=abc123 answers 200 OK and the Headers tab lists x-trace with value abc123.

## Playground (PLY)

The playground builds requests from grids and body editors and saves them per tab.

- **FR-PLY-01** (Must) The Params grid and the Path stay in sync in both directions, with values correctly encoded and disabled rows left out.
  - Path /get?color=teal&size=9 shows Params badge 2 with rows color=teal and size=9.
  - Unticking size makes the Path /get?color=teal and the badge 1.
  - Adding key "note" with value "a b&c" makes the Path /get?color=teal&note=a%20b%26c; the response args is {"color":"teal","note":"a b&c"}.
- **FR-PLY-02** (Should) Malformed JSON is refused before sending with a message starting "Body is not valid JSON:" and no response is shown; a valid body sends.
  - {"a": 1,} shows "Body is not valid JSON:" and no new response.
  - {"a": 1} returns 200 OK with json {"a":1}.
- **FR-PLY-03** (Could) GET sends no body: the body settings state this and are disabled.
  - The Body tab shows "GET requests are sent without a body." and the body type radios are disabled.
- **FR-PLY-04** (Could) Saved requests persist across reloads for the tab, can be deleted and cannot reuse an existing name.
  - Saving "Orders lookup" lists it last among 9 saved requests.
  - Saving again as "teapot" shows "A saved request named \"teapot\" already exists".
  - After a reload "Orders lookup" is still listed; deleting it leaves 8.

## HTML order form and catalogue (FRM)

A classic HTML order form posts to the echo, and a catalogue lists the endpoints.

- **FR-FRM-01** (Must) The HTML order form submits urlencoded fields to /api/echo/post, with repeated checkboxes arriving as a list.
  - Submitting shows JSON whose form has custname "Rae Kim", size "medium", topping ["bacon","mushroom"], delivery "19:30" and comments "Ring twice".
- **FR-FRM-02** (Could) The preferred delivery time must be between 11:00 and 21:00 in 15-minute steps; the browser blocks other values.
  - 22:00 is blocked with a "must be 21:00 or earlier" style message.
  - 19:10 is blocked because it is not on a 15-minute step.
- **FR-FRM-03** (Could) The endpoint catalogue lists every endpoint and can be filtered by group and search text.
  - The page reads "21 endpoints".
  - Group "Auth" shows "2 endpoints": /basic-auth/{user}/{passwd} and /bearer.
  - All groups with search "cookie" shows "3 endpoints".

## Non-functional requirements

- **NFR-01** (Must) An echo never drops, reorders or alters data it was sent.
  - Repeated keys, JSON bodies and form fields round-trip unchanged.
- **NFR-02** (Should) A slow response is awaited by the playground and is not treated as a failure.

## Glossary and rules

- **Echo** — A response that reflects the request that was received.
- **Saved request** — A playground request stored for the current browser tab.

## Out of scope

- File uploads
- Streaming and gzip endpoints
