/* * Copyright (c) 2012 the original author or authors. * * 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 org.eclipse.jetty.spdy.api; import java.nio.channels.WritePendingException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /** * <p>A {@link Stream} represents a bidirectional exchange of data on top of a {@link Session}.</p> * <p>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 * SPDY streams for a SPDY session.</p> * <p>SPDY streams may terminate without this implying that the SPDY session is terminated.</p> * <p>If SPDY is used to transport the HTTP protocol, then a SPDY stream maps to a HTTP request/response * cycle, and after the request/response cycle is completed, the stream is closed, and other streams * may be opened. Differently from HTTP, though, multiple SPDY streams may be opened concurrently * on the same SPDY session.</p> * <p>Like {@link Session}, {@link Stream} is the active part and by calling its API applications * can generate events on the stream; conversely, {@link StreamFrameListener} is the passive part, and its * callbacks are invoked when events happen on the stream.</p> * <p>A {@link Stream} can send multiple data frames one after the other but implementations use a * flow control mechanism that only sends the data frames if the other end has signalled that it can * accept the frame.</p> * <p>Data frames should be sent sequentially only when the previous frame has been completely sent. * The reason for this requirement is to avoid potentially confusing code such as:</p> * <pre> * // WRONG CODE, DO NOT USE IT * final Stream stream = ...; * stream.data(StringDataInfo("chunk1", false), 5, TimeUnit.SECONDS, new Handler<Void>() { ... }); * stream.data(StringDataInfo("chunk2", true), 1, TimeUnit.SECONDS, new Handler<Void>() { ... }); * </pre> * <p>where the second call to {@link #data(DataInfo, long, TimeUnit, Handler)} has a timeout smaller * than the previous call.</p> * <p>The behavior of such style of invocations is unspecified (it may even throw an exception - similar * to {@link WritePendingException}).</p> * <p>The correct sending of data frames is the following:</p> * <pre> * final Stream stream = ...; * ... * // Blocking version * stream.data(new StringDataInfo("chunk1", false)).get(1, TimeUnit.SECONDS); * stream.data(new StringDataInfo("chunk2", true)).get(1, TimeUnit.SECONDS); * * // Asynchronous version * stream.data(new StringDataInfo("chunk1", false), 1, TimeUnit.SECONDS, new Handler.Adapter<Void>() * { * public void completed(Void context) * { * stream.data(new StringDataInfo("chunk2", true)); * } * }); * </pre> * * @see StreamFrameListener */ public interface Stream { /** * @return the id of this stream */ public int getId(); /** * @return the priority of this stream */ public byte getPriority(); /** * @return the session this stream is associated to */ public Session getSession(); /** * <p>Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.</p> * <p>Callers may use the returned future to wait for the reply to be actually sent.</p> * * @param replyInfo the metadata to send * @return a future to wait for the reply to be sent * @see #reply(ReplyInfo, long, TimeUnit, Handler) * @see SessionFrameListener#onSyn(Stream, SynInfo) */ public Future<Void> reply(ReplyInfo replyInfo); /** * <p>Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.</p> * <p>Callers may pass a non-null completion handler to be notified of when the * reply has been actually sent.</p> * * @param replyInfo the metadata to send * @param timeout the operation's timeout * @param unit the timeout's unit * @param handler the completion handler that gets notified of reply sent * @see #reply(ReplyInfo) */ public void reply(ReplyInfo replyInfo, long timeout, TimeUnit unit, Handler<Void> handler); /** * <p>Sends asynchronously a DATA frame on this stream.</p> * <p>DATA frames should always be sent after a SYN_REPLY frame.</p> * <p>Callers may use the returned future to wait for the data to be actually sent.</p> * * @param dataInfo the metadata to send * @return a future to wait for the data to be sent * @see #data(DataInfo, long, TimeUnit, Handler) * @see #reply(ReplyInfo) */ public Future<Void> data(DataInfo dataInfo); /** * <p>Sends asynchronously a DATA frame on this stream.</p> * <p>DATA frames should always be sent after a SYN_REPLY frame.</p> * <p>Callers may pass a non-null completion handler to be notified of when the * data has been actually sent.</p> * * @param dataInfo the metadata to send * @param timeout the operation's timeout * @param unit the timeout's unit * @param handler the completion handler that gets notified of data sent * @see #data(DataInfo) */ public void data(DataInfo dataInfo, long timeout, TimeUnit unit, Handler<Void> handler); /** * <p>Sends asynchronously a HEADER frame on this stream.</p> * <p>HEADERS frames should always be sent after a SYN_REPLY frame.</p> * <p>Callers may use the returned future to wait for the headers to be actually sent.</p> * * @param headersInfo the metadata to send * @return a future to wait for the headers to be sent * @see #headers(HeadersInfo, long, TimeUnit, Handler) * @see #reply(ReplyInfo) */ public Future<Void> headers(HeadersInfo headersInfo); /** * <p>Sends asynchronously a HEADER frame on this stream.</p> * <p>HEADERS frames should always be sent after a SYN_REPLY frame.</p> * <p>Callers may pass a non-null completion handler to be notified of when the * headers have been actually sent.</p> * * @param headersInfo the metadata to send * @param timeout the operation's timeout * @param unit the timeout's unit * @param handler the completion handler that gets notified of headers sent * @see #headers(HeadersInfo) */ public void headers(HeadersInfo headersInfo, long timeout, TimeUnit unit, Handler<Void> handler); /** * @return whether this stream has been closed by both parties * @see #isHalfClosed() */ public boolean isClosed(); /** * @return whether this stream has been closed by one party only * @see #isClosed() * @param timeout the timeout for the stream creation * @param unit the timeout's unit */ public boolean isHalfClosed(); /** * @param key the attribute key * @return an arbitrary object associated with the given key to this stream * @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); }