Alan Gaudet
WEB-01 · Rev 1.0.1

cobweb

An HTTP/JSON API whose request handling is written entirely in COBOL, live on the public internet.

Live

See it on the bench

Recorded
> GET /api/fortune HTTP/1.1
> Host: cobweb.alangaudet.dev

< HTTP/1.1 200 OK
< Content-Type: application/json

{"fortune":"COBOL: still running the world's payrolls while you pick a framework."}
A live terminal. Each button sends a real request through this site's nginx to cobweb. The transcript shows the request as nginx forwards it, and the status, type and body cobweb returns.

Description

COBOL is usually described as a language nobody writes anymore. I wanted to find out whether a correct HTTP/JSON API could be written in it and run on the public internet as a real service: right status codes, handled error cases, and the same deployment as everything else I run. The terminal on this page talks to it.

Block diagram

  1. curl / browser
  2. Traefik + TLS
  3. socat listener
  4. cobweb-handle

Decisions

socat owns the socket, COBOL owns HTTP

One line of socat accepts TCP connections and forks a short-lived COBOL process per request, with the connection on its stdin and stdout. Everything after that is COBOL: the request line, header scanning, URL decoding, routing, status codes, CRLF framing and the JSON body. The same boundary makes the HTTP layer testable with no sockets, because each test feeds a raw request on stdin and checks stdout. The cost is one process per request.

Pipes instead of a socketpair

socat's default EXEC transport hands the child a Unix socketpair, and GnuCOBOL opens /dev/stdin as a sequential file through C stdio, which fails with ENXIO on a socket. The pipes option gives the child real pipes, and removing it makes every request fail. The body is read one byte at a time up to the exact Content-Length, because a line or end-of-file read deadlocks while curl holds the connection open waiting for the reply.

Working inside the column-72 margin

Compiled in fixed format, the source has a hard right margin at column 72, and GnuCOBOL does not process backslash escapes. The landing page's HTML, CSS and JavaScript are split into short string literals and spliced with STRING ... WITH POINTER, with each newline placed by hand as X"0A". The JavaScript the page emits builds its own newlines with String.fromCharCode(10).

Errata

  • Request and body buffers have a fixed size, and a body past the limit gets 413 Payload Too Large.
  • The text for upcase and rot13 comes from the first query parameter whatever its name, and an unencoded = ends it, so a literal = must be sent as %3D.

Revision history

Version Changes
1.0.1 Interactive landing console that calls each endpoint in the browser; Content-Length is matched case-insensitively.
1.0.0 First release: six endpoints, JSON 400, 404, 405 and 413 errors, multi-stage container.