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 javax.annotation.Nullable; 023import java.util.Optional; 024import java.util.function.Consumer; 025 026/** 027 * A special HTTP server whose only purpose is to provide <a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events">Server-Sent Event</a> functionality. 028 * <p> 029 * A Soklet application which supports Server-Sent Events will be configured with both a {@link Server} and a {@link ServerSentEventServer}. 030 * <p> 031 * See <a href="https://www.soklet.com/docs/server-sent-events">https://www.soklet.com/docs/server-sent-events</a> for detailed documentation. 032 * 033 * @author <a href="https://www.revetkn.com">Mark Allen</a> 034 */ 035public interface ServerSentEventServer extends AutoCloseable { 036 /** 037 * Starts the SSE server, which makes it able to accept requests from clients. 038 * <p> 039 * If the server is already started, no action is taken. 040 * <p> 041 * <strong>This method is designed for internal use by {@link com.soklet.Soklet} only and should not be invoked elsewhere.</strong> 042 */ 043 void start(); 044 045 /** 046 * Stops the SSE server, which makes it unable to accept requests from clients. 047 * <p> 048 * If the server is already stopped, no action is taken. 049 * <p> 050 * <strong>This method is designed for internal use by {@link com.soklet.Soklet} only and should not be invoked elsewhere.</strong> 051 */ 052 void stop(); 053 054 /** 055 * Is this SSE server started (that is, able to handle requests from clients)? 056 * 057 * @return {@code true} if the server is started, {@code false} otherwise 058 */ 059 @Nonnull 060 Boolean isStarted(); 061 062 /** 063 * {@link AutoCloseable}-enabled synonym for {@link #stop()}. 064 * <p> 065 * <strong>This method is designed for internal use by {@link com.soklet.Soklet} only and should not be invoked elsewhere.</strong> 066 * 067 * @throws Exception if an exception occurs while stopping the server 068 */ 069 @Override 070 default void close() throws Exception { 071 stop(); 072 } 073 074 /** 075 * Given a {@link ResourcePath} that corresponds to a <em>Resource Method</em> annotated with {@link com.soklet.annotation.ServerSentEventSource}, acquire a {@link ServerSentEventBroadcaster} which is capable of "pushing" messages to all connected Server-Sent Event clients. 076 * <p> 077 * Soklet guarantees exactly one {@link ServerSentEventBroadcaster} instance exists per {@link ResourcePath}. Soklet is responsible for the creation and management of {@link ServerSentEventBroadcaster} instances. 078 * <p> 079 * See <a href="https://www.soklet.com/docs/server-sent-events">https://www.soklet.com/docs/server-sent-events</a> for detailed documentation. 080 * 081 * @param resourcePath the {@link com.soklet.annotation.ServerSentEventSource}-annotated <em>Resource Method</em> for which to acquire a broadcaster 082 * @return a broadcaster for the given {@link ResourcePath}, or {@link Optional#empty()} if there is no broadcaster available 083 */ 084 @Nonnull 085 Optional<? extends ServerSentEventBroadcaster> acquireBroadcaster(@Nullable ResourcePath resourcePath); 086 087 /** 088 * The {@link com.soklet.Soklet} instance which manages this {@link ServerSentEventServer} will invoke this method exactly once at initialization time - this allows {@link com.soklet.Soklet} to "talk" to your {@link ServerSentEventServer}. 089 * <p> 090 * <strong>This method is designed for internal use by {@link com.soklet.Soklet} only and should not be invoked elsewhere.</strong> 091 * 092 * @param sokletConfiguration configuration for the Soklet instance that controls this server 093 * @param requestHandler a {@link com.soklet.Soklet}-internal request handler which takes a {@link ServerSentEventServer}-provided request as input and supplies a {@link MarshaledResponse} as output for the {@link ServerSentEventServer} to write back to the client 094 */ 095 void initialize(@Nonnull SokletConfiguration sokletConfiguration, 096 @Nonnull RequestHandler requestHandler); 097 098 /** 099 * Request/response processing contract for {@link ServerSentEventServer} implementations. 100 * <p> 101 * This is used internally by {@link com.soklet.Soklet} instances to "talk" to a {@link ServerSentEventServer} via {@link ServerSentEventServer#initialize(SokletConfiguration, RequestHandler)}. 102 * It's the responsibility of the {@link ServerSentEventServer} to implement HTTP mechanics: read bytes from the request, write bytes to the response, and so forth. 103 * <p> 104 * <strong>Most Soklet applications will use {@link com.soklet.core.impl.DefaultServerSentEventServer} and therefore do not need to implement this interface directly.</strong> 105 * 106 * @author <a href="https://www.revetkn.com">Mark Allen</a> 107 */ 108 @FunctionalInterface 109 interface RequestHandler { 110 /** 111 * Callback to be invoked by a {@link ServerSentEventServer} implementation after it has received a Server-Sent Event Source HTTP request but prior to writing initial data to the HTTP response. 112 * <p> 113 * <strong>Note: this method is only invoked during the initial request "handshake" - it is not called for subsequent Server-Sent Event stream writes performed via {@link ServerSentEventBroadcaster#broadcast(ServerSentEvent)} invocations.</strong> 114 * <p> 115 * For example, when a Server-Sent Event Source HTTP request is received, you might immediately write an HTTP 200 OK response if all looks good, or reject with a 401 due to invalid credentials. 116 * That is the extent of the request-handling logic performed here. The Server-Sent Event stream then remains open and can be written to via {@link ServerSentEventBroadcaster#broadcast(ServerSentEvent)}. 117 * <p> 118 * The {@link ServerSentEventServer} 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. 119 * <p> 120 * The {@link com.soklet.Soklet} instance will generate a {@link MarshaledResponse} for the request, which it "hands back" to the {@link ServerSentEventServer} to be sent over the wire to the client. 121 * 122 * @param request a Soklet {@link Request} representation of the {@link ServerSentEventServer}'s internal HTTP request data 123 * @param requestResultConsumer invoked by {@link com.soklet.Soklet} when it's time for the {@link ServerSentEventServer} to write HTTP response data to the client 124 */ 125 void handleRequest(@Nonnull Request request, 126 @Nonnull Consumer<RequestResult> requestResultConsumer); 127 } 128}