All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.http2.api.Stream Maven / Gradle / Ivy

The newest version!
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.http2.api;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;

import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.io.Retainable;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;

/**
 * 

A {@link Stream} represents a bidirectional exchange of data on top of a {@link Session}.

*

Differently from socket streams, where the input and output streams are permanently associated * with the socket (and hence with the connection that the socket represents), there can be multiple * HTTP/2 streams present concurrently for an HTTP/2 session.

*

A {@link Stream} maps to an HTTP request/response cycle, and after the request/response cycle is * completed, the stream is closed and removed from the session.

*

Like {@link Session}, {@link Stream} is the active part and by calling its API applications * can generate events on the stream; conversely, {@link Stream.Listener} is the passive part, and * its callbacks are invoked when events happen on the stream.

* * @see Stream.Listener */ public interface Stream { /** * Get the stream unique id. * @return the stream unique id */ public int getId(); /** * Get the {@link org.eclipse.jetty.http2.api.Stream.Listener} associated with this stream. * @return the {@link org.eclipse.jetty.http2.api.Stream.Listener} associated with this stream */ public Listener getListener(); /** * Get the session this stream is associated to. * @return the session this stream is associated to */ public Session getSession(); /** *

Sends the given HEADERS {@code frame} representing an HTTP response.

* * @param frame the HEADERS frame to send * @return the CompletableFuture that gets notified when the frame has been sent */ public default CompletableFuture headers(HeadersFrame frame) { return Promise.Completable.with(p -> headers(frame, Callback.from(() -> p.succeeded(this), p::failed))); } /** *

Sends the given HEADERS {@code frame}.

*

Typically used to send an HTTP response or to send the HTTP response trailers.

* * @param frame the HEADERS frame to send * @param callback the callback that gets notified when the frame has been sent */ public void headers(HeadersFrame frame, Callback callback); /** *

Sends the given PUSH_PROMISE {@code frame}.

* * @param frame the PUSH_PROMISE frame to send * @param listener the listener that gets notified of stream events * @return the CompletableFuture that gets notified of the pushed stream creation */ public default CompletableFuture push(PushPromiseFrame frame, Listener listener) { return Promise.Completable.with(p -> push(frame, p, listener)); } /** *

Sends the given PUSH_PROMISE {@code frame}.

* * @param frame the PUSH_PROMISE frame to send * @param promise the promise that gets notified of the pushed stream creation * @param listener the listener that gets notified of stream events */ public void push(PushPromiseFrame frame, Promise promise, Listener listener); /** *

Reads DATA frames from this stream, wrapping them in retainable * {@link Data} objects.

*

The returned {@link Stream.Data} object may be {@code null}, indicating * that the end of the read side of the stream has not yet been reached, which * may happen in these cases:

*
    *
  • not all the bytes have been received so far, for example the remote * peer did not send them yet, or they are in-flight
  • *
  • all the bytes have been received, but there is a trailer HEADERS * frame to be received to indicate the end of the read side of the * stream
  • *
*

When the returned {@link Stream.Data} object is not {@code null}, * the flow control window has been enlarged by the DATA frame length; * applications must call, either immediately or later (even * asynchronously from a different thread) {@link Stream.Data#release()} * to notify the implementation that the bytes have been processed.

*

{@link Stream.Data} objects may be stored away for later, asynchronous, * processing (for example, to process them only when all of them have been * received).

*

Once the returned {@link Stream.Data} object indicates that the end * of the read side of the stream has been reached, further calls to this * method will return a {@link Stream.Data} object with the same indication, * although the instance may be different.

* * @return a {@link Stream.Data} object containing the DATA frame, * or null if no DATA frame is available * @see #demand() * @see Listener#onDataAvailable(Stream) */ public Data readData(); /** *

Sends the given DATA {@code frame}.

* * @param frame the DATA frame to send * @return the CompletableFuture that gets notified when the frame has been sent */ public default CompletableFuture data(DataFrame frame) { return Promise.Completable.with(p -> data(frame, Callback.from(() -> p.succeeded(this), p::failed))); } /** *

Sends the given DATA {@code frame}.

* * @param frame the DATA frame to send * @param callback the callback that gets notified when the frame has been sent */ public void data(DataFrame frame, Callback callback); /** *

Sends the given RST_STREAM {@code frame}.

* * @param frame the RST_STREAM frame to send * @return the CompletableFuture that gets notified when the frame has been sent */ public default CompletableFuture reset(ResetFrame frame) { return Callback.Completable.with(c -> reset(frame, c)); } /** *

Sends the given RST_STREAM {@code frame}.

* * @param frame the RST_STREAM frame to send * @param callback the callback that gets notified when the frame has been sent */ public void reset(ResetFrame frame, Callback callback); /** * @param key the attribute key * @return an arbitrary object associated with the given key to this stream * or null if no object can be found for the given key. * @see #setAttribute(String, Object) */ public Object getAttribute(String key); /** * @param key the attribute key * @param value an arbitrary object to associate with the given key to this stream * @see #getAttribute(String) * @see #removeAttribute(String) */ public void setAttribute(String key, Object value); /** * @param key the attribute key * @return the arbitrary object associated with the given key to this stream * @see #setAttribute(String, Object) */ public Object removeAttribute(String key); /** * @return whether this stream is local or remote */ public boolean isLocal(); /** * @return whether this stream has been reset */ public boolean isReset(); /** * @return whether the stream is closed remotely. * @see #isClosed() */ boolean isRemotelyClosed(); /** * @return whether this stream is closed, both locally and remotely. */ public boolean isClosed(); /** * @return the stream idle timeout * @see #setIdleTimeout(long) */ public long getIdleTimeout(); /** * @param idleTimeout the stream idle timeout * @see #getIdleTimeout() * @see Stream.Listener#onIdleTimeout(Stream, TimeoutException, Promise) */ public void setIdleTimeout(long idleTimeout); /** *

Demands more {@code DATA} frames for this stream.

*

Calling this method causes {@link Listener#onDataAvailable(Stream)} * to be invoked, possibly at a later time, when the stream has data * to be read, but also when the stream has reached EOF.

*

This method is idempotent: calling it when there already is an * outstanding demand to invoke {@link Listener#onDataAvailable(Stream)} * is a no-operation.

*

The thread invoking this method may invoke directly * {@link Listener#onDataAvailable(Stream)}, unless another thread * that must invoke {@link Listener#onDataAvailable(Stream)} * notices the outstanding demand first.

*

It is always guaranteed that invoking this method from within * {@code onDataAvailable(Stream)} will not cause a * {@link StackOverflowError}.

* * @see #readData() * @see Listener#onDataAvailable(Stream) */ public void demand(); /** *

A {@link Stream.Listener} is the passive counterpart of a {@link Stream} and receives * events happening on an HTTP/2 stream.

*

HTTP/2 data is flow controlled - this means that only a finite number of data events * are delivered, until the flow control window is exhausted.

*

Applications control the delivery of data events by requesting them via * {@link Stream#demand()}; the first event is always delivered, while subsequent * events must be explicitly demanded.

*

Applications control the HTTP/2 flow control by completing the callback associated * with data events - this allows the implementation to recycle the data buffer and * eventually to enlarge the flow control window so that the sender can send more data.

* * @see Stream */ public interface Listener { /** *

A convenient constant for a {@link Listener} implementation that * demands and discards DATA frames, typically to be returned from * {@link Session.Listener#onNewStream(Stream, HeadersFrame)} * and {@link Listener#onPush(Stream, PushPromiseFrame)}.

*/ public static Listener AUTO_DISCARD = new Listener() {}; /** *

Callback method invoked when a stream is created locally by * {@link Session#newStream(HeadersFrame, Promise, Listener)}.

* * @param stream the newly created stream */ public default void onNewStream(Stream stream) { } /** *

Callback method invoked when a HEADERS frame representing the HTTP response has been received.

* * @param stream the stream * @param frame the HEADERS frame received */ public default void onHeaders(Stream stream, HeadersFrame frame) { if (!frame.isEndStream()) stream.demand(); } /** *

Callback method invoked when a PUSH_PROMISE frame has been received.

*

Applications that override this method are typically interested in * processing the pushed stream DATA frames, and must demand for pushed * DATA frames via {@link Stream#demand()} and then return either a * {@link Listener} implementation that overrides * {@link #onDataAvailable(Stream)} where applications can * read from the {@link Stream} via {@link Stream#readData()}, or * {@link #AUTO_DISCARD} that automatically reads and * discards DATA frames. * Returning {@code null} is possible but discouraged, and has the * same effect of demanding and discarding the pushed DATA frames.

* * @param stream the pushed stream * @param frame the PUSH_PROMISE frame received * @return a Stream.Listener that will be notified of pushed stream events */ public default Listener onPush(Stream stream, PushPromiseFrame frame) { stream.demand(); return AUTO_DISCARD; } /** *

Callback method invoked if the application has expressed * {@link Stream#demand() demand} for DATA frames, and if there * may be content available.

*

Applications that wish to handle DATA frames should call * {@link Stream#demand()} for this method to be invoked when * the data is available.

*

Server applications should typically demand from {@link #onNewStream(Stream)} * (upon receiving an HTTP request), while client applications * should typically demand from {@link #onHeaders(Stream, HeadersFrame)} * (upon receiving an HTTP response).

*

Just prior calling this method, the outstanding demand is * cancelled; applications that implement this method should read * content calling {@link Stream#readData()}, and call * {@link Stream#demand()} to signal to the implementation to call * again this method when there may be more content available.

*

Only one thread at a time invokes this method, although it * may not be the same thread across different invocations.

*

It is always guaranteed that invoking {@link Stream#demand()} * from within this method will not cause a {@link StackOverflowError}.

*

Typical usage:

*
{@code
         * class MyStreamListener implements Stream.Listener
         * {
         *     @Override
         *     public void onDataAvailable(Stream stream)
         *     {
         *         // Read a chunk of the content.
         *         Stream.Data data = stream.readData();
         *         if (data == null)
         *         {
         *             // No data available now, demand to be called back.
         *             stream.demand();
         *         }
         *         else
         *         {
         *             // Process the content.
         *             process(data.frame().getByteBuffer());
         *             // Notify that the content has been consumed.
         *             data.release();
         *             if (!data.frame().isEndStream())
         *             {
         *                 // Demand to be called back.
         *                 stream.demand();
         *             }
         *         }
         *     }
         * }
         * }
* * @param stream the stream * @see Stream#demand() */ public default void onDataAvailable(Stream stream) { while (true) { Data data = stream.readData(); if (data == null) { stream.demand(); return; } data.release(); if (data.frame().isEndStream()) return; } } /** *

Callback method invoked when a RST_STREAM frame has been received for this stream.

* * @param stream the stream * @param frame the RST_STREAM frame received * @param callback the callback to complete when the reset has been handled */ public default void onReset(Stream stream, ResetFrame frame, Callback callback) { callback.succeeded(); } /** *

Callback method invoked when the stream exceeds its idle timeout.

* * @param stream the stream * @param x the timeout failure * @param promise the promise to complete * @see #getIdleTimeout() */ public default void onIdleTimeout(Stream stream, TimeoutException x, Promise promise) { promise.succeeded(true); } /** *

Callback method invoked when the stream failed.

* * @param stream the stream * @param error the error code * @param reason the error reason, or null * @param failure the failure * @param callback the callback to complete when the failure has been handled */ public default void onFailure(Stream stream, int error, String reason, Throwable failure, Callback callback) { callback.succeeded(); } /** *

Callback method invoked after the stream has been closed.

* * @param stream the stream */ public default void onClosed(Stream stream) { } } /** *

A {@link Retainable} wrapper of a {@link DataFrame}.

*/ public abstract static class Data implements Retainable { public static Data eof(int streamId) { return new Data.EOF(streamId); } private final DataFrame frame; public Data(DataFrame frame) { this.frame = frame; } public DataFrame frame() { return frame; } @Override public String toString() { return "%s@%x[%s]".formatted(getClass().getSimpleName(), hashCode(), frame()); } private static class EOF extends Data { public EOF(int streamId) { super(new DataFrame(streamId, BufferUtil.EMPTY_BUFFER, true)); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy