package act.xio.undertow;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import act.Act;
import act.xio.Network;
import act.xio.NetworkBase;
import act.xio.NetworkHandler;
import io.undertow.UndertowOptions;
import io.undertow.connector.ByteBufferPool;
import io.undertow.server.DefaultByteBufferPool;
import io.undertow.server.HttpHandler;
import io.undertow.server.protocol.http.HttpOpenListener;
import org.osgl.logging.LogManager;
import org.osgl.logging.Logger;
import org.osgl.util.E;
import org.osgl.util.IO;
import org.xnio.*;
import org.xnio.channels.AcceptingChannel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
/**
* Implement {@link Network} using undertow
*/
public class UndertowNetwork extends NetworkBase {
private static final Logger logger = LogManager.get(UndertowNetwork.class);
private Xnio xnio;
private int ioThreads;
private XnioWorker worker;
private OptionMap socketOptions;
private OptionMap serverOptions;
private List<AcceptingChannel<? extends StreamConnection>> channels;
@Override
protected void bootUp() {
try {
xnio = Xnio.getInstance(UndertowNetwork.class.getClassLoader());
worker = createWorker();
socketOptions = createSocketOptions();
serverOptions = OptionMap.builder()
.set(UndertowOptions.BUFFER_PIPELINED_DATA, true)
.set(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false)
.set(UndertowOptions.ALWAYS_SET_DATE, true)
.set(UndertowOptions.RECORD_REQUEST_START_TIME, false)
.set(UndertowOptions.NO_REQUEST_TIMEOUT, 60 * 1000)
.set(UndertowOptions.ENABLE_STATISTICS, Act.conf().xioStatistics())
.getMap();
channels = new ArrayList<>();
} catch (Exception e) {
throw E.unexpected(e, "Error booting up Undertow service: %s", e.getMessage());
}
}
@Override
protected void setUpClient(NetworkHandler client, int port) throws IOException {
HttpHandler handler = new ActHttpHandler(client);
ByteBufferPool buffers = new DefaultByteBufferPool(true, 16 * 1024, -1, 4);
HttpOpenListener openListener = new HttpOpenListener(buffers, serverOptions);
openListener.setRootHandler(handler);
ChannelListener<AcceptingChannel<StreamConnection>> acceptListener = ChannelListeners.openListenerAdapter(openListener);
AcceptingChannel<? extends StreamConnection> server = worker.createStreamConnectionServer(new InetSocketAddress(port), acceptListener, socketOptions);
server.resumeAccepts();
channels.add(server);
}
@Override
protected void close() {
if (null == channels) {
// not booted yet
return;
}
for (AcceptingChannel<? extends StreamConnection> channel : channels) {
IO.close(channel);
}
channels.clear();
worker.shutdownNow();
}
private XnioWorker createWorker() throws IOException {
ioThreads = Runtime.getRuntime().availableProcessors() * 2;
int workerThreads = ioThreads * 8;
int maxWorkerThreads = Act.conf().xioMaxWorkerThreads();
if (maxWorkerThreads > 0) {
workerThreads = Math.min(maxWorkerThreads, workerThreads);
}
return xnio.createWorker(OptionMap.builder()
.set(Options.WORKER_IO_THREADS, ioThreads)
.set(Options.WORKER_TASK_CORE_THREADS, workerThreads)
.set(Options.WORKER_TASK_MAX_THREADS, workerThreads)
.set(Options.CONNECTION_HIGH_WATER, 1000000)
.set(Options.CONNECTION_LOW_WATER, 1000000)
.set(Options.TCP_NODELAY, true)
.set(Options.CORK, true)
.getMap());
}
private OptionMap createSocketOptions() {
OptionMap socketOptions = OptionMap.builder()
.set(Options.WORKER_IO_THREADS, ioThreads)
.set(Options.TCP_NODELAY, true)
.set(Options.REUSE_ADDRESSES, true)
.set(Options.BALANCING_TOKENS, 1)
.set(Options.BALANCING_CONNECTIONS, 2)
.set(Options.BACKLOG, 10000)
.getMap();
return socketOptions;
}
}