@n7e/http
v0.6.1
Published
A library defining objects and procedures for working with the HTTP protocol.
Downloads
25
Readme
HTTP
A library defining objects and procedures for working with the HTTP protocol.
For further insights, read on.
Installation
To install this library use your favorite package manager. No additional steps are required to start using the library.
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 and Response to return an instance
of the same type when using inherited methods from the Message
interface.
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:
request.method; // GET
When we call the withMethod
method to "change" the request method a new
request is produced leaving the existing request unchanged.
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
:
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
:
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.
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.
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
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
.
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.
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
.
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.
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
.
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.
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
.
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.
import { DefaultStreamFactory } from "@n7e/http";
const streamFactory = new DefaultStreamFactory();
const stream = streamFactory.createStream("content");