@hardlydifficult/http 1.0.2 → 1.0.3
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 +51 -54
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,88 +11,85 @@ npm install @hardlydifficult/http
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import {
|
|
15
|
-
import
|
|
14
|
+
import { readBody, sendJson, safeCompare, MAX_BODY_BYTES } from "@hardlydifficult/http";
|
|
15
|
+
import { createServer } from "http";
|
|
16
16
|
|
|
17
|
-
const server =
|
|
18
|
-
// Read request body with default 1MB limit
|
|
17
|
+
const server = createServer(async (req, res) => {
|
|
19
18
|
const body = await readBody(req);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// Send JSON response with CORS support
|
|
25
|
-
sendJson(res, isValid ? 200 : 401, { valid: isValid }, "https://example.com");
|
|
19
|
+
const isValid = safeCompare(body, "expected-secret");
|
|
20
|
+
|
|
21
|
+
sendJson(res, isValid ? 200 : 403, { authorized: isValid }, "https://example.com");
|
|
26
22
|
});
|
|
27
23
|
|
|
28
24
|
server.listen(3000);
|
|
25
|
+
// → Server starts and safely compares incoming request bodies
|
|
29
26
|
```
|
|
30
27
|
|
|
31
|
-
##
|
|
32
|
-
|
|
33
|
-
Protects against timing attacks by using `crypto.timingSafeEqual` internally.
|
|
34
|
-
|
|
35
|
-
### `safeCompare(a: string, b: string): boolean`
|
|
28
|
+
## Body Reading with Size Limit
|
|
36
29
|
|
|
37
|
-
|
|
30
|
+
Reads the full HTTP request body as a string, enforcing a configurable maximum size (default 1 MB). Throws an error if the payload exceeds the limit.
|
|
38
31
|
|
|
39
32
|
```typescript
|
|
40
|
-
import {
|
|
33
|
+
import { readBody, MAX_BODY_BYTES } from "@hardlydifficult/http";
|
|
34
|
+
|
|
35
|
+
// Default limit: 1 MB
|
|
36
|
+
const body = await readBody(request);
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
safeCompare("", "something"); // false
|
|
45
|
-
safeCompare("héllo", "héllo"); // true (unicode-safe)
|
|
38
|
+
// Custom limit: 500 KB
|
|
39
|
+
const body = await readBody(request, 1024 * 500);
|
|
46
40
|
```
|
|
47
41
|
|
|
48
|
-
|
|
42
|
+
### Error Handling
|
|
49
43
|
|
|
50
|
-
|
|
44
|
+
If the body exceeds the specified limit, the promise rejects with an `"Payload too large"` error.
|
|
51
45
|
|
|
52
|
-
|
|
46
|
+
## JSON Response with CORS
|
|
53
47
|
|
|
54
|
-
|
|
48
|
+
Sends a JSON response with CORS headers enabled.
|
|
55
49
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
| Parameter | Type | Description |
|
|
51
|
+
|---------|------|-------------|
|
|
52
|
+
| `res` | `ServerResponse` | Node.js HTTP response object |
|
|
53
|
+
| `status` | `number` | HTTP status code |
|
|
54
|
+
| `body` | `unknown` | Any serializable data |
|
|
55
|
+
| `corsOrigin` | `string` | Allowed origin for CORS (e.g., `"*"` or `"https://example.com"`) |
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
```typescript
|
|
58
|
+
import { sendJson } from "@hardlydifficult/http";
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
sendJson(
|
|
61
|
+
res,
|
|
62
|
+
201,
|
|
63
|
+
{ id: 123, message: "Created" },
|
|
64
|
+
"https://frontend.example.com"
|
|
65
|
+
);
|
|
66
|
+
// → Sets headers: Content-Type, Access-Control-Allow-Origin, etc.
|
|
65
67
|
```
|
|
66
68
|
|
|
67
|
-
##
|
|
68
|
-
|
|
69
|
-
Sends JSON responses with CORS headers enabled.
|
|
70
|
-
|
|
71
|
-
### `sendJson(res: ServerResponse, status: number, body: unknown, corsOrigin: string): void`
|
|
69
|
+
## Constant-Time String Comparison
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
Performs secure string comparison using `crypto.timingSafeEqual` to prevent timing attacks.
|
|
74
72
|
|
|
75
73
|
```typescript
|
|
76
|
-
import {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// Sends:
|
|
81
|
-
// Content-Type: application/json
|
|
82
|
-
// Access-Control-Allow-Origin: https://example.com
|
|
83
|
-
// Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
|
|
84
|
-
// Access-Control-Allow-Headers: Content-Type, Authorization
|
|
85
|
-
// Body: {"message":"OK"}
|
|
74
|
+
import { safeCompare } from "@hardlydifficult/http";
|
|
75
|
+
|
|
76
|
+
const isMatch = safeCompare(userProvidedToken, storedToken);
|
|
77
|
+
// → true if strings are identical, false otherwise
|
|
86
78
|
```
|
|
87
79
|
|
|
88
|
-
|
|
80
|
+
### Behavior Details
|
|
89
81
|
|
|
90
|
-
|
|
82
|
+
- Returns `true` only for identical strings (including empty strings).
|
|
83
|
+
- Returns `false` for different-length strings, with constant-time behavior.
|
|
84
|
+
- Handles Unicode correctly by comparing UTF-8 encoded buffers.
|
|
91
85
|
|
|
92
|
-
|
|
86
|
+
### Example Edge Cases
|
|
93
87
|
|
|
94
88
|
```typescript
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
safeCompare("", ""); // true
|
|
90
|
+
safeCompare("abc", "abc"); // true
|
|
91
|
+
safeCompare("abc", "abd"); // false
|
|
92
|
+
safeCompare("short", "longer"); // false
|
|
93
|
+
safeCompare("héllo", "héllo"); // true
|
|
94
|
+
safeCompare("héllo", "hello"); // false
|
|
98
95
|
```
|