001/*
002 * Copyright 2022-2026 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;
018
019import org.jspecify.annotations.NonNull;
020
021import javax.annotation.concurrent.ThreadSafe;
022import java.time.Duration;
023import java.time.Instant;
024import java.util.Optional;
025
026/**
027 * Runtime context for a streaming response producer.
028 *
029 * @author <a href="https://www.revetkn.com">Mark Allen</a>
030 */
031@ThreadSafe
032public interface StreamingResponseContext {
033        /**
034         * The cancelation token for this streaming response.
035         *
036         * @return the cancelation token
037         */
038        @NonNull
039        CancelationToken getCancelationToken();
040
041        /**
042         * The request that produced this streaming response.
043         *
044         * @return the request that produced this streaming response
045         */
046        @NonNull
047        Request getRequest();
048
049        /**
050         * The absolute deadline for this stream, if one is configured.
051         *
052         * @return the streaming deadline, or {@link Optional#empty()} if no deadline is configured
053         */
054        @NonNull
055        Optional<Instant> getDeadline();
056
057        /**
058         * The idle timeout for this stream, if one is configured.
059         *
060         * @return the streaming idle timeout, or {@link Optional#empty()} if disabled
061         */
062        @NonNull
063        Optional<Duration> getIdleTimeout();
064
065        /**
066         * Is the streaming response canceled?
067         *
068         * @return {@code true} if canceled
069         */
070        @NonNull
071        default Boolean isCanceled() {
072                return getCancelationToken().isCanceled();
073        }
074
075        /**
076         * The cancelation reason, if canceled.
077         *
078         * @return the cancelation reason, or {@link Optional#empty()} if not canceled
079         */
080        @NonNull
081        default Optional<StreamTerminationReason> getCancelationReason() {
082                return getCancelationToken().getCancelationReason();
083        }
084
085        /**
086         * The underlying cancelation cause, if available.
087         *
088         * @return the underlying cause, or {@link Optional#empty()} if none is available
089         */
090        @NonNull
091        default Optional<Throwable> getCancelationCause() {
092                return getCancelationToken().getCancelationCause();
093        }
094
095        /**
096         * Registers a callback that runs when the stream is canceled.
097         *
098         * @param callback the callback to run on cancelation
099         * @return a handle that removes the callback when closed
100         */
101        @NonNull
102        default AutoCloseable onCancel(@NonNull Runnable callback) {
103                return getCancelationToken().onCancel(callback);
104        }
105
106        /**
107         * Throws if the stream has been canceled.
108         *
109         * @throws StreamingResponseCanceledException if canceled
110         */
111        default void throwIfCanceled() throws StreamingResponseCanceledException {
112                getCancelationToken().throwIfCanceled();
113        }
114}