1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
// This is not a real HTTP server. We read blindly one time into 'requestBuf',
// then write this fixed 'responseBuf'. The point of this benchmark is to
// exercise the event loop in a simple yet semi-realistic way.
const shared32 = new Int32Array(libdeno.shared);
const INDEX_NUM_RECORDS = 0;
const INDEX_RECORDS = 1;
const RECORD_OFFSET_PROMISE_ID = 0;
const RECORD_OFFSET_OP = 1;
const RECORD_OFFSET_ARG = 2;
const RECORD_OFFSET_RESULT = 3;
const RECORD_SIZE = 4;
const OP_LISTEN = 1;
const OP_ACCEPT = 2;
const OP_READ = 3;
const OP_WRITE = 4;
const OP_CLOSE = 5;
const NUM_RECORDS = (shared32.length - INDEX_RECORDS) / RECORD_SIZE;
if (NUM_RECORDS != 100) {
throw Error("expected 100 entries");
}
const requestBuf = new Uint8Array(64 * 1024);
const responseBuf = new Uint8Array(
"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n"
.split("")
.map(c => c.charCodeAt(0))
);
const promiseMap = new Map();
let nextPromiseId = 1;
function createResolvable() {
let methods;
const promise = new Promise((resolve, reject) => {
methods = { resolve, reject };
});
return Object.assign(promise, methods);
}
/** Returns Promise<number> */
function sendAsync(op, arg, zeroCopyData) {
const id = nextPromiseId++;
const p = createResolvable();
shared32[INDEX_NUM_RECORDS] = 1;
setRecord(0, RECORD_OFFSET_PROMISE_ID, id);
setRecord(0, RECORD_OFFSET_OP, op);
setRecord(0, RECORD_OFFSET_ARG, arg);
setRecord(0, RECORD_OFFSET_RESULT, -1);
promiseMap.set(id, p);
libdeno.send(null, zeroCopyData);
return p;
}
/** Returns u32 number */
function sendSync(op, arg) {
shared32[INDEX_NUM_RECORDS] = 1;
setRecord(0, RECORD_OFFSET_PROMISE_ID, 0);
setRecord(0, RECORD_OFFSET_OP, op);
setRecord(0, RECORD_OFFSET_ARG, arg);
setRecord(0, RECORD_OFFSET_RESULT, -1);
libdeno.send();
return getRecord(0, RECORD_OFFSET_RESULT);
}
function setRecord(i, off, value) {
if (i >= NUM_RECORDS) {
throw Error("out of range");
}
shared32[INDEX_RECORDS + RECORD_SIZE * i + off] = value;
}
function getRecord(i, off) {
if (i >= NUM_RECORDS) {
throw Error("out of range");
}
return shared32[INDEX_RECORDS + RECORD_SIZE * i + off];
}
function handleAsyncMsgFromRust() {
for (let i = 0; i < shared32[INDEX_NUM_RECORDS]; i++) {
let id = getRecord(i, RECORD_OFFSET_PROMISE_ID);
const p = promiseMap.get(id);
promiseMap.delete(id);
p.resolve(getRecord(i, RECORD_OFFSET_RESULT));
}
}
/** Listens on 0.0.0.0:4500, returns rid. */
function listen() {
return sendSync(OP_LISTEN, -1);
}
/** Accepts a connection, returns rid. */
async function accept(rid) {
return await sendAsync(OP_ACCEPT, rid);
}
/**
* Reads a packet from the rid, presumably an http request. data is ignored.
* Returns bytes read.
*/
async function read(rid, data) {
return await sendAsync(OP_READ, rid, data);
}
/** Writes a fixed HTTP response to the socket rid. Returns bytes written. */
async function write(rid, data) {
return await sendAsync(OP_WRITE, rid, data);
}
function close(rid) {
return sendSync(OP_CLOSE, rid);
}
async function serve(rid) {
while (true) {
const nread = await read(rid, requestBuf);
if (nread <= 0) {
break;
}
const nwritten = await write(rid, responseBuf);
if (nwritten < 0) {
break;
}
}
close(rid);
}
async function main() {
libdeno.recv(handleAsyncMsgFromRust);
libdeno.print("http_bench.js start");
const listener_rid = listen();
libdeno.print(`listening http://127.0.0.1:4544/ rid = ${listener_rid}`);
while (true) {
const rid = await accept(listener_rid);
// libdeno.print(`accepted ${rid}`);
if (rid < 0) {
libdeno.print(`accept error ${rid}`);
return;
}
serve(rid);
}
}
main();
|