001/*
002 * Copyright 2022-2025 Revetware LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.soklet.core;
018
019import com.soklet.SokletConfiguration;
020
021import javax.annotation.Nonnull;
022import java.util.function.Consumer;
023
024/**
025 * Contract for HTTP server implementations that are designed to be managed by a {@link com.soklet.Soklet} instance.
026 * <p>
027 * <strong>Most Soklet applications will use {@link com.soklet.core.impl.DefaultServer} and therefore do not need to implement this interface directly.</strong>
028 * <p>
029 * For example:
030 * <pre>{@code  SokletConfiguration config = SokletConfiguration.withServer(
031 *   DefaultServer.withPort(8080).build()
032 * ).build();
033 *
034 * try (Soklet soklet = new Soklet(config)) {
035 *   soklet.start();
036 *   System.out.println("Soklet started, press [enter] to exit");
037 *   System.in.read(); // or Thread.currentThread().join() in containers
038 * }}</pre>
039 *
040 * @author <a href="https://www.revetkn.com">Mark Allen</a>
041 */
042public interface Server extends AutoCloseable {
043        /**
044         * Starts the server, which makes it able to accept requests from clients.
045         * <p>
046         * If the server is already started, no action is taken.
047         * <p>
048         * <strong>This method is designed for internal use by {@link com.soklet.Soklet} only and should not be invoked elsewhere.</strong>
049         */
050        void start();
051
052        /**
053         * Stops the server, which makes it unable to accept requests from clients.
054         * <p>
055         * If the server is already stopped, no action is taken.
056         * <p>
057         * <strong>This method is designed for internal use by {@link com.soklet.Soklet} only and should not be invoked elsewhere.</strong>
058         */
059        void stop();
060
061        /**
062         * Is this server started (that is, able to handle requests from clients)?
063         *
064         * @return {@code true} if the server is started, {@code false} otherwise
065         */
066        @Nonnull
067        Boolean isStarted();
068
069        /**
070         * The {@link com.soklet.Soklet} instance which manages this {@link Server} will invoke this method exactly once at initialization time - this allows {@link com.soklet.Soklet} to "talk" to your {@link Server}.
071         * <p>
072         * <strong>This method is designed for internal use by {@link com.soklet.Soklet} only and should not be invoked elsewhere.</strong>
073         *
074         * @param sokletConfiguration configuration for the Soklet instance that controls this server
075         * @param requestHandler      a {@link com.soklet.Soklet}-internal request handler which takes a {@link Server}-provided request as input and supplies a {@link MarshaledResponse} as output for the {@link Server} to write back to the client
076         */
077        void initialize(@Nonnull SokletConfiguration sokletConfiguration,
078                                                                        @Nonnull RequestHandler requestHandler);
079
080        /**
081         * {@link AutoCloseable}-enabled synonym for {@link #stop()}.
082         * <p>
083         * <strong>This method is designed for internal use by {@link com.soklet.Soklet} only and should not be invoked elsewhere.</strong>
084         *
085         * @throws Exception if an exception occurs while stopping the server
086         */
087        @Override
088        default void close() throws Exception {
089                stop();
090        }
091
092        /**
093         * Request/response processing contract for {@link Server} implementations.
094         * <p>
095         * This is used internally by {@link com.soklet.Soklet} instances to "talk" to a {@link Server} via {@link Server#initialize(SokletConfiguration, RequestHandler)}.  It's the responsibility of the {@link Server} to implement HTTP mechanics: read bytes from the request, write bytes to the response, and so forth.
096         * <p>
097         * <strong>Most Soklet applications will use {@link com.soklet.core.impl.DefaultServer} and therefore do not need to implement this interface directly.</strong>
098         *
099         * @author <a href="https://www.revetkn.com">Mark Allen</a>
100         */
101        @FunctionalInterface
102        interface RequestHandler {
103                /**
104                 * Callback to be invoked by a {@link Server} implementation after it has received an HTTP request but prior to writing an HTTP response.
105                 * <p>
106                 * The {@link Server} is responsible for converting its internal request representation into a {@link Request}, which a {@link com.soklet.Soklet} instance consumes and performs Soklet application request processing logic.
107                 * <p>
108                 * The {@link com.soklet.Soklet} instance will generate a {@link MarshaledResponse} for the request, which it "hands back" to the {@link Server} to be sent over the wire to the client.
109                 *
110                 * @param request               a Soklet {@link Request} representation of the {@link Server}'s internal HTTP request data
111                 * @param requestResultConsumer invoked by {@link com.soklet.Soklet} when it's time for the {@link Server} to write HTTP response data to the client
112                 */
113                void handleRequest(@Nonnull Request request,
114                                                                                         @Nonnull Consumer<RequestResult> requestResultConsumer);
115        }
116}