@hardlydifficult/http 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @hardlydifficult/http
|
|
2
2
|
|
|
3
|
-
HTTP utilities for safe request/response handling
|
|
3
|
+
HTTP utilities for safe request/response handling: constant-time string comparison, body reading with size limits, and JSON responses with CORS headers.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -11,75 +11,104 @@ npm install @hardlydifficult/http
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
//
|
|
23
|
-
|
|
14
|
+
import { createServer } from "http";
|
|
15
|
+
import { readBody, sendJson, safeCompare } from "@hardlydifficult/http";
|
|
16
|
+
|
|
17
|
+
const server = createServer(async (req, res) => {
|
|
18
|
+
// Read request body safely
|
|
19
|
+
const body = await readBody(req);
|
|
20
|
+
const token = JSON.parse(body).token;
|
|
21
|
+
|
|
22
|
+
// Compare tokens securely
|
|
23
|
+
const isMatch = safeCompare(token, process.env.SECRET_TOKEN ?? "");
|
|
24
|
+
if (!isMatch) {
|
|
25
|
+
sendJson(res, 401, { error: "Unauthorized" }, "https://example.com");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Send JSON response with CORS headers
|
|
30
|
+
sendJson(res, 200, { message: "Access granted" }, "https://example.com");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
server.listen(3000);
|
|
24
34
|
```
|
|
25
35
|
|
|
26
|
-
##
|
|
36
|
+
## Request Handling
|
|
27
37
|
|
|
28
|
-
###
|
|
38
|
+
### `readBody`
|
|
29
39
|
|
|
30
|
-
|
|
40
|
+
Reads the full request body as a string, rejecting if it exceeds `maxBytes`.
|
|
31
41
|
|
|
32
42
|
```typescript
|
|
33
|
-
import {
|
|
43
|
+
import { readBody } from "@hardlydifficult/http";
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
const
|
|
45
|
+
// Default limit is 1 MB
|
|
46
|
+
const body = await readBody(req);
|
|
47
|
+
|
|
48
|
+
// Custom limit (e.g., 500 KB)
|
|
49
|
+
const body = await readBody(req, 500 * 1024);
|
|
37
50
|
```
|
|
38
51
|
|
|
39
|
-
| Parameter | Type
|
|
40
|
-
|
|
41
|
-
|
|
|
42
|
-
|
|
|
52
|
+
| Parameter | Type | Description |
|
|
53
|
+
|---------|------|-------------|
|
|
54
|
+
| `req` | `IncomingMessage` | Node.js HTTP request |
|
|
55
|
+
| `maxBytes?` | `number` | Maximum body size in bytes (default: `1048576`) |
|
|
56
|
+
|
|
57
|
+
Throws `"Payload too large"` error when body exceeds limit.
|
|
43
58
|
|
|
44
|
-
###
|
|
59
|
+
### `MAX_BODY_BYTES`
|
|
45
60
|
|
|
46
|
-
|
|
61
|
+
Default maximum body size: `1024 * 1024` (1 MB).
|
|
47
62
|
|
|
48
63
|
```typescript
|
|
49
|
-
import {
|
|
64
|
+
import { MAX_BODY_BYTES } from "@hardlydifficult/http";
|
|
50
65
|
|
|
51
|
-
|
|
52
|
-
// Throws Error if body exceeds MAX_BODY_BYTES
|
|
66
|
+
console.log(MAX_BODY_BYTES); // 1048576
|
|
53
67
|
```
|
|
54
68
|
|
|
55
|
-
|
|
56
|
-
|-----------|--------------------|--------------------------------|
|
|
57
|
-
| req | IncomingMessage | Node.js HTTP request object |
|
|
69
|
+
## Response Handling
|
|
58
70
|
|
|
59
|
-
###
|
|
71
|
+
### `sendJson`
|
|
60
72
|
|
|
61
|
-
Sends a JSON response with
|
|
73
|
+
Sends a JSON response with CORS headers.
|
|
62
74
|
|
|
63
75
|
```typescript
|
|
64
|
-
import { sendJson } from
|
|
76
|
+
import { sendJson } from "@hardlydifficult/http";
|
|
65
77
|
|
|
66
|
-
sendJson(res, {
|
|
67
|
-
// Sends: {"message":"Hello world"} with CORS headers
|
|
78
|
+
sendJson(res, 200, { data: "example" }, "https://example.com");
|
|
68
79
|
```
|
|
69
80
|
|
|
70
|
-
| Parameter | Type
|
|
71
|
-
|
|
72
|
-
| res
|
|
73
|
-
|
|
|
81
|
+
| Parameter | Type | Description |
|
|
82
|
+
|---------|------|-------------|
|
|
83
|
+
| `res` | `ServerResponse` | Node.js HTTP response |
|
|
84
|
+
| `status` | `number` | HTTP status code |
|
|
85
|
+
| `body` | `unknown` | Serializable data to send |
|
|
86
|
+
| `corsOrigin` | `string` | `Access-Control-Allow-Origin` value |
|
|
87
|
+
|
|
88
|
+
Sets headers:
|
|
89
|
+
- `Content-Type: application/json`
|
|
90
|
+
- `Access-Control-Allow-Origin: corsOrigin`
|
|
91
|
+
- `Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS`
|
|
92
|
+
- `Access-Control-Allow-Headers: Content-Type, Authorization`
|
|
74
93
|
|
|
75
|
-
##
|
|
94
|
+
## Security
|
|
76
95
|
|
|
77
|
-
###
|
|
96
|
+
### `safeCompare`
|
|
78
97
|
|
|
79
|
-
|
|
98
|
+
Constant-time string comparison to prevent timing attacks.
|
|
80
99
|
|
|
81
100
|
```typescript
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
```
|
|
101
|
+
import { safeCompare } from "@hardlydifficult/http";
|
|
102
|
+
|
|
103
|
+
const isValid = safeCompare(userInput, secretToken);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Returns `true` for identical strings (including empty strings) and `false` otherwise, regardless of string length or content differences.
|
|
107
|
+
|
|
108
|
+
Handles:
|
|
109
|
+
- Equal/unequal strings
|
|
110
|
+
- Different-length strings
|
|
111
|
+
- Unicode characters
|
|
112
|
+
- Empty strings
|
|
113
|
+
|
|
114
|
+
All comparisons run in time proportional to the first string's length.
|