![JAR search and dependency download from the Maven repository](/logo.png)
objectos.way.HttpServer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of objectos.way Show documentation
Show all versions of objectos.way Show documentation
Objectos Way allows you to build web applications using only Java.
/*
* Copyright (C) 2023-2024 Objectos Software LTDA.
*
* 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.
*/
package objectos.way;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.time.Clock;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import objectos.way.HttpExchange.ParseStatus;
/**
* The Objectos Way {@link HttpServer} implementation.
*/
final class HttpServer implements Http.Server, Runnable {
record Notes(
Note.Ref1 started,
Note.Ref0 stopped,
Note.Ref1 ioError,
Note.Ref1 loopError,
Note.Ref1 internalServerError
) implements Http.Server.Notes {
static Notes get() {
Class> s = Http.Server.class;
return new Notes(
Note.Ref1.create(s, "Started", Note.INFO),
Note.Ref0.create(s, "Stopped", Note.INFO),
Note.Ref1.create(s, "I/O error", Note.ERROR),
Note.Ref1.create(s, "Loop error", Note.ERROR),
Note.Ref1.create(s, "Internal server error", Note.ERROR)
);
}
}
private final Notes notes = Notes.get();
private final int bufferSizeInitial;
private final int bufferSizeMax;
private final Clock clock;
private final Http.HandlerFactory factory;
private final Note.Sink noteSink;
private final int port;
private ServerSocket serverSocket;
private Thread thread;
public HttpServer(HttpServerConfig builder) {
bufferSizeInitial = builder.bufferSizeInitial;
bufferSizeMax = builder.bufferSizeMax;
clock = builder.clock;
factory = builder.factory;
noteSink = builder.noteSink;
port = builder.port;
}
@Override
public final void start() throws IOException {
if (thread != null) {
throw new IllegalStateException(
"The service has already been started."
);
}
InetAddress address;
address = InetAddress.getLoopbackAddress();
InetSocketAddress socketAddress;
socketAddress = new InetSocketAddress(address, port);
serverSocket = new ServerSocket();
serverSocket.bind(socketAddress);
thread = Thread.ofPlatform().name("HTTP").start(this);
}
@Override
public final InetAddress address() {
checkStarted();
return serverSocket.getInetAddress();
}
@Override
public final int port() {
checkStarted();
return serverSocket.getLocalPort();
}
private void checkStarted() {
if (thread == null) {
throw new IllegalStateException(
"Cannot query this service: service is not running."
);
}
}
@Override
public final void close() throws IOException {
if (thread == null) {
throw new IllegalStateException(
"Cannot stop this service: service is not running."
);
}
try {
thread.interrupt();
} finally {
try {
serverSocket.close();
} finally {
thread = null;
serverSocket = null;
}
}
}
@Override
public final void run() {
ThreadFactory factory;
factory = Thread.ofVirtual().name("http-", 1).factory();
try (ExecutorService executor = Executors.newThreadPerTaskExecutor(factory)) {
noteSink.send(notes.started, serverSocket);
while (!Thread.currentThread().isInterrupted()) {
Socket socket;
socket = serverSocket.accept();
Runnable task;
task = new ExchangeTask(socket);
executor.submit(task);
}
noteSink.send(notes.stopped);
} catch (SocketException e) {
if (serverSocket != null && !serverSocket.isClosed()) {
onIOException(e);
}
} catch (IOException e) {
onIOException(e);
}
}
private void onIOException(IOException e) {
noteSink.send(notes.ioError, e);
}
private class ExchangeTask implements Runnable {
private final Socket socket;
public ExchangeTask(Socket socket) {
this.socket = socket;
}
@Override
public final void run() {
try (HttpExchange http = new HttpExchange(socket, bufferSizeInitial, bufferSizeMax, clock, noteSink)) {
while (!Thread.currentThread().isInterrupted()) {
ParseStatus parse;
parse = http.parse();
if (parse == ParseStatus.EOF) {
break;
}
if (parse.isError()) {
throw new UnsupportedOperationException("Implement me");
}
try {
Http.Handler handler;
handler = factory.create();
handler.handle(http);
} catch (Http.AbstractHandlerException ex) {
ex.handle(http);
} catch (HttpExchange.SendException ex) {
IOException cause;
cause = ex.getCause();
noteSink.send(notes.ioError, cause);
break; // could not send response. End this exchange
} catch (Throwable t) {
noteSink.send(notes.internalServerError, t);
if (!http.processed()) {
http.internalServerError(t);
}
}
if (!http.keepAlive()) {
break;
}
}
} catch (IOException e) {
noteSink.send(notes.ioError, e);
} catch (Throwable t) {
noteSink.send(notes.loopError, t);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy