ehbp 0.0.2 → 0.0.4
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/dist/cjs/client.d.ts.map +1 -0
- package/dist/cjs/client.js +160 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/identity.d.ts.map +1 -0
- package/dist/cjs/identity.js +274 -0
- package/dist/cjs/identity.js.map +1 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/protocol.d.ts.map +1 -0
- package/dist/cjs/protocol.js +22 -0
- package/dist/cjs/protocol.js.map +1 -0
- package/dist/esm/client.d.ts +51 -0
- package/dist/esm/client.d.ts.map +1 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/{example.d.ts.map → esm/example.d.ts.map} +1 -1
- package/dist/esm/example.js.map +1 -0
- package/dist/esm/identity.d.ts +52 -0
- package/dist/esm/identity.d.ts.map +1 -0
- package/dist/esm/identity.js.map +1 -0
- package/{src/index.ts → dist/esm/index.d.ts} +2 -4
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/protocol.d.ts +19 -0
- package/dist/esm/protocol.d.ts.map +1 -0
- package/dist/esm/protocol.js.map +1 -0
- package/dist/{streaming-test.d.ts.map → esm/streaming-test.d.ts.map} +1 -1
- package/dist/esm/streaming-test.js.map +1 -0
- package/dist/{test → esm/test}/client.test.d.ts.map +1 -1
- package/dist/esm/test/client.test.js.map +1 -0
- package/dist/esm/test/identity.test.d.ts.map +1 -0
- package/dist/esm/test/identity.test.js.map +1 -0
- package/dist/esm/test/streaming.test.d.ts.map +1 -0
- package/dist/esm/test/streaming.test.js.map +1 -0
- package/package.json +23 -8
- package/build-browser.js +0 -54
- package/chat.html +0 -285
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/example.js.map +0 -1
- package/dist/identity.d.ts.map +0 -1
- package/dist/identity.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js.map +0 -1
- package/dist/streaming-test.js.map +0 -1
- package/dist/test/client.test.js.map +0 -1
- package/dist/test/identity.test.d.ts.map +0 -1
- package/dist/test/identity.test.js.map +0 -1
- package/dist/test/streaming.test.d.ts.map +0 -1
- package/dist/test/streaming.test.js.map +0 -1
- package/src/client.ts +0 -181
- package/src/example.ts +0 -126
- package/src/identity.ts +0 -339
- package/src/protocol.ts +0 -19
- package/src/streaming-test.ts +0 -118
- package/src/test/client.test.ts +0 -93
- package/src/test/identity.test.ts +0 -46
- package/src/test/streaming.test.ts +0 -85
- package/test.html +0 -271
- package/tsconfig.json +0 -19
- /package/dist/{client.d.ts → cjs/client.d.ts} +0 -0
- /package/dist/{identity.d.ts → cjs/identity.d.ts} +0 -0
- /package/dist/{index.d.ts → cjs/index.d.ts} +0 -0
- /package/dist/{protocol.d.ts → cjs/protocol.d.ts} +0 -0
- /package/dist/{client.js → esm/client.js} +0 -0
- /package/dist/{example.d.ts → esm/example.d.ts} +0 -0
- /package/dist/{example.js → esm/example.js} +0 -0
- /package/dist/{identity.js → esm/identity.js} +0 -0
- /package/dist/{index.js → esm/index.js} +0 -0
- /package/dist/{protocol.js → esm/protocol.js} +0 -0
- /package/dist/{streaming-test.d.ts → esm/streaming-test.d.ts} +0 -0
- /package/dist/{streaming-test.js → esm/streaming-test.js} +0 -0
- /package/dist/{test → esm/test}/client.test.d.ts +0 -0
- /package/dist/{test → esm/test}/client.test.js +0 -0
- /package/dist/{test → esm/test}/identity.test.d.ts +0 -0
- /package/dist/{test → esm/test}/identity.test.js +0 -0
- /package/dist/{test → esm/test}/streaming.test.d.ts +0 -0
- /package/dist/{test → esm/test}/streaming.test.js +0 -0
package/src/protocol.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Protocol constants for EHBP (Encrypted HTTP Body Protocol)
|
|
3
|
-
*/
|
|
4
|
-
export const PROTOCOL = {
|
|
5
|
-
ENCAPSULATED_KEY_HEADER: 'Ehbp-Encapsulated-Key',
|
|
6
|
-
CLIENT_PUBLIC_KEY_HEADER: 'Ehbp-Client-Public-Key',
|
|
7
|
-
KEYS_MEDIA_TYPE: 'application/ohttp-keys',
|
|
8
|
-
KEYS_PATH: '/.well-known/hpke-keys',
|
|
9
|
-
FALLBACK_HEADER: 'Ehbp-Fallback'
|
|
10
|
-
} as const;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* HPKE suite configuration matching the Go implementation
|
|
14
|
-
*/
|
|
15
|
-
export const HPKE_CONFIG = {
|
|
16
|
-
KEM: 0x0020, // X25519 HKDF SHA256
|
|
17
|
-
KDF: 0x0001, // HKDF SHA256
|
|
18
|
-
AEAD: 0x0002 // AES-256-GCM
|
|
19
|
-
} as const;
|
package/src/streaming-test.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { Identity, createTransport } from './index.js';
|
|
4
|
-
|
|
5
|
-
async function streamingTest() {
|
|
6
|
-
console.log('EHBP Streaming Test');
|
|
7
|
-
console.log('===================');
|
|
8
|
-
|
|
9
|
-
try {
|
|
10
|
-
// Create client identity
|
|
11
|
-
console.log('Creating client identity...');
|
|
12
|
-
const clientIdentity = await Identity.generate();
|
|
13
|
-
console.log('Client identity created');
|
|
14
|
-
|
|
15
|
-
// Create transport
|
|
16
|
-
console.log('Creating transport...');
|
|
17
|
-
const serverURL = 'http://localhost:8080';
|
|
18
|
-
const transport = await createTransport(serverURL, clientIdentity);
|
|
19
|
-
console.log('Transport created');
|
|
20
|
-
|
|
21
|
-
// Test 1: Basic streaming request
|
|
22
|
-
console.log('\n--- Test 1: Basic Streaming ---');
|
|
23
|
-
const streamResponse = await transport.get(`${serverURL}/stream`);
|
|
24
|
-
console.log('Stream request sent, status:', streamResponse.status);
|
|
25
|
-
|
|
26
|
-
if (streamResponse.ok) {
|
|
27
|
-
console.log('Reading stream data...');
|
|
28
|
-
const reader = streamResponse.body?.getReader();
|
|
29
|
-
if (reader) {
|
|
30
|
-
const decoder = new TextDecoder();
|
|
31
|
-
|
|
32
|
-
while (true) {
|
|
33
|
-
const { done, value } = await reader.read();
|
|
34
|
-
if (done) break;
|
|
35
|
-
|
|
36
|
-
const text = decoder.decode(value, { stream: true });
|
|
37
|
-
process.stdout.write(text);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
console.log('\nStream completed');
|
|
41
|
-
} else {
|
|
42
|
-
console.log('No readable stream available');
|
|
43
|
-
}
|
|
44
|
-
} else {
|
|
45
|
-
console.log('Stream request failed with status:', streamResponse.status);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Test 2: Multiple concurrent streams
|
|
49
|
-
console.log('\n--- Test 2: Concurrent Streams ---');
|
|
50
|
-
const concurrentStreams = 3;
|
|
51
|
-
const streamPromises = [];
|
|
52
|
-
|
|
53
|
-
for (let i = 0; i < concurrentStreams; i++) {
|
|
54
|
-
streamPromises.push(
|
|
55
|
-
(async (streamId: number) => {
|
|
56
|
-
const response = await transport.get(`${serverURL}/stream`);
|
|
57
|
-
if (response.ok && response.body) {
|
|
58
|
-
const reader = response.body.getReader();
|
|
59
|
-
const decoder = new TextDecoder();
|
|
60
|
-
|
|
61
|
-
while (true) {
|
|
62
|
-
const { done, value } = await reader.read();
|
|
63
|
-
if (done) break;
|
|
64
|
-
|
|
65
|
-
const text = decoder.decode(value, { stream: true });
|
|
66
|
-
|
|
67
|
-
// Prefix with stream ID to show concurrency
|
|
68
|
-
process.stdout.write(`[Stream ${streamId}] ${text}`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return { streamId };
|
|
72
|
-
}
|
|
73
|
-
return { streamId };
|
|
74
|
-
})(i + 1)
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const results = await Promise.all(streamPromises);
|
|
79
|
-
console.log('\nConcurrent streams completed:');
|
|
80
|
-
results.forEach(result => {
|
|
81
|
-
console.log(` - Stream ${result.streamId}: completed`);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// Test 3: Large data streaming (if server supports it)
|
|
85
|
-
console.log('\n--- Test 3: Large Data Stream ---');
|
|
86
|
-
try {
|
|
87
|
-
const largeStreamResponse = await transport.get(`${serverURL}/stream`);
|
|
88
|
-
if (largeStreamResponse.ok) {
|
|
89
|
-
console.log('Reading large data stream...');
|
|
90
|
-
const reader = largeStreamResponse.body?.getReader();
|
|
91
|
-
if (reader) {
|
|
92
|
-
const decoder = new TextDecoder();
|
|
93
|
-
|
|
94
|
-
while (true) {
|
|
95
|
-
const { done, value } = await reader.read();
|
|
96
|
-
if (done) break;
|
|
97
|
-
|
|
98
|
-
const text = decoder.decode(value, { stream: true });
|
|
99
|
-
process.stdout.write(text);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.log('Large data stream test failed:', error instanceof Error ? error.message : String(error));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
console.log('\nAll streaming tests completed successfully!');
|
|
108
|
-
|
|
109
|
-
} catch (error) {
|
|
110
|
-
console.error('Streaming test failed:', error);
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Run the streaming test
|
|
116
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
117
|
-
streamingTest().catch(console.error);
|
|
118
|
-
}
|
package/src/test/client.test.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { describe, it, before, after } from 'node:test';
|
|
2
|
-
import assert from 'node:assert';
|
|
3
|
-
import { Identity, Transport, createTransport } from '../index.js';
|
|
4
|
-
import { PROTOCOL } from '../protocol.js';
|
|
5
|
-
|
|
6
|
-
describe('Transport', () => {
|
|
7
|
-
let clientIdentity: Identity;
|
|
8
|
-
let serverIdentity: Identity;
|
|
9
|
-
|
|
10
|
-
before(async () => {
|
|
11
|
-
clientIdentity = await Identity.generate();
|
|
12
|
-
serverIdentity = await Identity.generate();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('should create transport with server public key', () => {
|
|
16
|
-
const transport = new Transport(
|
|
17
|
-
clientIdentity,
|
|
18
|
-
'localhost:8080',
|
|
19
|
-
serverIdentity.getPublicKey()
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
assert(transport instanceof Transport, 'Should create transport instance');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should encrypt and decrypt request', async () => {
|
|
26
|
-
const serverPublicKey = serverIdentity.getPublicKey();
|
|
27
|
-
const originalBody = new TextEncoder().encode('Hello, World!');
|
|
28
|
-
const request = new Request('http://localhost:8080/test', {
|
|
29
|
-
method: 'POST',
|
|
30
|
-
body: originalBody
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const encryptedRequest = await clientIdentity.encryptRequest(request, serverPublicKey);
|
|
34
|
-
|
|
35
|
-
// Check that headers are set
|
|
36
|
-
assert(encryptedRequest.headers.get(PROTOCOL.CLIENT_PUBLIC_KEY_HEADER), 'Client public key header should be set');
|
|
37
|
-
assert(encryptedRequest.headers.get(PROTOCOL.ENCAPSULATED_KEY_HEADER), 'Encapsulated key header should be set');
|
|
38
|
-
|
|
39
|
-
// Check that body is encrypted (different from original)
|
|
40
|
-
const encryptedBody = await encryptedRequest.arrayBuffer();
|
|
41
|
-
assert(encryptedBody.byteLength > 0, 'Encrypted body should not be empty');
|
|
42
|
-
assert(encryptedBody.byteLength !== originalBody.length, 'Encrypted body should have different length');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should handle request without body', async () => {
|
|
46
|
-
const serverPublicKey = serverIdentity.getPublicKey();
|
|
47
|
-
const request = new Request('http://localhost:8080/test', {
|
|
48
|
-
method: 'GET'
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
const encryptedRequest = await clientIdentity.encryptRequest(request, serverPublicKey);
|
|
52
|
-
|
|
53
|
-
// Check that only client public key header is set
|
|
54
|
-
assert(encryptedRequest.headers.get(PROTOCOL.CLIENT_PUBLIC_KEY_HEADER), 'Client public key header should be set');
|
|
55
|
-
assert(!encryptedRequest.headers.get(PROTOCOL.ENCAPSULATED_KEY_HEADER), 'Encapsulated key header should not be set for empty body');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should connect to actual server and POST to /secure endpoint', async (t) => {
|
|
59
|
-
const serverURL = 'http://localhost:8080';
|
|
60
|
-
|
|
61
|
-
try {
|
|
62
|
-
const keysResponse = await fetch(`${serverURL}${PROTOCOL.KEYS_PATH}`);
|
|
63
|
-
if (!keysResponse.ok) {
|
|
64
|
-
t.skip('Server not running at localhost:8080');
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
} catch (error) {
|
|
68
|
-
t.skip('Server not running at localhost:8080');
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Create transport that will connect to the real server
|
|
73
|
-
const transport = await createTransport(serverURL, clientIdentity);
|
|
74
|
-
|
|
75
|
-
const testName = 'Integration Test User';
|
|
76
|
-
|
|
77
|
-
const serverPubKeyHex = await transport.getServerPublicKeyHex();
|
|
78
|
-
assert.strictEqual(serverPubKeyHex.length, 64, 'Server public key should be 64 bytes');
|
|
79
|
-
|
|
80
|
-
// Make actual POST request to /secure endpoint
|
|
81
|
-
const response = await transport.post(`${serverURL}/secure`, testName, {
|
|
82
|
-
headers: { 'Content-Type': 'text/plain' }
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// Verify response
|
|
86
|
-
assert(response.ok, `Response should be ok, got status: ${response.status}`);
|
|
87
|
-
|
|
88
|
-
const responseText = await response.text();
|
|
89
|
-
assert.strictEqual(responseText, `Hello, ${testName}`, 'Server should respond with Hello, {name}');
|
|
90
|
-
|
|
91
|
-
console.log(`✓ Integration test passed: ${responseText}`);
|
|
92
|
-
});
|
|
93
|
-
});
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { describe, it } from 'node:test';
|
|
2
|
-
import assert from 'node:assert';
|
|
3
|
-
import { Identity } from '../identity.js';
|
|
4
|
-
|
|
5
|
-
describe('Identity', () => {
|
|
6
|
-
it('should generate a new identity', async () => {
|
|
7
|
-
const identity = await Identity.generate();
|
|
8
|
-
|
|
9
|
-
assert(identity.getPublicKey() instanceof CryptoKey, 'Public key should be a CryptoKey');
|
|
10
|
-
assert(identity.getPrivateKey() instanceof CryptoKey, 'Private key should be a CryptoKey');
|
|
11
|
-
const publicKeyHex = await identity.getPublicKeyHex();
|
|
12
|
-
assert(publicKeyHex.length > 0, 'Public key hex should not be empty');
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('should serialize and deserialize identity', async () => {
|
|
16
|
-
const original = await Identity.generate();
|
|
17
|
-
const json = await original.toJSON();
|
|
18
|
-
const restored = await Identity.fromJSON(json);
|
|
19
|
-
|
|
20
|
-
const originalHex = await original.getPublicKeyHex();
|
|
21
|
-
const restoredHex = await restored.getPublicKeyHex();
|
|
22
|
-
assert(originalHex === restoredHex, 'Public keys should match');
|
|
23
|
-
assert(original.getPrivateKey() instanceof CryptoKey, 'Private key should be a CryptoKey');
|
|
24
|
-
assert(restored.getPrivateKey() instanceof CryptoKey, 'Private key should be a CryptoKey');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should marshal configuration', async () => {
|
|
28
|
-
const identity = await Identity.generate();
|
|
29
|
-
const config = await identity.marshalConfig();
|
|
30
|
-
|
|
31
|
-
assert(config.length > 0, 'Config should not be empty');
|
|
32
|
-
assert(config[0] === 0, 'Key ID should be 0');
|
|
33
|
-
assert(config[1] === 0x00, 'KEM ID high byte should be 0x00');
|
|
34
|
-
assert(config[2] === 0x20, 'KEM ID low byte should be 0x20');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should unmarshal public configuration', async () => {
|
|
38
|
-
const identity = await Identity.generate();
|
|
39
|
-
const config = await identity.marshalConfig();
|
|
40
|
-
const restored = await Identity.unmarshalPublicConfig(config);
|
|
41
|
-
|
|
42
|
-
const originalHex = await identity.getPublicKeyHex();
|
|
43
|
-
const restoredHex = await restored.getPublicKeyHex();
|
|
44
|
-
assert(restoredHex === originalHex, 'Public keys should match');
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { describe, it } from 'node:test';
|
|
2
|
-
import assert from 'node:assert';
|
|
3
|
-
|
|
4
|
-
describe('Streaming', () => {
|
|
5
|
-
|
|
6
|
-
it('should handle streaming responses', async () => {
|
|
7
|
-
// Create a mock streaming response
|
|
8
|
-
const mockStreamData = 'Number: 1\nNumber: 2\nNumber: 3\n';
|
|
9
|
-
const mockResponse = new Response(mockStreamData, {
|
|
10
|
-
status: 200,
|
|
11
|
-
headers: {
|
|
12
|
-
'Content-Type': 'text/plain',
|
|
13
|
-
'Ehbp-Encapsulated-Key': 'abcd1234' // Mock encapsulated key
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
// Test that we can read from a stream
|
|
18
|
-
const reader = mockResponse.body?.getReader();
|
|
19
|
-
assert(reader, 'Response should have a readable stream');
|
|
20
|
-
|
|
21
|
-
const decoder = new TextDecoder();
|
|
22
|
-
let receivedData = '';
|
|
23
|
-
|
|
24
|
-
while (true) {
|
|
25
|
-
const { done, value } = await reader.read();
|
|
26
|
-
if (done) break;
|
|
27
|
-
|
|
28
|
-
const text = decoder.decode(value, { stream: true });
|
|
29
|
-
receivedData += text;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
assert(receivedData === mockStreamData, 'Should receive all stream data');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should handle empty streams', async () => {
|
|
36
|
-
const emptyResponse = new Response('', {
|
|
37
|
-
status: 200,
|
|
38
|
-
headers: {
|
|
39
|
-
'Ehbp-Encapsulated-Key': 'abcd1234'
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const reader = emptyResponse.body?.getReader();
|
|
44
|
-
assert(reader, 'Response should have a readable stream');
|
|
45
|
-
|
|
46
|
-
const { done } = await reader.read();
|
|
47
|
-
assert(done, 'Empty stream should be done immediately');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should handle chunked data correctly', async () => {
|
|
51
|
-
// Simulate chunked data
|
|
52
|
-
const chunks = ['Hello', ' ', 'World', '!'];
|
|
53
|
-
const stream = new ReadableStream({
|
|
54
|
-
start(controller) {
|
|
55
|
-
chunks.forEach(chunk => {
|
|
56
|
-
controller.enqueue(new TextEncoder().encode(chunk));
|
|
57
|
-
});
|
|
58
|
-
controller.close();
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const response = new Response(stream, {
|
|
63
|
-
status: 200,
|
|
64
|
-
headers: {
|
|
65
|
-
'Ehbp-Encapsulated-Key': 'abcd1234'
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const reader = response.body?.getReader();
|
|
70
|
-
assert(reader, 'Response should have a readable stream');
|
|
71
|
-
|
|
72
|
-
const decoder = new TextDecoder();
|
|
73
|
-
let receivedData = '';
|
|
74
|
-
|
|
75
|
-
while (true) {
|
|
76
|
-
const { done, value } = await reader.read();
|
|
77
|
-
if (done) break;
|
|
78
|
-
|
|
79
|
-
const text = decoder.decode(value, { stream: true });
|
|
80
|
-
receivedData += text;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
assert(receivedData === 'Hello World!', 'Should receive all chunks correctly');
|
|
84
|
-
});
|
|
85
|
-
});
|
package/test.html
DELETED
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>EHBP Streaming Test</title>
|
|
7
|
-
<style>
|
|
8
|
-
body {
|
|
9
|
-
font-family: Arial, sans-serif;
|
|
10
|
-
max-width: 800px;
|
|
11
|
-
margin: 0 auto;
|
|
12
|
-
padding: 20px;
|
|
13
|
-
background-color: #f5f5f5;
|
|
14
|
-
}
|
|
15
|
-
.container {
|
|
16
|
-
background: white;
|
|
17
|
-
padding: 20px;
|
|
18
|
-
border-radius: 8px;
|
|
19
|
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
20
|
-
}
|
|
21
|
-
h1 {
|
|
22
|
-
color: #333;
|
|
23
|
-
text-align: center;
|
|
24
|
-
}
|
|
25
|
-
.test-section {
|
|
26
|
-
margin: 20px 0;
|
|
27
|
-
padding: 15px;
|
|
28
|
-
border: 1px solid #ddd;
|
|
29
|
-
border-radius: 5px;
|
|
30
|
-
}
|
|
31
|
-
.test-section h3 {
|
|
32
|
-
margin-top: 0;
|
|
33
|
-
color: #555;
|
|
34
|
-
}
|
|
35
|
-
button {
|
|
36
|
-
background-color: #007bff;
|
|
37
|
-
color: white;
|
|
38
|
-
border: none;
|
|
39
|
-
padding: 10px 20px;
|
|
40
|
-
border-radius: 4px;
|
|
41
|
-
cursor: pointer;
|
|
42
|
-
margin: 5px;
|
|
43
|
-
}
|
|
44
|
-
button:hover {
|
|
45
|
-
background-color: #0056b3;
|
|
46
|
-
}
|
|
47
|
-
button:disabled {
|
|
48
|
-
background-color: #6c757d;
|
|
49
|
-
cursor: not-allowed;
|
|
50
|
-
}
|
|
51
|
-
.output {
|
|
52
|
-
background-color: #f8f9fa;
|
|
53
|
-
border: 1px solid #dee2e6;
|
|
54
|
-
border-radius: 4px;
|
|
55
|
-
padding: 10px;
|
|
56
|
-
margin: 10px 0;
|
|
57
|
-
height: 100px;
|
|
58
|
-
font-family: monospace;
|
|
59
|
-
white-space: pre-wrap;
|
|
60
|
-
overflow-y: auto;
|
|
61
|
-
}
|
|
62
|
-
.status {
|
|
63
|
-
padding: 10px;
|
|
64
|
-
margin: 10px 0;
|
|
65
|
-
border-radius: 4px;
|
|
66
|
-
}
|
|
67
|
-
.status.success {
|
|
68
|
-
background-color: #d4edda;
|
|
69
|
-
color: #155724;
|
|
70
|
-
border: 1px solid #c3e6cb;
|
|
71
|
-
}
|
|
72
|
-
.status.error {
|
|
73
|
-
background-color: #f8d7da;
|
|
74
|
-
color: #721c24;
|
|
75
|
-
border: 1px solid #f5c6cb;
|
|
76
|
-
}
|
|
77
|
-
.status.info {
|
|
78
|
-
background-color: #d1ecf1;
|
|
79
|
-
color: #0c5460;
|
|
80
|
-
border: 1px solid #bee5eb;
|
|
81
|
-
}
|
|
82
|
-
.clear-btn {
|
|
83
|
-
background-color: #6c757d;
|
|
84
|
-
}
|
|
85
|
-
.clear-btn:hover {
|
|
86
|
-
background-color: #545b62;
|
|
87
|
-
}
|
|
88
|
-
</style>
|
|
89
|
-
</head>
|
|
90
|
-
<body>
|
|
91
|
-
<div class="container">
|
|
92
|
-
<h1>EHBP Test</h1>
|
|
93
|
-
|
|
94
|
-
<div class="test-section">
|
|
95
|
-
<h3>Server Configuration</h3>
|
|
96
|
-
<label for="serverUrl">Server URL:</label>
|
|
97
|
-
<input type="text" id="serverUrl" value="http://localhost:8080" style="width: 300px; padding: 5px; margin: 5px;">
|
|
98
|
-
<div id="serverStatus" class="status info">Ready to test</div>
|
|
99
|
-
</div>
|
|
100
|
-
|
|
101
|
-
<div class="test-section">
|
|
102
|
-
<h3>Test 1: POST to /secure</h3>
|
|
103
|
-
<button id="test1Btn" onclick="runTest1()">Run POST Test</button>
|
|
104
|
-
<button class="clear-btn" onclick="clearOutput('test1Output')">Clear</button>
|
|
105
|
-
<div id="test1Output" class="output"></div>
|
|
106
|
-
</div>
|
|
107
|
-
|
|
108
|
-
<div class="test-section">
|
|
109
|
-
<h3>Test 2: Streaming</h3>
|
|
110
|
-
<button id="test2Btn" onclick="runTest2()">Run Streaming Test</button>
|
|
111
|
-
<button class="clear-btn" onclick="clearOutput('test2Output')">Clear</button>
|
|
112
|
-
<div id="test2Output" class="output"></div>
|
|
113
|
-
</div>
|
|
114
|
-
|
|
115
|
-
</div>
|
|
116
|
-
|
|
117
|
-
<script type="module">
|
|
118
|
-
import { Identity, createTransport } from './dist/browser.js';
|
|
119
|
-
|
|
120
|
-
let transport = null;
|
|
121
|
-
let clientIdentity = null;
|
|
122
|
-
|
|
123
|
-
async function initializeClient() {
|
|
124
|
-
try {
|
|
125
|
-
updateStatus('serverStatus', 'info', 'Initializing client...');
|
|
126
|
-
clientIdentity = await Identity.generate();
|
|
127
|
-
const serverUrl = document.getElementById('serverUrl').value;
|
|
128
|
-
transport = await createTransport(serverUrl, clientIdentity);
|
|
129
|
-
updateStatus('serverStatus', 'success', `Connected to ${serverUrl}`);
|
|
130
|
-
return true;
|
|
131
|
-
} catch (error) {
|
|
132
|
-
updateStatus('serverStatus', 'error', `Failed to connect: ${error.message}`);
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function updateStatus(elementId, type, message) {
|
|
138
|
-
const element = document.getElementById(elementId);
|
|
139
|
-
element.className = `status ${type}`;
|
|
140
|
-
element.textContent = message;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function appendOutput(elementId, text) {
|
|
144
|
-
const element = document.getElementById(elementId);
|
|
145
|
-
element.textContent += text;
|
|
146
|
-
element.scrollTop = element.scrollHeight;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function clearOutput(elementId) {
|
|
150
|
-
document.getElementById(elementId).textContent = '';
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function clearAllOutputs() {
|
|
154
|
-
clearOutput('test1Output');
|
|
155
|
-
clearOutput('test2Output');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async function runTest1() {
|
|
159
|
-
const btn = document.getElementById('test1Btn');
|
|
160
|
-
const output = document.getElementById('test1Output');
|
|
161
|
-
|
|
162
|
-
btn.disabled = true;
|
|
163
|
-
clearOutput('test1Output');
|
|
164
|
-
|
|
165
|
-
let endpoint = '/secure';
|
|
166
|
-
const exampleName = 'John Doe';
|
|
167
|
-
|
|
168
|
-
appendOutput('test1Output', 'Starting POST ' + endpoint + ' test...\n');
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
if (!transport) {
|
|
172
|
-
const initialized = await initializeClient();
|
|
173
|
-
if (!initialized) return;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const serverUrl = document.getElementById('serverUrl').value;
|
|
177
|
-
|
|
178
|
-
try {
|
|
179
|
-
const testBody = "John Doe";
|
|
180
|
-
appendOutput('test1Output', `Sending POST request with body: ${testBody}\n`);
|
|
181
|
-
|
|
182
|
-
const response1 = await transport.post(`${serverUrl}${endpoint}`, testBody, {
|
|
183
|
-
headers: {
|
|
184
|
-
'Content-Type': 'text/plain'
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
appendOutput('test1Output', `Response status: ${response1.status}\n`);
|
|
189
|
-
if (response1.ok) {
|
|
190
|
-
const responseText = await response1.text();
|
|
191
|
-
appendOutput('test1Output', `Response body: ${responseText}\n`);
|
|
192
|
-
if (responseText !== "Hello, "+exampleName) {
|
|
193
|
-
appendOutput('test1Output', '✗ Basic POST test failed with response: '+responseText+'\n\n');
|
|
194
|
-
return;
|
|
195
|
-
} else {
|
|
196
|
-
appendOutput('test1Output', '✓ Basic POST test passed\n\n');
|
|
197
|
-
}
|
|
198
|
-
} else {
|
|
199
|
-
appendOutput('test1Output', `✗ Basic POST test failed with status: ${response1.status}\n\n`);
|
|
200
|
-
}
|
|
201
|
-
} catch (error) {
|
|
202
|
-
appendOutput('test1Output', `✗ Basic POST test failed with error: ${error.message}\n\n`);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
appendOutput('test1Output', 'POST ' + endpoint + ' test completed!\n');
|
|
206
|
-
} catch (error) {
|
|
207
|
-
appendOutput('test1Output', `Error: ${error.message}\n`);
|
|
208
|
-
} finally {
|
|
209
|
-
btn.disabled = false;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
async function runTest2() {
|
|
214
|
-
const btn = document.getElementById('test2Btn');
|
|
215
|
-
const output = document.getElementById('test2Output');
|
|
216
|
-
|
|
217
|
-
btn.disabled = true;
|
|
218
|
-
clearOutput('test2Output');
|
|
219
|
-
appendOutput('test2Output', 'Starting basic streaming test...\n');
|
|
220
|
-
|
|
221
|
-
try {
|
|
222
|
-
if (!transport) {
|
|
223
|
-
const initialized = await initializeClient();
|
|
224
|
-
if (!initialized) return;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const serverUrl = document.getElementById('serverUrl').value;
|
|
228
|
-
const response = await transport.get(`${serverUrl}/stream`);
|
|
229
|
-
|
|
230
|
-
appendOutput('test2Output', `Stream response status: ${response.status}\n`);
|
|
231
|
-
|
|
232
|
-
if (response.ok) {
|
|
233
|
-
appendOutput('test2Output', 'Reading stream data...\n');
|
|
234
|
-
const reader = response.body?.getReader();
|
|
235
|
-
|
|
236
|
-
if (reader) {
|
|
237
|
-
const decoder = new TextDecoder();
|
|
238
|
-
|
|
239
|
-
while (true) {
|
|
240
|
-
const { done, value } = await reader.read();
|
|
241
|
-
if (done) break;
|
|
242
|
-
|
|
243
|
-
const text = decoder.decode(value, { stream: true });
|
|
244
|
-
appendOutput('test2Output', text);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
appendOutput('test2Output', '\nStream completed!\n');
|
|
248
|
-
} else {
|
|
249
|
-
appendOutput('test2Output', 'No readable stream available\n');
|
|
250
|
-
}
|
|
251
|
-
} else {
|
|
252
|
-
appendOutput('test2Output', `Stream request failed with status: ${response.status}\n`);
|
|
253
|
-
}
|
|
254
|
-
} catch (error) {
|
|
255
|
-
appendOutput('test2Output', `Error: ${error.message}\n`);
|
|
256
|
-
} finally {
|
|
257
|
-
btn.disabled = false;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
window.runTest1 = runTest1;
|
|
262
|
-
window.runTest2 = runTest2;
|
|
263
|
-
window.clearOutput = clearOutput;
|
|
264
|
-
window.clearAllOutputs = clearAllOutputs;
|
|
265
|
-
|
|
266
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
267
|
-
updateStatus('serverStatus', 'info', 'Ready to test - click a test button to start');
|
|
268
|
-
});
|
|
269
|
-
</script>
|
|
270
|
-
</body>
|
|
271
|
-
</html>
|
package/tsconfig.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "node",
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"rootDir": "./src",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"forceConsistentCasingInFileNames": true,
|
|
12
|
-
"declaration": true,
|
|
13
|
-
"declarationMap": true,
|
|
14
|
-
"sourceMap": true,
|
|
15
|
-
"resolveJsonModule": true
|
|
16
|
-
},
|
|
17
|
-
"include": ["src/**/*"],
|
|
18
|
-
"exclude": ["node_modules", "dist"]
|
|
19
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|