Skip to content

Commit 7e6c49c

Browse files
[D, Juptune] Fix: Don't recreate reader & writer each loop - supports pipelining (#10036)
* fix(d,juptune): Don't recreate reader & writer each loop - supports pipelining * feat(d,juptune): add JSON benchmark
1 parent 3f11c70 commit 7e6c49c

File tree

5 files changed

+130
-8
lines changed

5 files changed

+130
-8
lines changed

frameworks/D/juptune/benchmark_config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
{
55
"default": {
66
"plaintext_url": "/plaintext",
7+
"json_url": "/json",
78
"port": 8080,
89
"approach": "Realistic",
910
"classification": "Platform",

frameworks/D/juptune/juptune.dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
FROM debian:bookworm-slim
22

33
ARG LDC_VERSION=1.41.0
4-
ARG JUPTUNE_REF=3a27a36ce2f5f2ff7df7151311e18d9c3660ea8c
4+
ARG JUPTUNE_REF=52294fa45912dacac24efc80b4a2fad8db958841
55
ARG TFB_TEST_NAME
66

77
ENV TEST_NAME=${TFB_TEST_NAME}

frameworks/D/juptune/src/main.d

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import juptune.core.util,
99
juptune.http;
1010

1111
import tests.common : log;
12-
import tests.plaintext;
12+
import tests.plaintext, tests.json;
1313

1414
/++++ Constant config ++++/
1515

@@ -73,6 +73,7 @@ void router() nothrow
7373
{
7474
FAILSAFE,
7575
plaintext,
76+
json,
7677
}
7778

7879
enum Method
@@ -84,6 +85,7 @@ void router() nothrow
8485
union RouteInput
8586
{
8687
PlainTextHeaderInput plaintext;
88+
JsonHeaderInput json;
8789
}
8890

8991
while(!juptuneEventLoopIsThreadCanceled())
@@ -103,17 +105,18 @@ void router() nothrow
103105
auto _ = client.close();
104106

105107
Http1MessageSummary readSummary, writeSummary;
108+
109+
// Read & Write primitives
110+
ubyte[HTTP_READ_BUFFER_BYTES] readBuffer;
111+
ubyte[HTTP_WRITE_BUFFER_BYTES] writeBuffer;
112+
auto reader = Http1Reader(client, readBuffer, HTTP_CONFIG);
113+
auto writer = Http1Writer(client, writeBuffer, HTTP_CONFIG);
114+
106115
do
107116
{
108117
if(!client.isOpen)
109118
return;
110119

111-
// Read & Write primitives
112-
ubyte[HTTP_READ_BUFFER_BYTES] readBuffer;
113-
ubyte[HTTP_WRITE_BUFFER_BYTES] writeBuffer;
114-
auto reader = Http1Reader(client, readBuffer, HTTP_CONFIG);
115-
auto writer = Http1Writer(client, writeBuffer, HTTP_CONFIG);
116-
117120
// Routing state
118121
Route route;
119122
Method method;
@@ -158,6 +161,10 @@ void router() nothrow
158161
route = Route.plaintext;
159162
break;
160163

164+
case "/json":
165+
route = Route.json;
166+
break;
167+
161168
default:
162169
setError(404, "Not found");
163170
break;
@@ -195,6 +202,9 @@ void router() nothrow
195202

196203
case plaintext:
197204
break;
205+
206+
case json:
207+
break;
198208
}
199209
});
200210
}
@@ -218,6 +228,9 @@ void router() nothrow
218228

219229
case plaintext:
220230
break;
231+
232+
case json:
233+
break;
221234
}
222235
});
223236
} while(chunk.hasDataLeft);
@@ -255,6 +268,10 @@ void router() nothrow
255268
case plaintext:
256269
handlePlainText(input.plaintext, writer, writeSummary);
257270
break;
271+
272+
case json:
273+
handleJson(input.json, writer, writeSummary);
274+
break;
258275
}
259276
} while(!readSummary.connectionClosed && !writeSummary.connectionClosed);
260277
}, client, &asyncMoveSetter!TcpSocket);

frameworks/D/juptune/src/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ project('juptune-tfb', 'd')
77
srcs = files(
88
'./main.d',
99
'./tests/common.d',
10+
'./tests/json.d',
1011
'./tests/plaintext.d',
1112
)
1213

frameworks/D/juptune/src/tests/json.d

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
module tests.json;
2+
3+
import juptune.core.ds : ArrayNonShrink;
4+
import juptune.core.util : Result;
5+
import juptune.http : Http1Writer, Http1Version, Http1MessageSummary;
6+
7+
import tests.common : putServerAndDate, log;
8+
9+
struct JsonHeaderInput
10+
{
11+
// No header input, it's here just so the overarching logic exists.
12+
}
13+
14+
private struct Message
15+
{
16+
string message;
17+
}
18+
19+
void handleJson(
20+
scope ref JsonHeaderInput input,
21+
scope ref Http1Writer writer,
22+
scope ref Http1MessageSummary summary,
23+
) nothrow
24+
{
25+
import juptune.core.util : then;
26+
import juptune.core.util.conv : IntToCharBuffer, toBase10;
27+
28+
ArrayNonShrink!char buffer;
29+
buffer.reserve(256);
30+
31+
auto result = serialise(buffer, Message("Hello, World!"));
32+
if(result.isError)
33+
{
34+
result = writer.putResponseLine(Http1Version.http11, 500, "Internal Error").then!(
35+
() => writer.putServerAndDate(),
36+
() => writer.finishHeaders(),
37+
() => writer.finishBody(),
38+
() => writer.finishTrailers(),
39+
() => writer.finishMessage(summary)
40+
);
41+
if(result.isError)
42+
{
43+
log("writing error response [json] failed: ", result);
44+
return;
45+
}
46+
47+
log("serialising value failed: ", result);
48+
return;
49+
}
50+
51+
IntToCharBuffer contentLengthBuffer;
52+
const contentLength = toBase10(buffer.length, contentLengthBuffer);
53+
54+
result = writer.putResponseLine(Http1Version.http11, 200, "OK").then!(
55+
() => writer.putServerAndDate(),
56+
() => writer.putHeader("Content-Length", contentLength),
57+
() => writer.putHeader("Content-Type", "application/json"),
58+
() => writer.finishHeaders(),
59+
() => writer.putBody(buffer.slice),
60+
() => writer.finishBody(),
61+
() => writer.finishTrailers(),
62+
() => writer.finishMessage(summary)
63+
);
64+
if(result.isError)
65+
{
66+
log("writing response [json] failed: ", result);
67+
return;
68+
}
69+
}
70+
71+
// There's currently no built-in serialiser, however because of D's incredibly powerful metaprogramming
72+
// this watered-down serialiser would likely generate almost the exact same code as a full-blown serialiser
73+
// in this simple case.
74+
private Result serialise(T)(scope ref ArrayNonShrink!char buffer, T value)
75+
if(is(T == struct))
76+
{
77+
import juptune.data.json : JsonBuilder;
78+
scope append = (scope const(char)[] text) {
79+
buffer.put(text);
80+
return Result.noError;
81+
};
82+
83+
ubyte[8] depthBuffer;
84+
auto builder = JsonBuilder!(typeof(append))(append, depthBuffer[]);
85+
86+
auto result = builder.startObject();
87+
if(result.isError)
88+
return result;
89+
90+
static foreach(fieldSymbol; value.tupleof)
91+
{{
92+
immutable FieldName = __traits(identifier, fieldSymbol);
93+
94+
result = builder.putObjectValue(FieldName, mixin("value."~FieldName));
95+
if(result.isError)
96+
return result;
97+
}}
98+
99+
result = builder.endObject();
100+
if(result.isError)
101+
return result;
102+
return builder.finish();
103+
}

0 commit comments

Comments
 (0)