Skip to content

Commit bc0d836

Browse files
committed
performance: netty optimize response headers
1 parent 5ea74d6 commit bc0d836

File tree

10 files changed

+91
-66
lines changed

10 files changed

+91
-66
lines changed

jooby/src/main/java/io/jooby/internal/output/ContextOutputFactory.java

Lines changed: 0 additions & 40 deletions
This file was deleted.

jooby/src/main/java/io/jooby/output/ByteBufferedOutputFactory.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
import edu.umd.cs.findbugs.annotations.NonNull;
1212
import io.jooby.internal.output.CompositeOutput;
13-
import io.jooby.internal.output.ContextOutputFactory;
1413
import io.jooby.internal.output.StaticOutput;
14+
import io.jooby.internal.output.WrappedOutput;
1515

1616
/**
1717
* An output factory backed by {@link ByteBuffer}.
@@ -20,6 +20,33 @@
2020
* @since 4.0.0
2121
*/
2222
public class ByteBufferedOutputFactory implements OutputFactory {
23+
24+
private static class ContextOutputFactory extends ByteBufferedOutputFactory {
25+
public ContextOutputFactory(OutputOptions options) {
26+
super(options);
27+
}
28+
29+
@Override
30+
public Output wrap(@NonNull ByteBuffer buffer) {
31+
return new WrappedOutput(buffer);
32+
}
33+
34+
@Override
35+
public Output wrap(@NonNull String value, @NonNull Charset charset) {
36+
return new WrappedOutput(charset.encode(value));
37+
}
38+
39+
@Override
40+
public Output wrap(@NonNull byte[] bytes) {
41+
return new WrappedOutput(ByteBuffer.wrap(bytes));
42+
}
43+
44+
@Override
45+
public Output wrap(@NonNull byte[] bytes, int offset, int length) {
46+
return new WrappedOutput(ByteBuffer.wrap(bytes, offset, length));
47+
}
48+
}
49+
2350
private OutputOptions options;
2451

2552
public ByteBufferedOutputFactory(OutputOptions options) {

jooby/src/main/java/io/jooby/output/Output.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* @see BufferedOutput
2121
*/
2222
public interface Output {
23+
2324
/**
2425
* A read-only view as {@link ByteBuffer}.
2526
*

modules/jooby-netty/src/main/java/io/jooby/internal/netty/HeadersMultiMap.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,9 @@ static void encoderHeader(CharSequence name, CharSequence value, ByteBuf buf) {
490490
}
491491

492492
private static void writeAscii(ByteBuf buf, int offset, CharSequence value) {
493-
if (value instanceof AsciiString ascii) {
493+
if (value instanceof NettyString netty) {
494+
buf.setBytes(offset, netty.bytes, 0, netty.bytes.length);
495+
} else if (value instanceof AsciiString ascii) {
494496
buf.setBytes(offset, ascii.array(), ascii.arrayOffset(), value.length());
495497
} else {
496498
buf.setCharSequence(offset, value, CharsetUtil.US_ASCII);

modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyDateService.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,22 @@
1111
import java.util.concurrent.ScheduledExecutorService;
1212
import java.util.concurrent.TimeUnit;
1313

14-
import io.netty.util.AsciiString;
15-
1614
public class NettyDateService implements Runnable {
1715
private static final int DATE_INTERVAL = 1000;
18-
private AsciiString date;
16+
private CharSequence date;
1917

2018
public NettyDateService(ScheduledExecutorService scheduler) {
2119
scheduler.scheduleAtFixedRate(this, 0, DATE_INTERVAL, TimeUnit.MILLISECONDS);
2220
}
2321

24-
public AsciiString date() {
22+
public CharSequence date() {
2523
return this.date;
2624
}
2725

2826
@Override
2927
public void run() {
3028
this.date =
31-
new AsciiString(
29+
new NettyString(
3230
DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC)));
3331
}
3432
}

modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyHandler.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
import io.netty.handler.codec.http.multipart.*;
2323
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
2424
import io.netty.handler.timeout.IdleStateEvent;
25-
import io.netty.util.AsciiString;
2625

2726
public class NettyHandler extends ChannelInboundHandlerAdapter {
2827
private final Logger log = LoggerFactory.getLogger(NettyServer.class);
29-
private static final AsciiString server = AsciiString.cached("N");
28+
private static final CharSequence server = NettyString.of("N");
29+
public static final CharSequence CONTENT_TYPE = NettyString.of("content-type");
30+
public static final CharSequence TEXT_PLAIN = NettyString.of("text/plain");
31+
public static final CharSequence DATE = NettyString.of("date");
32+
public static final CharSequence SERVER = NettyString.of("server");
3033
private final NettyDateService serverDate;
3134
private final List<Jooby> applications;
3235
private Router router;
@@ -66,10 +69,10 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) {
6669
context = new NettyContext(ctx, req, app, path, bufferSize, http2);
6770

6871
if (defaultHeaders) {
69-
context.setHeaders.set(HttpHeaderNames.DATE, serverDate.date());
70-
context.setHeaders.set(HttpHeaderNames.SERVER, server);
72+
context.setHeaders.set(DATE, serverDate.date());
73+
context.setHeaders.set(SERVER, server);
7174
}
72-
context.setHeaders.set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN);
75+
context.setHeaders.set(CONTENT_TYPE, TEXT_PLAIN);
7376

7477
if (req.method() == HttpMethod.GET) {
7578
router.match(context).execute(context);

modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyOutputFactory.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,6 @@ public ByteBufAllocator getAllocator() {
8989
return new NettyOutputStatic(buffer.remaining(), () -> Unpooled.wrappedBuffer(buffer));
9090
}
9191

92-
@Override
93-
@NonNull public Output wrap(@NonNull String value, @NonNull Charset charset) {
94-
return new NettyByteBufOutput(Unpooled.wrappedBuffer(value.getBytes(charset)));
95-
}
96-
9792
@Override
9893
@NonNull public Output wrap(@NonNull byte[] bytes) {
9994
return new NettyOutputStatic(bytes.length, () -> Unpooled.wrappedBuffer(bytes));

modules/jooby-netty/src/main/java/io/jooby/internal/netty/NettyOutputStatic.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,16 @@
1010
import edu.umd.cs.findbugs.annotations.NonNull;
1111
import io.jooby.Context;
1212
import io.netty.buffer.ByteBuf;
13-
import io.netty.util.AsciiString;
1413

1514
public class NettyOutputStatic implements NettyByteBufRef {
1615
private final Supplier<ByteBuf> provider;
1716
private final int size;
18-
private final AsciiString contentLength;
17+
private final NettyString contentLength;
1918

2019
protected NettyOutputStatic(int length, Supplier<ByteBuf> provider) {
2120
this.provider = provider;
2221
this.size = length;
23-
this.contentLength = AsciiString.cached(Integer.toString(length));
22+
this.contentLength = new NettyString(Integer.toString(length));
2423
}
2524

2625
@Override
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package io.jooby.internal.netty;
7+
8+
import java.nio.charset.StandardCharsets;
9+
10+
import edu.umd.cs.findbugs.annotations.NonNull;
11+
12+
public class NettyString implements CharSequence {
13+
14+
final byte[] bytes;
15+
private final String value;
16+
17+
public NettyString(String value) {
18+
this.value = value;
19+
this.bytes = value.getBytes(StandardCharsets.US_ASCII);
20+
}
21+
22+
public static NettyString of(String value) {
23+
return new NettyString(value);
24+
}
25+
26+
@Override
27+
public int length() {
28+
return value.length();
29+
}
30+
31+
@Override
32+
public char charAt(int index) {
33+
return value.charAt(index);
34+
}
35+
36+
@Override
37+
@NonNull public CharSequence subSequence(int start, int end) {
38+
return value.subSequence(start, end);
39+
}
40+
41+
@Override
42+
@NonNull public String toString() {
43+
return value;
44+
}
45+
}

tests/src/test/java/examples/Performance.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import static io.jooby.ExecutionMode.EVENT_LOOP;
99
import static io.jooby.MediaType.JSON;
1010

11-
import java.nio.charset.StandardCharsets;
12-
1311
import io.jooby.Jooby;
1412
import io.jooby.StatusCode;
1513
import io.jooby.netty.NettyServer;
@@ -18,15 +16,12 @@ public class Performance extends Jooby {
1816

1917
private static final String MESSAGE = "Hello, World!";
2018

21-
private static final byte[] MESSAGE_BYTES = MESSAGE.getBytes(StandardCharsets.UTF_8);
22-
2319
{
2420
var outputFactory = getOutputFactory();
25-
var message = outputFactory.wrap(MESSAGE_BYTES);
21+
var message = outputFactory.wrap(MESSAGE);
2622
get(
2723
"/plaintext",
2824
ctx -> {
29-
// return ctx.send(outputFactory.wrap(MESSAGE_BYTES));
3025
return ctx.send(message);
3126
});
3227

0 commit comments

Comments
 (0)