Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions docs/content/docs/capabilities/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
title: Capabilities
description: "Capabilities — Http11Probe documentation"
weight: 12
sidebar:
open: false
---

Capability tests probe optional HTTP features that servers may or may not implement. Unlike compliance tests, these are **unscored** — they map what each server supports rather than what it fails at.

## Scoring

All capability tests are **unscored**:

- **Pass** — Server correctly supports the feature
- **Warn** — Server does not support the feature (not a failure)
- **Fail** — Only for actual errors (unexpected status codes, connection errors)

## Conditional Requests (Caching)

These tests check whether the server supports ETag and Last-Modified based conditional requests (RFC 9110 §13).

{{< cards >}}
{{< card link="etag-304" title="ETAG-304" subtitle="ETag conditional GET returns 304 Not Modified." >}}
{{< card link="last-modified-304" title="LAST-MODIFIED-304" subtitle="Last-Modified conditional GET returns 304 Not Modified." >}}
{{< card link="etag-in-304" title="ETAG-IN-304" subtitle="304 response includes ETag header." >}}
{{< card link="inm-precedence" title="INM-PRECEDENCE" subtitle="If-None-Match takes precedence over If-Modified-Since." >}}
{{< card link="inm-wildcard" title="INM-WILDCARD" subtitle="If-None-Match: * on existing resource returns 304." >}}
{{< /cards >}}

## Conditional Request Edge Cases

These tests probe how servers handle invalid or unusual conditional headers — future dates, garbage values, unquoted ETags, and weak comparison (RFC 9110 §13).

{{< cards >}}
{{< card link="ims-future" title="IMS-FUTURE" subtitle="If-Modified-Since with future date ignored." >}}
{{< card link="ims-invalid" title="IMS-INVALID" subtitle="If-Modified-Since with garbage date ignored." >}}
{{< card link="inm-unquoted" title="INM-UNQUOTED" subtitle="If-None-Match with unquoted ETag." >}}
{{< card link="etag-weak" title="ETAG-WEAK" subtitle="Weak ETag comparison for GET." >}}
{{< /cards >}}
62 changes: 62 additions & 0 deletions docs/content/docs/capabilities/etag-304.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: "ETAG-304"
description: "CAP-ETAG-304 capability test documentation"
weight: 10
---

| | |
|---|---|
| **Test ID** | `CAP-ETAG-304` |
| **Category** | Capabilities |
| **Type** | Sequence (2 steps) |
| **Scored** | No |
| **RFC** | [RFC 9110 §13.1.2](https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2) |
| **RFC Level** | SHOULD |
| **Expected** | `304` |

## What it does

This is a **sequence test** — it sends two requests on the same TCP connection to test ETag-based conditional request handling.

### Step 1: Initial GET (capture ETag)

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
Connection: keep-alive\r\n
\r\n
```

Captures the `ETag` header from the response for use in step 2.

### Step 2: Conditional GET (If-None-Match)

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
If-None-Match: "abc123"\r\n
\r\n
```

Sends the captured ETag value in an `If-None-Match` header. If the resource hasn't changed, the server should return `304 Not Modified`.

## What the RFC says

> "An origin server MUST use the strong comparison function when comparing entity-tags for If-None-Match, because the client intends to use the cached representation." — RFC 9110 §13.1.2

> "If the field value is '*', the condition is false if the origin server has a current representation for the target resource." — RFC 9110 §13.1.2

## Why it matters

ETag-based conditional requests are the most reliable caching mechanism in HTTP. They enable efficient revalidation without relying on timestamps, which can be unreliable across servers or after deployments.

## Verdicts

- **Pass** — Step 2 returns `304 Not Modified`
- **Warn** — Server does not include ETag in step 1, or returns `200` in step 2 (no conditional support)
- **Fail** — Unexpected error (non-2xx/304 response)

## Sources

- [RFC 9110 §13.1.2](https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2)
- [RFC 9110 §8.8.3](https://www.rfc-editor.org/rfc/rfc9110#section-8.8.3)
60 changes: 60 additions & 0 deletions docs/content/docs/capabilities/etag-in-304.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: "ETAG-IN-304"
description: "CAP-ETAG-IN-304 capability test documentation"
weight: 12
---

| | |
|---|---|
| **Test ID** | `CAP-ETAG-IN-304` |
| **Category** | Capabilities |
| **Type** | Sequence (2 steps) |
| **Scored** | No |
| **RFC** | [RFC 9110 §15.4.5](https://www.rfc-editor.org/rfc/rfc9110#section-15.4.5) |
| **RFC Level** | SHOULD |
| **Expected** | `304` with ETag |

## What it does

This is a **sequence test** — it verifies that a `304 Not Modified` response includes the `ETag` header, allowing clients to update their cached validators.

### Step 1: Initial GET (capture ETag)

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
Connection: keep-alive\r\n
\r\n
```

Captures the `ETag` header from the response.

### Step 2: Conditional GET (If-None-Match)

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
If-None-Match: "abc123"\r\n
\r\n
```

Sends the captured ETag. If the server returns `304`, this test checks whether the `ETag` header is present in that response.

## What the RFC says

> "A server generating a 304 response MUST generate any of the following header fields that would have been sent in a 200 (OK) response to the same request: ... ETag" — RFC 9110 §15.4.5

## Why it matters

Including the ETag in a `304` response lets clients confirm which representation they have cached and update their stored validator. Without it, clients may lose track of the ETag and fall back to unconditional requests.

## Verdicts

- **Pass** — Step 2 returns `304` with an ETag header
- **Warn** — Server does not support ETags, or returns `304` without an ETag header
- **Fail** — Unexpected error (non-2xx/304 response)

## Sources

- [RFC 9110 §15.4.5](https://www.rfc-editor.org/rfc/rfc9110#section-15.4.5)
- [RFC 9110 §8.8.3](https://www.rfc-editor.org/rfc/rfc9110#section-8.8.3)
62 changes: 62 additions & 0 deletions docs/content/docs/capabilities/etag-weak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: "ETAG-WEAK"
description: "CAP-ETAG-WEAK capability test documentation"
weight: 18
---

| | |
|---|---|
| **Test ID** | `CAP-ETAG-WEAK` |
| **Category** | Capabilities |
| **Type** | Sequence (2 steps) |
| **Scored** | No |
| **RFC** | [RFC 9110 §13.1.2](https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2) |
| **RFC Level** | SHOULD |
| **Expected** | `304` |

## What it does

This is a **sequence test** — it captures the server's ETag and resends it with a `W/` weak prefix in `If-None-Match` to test whether the server uses the weak comparison function for GET requests.

### Step 1: Initial GET (capture ETag)

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
Connection: keep-alive\r\n
\r\n
```

Captures the `ETag` header from the response. If the ETag is strong (e.g., `"abc123"`), step 2 will prepend `W/` to make it weak (`W/"abc123"`). If already weak, it is sent as-is.

### Step 2: Conditional GET (If-None-Match: W/etag)

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
If-None-Match: W/"abc123"\r\n
\r\n
```

The weak ETag should still match via the weak comparison function, which only compares the opaque-tag portion.

## What the RFC says

> "A recipient MUST use the weak comparison function when comparing entity-tags for If-None-Match." — RFC 9110 §13.1.2

The weak comparison function is defined as: "two entity-tags are equivalent if their opaque-tags match character-by-character, regardless of either or both being tagged as 'weak'."

## Why it matters

GET conditional requests must use weak comparison. A server that only does byte-for-byte matching of the full ETag string (including `W/` prefix) will fail to match weak ETags, causing unnecessary full responses for cacheable content.

## Verdicts

- **Pass** — Step 2 returns `304` (weak comparison matched)
- **Warn** — No ETag in step 1, or step 2 returns `200` (server didn't use weak comparison)
- **Fail** — Unexpected error (non-2xx/304 response)

## Sources

- [RFC 9110 §13.1.2](https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2)
- [RFC 9110 §8.8.3.2](https://www.rfc-editor.org/rfc/rfc9110#section-8.8.3.2)
59 changes: 59 additions & 0 deletions docs/content/docs/capabilities/ims-future.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
title: "IMS-FUTURE"
description: "CAP-IMS-FUTURE capability test documentation"
weight: 15
---

| | |
|---|---|
| **Test ID** | `CAP-IMS-FUTURE` |
| **Category** | Capabilities |
| **Type** | Sequence (2 steps) |
| **Scored** | No |
| **RFC** | [RFC 9110 §13.1.3](https://www.rfc-editor.org/rfc/rfc9110#section-13.1.3) |
| **RFC Level** | SHOULD |
| **Expected** | `200` |

## What it does

This is a **sequence test** — it sends an `If-Modified-Since` header with a date far in the future to check whether the server correctly ignores it.

### Step 1: Initial GET (confirm 2xx)

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
Connection: keep-alive\r\n
\r\n
```

Verifies the resource exists and returns a success response.

### Step 2: Conditional GET (If-Modified-Since: future date)

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
If-Modified-Since: Thu, 01 Jan 2099 00:00:00 GMT\r\n
\r\n
```

Sends a future date. A compliant server must ignore an `If-Modified-Since` value that is later than the server's current time and return the resource normally.

## What the RFC says

> "A recipient MUST ignore If-Modified-Since if the field value is not a valid HTTP-date, or if the field value is a date in the future (compared to the server's current time)." — RFC 9110 §13.1.3

## Why it matters

A server that blindly compares dates without checking whether the date is in the future could incorrectly return `304 Not Modified` for every request with a future timestamp, allowing cache-poisoning or stale-content attacks.

## Verdicts

- **Pass** — Step 2 returns `200` (correctly ignores future date)
- **Warn** — Server returns `304` (didn't validate the date against current time)
- **Fail** — Unexpected error (non-2xx/304 response)

## Sources

- [RFC 9110 §13.1.3](https://www.rfc-editor.org/rfc/rfc9110#section-13.1.3)
59 changes: 59 additions & 0 deletions docs/content/docs/capabilities/ims-invalid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
title: "IMS-INVALID"
description: "CAP-IMS-INVALID capability test documentation"
weight: 16
---

| | |
|---|---|
| **Test ID** | `CAP-IMS-INVALID` |
| **Category** | Capabilities |
| **Type** | Sequence (2 steps) |
| **Scored** | No |
| **RFC** | [RFC 9110 §13.1.3](https://www.rfc-editor.org/rfc/rfc9110#section-13.1.3) |
| **RFC Level** | SHOULD |
| **Expected** | `200` |

## What it does

This is a **sequence test** — it sends an `If-Modified-Since` header with an unparseable garbage value to check whether the server correctly ignores it.

### Step 1: Initial GET (confirm 2xx)

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
Connection: keep-alive\r\n
\r\n
```

Verifies the resource exists and returns a success response.

### Step 2: Conditional GET (If-Modified-Since: garbage)

```http
GET / HTTP/1.1\r\n
Host: localhost:8080\r\n
If-Modified-Since: not-a-date\r\n
\r\n
```

Sends a value that is not a valid HTTP-date. A compliant server must ignore the header and return the resource normally.

## What the RFC says

> "A recipient MUST ignore If-Modified-Since if the field value is not a valid HTTP-date." — RFC 9110 §13.1.3

## Why it matters

If a server treats an unparseable date as "very old" and returns `304`, it could cause clients to serve stale cached content. Correct behavior is to ignore the invalid header entirely.

## Verdicts

- **Pass** — Step 2 returns `200` (correctly ignores invalid date)
- **Warn** — Server returns `304` (treated garbage as a valid date)
- **Fail** — Unexpected error (non-2xx/304 response)

## Sources

- [RFC 9110 §13.1.3](https://www.rfc-editor.org/rfc/rfc9110#section-13.1.3)
Loading
Loading