|
1 | 1 | package hello; |
2 | 2 |
|
3 | | -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH; |
4 | | -import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; |
5 | | -import static io.netty.handler.codec.http.HttpHeaderNames.DATE; |
6 | | -import static io.netty.handler.codec.http.HttpHeaderNames.SERVER; |
7 | | -import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON; |
8 | | -import static io.netty.handler.codec.http.HttpHeaderValues.TEXT_PLAIN; |
| 3 | +import static hello.HttpResponses.makeJsonResponse; |
| 4 | +import static hello.HttpResponses.makePlaintextResponse; |
| 5 | +import static hello.JsonUtils.acquireJsonStreamFromEventLoop; |
| 6 | +import static hello.JsonUtils.releaseJsonStreamFromEventLoop; |
9 | 7 | import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; |
10 | | -import static io.netty.handler.codec.http.HttpResponseStatus.OK; |
11 | 8 | import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; |
12 | 9 |
|
13 | | -import java.io.IOException; |
14 | 10 | import java.text.DateFormat; |
15 | 11 | import java.text.SimpleDateFormat; |
16 | | -import java.util.Arrays; |
17 | 12 | import java.util.Date; |
18 | 13 | import java.util.concurrent.ScheduledExecutorService; |
19 | 14 | import java.util.concurrent.TimeUnit; |
20 | 15 |
|
21 | 16 | import com.jsoniter.output.JsonStream; |
22 | | -import com.jsoniter.output.JsonStreamPool; |
23 | | -import com.jsoniter.spi.JsonException; |
24 | 17 |
|
25 | | -import io.netty.buffer.ByteBuf; |
26 | 18 | import io.netty.buffer.Unpooled; |
27 | 19 | import io.netty.channel.ChannelFutureListener; |
28 | 20 | import io.netty.channel.ChannelHandlerContext; |
|
33 | 25 | import io.netty.handler.codec.http.HttpRequest; |
34 | 26 | import io.netty.handler.codec.http.LastHttpContent; |
35 | 27 | import io.netty.util.AsciiString; |
36 | | -import io.netty.util.CharsetUtil; |
37 | 28 | import io.netty.util.ReferenceCountUtil; |
38 | 29 | import io.netty.util.concurrent.FastThreadLocal; |
39 | 30 |
|
40 | 31 | public class HelloServerHandler extends ChannelInboundHandlerAdapter { |
41 | 32 |
|
42 | | - private static final FastThreadLocal<DateFormat> FORMAT = new FastThreadLocal<DateFormat>() { |
43 | | - @Override |
44 | | - protected DateFormat initialValue() { |
45 | | - return new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z"); |
46 | | - } |
47 | | - }; |
48 | | - |
49 | | - private static Message newMsg() { |
50 | | - return new Message("Hello, World!"); |
51 | | - } |
52 | | - |
53 | | - private static byte[] serializeMsg(Message obj) { |
54 | | - JsonStream stream = JsonStreamPool.borrowJsonStream(); |
55 | | - try { |
56 | | - stream.reset(null); |
57 | | - stream.writeVal(Message.class, obj); |
58 | | - return Arrays.copyOfRange(stream.buffer().data(), 0, stream.buffer().tail()); |
59 | | - } catch (IOException e) { |
60 | | - throw new JsonException(e); |
61 | | - } finally { |
62 | | - JsonStreamPool.returnJsonStream(stream); |
63 | | - } |
64 | | - } |
65 | | - |
66 | | - private static int jsonLen() { |
67 | | - return serializeMsg(newMsg()).length; |
68 | | - } |
| 33 | + private static final FastThreadLocal<DateFormat> FORMAT = new FastThreadLocal<>() { |
| 34 | + @Override |
| 35 | + protected DateFormat initialValue() { |
| 36 | + return new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z"); |
| 37 | + } |
| 38 | + }; |
69 | 39 |
|
70 | | - private static final byte[] STATIC_PLAINTEXT = "Hello, World!".getBytes(CharsetUtil.UTF_8); |
71 | | - private static final int STATIC_PLAINTEXT_LEN = STATIC_PLAINTEXT.length; |
| 40 | + protected volatile AsciiString date = new AsciiString(FORMAT.get().format(new Date())); |
72 | 41 |
|
73 | | - private static final CharSequence PLAINTEXT_CLHEADER_VALUE = AsciiString.cached(String.valueOf(STATIC_PLAINTEXT_LEN)); |
74 | | - private static final int JSON_LEN = jsonLen(); |
75 | | - private static final CharSequence JSON_CLHEADER_VALUE = AsciiString.cached(String.valueOf(JSON_LEN)); |
76 | | - private static final CharSequence SERVER_NAME = AsciiString.cached("Netty"); |
77 | | - |
78 | | - private volatile CharSequence date = new AsciiString(FORMAT.get().format(new Date())); |
79 | | - |
80 | | - HelloServerHandler(ScheduledExecutorService service) { |
| 42 | + public HelloServerHandler(ScheduledExecutorService service) { |
81 | 43 | service.scheduleWithFixedDelay(new Runnable() { |
82 | 44 | private final DateFormat format = FORMAT.get(); |
83 | 45 |
|
@@ -118,42 +80,39 @@ private void process(ChannelHandlerContext ctx, HttpRequest request) throws Exce |
118 | 80 | String uri = request.uri(); |
119 | 81 | switch (uri) { |
120 | 82 | case "/plaintext": |
121 | | - writePlainResponse(ctx, Unpooled.wrappedBuffer(STATIC_PLAINTEXT)); |
| 83 | + writePlainResponse(ctx, date); |
122 | 84 | return; |
123 | 85 | case "/json": |
124 | | - byte[] json = serializeMsg(newMsg()); |
125 | | - writeJsonResponse(ctx, Unpooled.wrappedBuffer(json)); |
| 86 | + // even for the virtual thread case we expect virtual threads to be executed inlined! |
| 87 | + var stream = acquireJsonStreamFromEventLoop(); |
| 88 | + try { |
| 89 | + writeJsonResponse(ctx, stream, date); |
| 90 | + } finally { |
| 91 | + releaseJsonStreamFromEventLoop(stream); |
| 92 | + } |
126 | 93 | return; |
127 | 94 | } |
| 95 | + // we drain in-flight responses before closing the connection |
| 96 | + channelReadComplete(ctx); |
128 | 97 | FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND, Unpooled.EMPTY_BUFFER, false); |
129 | | - ctx.write(response).addListener(ChannelFutureListener.CLOSE); |
130 | | - } |
131 | | - |
132 | | - private void writePlainResponse(ChannelHandlerContext ctx, ByteBuf buf) { |
133 | | - ctx.write(makeResponse(buf, TEXT_PLAIN, PLAINTEXT_CLHEADER_VALUE), ctx.voidPromise()); |
| 98 | + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); |
134 | 99 | } |
135 | 100 |
|
136 | | - private void writeJsonResponse(ChannelHandlerContext ctx, ByteBuf buf) { |
137 | | - ctx.write(makeResponse(buf, APPLICATION_JSON, JSON_CLHEADER_VALUE), ctx.voidPromise()); |
| 101 | + protected void writePlainResponse(ChannelHandlerContext ctx, AsciiString date) { |
| 102 | + ctx.write(makePlaintextResponse(date), ctx.voidPromise()); |
138 | 103 | } |
139 | 104 |
|
140 | | - private FullHttpResponse makeResponse(ByteBuf buf, CharSequence contentType, CharSequence contentLength) { |
141 | | - final FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, buf, false); |
142 | | - response.headers() |
143 | | - .set(CONTENT_TYPE, contentType) |
144 | | - .set(SERVER, SERVER_NAME) |
145 | | - .set(DATE, date) |
146 | | - .set(CONTENT_LENGTH, contentLength); |
147 | | - return response; |
| 105 | + protected void writeJsonResponse(ChannelHandlerContext ctx, JsonStream stream, AsciiString date) { |
| 106 | + ctx.write(makeJsonResponse(stream, date), ctx.voidPromise()); |
148 | 107 | } |
149 | 108 |
|
150 | 109 | @Override |
151 | | - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| 110 | + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { |
152 | 111 | ctx.close(); |
153 | 112 | } |
154 | 113 |
|
155 | 114 | @Override |
156 | | - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { |
| 115 | + public void channelReadComplete(ChannelHandlerContext ctx) { |
157 | 116 | ctx.flush(); |
158 | 117 | } |
159 | 118 | } |
0 commit comments