Skip to content

Wire Format Reference

samgerene edited this page Oct 11, 2026 · 8 revisions

Wire Format Reference

This page is the authoritative reference for the ARGUS/1.0 text protocol -- the verbs, status codes, header names, body encoding, and correlation semantics that ArgusTransfer transmits over a named pipe. For a narrative overview and the request flow through the framework, see Architecture and Protocol.

All values listed here are sourced verbatim from the production code (ArgusTransfer/Protocol/). If the table on this page disagrees with the source, the source wins -- please file an issue.

Request Line

Every request begins with a single line:

{VERB} {ROUTE} ARGUS/1.0\r\n
  • {VERB} is one of the values in the Verbs table below (uppercase, case-sensitive on the wire).
  • {ROUTE} may include a query string (?key=value&key2=value2); query parameters are also surfaced separately on ArgusRequest.QueryParameters.
  • The line has exactly three space-separated parts; anything else is rejected (400 Bad Request from the host).
  • The third part must be exactly ARGUS/1.0 (case-sensitive). Any other version token -- a non-Argus client such as HTTP/1.1, or a different protocol version -- is rejected with 400 Bad Request and the detail Unsupported protocol version: '...'; expected 'ARGUS/1.0'. There is no version negotiation.

The route is percent-encoded on the wire: in the path, %, spaces and control characters (including CR and LF) are encoded, e.g. /items/a b is sent as /items/a%20b, and the server decodes them again, so ArgusRequest.Route on the server is exactly the route the client set. An encoded slash (%2F) is never decoded, so it cannot act as a path separator. Query parameter keys and values are URL-encoded; in a query written directly into the route string (e.g. client.GetAsync("/items?page=1")) only spaces and control characters are encoded, because % there is taken to be an existing escape.

Mixed versions: a client of version 1.3.0 or later sends % in a route as %25, which servers up to 1.2.1 do not decode. Routes with spaces or control characters never worked with those servers.

Response Status Line

Every response begins with a single line:

ARGUS/1.0 {Code} {ReasonPhrase}\r\n
  • The first part must be exactly ARGUS/1.0 (case-sensitive); the client rejects any other version token with a FormatException, which the default retry policy does not retry.
  • {Code} is the numeric value of an ArgusStatusCode.
  • {ReasonPhrase} is the canonical phrase from ArgusStatusCodeExtensions.ToReasonPhrase() -- see the Status Codes table.

Standard Headers

The standard header names are exposed as public const string values on ArgusHeaderNames. Header lines follow the request or status line, terminated by a blank line. Header names are case-insensitive. The request or status line plus all header lines may not exceed the reader's MaxHeaderSize (32 KB by default; ArgusPipeHostOptions.MaxRequestHeaderSize on the host); a larger header block raises ArgusProtocolException, which the host answers with 400 Bad Request.

Header Constant Purpose
X-Correlation-Token ArgusHeaderNames.CorrelationToken A Guid that uniquely identifies the request/response exchange. The router automatically stamps the request's token on the response.
X-Timestamp ArgusHeaderNames.Timestamp ISO 8601 timestamp of when the message was created.
Content-Type ArgusHeaderNames.ContentType MIME type of the body (e.g., text/plain, application/json, application/problem+json).
Content-Length ArgusHeaderNames.ContentLength Size of the body in bytes (UTF-8 encoded, after compression) when the body is sent inline (not chunked).
Accept ArgusHeaderNames.Accept The acceptable response content types: a comma-separated list of media ranges (type/subtype, type/*, */*) with optional quality values (;q=0.5). Not sent by default (any type). The server returns 406 NotAcceptable only if no registered serializer is acceptable. See Accept header rules.
Authorization ArgusHeaderNames.Authorization The client's credentials as {scheme} {parameter}, e.g. Bearer token123. See Authentication.
Content-Encoding ArgusHeaderNames.ContentEncoding The encoding the body is compressed with, e.g. gzip. See Compression.
Accept-Encoding ArgusHeaderNames.AcceptEncoding The encodings the client accepts for the response body, e.g. gzip; q=0 excludes an encoding and * accepts any.

A message that carries a streamed body sends Transfer-Encoding: chunked instead of Content-Length. See Body Encoding below.

Custom headers are supported -- write any key into ArgusRequest.Headers or ArgusResponse.Headers and the wire serializers transmit them verbatim. Header names must be tokens of visible ASCII characters without :, and header values must not contain carriage returns, line feeds or NUL characters. The serializers reject a message that violates this with InvalidOperationException naming the header, so a value taken from untrusted input cannot inject additional headers; the host answers a handler response with such a header with 500 Internal Server Error. ArgusRequest.Authorization and SetAuthorization already reject line breaks when the value is set.

Verbs

The ArgusVerb enum defines the six supported verbs:

Verb Purpose
GET Retrieve a resource or collection of resources.
POST Create a new resource.
PUT Replace an existing resource.
PATCH Partially update an existing resource.
HEAD Retrieve metadata about a resource without the body.
DELETE Delete an existing resource.

The router automatically strips the body from the response of a successful HEAD request after the handler executes.

Status Codes

The ArgusStatusCode enum defines thirteen status codes. The reason phrase column is exactly what the server emits on the wire (and what ArgusRequestException.ReasonPhrase will carry on the client).

Code Name Reason Phrase When to use
200 Ok OK Request succeeded; the body (if any) is the resource.
201 Created Created A new resource was created; the body is typically the new resource.
204 NoContent No Content Request succeeded; intentionally no body.
400 BadRequest Bad Request The request was malformed or contained invalid data.
401 Unauthorized Unauthorized The request lacks valid authentication credentials.
403 Forbidden Forbidden The authenticated caller lacks permission for the requested resource.
404 NotFound Not Found The requested resource or route was not found. The router returns this when no template matches.
406 NotAcceptable Not Acceptable The server cannot produce a response matching the Accept header.
409 Conflict Conflict The request conflicts with the current state of the resource.
422 UnprocessableEntity Unprocessable Entity Syntactically valid but the body failed semantic validation.
500 InternalServerError Internal Server Error An unexpected error occurred on the server.
501 NotImplemented Not Implemented The route matched but the verb is not registered. The router returns this automatically.
503 ServiceUnavailable Service Unavailable The server is temporarily unable to handle the request.

ArgusStatusCodeExtensions.TryParse(int code, out ArgusStatusCode statusCode) parses a numeric code back into the enum.

Body Encoding

A message carries its payload either inline with Content-Length or streamed with Transfer-Encoding: chunked -- never both.

The header block is UTF-8 text with \r\n line endings (readers also accept \n). The body is binary: Content-Length and chunk sizes count bytes, and the client and pipe host read and write bodies as raw bytes, so non-ASCII text and arbitrary binary data round-trip exactly. A string body is encoded as UTF-8. A body that ends before its announced length (for example because the connection closed) raises EndOfStreamException.

Content-Length body

Content-Length: {byteCount}
Content-Type: text/plain

{body}

This is the default path used when ArgusRequest.Body / ArgusResponse.Body is set to a string. The IArgusBodySerializer registered for the Content-Type is invoked to read or write the string.

Chunked transfer encoding

When BodyStream is set instead of Body, the wire serializer emits:

Transfer-Encoding: chunked
Content-Type: application/octet-stream

{hex-chunk-size}\r\n
{chunk-data}\r\n
...
0\r\n
\r\n

{hex-chunk-size} is the number of bytes in the chunk; chunk data is raw bytes followed by \r\n. The default chunk size is 8 KiB. The reader validates the chunk-size line as a non-negative hexadecimal number, throwing FormatException for malformed sizes or chunk data that is not followed by \r\n. A configurable maxBodySize cap throws InvalidOperationException if the cumulative chunked body exceeds the limit, and large chunks are read in bounded pieces. These checks defend against denial-of-service via a hostile sender.

Compressed bodies

When a message has a Content-Encoding header, the body is compressed with that encoding before framing: Content-Length announces the compressed size, and a streamed body's chunks carry compressed bytes. The receiver decompresses the body and removes the Content-Encoding header. See Compression.

POST /reports ARGUS/1.0
X-Correlation-Token: a1b2c3d4-e5f6-7890-abcd-ef1234567890
X-Timestamp: 2026-03-28T10:30:00.0000000Z
Accept-Encoding: gzip
Content-Encoding: gzip
Content-Type: text/plain
Content-Length: 318

{318 bytes of gzip data}

Early Responses

The server may respond before it has read the whole request: when the request line and headers exceed MaxRequestHeaderSize, when Content-Length exceeds MaxRequestBodySize, or when a chunked body grows beyond it, the host answers 400 Bad Request right away and closes the connection without reading the rest. A client must therefore read the response while it is still writing the request: the host's pipe is unbuffered on Windows, so a client that finishes writing before it starts reading blocks on its write while the host waits to deliver its response.

ArgusClient does this automatically: it reads the response concurrently with writing the request and cancels the remaining write when an early response arrives. Custom clients built on ArgusRequestSerializer / ArgusResponseSerializer should do the same.

Correlation Tokens

Every ArgusRequest is created with a fresh Guid in CorrelationToken. The router stamps that same token onto the ArgusResponse after the handler completes, so a client can correlate responses to their originating requests. The token is also surfaced on ArgusContext.CorrelationToken for handlers and middleware that want to log or thread it through.

Example Wire Exchange

Request:

POST /sampleitems ARGUS/1.0
X-Correlation-Token: a1b2c3d4-e5f6-7890-abcd-ef1234567890
X-Timestamp: 2026-03-28T10:30:00.0000000Z
Content-Type: application/json
Content-Length: 42

{"name":"Widget","description":"A widget"}

Response:

ARGUS/1.0 201 Created
X-Correlation-Token: a1b2c3d4-e5f6-7890-abcd-ef1234567890
X-Timestamp: 2026-03-28T10:30:00.1234567Z
Content-Type: application/json
Content-Length: 89

{"id":"f7e6d5c4-b3a2-1098-7654-321fedcba098","name":"Widget","description":"A widget"}

See also

Clone this wiki locally