Skip to main content
Deno 2 is finally here 🎉️
Learn more

conditional-request-middleware

HTTP conditional request middleware.

Compliant with RFC 9110, 13. Conditional Requests

deno land deno doc GitHub release (latest by date) codecov GitHub

test NPM

Middleware

For a definition of Universal HTTP middleware, see the http-middleware project.

Usage

To evaluate precondition, you need to provide a function to retrieve the selected representation.

The following example evaluates the If-None-Match precondition and handle response.

import {
  conditionalRequest,
  type Handler,
} from "https://deno.land/x/conditional_request_middleware@$VERSION/mod.ts";
import {
  assertEquals,
  assertFalse,
} from "https://deno.land/std/testing/asserts.ts";
import { assertSpyCalls, spy } from "https://deno.land/std/testing/mock.ts";

const selectRepresentation = spy((request: Request) => {
  return new Response("<body>", { headers: { etag: "<etag>" } });
});
const middleware = conditionalRequest(selectRepresentation);
const request = new Request("<uri>", {
  headers: { "if-none-match": "<etag>" },
});
declare const _handler: Handler;
const handler = spy(_handler);

const response = await middleware(request, handler);

assertSpyCalls(handler, 0);
assertSpyCalls(selectRepresentation, 1);
assertEquals(response.status, 304);
assertFalse(response.body);

Precondition

RFC 9110, 13.1. Preconditions compliant and supports the following precondition:

If multiple precondition headers are present, precondition is processed according to precedence.

IfMatch

If-Match header field precondition.

import { IfMatch } from "https://deno.land/x/conditional_request_middleware@$VERSION/mod.ts";
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

const precondition = new IfMatch();
const request = new Request("<uri>", {
  headers: { "if-match": "<strong:etag>" },
});
const selectedRepresentation = new Response("<content>", {
  headers: { etag: "<weak:etag>" },
});
declare const evalResult: false;

assertEquals(precondition.field, "if-match");
assertEquals(
  precondition.evaluate(request, selectedRepresentation),
  evalResult,
);
assertEquals(
  precondition.respond(request, selectedRepresentation, evalResult)?.status,
  412,
);

Effects

Precondition will effect following:

If evaluation is false:

IfNoneMatch

If-None-Match header field precondition.

import { IfNoneMatch } from "https://deno.land/x/conditional_request_middleware@$VERSION/mod.ts";
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

const precondition = new IfNoneMatch();
const request = new Request("<uri>", {
  headers: { "if-none-match": "<weak:etag>" },
});
const selectedRepresentation = new Response("<content>", {
  headers: { etag: "<weak:etag>" },
});
declare const evalResult: false;

assertEquals(precondition.field, "if-none-match");
assertEquals(
  precondition.evaluate(request, selectedRepresentation),
  evalResult,
);
assertEquals(
  precondition.respond(request, selectedRepresentation, evalResult)?.status,
  304,
);

Effects

Precondition will effect following:

If evaluation is false:

IfModifiedSince

If-Modified-Since header field precondition.

import { IfModifiedSince } from "https://deno.land/x/conditional_request_middleware@$VERSION/mod.ts";
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

const precondition = new IfModifiedSince();
const request = new Request("<uri>", {
  headers: { "if-modified-since": "<after:HTTP-date>" },
});
const selectedRepresentation = new Response("<content>", {
  headers: { "last-modified": "<before:HTTP-date>" },
});
declare const evalResult: false;

assertEquals(precondition.field, "if-modified-since");
assertEquals(
  precondition.evaluate(request, selectedRepresentation),
  evalResult,
);
assertEquals(
  precondition.respond(request, selectedRepresentation, evalResult)?.status,
  304,
);

Effects

Precondition will effect following:

If evaluation is false:

  • HTTP content
  • HTTP response status
  • HTTP headers
    • Content-Type
    • Content-Encoding
    • Content-Length
    • Content-Language

IfUnmodifiedSince

If-Unmodified-Since header field precondition.

import { IfUnmodifiedSince } from "https://deno.land/x/conditional_request_middleware@$VERSION/mod.ts";
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

const precondition = new IfUnmodifiedSince();
const request = new Request("<uri>", {
  headers: { "if-unmodified-since": "<before:HTTP-date>" },
});
const selectedRepresentation = new Response("<content>", {
  headers: { "last-modified": "<after:HTTP-date>" },
});
declare const evalResult: false;

assertEquals(precondition.field, "if-unmodified-since");
assertEquals(
  precondition.evaluate(request, selectedRepresentation),
  evalResult,
);
assertEquals(
  precondition.respond(request, selectedRepresentation, evalResult)?.status,
  412,
);

Effects

Precondition will effect following:

If evaluation is false:

IfRange

If-Range header field precondition.

import { IfRange } from "https://deno.land/x/conditional_request_middleware@$VERSION/mod.ts";
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

const precondition = new IfRange();
const request = new Request("<uri>", {
  headers: { "if-range": "<strong:etag>", range: "<range-unit>=<range-set>" },
});
const selectedRepresentation = new Response("<content>", {
  headers: { etag: "<strong:etag>" },
});
declare const evalResult: false;

assertEquals(precondition.field, "if-range");
assertEquals(
  precondition.evaluate(request, selectedRepresentation),
  evalResult,
);
assertEquals(
  (await precondition.respond(request, selectedRepresentation, evalResult))
    ?.status,
  206,
);

Effects

Precondition will effect following:

If evaluation is true:

Conditions

Middleware will execute only if the following conditions are met:

  • Request is conditional request
  • Request method is not CONNECT, OPTIONS or TRACE
  • Select representation status is 2xx or 412

License

Copyright © 2023-present httpland.

Released under the MIT license