View Javadoc
1   /*
2    * Copyright 2024 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package io.netty.testsuite_jpms.main;
18  
19  import io.netty.bootstrap.Bootstrap;
20  import io.netty.bootstrap.ServerBootstrap;
21  import io.netty.channel.Channel;
22  import io.netty.channel.ChannelHandler;
23  import io.netty.channel.ChannelHandlerContext;
24  import io.netty.channel.ChannelOption;
25  import io.netty.channel.EventLoopGroup;
26  import io.netty.channel.IoHandlerFactory;
27  import io.netty.channel.MultiThreadIoEventLoopGroup;
28  import io.netty.channel.epoll.EpollIoHandler;
29  import io.netty.channel.epoll.EpollServerSocketChannel;
30  import io.netty.channel.kqueue.KQueueIoHandler;
31  import io.netty.channel.kqueue.KQueueServerSocketChannel;
32  import io.netty.channel.nio.NioIoHandler;
33  import io.netty.channel.socket.ServerSocketChannel;
34  import io.netty.channel.socket.nio.NioDatagramChannel;
35  import io.netty.channel.socket.nio.NioServerSocketChannel;
36  import io.netty.channel.uring.IoUringIoHandler;
37  import io.netty.channel.uring.IoUringServerSocketChannel;
38  import io.netty.handler.codec.http3.Http3;
39  import io.netty.handler.codec.quic.InsecureQuicTokenHandler;
40  import io.netty.handler.codec.quic.QuicSslContext;
41  import io.netty.handler.codec.quic.QuicSslContextBuilder;
42  import io.netty.handler.logging.LogLevel;
43  import io.netty.handler.logging.LoggingHandler;
44  import io.netty.handler.ssl.IdentityCipherSuiteFilter;
45  import io.netty.handler.ssl.SslContext;
46  import io.netty.handler.ssl.SslHandler;
47  import io.netty.handler.ssl.SslProvider;
48  import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
49  import io.netty.pkitesting.CertificateBuilder;
50  import io.netty.pkitesting.X509Bundle;
51  
52  import java.net.InetSocketAddress;
53  import java.util.concurrent.TimeUnit;
54  import java.util.stream.Collectors;
55  
56  import static io.netty.handler.ssl.SslContextBuilder.forServer;
57  
58  /**
59   * <p>An HTTP server that sends back the content of the received HTTP request
60   * in a pretty plaintext form.</p>
61   *
62   * <p>Running the server:
63   * <ul>
64   *     <li>./target/maven-jlink/default/bin/java
65   *     -m io.netty.testsuite_jpms.main/io.netty.testsuite_jpms.main.HttpHelloWorldServer</li>
66   *     <li>./target/maven-jlink/default/bin/http (shortcut)</li>
67   * </ul>
68   *
69   * <p>Running with OpenSSL requires to add the
70   * io.netty.internal.tcnative.openssl.${os.detected.name}.${os.detected.arch} module, e.g.
71   * ./target/maven-jlink/default/bin/java --add-modules io.netty.internal.tcnative.openssl.osx.aarch_64
72   * -m io.netty.testsuite_jpms.main/io.netty.testsuite_jpms.main.HttpHelloWorldServer --ssl --ssl-provider OPENSSL
73   *
74   * <p>Running with native requires to add io.netty.transport.kqueue.${os.detected.name}.${os.detected.arch}, e.g.
75   * ./target/maven-jlink/default/bin/java --add-modules io.netty.transport.kqueue.osx.aarch_64
76   * -m io.netty.testsuite_jpms.main/io.netty.testsuite_jpms.main.HttpHelloWorldServer --transport kqueue
77   */
78  public final class HttpHelloWorldServer {
79  
80      private HttpHelloWorldServer() {
81      }
82  
83      public static void main(String[] args) throws Exception {
84  
85          String transport = "nio";
86          boolean ssl = false;
87          SslProvider sslProvider = SslProvider.JDK;
88          boolean http3 = false;
89  
90          Integer port = null;
91          for (int i = 0; i < args.length; i++) {
92              if (args[i].equals("--help")) {
93                  System.out.println("usage: [options]");
94                  System.out.println("--ssl");
95                  System.out.println("--ssl-provider [ JDK | OPENSSL ]");
96                  System.out.println("--port <port>");
97                  System.out.println("--transport [ nio | kqueue | epoll | io_uring ]");
98                  System.out.println("--http3");
99                  System.exit(0);
100             }
101             if (args[i].equals("--ssl")) {
102                 ssl = true;
103             }
104             if (args[i].equals("--ssl-provider")) {
105                 if (i < args.length - 1) {
106                     sslProvider = SslProvider.valueOf(args[++i]);
107                 } else {
108                     System.exit(1);
109                 }
110             }
111             if (args[i].equals("--port")) {
112                 if (i < args.length - 1) {
113                     port = Integer.parseInt(args[++i]);
114                 } else {
115                     System.exit(1);
116                 }
117             }
118             if (args[i].equals("--transport")) {
119                 if (i < args.length - 1) {
120                     transport = args[++i];
121                 } else {
122                     System.exit(1);
123                 }
124             }
125             if (args[i].equals("--http3")) {
126                 http3 = true;
127             }
128         }
129 
130         if (port == null) {
131             port = ssl ? 8443 : 8080;
132         }
133 
134         IoHandlerFactory ioHandlerFactory;
135         Class<? extends ServerSocketChannel> serverSocketChannelFactory;
136         switch (transport) {
137             case "nio":
138                 ioHandlerFactory = NioIoHandler.newFactory();
139                 serverSocketChannelFactory = NioServerSocketChannel.class;
140                 break;
141             case "kqueue":
142                 ioHandlerFactory = KQueueIoHandler.newFactory();
143                 serverSocketChannelFactory = KQueueServerSocketChannel.class;
144                 break;
145             case "epoll":
146                 ioHandlerFactory = EpollIoHandler.newFactory();
147                 serverSocketChannelFactory = EpollServerSocketChannel.class;
148                 break;
149             case "io_uring":
150                 ioHandlerFactory = IoUringIoHandler.newFactory();
151                 serverSocketChannelFactory = IoUringServerSocketChannel.class;
152                 break;
153             default:
154                 System.exit(1);
155                 return;
156         }
157 
158         X509Bundle cert = new CertificateBuilder()
159                 .subject("cn=localhost")
160                 .setIsCertificateAuthority(true)
161                 .buildSelfSigned();
162 
163         // Configure the server.
164         EventLoopGroup group = new MultiThreadIoEventLoopGroup(ioHandlerFactory);
165         try {
166             Channel ch;
167             if (http3) {
168                 QuicSslContext quicCslContext = QuicSslContextBuilder.forServer(cert.toKeyManagerFactory(), null)
169                         .applicationProtocols(Http3.supportedApplicationProtocols()).build();
170                 ChannelHandler codec = Http3.newQuicServerCodecBuilder()
171                         .sslContext(quicCslContext)
172                         .maxIdleTimeout(5000, TimeUnit.MILLISECONDS)
173                         .initialMaxData(10000000)
174                         .initialMaxStreamDataBidirectionalLocal(1000000)
175                         .initialMaxStreamDataBidirectionalRemote(1000000)
176                         .initialMaxStreamsBidirectional(100)
177                         .tokenHandler(InsecureQuicTokenHandler.INSTANCE)
178                         .handler(new Http3HelloWorldServerInitializer())
179                         .build();
180 
181                 Bootstrap bs = new Bootstrap();
182                 ch = bs.group(group)
183                         .channel(NioDatagramChannel.class)
184                         .handler(codec)
185                         .bind(new InetSocketAddress(port)).sync().channel();
186             } else {
187                 SslContext sslContext;
188                 if (ssl) {
189                     sslContext = forServer(cert.toKeyManagerFactory())
190                             .sslProvider(sslProvider)
191                             .protocols("TLSv1.2")
192                             .trustManager(InsecureTrustManagerFactory.INSTANCE)
193                             .ciphers(null, IdentityCipherSuiteFilter.INSTANCE)
194                             .sessionCacheSize(0)
195                             .sessionTimeout(0)
196                             .build();
197                 } else {
198                     sslContext = null;
199                 }
200                 ServerBootstrap b = new ServerBootstrap();
201                 b.option(ChannelOption.SO_BACKLOG, 1024);
202                 b.group(group)
203                         .channel(serverSocketChannelFactory)
204                         .handler(new LoggingHandler(LogLevel.INFO))
205                         .childHandler(new HttpHelloWorldServerInitializer(sslContext));
206 
207                 ch = b.bind(port).sync().channel();
208             }
209 
210             System.err.println("Open your web browser and navigate to " +
211                     ((ssl || http3)? "https" : "http") + "://127.0.0.1:" + port + '/');
212 
213             ch.closeFuture().sync();
214         } finally {
215             group.shutdownGracefully();
216         }
217     }
218 
219     static String content(ChannelHandlerContext ctx) {
220         String modulesInfo = ModuleLayer.boot().modules().stream()
221                 .map(module -> "- " + module.getName() + " " +
222                         (module.getDescriptor().isAutomatic() ? "(automatic)" : ""))
223                 .collect(Collectors.joining("\r\n", "Boot layer:\r\n", "\r\n"));
224 
225         String channelType = ctx.channel().getClass().getName();
226 
227         String sslEngineInfo = "";
228         SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
229         if (sslHandler != null) {
230             sslEngineInfo = "SSL Engine: " + sslHandler.engine().getClass().getName() + "\r\n";
231         }
232 
233         return "Hello World\r\n" +
234                 "Transport: " + channelType + "\r\n" +
235                 sslEngineInfo +
236                 modulesInfo;
237     }
238 }