# bhttp-ts

A [BHTTP (RFC 9292: Binary Representation of HTTP Messages)](https://datatracker.ietf.org/doc/html/rfc9292) encoder and decoder for the [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request)/[Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) interface of [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).

This module works on Node.js, Cloudflare Workers, and other JavaScript runtimes supporting the Fetch API.

> **Note**: This is a fork of [dajiaji/bhttp-js](https://github.com/dajiaji/bhttp-js), converted from Deno to a standard npm package.

## Installation

```sh
npm install bhttp-ts
```

## Usage

### Encode/Decode Request

```ts
import { BHttpDecoder, BHttpEncoder } from "bhttp-ts";

const req = new Request("https://www.example.com/hello.txt", {
  method: "GET",
  headers: {
    "User-Agent": "curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3",
    "Accept-Language": "en, mi",
  },
});

// Encode a Request object to a BHTTP binary
const encoder = new BHttpEncoder();
const binReq = await encoder.encodeRequest(req);

// Decode the BHTTP binary to a Request object
const decoder = new BHttpDecoder();
const decodedReq = decoder.decodeRequest(binReq);
```

### Encode/Decode Response

```ts
import { BHttpDecoder, BHttpEncoder } from "bhttp-ts";

const res = new Response("Hello World!", {
  status: 200,
  headers: { "Content-Type": "text/plain" },
});

// Encode a Response object to a BHTTP binary
const encoder = new BHttpEncoder();
const binRes = await encoder.encodeResponse(res);

// Decode the BHTTP binary to a Response object
const decoder = new BHttpDecoder();
const decodedRes = decoder.decodeResponse(binRes);
```

### Streaming API

For indeterminate-length messages, use the streaming encoders and decoder:

```ts
import {
  BHttpRequestStreamEncoder,
  BHttpResponseStreamEncoder,
  BHttpStreamDecoder,
} from "bhttp-ts";

// Streaming request encoding
const reqEncoder = new BHttpRequestStreamEncoder();
yield reqEncoder.encodePreamble("POST", "https", "example.com", "/api", headers);
yield reqEncoder.encodeContentChunk(chunk1);
yield reqEncoder.encodeContentChunk(chunk2);
yield reqEncoder.encodeEnd();

// Streaming response encoding
const resEncoder = new BHttpResponseStreamEncoder();
yield resEncoder.encodePreamble(200, headers);
yield resEncoder.encodeContentChunk(chunk1);
yield resEncoder.encodeEnd(trailers);

// Streaming decode
const decoder = new BHttpStreamDecoder();
for (const chunk of incomingData) {
  for (const event of decoder.push(chunk)) {
    switch (event.type) {
      case "request-preamble":
        // event.method, event.scheme, event.authority, event.path, event.headers
        break;
      case "response-preamble":
        // event.status, event.headers
        break;
      case "content":
        // event.data
        break;
      case "trailers":
        // event.headers
        break;
    }
  }
}
for (const event of decoder.end()) {
  // handle final events
}
```

When working with Fetch `Request` and `Response` objects, the high-level
streaming methods preserve backpressure and cancellation automatically:

```ts
const encoder = new BHttpEncoder();
const decoder = new BHttpDecoder();

const encoded = encoder.encodeRequestStream(request);
const decoded = await decoder.decodeRequestStream(encoded);
```

Response equivalents are `encodeResponseStream` and `decodeResponseStream`.
For GET/HEAD requests and 204/205/304 responses, decoding consumes and validates
the complete input through EOF before resolving, while discarding content.
Other messages finish validation when their decoded body is consumed to EOF.
The existing `BHttpRequestStreamEncoder`, `BHttpResponseStreamEncoder`, and
`BHttpStreamDecoder` remain available when manual framing is required.

## API

### BHttpEncoder

- `encodeRequest(request: Request, options?: BHttpEncoderOptions): Promise<Uint8Array>` - Encode a Request to known-length BHTTP
- `encodeResponse(response: Response, options?: BHttpEncoderOptions): Promise<Uint8Array>` - Encode a Response to known-length BHTTP
- `encodeRequestStream(request: Request, options?: BHttpEncoderOptions): ReadableStream<Uint8Array>` - Encode a streaming Request to indeterminate-length BHTTP
- `encodeResponseStream(response: Response, options?: BHttpEncoderOptions): ReadableStream<Uint8Array>` - Encode a streaming Response to indeterminate-length BHTTP

`BHttpEncoderOptions` accepts `padding` and `maxMessageSize`. Padding defaults to
`0` (disabled), including when only `maxMessageSize` is set. A positive safe integer
pads the complete encoded message to that byte multiple, using zero bytes after
the trailers. For example, `padding: 1024` rounds a 1,100-byte message up to
2,048 bytes. Already aligned messages receive no extra padding.

```ts
const bytes = await encoder.encodeRequest(request, { padding: 1024 });
const stream = encoder.encodeResponseStream(response, { padding: 16384 });
```

`maxMessageSize` includes padding; exceeding it throws `MessageLimitExceededError`
(or errors the stream). Streaming padding is emitted at EOF in bounded blocks,
without buffering the whole message. Manual framing encoders remain unpadded.

Buffered encoders allocate the full padded message. `maxMessageSize` defaults to
`Number.MAX_SAFE_INTEGER`, so large padding values can cause large allocations.
Set an explicit encoded-size limit, for example `{ padding: 1024, maxMessageSize: 1048576 }`.

### BHttpDecoder

- `decodeRequest(data: ArrayBuffer | Uint8Array): Request` - Decode BHTTP to a Request
- `decodeResponse(data: ArrayBuffer | Uint8Array): Response` - Decode BHTTP to a Response
- `decodeRequestStream(stream: ReadableStream<Uint8Array>): Promise<Request>` - Decode a streaming BHTTP request
- `decodeResponseStream(stream: ReadableStream<Uint8Array>): Promise<Response>` - Decode a streaming BHTTP response

### BHttpRequestStreamEncoder / BHttpResponseStreamEncoder

For encoding indeterminate-length messages incrementally.

### BHttpStreamDecoder

For decoding BHTTP messages incrementally, emitting events as data arrives.

## References

- [RFC 9292: Binary Representation of HTTP Messages](https://datatracker.ietf.org/doc/html/rfc9292)
- [Fetch - Living Standard](https://fetch.spec.whatwg.org/)

## License

MIT - See [LICENSE](./LICENSE) for details.
