1 /*
2  * JBoss, Home of Professional Open Source.
3  * Copyright 2014 Red Hat, Inc., and individual contributors
4  * as indicated by the @author tags.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */

18
19 package io.undertow.server;
20
21 import io.undertow.UndertowLogger;
22 import io.undertow.UndertowMessages;
23 import org.xnio.ChannelListener;
24 import org.xnio.ChannelListeners;
25 import org.xnio.Option;
26 import org.xnio.OptionMap;
27 import io.undertow.connector.ByteBufferPool;
28 import io.undertow.connector.PooledByteBuffer;
29 import org.xnio.Pool;
30 import org.xnio.StreamConnection;
31 import org.xnio.XnioIoThread;
32 import org.xnio.XnioWorker;
33 import org.xnio.conduits.ConduitStreamSinkChannel;
34 import org.xnio.conduits.ConduitStreamSourceChannel;
35 import org.xnio.conduits.StreamSinkConduit;
36 import org.xnio.conduits.StreamSourceConduit;
37
38 import java.io.IOException;
39 import java.net.SocketAddress;
40 import java.nio.ByteBuffer;
41 import java.util.LinkedList;
42 import java.util.List;
43
44 public abstract class AbstractServerConnection  extends ServerConnection {
45     protected final StreamConnection channel;
46     protected final CloseSetter closeSetter;
47     protected final ByteBufferPool bufferPool;
48     protected final HttpHandler rootHandler;
49     protected final OptionMap undertowOptions;
50     protected final StreamSourceConduit originalSourceConduit;
51     protected final StreamSinkConduit originalSinkConduit;
52     protected final List<CloseListener> closeListeners = new LinkedList<>();
53
54     protected HttpServerExchange current;
55
56     private final int bufferSize;
57
58     private XnioBufferPoolAdaptor poolAdaptor;
59
60     /**
61      * Any extra bytes that were read from the channel. This could be data for this requests, or the next response.
62      */

63     protected PooledByteBuffer extraBytes;
64
65     public AbstractServerConnection(StreamConnection channel, final ByteBufferPool bufferPool, final HttpHandler rootHandler, final OptionMap undertowOptions, final int bufferSize) {
66         this.channel = channel;
67         this.bufferPool = bufferPool;
68         this.rootHandler = rootHandler;
69         this.undertowOptions = undertowOptions;
70         this.bufferSize = bufferSize;
71         closeSetter = new CloseSetter();
72         if (channel != null) {
73             this.originalSinkConduit = channel.getSinkChannel().getConduit();
74             this.originalSourceConduit = channel.getSourceChannel().getConduit();
75             channel.setCloseListener(closeSetter);
76         } else {
77             this.originalSinkConduit = null;
78             this.originalSourceConduit = null;
79         }
80     }
81
82     @Override
83     public Pool<ByteBuffer> getBufferPool() {
84         if(poolAdaptor == null) {
85             poolAdaptor = new XnioBufferPoolAdaptor(getByteBufferPool());
86         }
87         return poolAdaptor;
88     }
89
90     /**
91      * Get the root HTTP handler for this connection.
92      *
93      * @return the root HTTP handler for this connection
94      */

95     public HttpHandler getRootHandler() {
96         return rootHandler;
97     }
98
99     /**
100      * Get the buffer pool for this connection.
101      *
102      * @return the buffer pool for this connection
103      */

104     @Override
105     public ByteBufferPool getByteBufferPool() {
106         return bufferPool;
107     }
108
109     /**
110      * Get the underlying channel.
111      *
112      * @return the underlying channel
113      */

114     public StreamConnection getChannel() {
115         return channel;
116     }
117
118     @Override
119     public ChannelListener.Setter<ServerConnection> getCloseSetter() {
120         return closeSetter;
121     }
122
123     @Override
124     public XnioWorker getWorker() {
125         return channel.getWorker();
126     }
127
128     @Override
129     public XnioIoThread getIoThread() {
130         if(channel == null) {
131             return null;
132         }
133         return channel.getIoThread();
134     }
135
136
137     @Override
138     public boolean isOpen() {
139         return channel.isOpen();
140     }
141
142     @Override
143     public boolean supportsOption(final Option<?> option) {
144         return channel.supportsOption(option);
145     }
146
147     @Override
148     public <T> T getOption(final Option<T> option) throws IOException {
149         return channel.getOption(option);
150     }
151
152     @Override
153     public <T> T setOption(final Option<T> option, final T value) throws IllegalArgumentException, IOException {
154         return channel.setOption(option, value);
155     }
156
157     @Override
158     public void close() throws IOException {
159         channel.close();
160     }
161
162     @Override
163     public SocketAddress getPeerAddress() {
164         return channel.getPeerAddress();
165     }
166
167     @Override
168     public <A extends SocketAddress> A getPeerAddress(final Class<A> type) {
169         return channel.getPeerAddress(type);
170     }
171
172     @Override
173     public SocketAddress getLocalAddress() {
174         return channel.getLocalAddress();
175     }
176
177     @Override
178     public <A extends SocketAddress> A getLocalAddress(final Class<A> type) {
179         return channel.getLocalAddress(type);
180     }
181
182     @Override
183     public OptionMap getUndertowOptions() {
184         return undertowOptions;
185     }
186
187     /**
188      * @return The size of the buffers allocated by the buffer pool
189      */

190     @Override
191     public int getBufferSize() {
192         return bufferSize;
193     }
194
195     public PooledByteBuffer getExtraBytes() {
196         if(extraBytes != null && !extraBytes.getBuffer().hasRemaining()) {
197             extraBytes.close();
198             extraBytes = null;
199             return null;
200         }
201         return extraBytes;
202     }
203
204     public void setExtraBytes(final PooledByteBuffer extraBytes) {
205         this.extraBytes = extraBytes;
206     }
207
208     /**
209      * @return The original source conduit
210      */

211     public StreamSourceConduit getOriginalSourceConduit() {
212         return originalSourceConduit;
213     }
214
215     /**
216      * @return The original underlying sink conduit
217      */

218     public StreamSinkConduit getOriginalSinkConduit() {
219         return originalSinkConduit;
220     }
221
222     /**
223      * Resets the channel to its original state, effectively disabling all current conduit
224      * wrappers. The current state is encapsulated inside a {@link ConduitState} object that
225      * can be used the restore the channel.
226      *
227      * @return An opaque representation of the previous channel state
228      */

229     public ConduitState resetChannel() {
230         ConduitState ret = new ConduitState(channel.getSinkChannel().getConduit(), channel.getSourceChannel().getConduit());
231         channel.getSinkChannel().setConduit(originalSinkConduit);
232         channel.getSourceChannel().setConduit(originalSourceConduit);
233         return ret;
234     }
235
236     /**
237      * Resets the channel to its original state, effectively disabling all current conduit
238      * wrappers. The current state is lost.
239      */

240     public void clearChannel() {
241         channel.getSinkChannel().setConduit(originalSinkConduit);
242         channel.getSourceChannel().setConduit(originalSourceConduit);
243     }
244     /**
245      * Restores the channel conduits to a previous state.
246      *
247      * @param state The original state
248      * @see #resetChannel()
249      */

250     public void restoreChannel(final ConduitState state) {
251         channel.getSinkChannel().setConduit(state.sink);
252         channel.getSourceChannel().setConduit(state.source);
253     }
254
255     public static class ConduitState {
256         final StreamSinkConduit sink;
257         final StreamSourceConduit source;
258
259         private ConduitState(final StreamSinkConduit sink, final StreamSourceConduit source) {
260             this.sink = sink;
261             this.source = source;
262         }
263     }
264
265     protected static StreamSinkConduit sink(ConduitState state) {
266         return state.sink;
267     }
268
269     protected static StreamSourceConduit source(ConduitState state) {
270         return state.source;
271     }
272
273     @Override
274     public void addCloseListener(CloseListener listener) {
275         this.closeListeners.add(listener);
276     }
277
278     @Override
279     protected ConduitStreamSinkChannel getSinkChannel() {
280         return channel.getSinkChannel();
281     }
282
283     @Override
284     protected ConduitStreamSourceChannel getSourceChannel() {
285         return channel.getSourceChannel();
286     }
287
288     protected void setUpgradeListener(HttpUpgradeListener upgradeListener) {
289         throw UndertowMessages.MESSAGES.upgradeNotSupported();
290     }
291
292     @Override
293     protected void maxEntitySizeUpdated(HttpServerExchange exchange) {
294     }
295
296     private class CloseSetter implements ChannelListener.Setter<ServerConnection>, ChannelListener<StreamConnection> {
297
298         private ChannelListener<? super ServerConnection> listener;
299
300         @Override
301         public void set(ChannelListener<? super ServerConnection> listener) {
302             this.listener = listener;
303         }
304
305         @Override
306         public void handleEvent(StreamConnection channel) {
307             try {
308                 for (CloseListener l : closeListeners) {
309                     try {
310                         l.closed(AbstractServerConnection.this);
311                     } catch (Throwable e) {
312                         UndertowLogger.REQUEST_LOGGER.exceptionInvokingCloseListener(l, e);
313                     }
314                 }
315                 if (current != null) {
316                     current.endExchange();
317                 }
318                 ChannelListeners.invokeChannelListener(AbstractServerConnection.this, listener);
319             } finally {
320                 if(extraBytes != null) {
321                     extraBytes.close();
322                     extraBytes = null;
323                 }
324             }
325         }
326     }
327 }
328