npm package: @n7e/http
Description: A library defining objects and procedures for working with the HTTP protocol.
Author: Martin Pettersson
Latest version: 0.8.1

# HTTP

A library defining objects and procedures for working with the HTTP protocol.

For further insights, read on.

- [Installation](#installation)
- [Messages](#messages)
    - [Immutability](#immutability)
    - [Request](#request)
    - [Response](#response)
- [URI](#uri)
- [URI Query Parser](#uri-query-parser)
- [Factories](#factories)
    - [Request Factory](#request-factory)
    - [Response Factory](#response-factory)
    - [URI Factory](#uri-factory)
    - [Stream Factory](#response-factory)


## Installation

To install this library use your favorite package manager. No additional
steps are required to start using the library.

```shell
npm install @n7e/http
```

*This library is implemented in TypeScript but can be used with JavaScript
without any additional steps.*


## Messages

HTTP is a stateless request/response protocol that operates by exchanging
messages across a reliable transport- or session-layer "connection". An HTTP
"client" is a program that establishes a connection to a server for the purpose
of sending one or more HTTP requests.  An HTTP "server" is a program that
accepts connections in order to service HTTP requests by sending HTTP responses.

Requests and responses has some properties in common, namely:

- Protocol version
- Header fields
- Body

An HTTP message is represented by the `Message` interface, which is generic to
the type extending it. This means that any methods on the `Message` interface
returning the same instance will return a `Message`. Whilst methods on an
extending interface will return instances of that same interface. This allows
instances of [Request](#request) and [Response](#response) to return an instance
of the same type when using inherited methods from the `Message` interface.

```typescript
import { Message } from "@n7e/http";

const messageWithContentType: Message = message.withHeader("Content-Type", "text/plain");
```


### Immutability

Messages are considered immutable and are implemented in such a way that any
method that normally would mutate the instance produces a copy instead.

Given a GET request message:

```typescript
request.method; // GET
```

When we call the `withMethod` method to "change" the request method a new
request is produced leaving the existing request unchanged.

```typescript
import { RequestMethod } from "@n7e/http";

const postRequest = request.withMethod(RequestMethod.POST);

request.method; // GET
postRequest.method; // POST
```


### Request

A client sends an HTTP request to a server in the form of a request message,
beginning with a request-line that includes a method, URI, and protocol version,
followed by header fields containing request modifiers, client information, and
representation metadata, an empty line to indicate the end of the header
section, and finally a message body containing the payload body (if any).

Example request message:

```
GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: example.com
Accept-Language: en, mi
```

An HTTP request message is represented by the `Request` interface and inherits
all methods from `Message`.

The `Request` interface extends `Message` interface in such a way that any
inherited methods of the `Message` interface will return an instance of
`Request`:

```typescript
import { Request } from "@n7e/http";

const requestWithContentType: Request = request.withHeader("Content-Type", "text/plain");
```


### Response

A server responds to a client's request by sending one or more HTTP response
messages, each beginning with a status line that includes the protocol version,
a success or error code, and textual reason phrase, possibly followed by header
fields containing server information, resource metadata, and representation
metadata, an empty line to indicate the end of the header section, and finally a
message body containing the payload body (if any).

```
HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain

Hello World! My payload includes a trailing CRLF.
```

The `Response` interface extends `Message` interface in such a way that any
inherited methods of the `Message` interface will return an instance of
`Response`:

```typescript
import { Response } from "@n7e/http";

const responseWithContentType: Response = response.withHeader("Content-Type", "text/plain");
```


## URI

HTTP relies upon the Uniform Resource Identifier (URI) standard to indicate the
target resource and relationships between resources.

A URI is represented by the `Uri` interface. The interface provides properties
and methods to access and manipulate defined URI components.


## URI Query Parser

Although there are a couple of common formats, since there's no specification
for the URI query component format this library does not attempt to parse the
URI query. It's up to the user to do this but this library provides an
abstraction for parsing and formatting query components through the
`UriQueryParser` interface.

```typescript
import type { Uri, UriQueryParser } from "@n7e/http";

function someFunction(uri: Uri, queryParser: UriQueryParser): void {
    const parameters = queryParser.parse(uri.query ?? "");

    // ...
}
```

*`UriQueryParser` is just an interface describing the functionality of a URI
query parser. To create a URI query parser instance you need to reference a
specific implementation.*


### Default Implementation

This library provides a default URI query parser implementation.

```typescript
import { DefaultUriQueryParser } from "@n7e/http";

const queryParser = new DefaultUriQueryParser();
```

The expected and produced query format can be described as follows:

- Key/value pairs are separated by a single `&`
- Keys and values are separated by a single `=`
- Multiple key values are represented by multiple identical keys

```typescript
const parameters = queryParser.parse("key=value&anotherKey=another%20value&numbers=one&numbers=two");

// parameters -> Map(3) {
//     "key" => ["value"],
//     "anotherKey" => ["another value"],
//     "numbers" => ["one", "two"],
// }
```


## Factories

To not only abstract the usage of HTTP related objects but also the creation of
them this library provides a set of factory interfaces and associated default
implementations. Using these interfaces allows the underlying implementations to
be swapped out and can benefit testing greatly.


### Request Factory

To create a request instance you should use a `RequestFactory`.

```typescript
import { RequestFactory, RequestMethod } from "@n7e/http";

function someFunction(requestFactory: RequestFactory): void {
    const request = requestFactory.createRequest(RequestMethod.GET, "http://example.com");

    // ...
}
```

*`RequestFactory` is just an interface describing the functionality of a request
factory. To create a request factory instance you need to reference a specific
implementation.*


#### Default Implementation

This library provides a default implementation of `RequestFactory`. See the
following example of how to create a request instance.

```typescript
import { DefaultRequestFactory, DefaultUriFactory, RequestMethod } from "@n7e/http";

const requestFactory = new DefaultRequestFactory(new DefaultUriFactory());
const request = requestFactory.createRequest(RequestMethod.GET, "http://example.com");
```


### Response Factory

To create a response instance you should use a `ResponseFactory`.

```typescript
import { ResponseFactory, ResponseStatus } from "@n7e/http";

function someFunction(responseFactory: ResponseFactory): void {
    const response = responseFactory.createResponse(ResponseStatus.OK);

    // ...
}
```

*`ResponseFactory` is just an interface describing the functionality of a
response factory. To create a response factory instance you need to reference a
specific implementation.*


#### Default Implementation

This library provides a default implementation of `ResponseFactory`. See the
following example of how to create a response instance.

```typescript
import { DefaultResponseFactory, DefaultStreamFactory, ResponseStatus } from "@n7e/http";

const responseFactory = new DefaultResponseFactory(new DefaultStreamFactory());
const response = responseFactory.createResponse(ResponseStatus.OK);
```


### URI Factory

To create a URI instance you should use a `UriFactory`.

```typescript
import type { UriFactory } from "@n7e/http";

function someFunction(uriFactory: UriFactory): void {
    const uri = uriFactory.createUri("http://example.com");

    // ...
}
```

*`UriFactory` is just an interface describing the functionality of a URI
factory. To create a URI factory instance you need to reference a specific
implementation.*


#### Default Implementation

This library provides a default implementation of `UriFactory`. See the
following example of how to create a URI instance.

```typescript
import { DefaultUriFactory } from "@n7e/http";

const uriFactory = new DefaultUriFactory();
const uri = uriFactory.createUri("http://example.com");
```


### Stream Factory

To create a stream instance you should use a `StreamFactory`.

```typescript
import type { StreamFactory } from "@n7e/http";

function someFunction(streamFactory: StreamFactory): void {
    const stream = streamFactory.createStream("content");

    // ...
}
```

*`StreamFactory` is just an interface describing the functionality of a stream
factory. To create a stream factory instance you need to reference a specific
implementation.*


#### Default Implementation

This library provides a default implementation of `StreamFactory`. See the
following example of how to create a stream instance.

```typescript
import { DefaultStreamFactory } from "@n7e/http";

const streamFactory = new DefaultStreamFactory();
const stream = streamFactory.createStream("content");
```