1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.http2.server;
20
21 import java.nio.ByteBuffer;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Queue;
25 import java.util.concurrent.Executor;
26
27 import org.eclipse.jetty.http.BadMessageException;
28 import org.eclipse.jetty.http.HttpField;
29 import org.eclipse.jetty.http.HttpHeader;
30 import org.eclipse.jetty.http.HttpMethod;
31 import org.eclipse.jetty.http.MetaData;
32 import org.eclipse.jetty.http.MetaData.Request;
33 import org.eclipse.jetty.http2.HTTP2Connection;
34 import org.eclipse.jetty.http2.ISession;
35 import org.eclipse.jetty.http2.IStream;
36 import org.eclipse.jetty.http2.api.server.ServerSessionListener;
37 import org.eclipse.jetty.http2.frames.DataFrame;
38 import org.eclipse.jetty.http2.frames.Frame;
39 import org.eclipse.jetty.http2.frames.HeadersFrame;
40 import org.eclipse.jetty.http2.frames.PrefaceFrame;
41 import org.eclipse.jetty.http2.frames.SettingsFrame;
42 import org.eclipse.jetty.http2.parser.ServerParser;
43 import org.eclipse.jetty.http2.parser.SettingsBodyParser;
44 import org.eclipse.jetty.io.ByteBufferPool;
45 import org.eclipse.jetty.io.Connection;
46 import org.eclipse.jetty.io.EndPoint;
47 import org.eclipse.jetty.server.Connector;
48 import org.eclipse.jetty.server.HttpConfiguration;
49 import org.eclipse.jetty.util.B64Code;
50 import org.eclipse.jetty.util.BufferUtil;
51 import org.eclipse.jetty.util.Callback;
52 import org.eclipse.jetty.util.ConcurrentArrayQueue;
53 import org.eclipse.jetty.util.TypeUtil;
54
55 public class HTTP2ServerConnection extends HTTP2Connection implements Connection.UpgradeTo
56 {
57 private final Queue<HttpChannelOverHTTP2> channels = new ConcurrentArrayQueue<>();
58 private final ServerSessionListener listener;
59 private final HttpConfiguration httpConfig;
60 private final List<Frame> upgradeFrames = new ArrayList<>();
61
62 public HTTP2ServerConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, ServerParser parser, ISession session, int inputBufferSize, ServerSessionListener listener)
63 {
64 super(byteBufferPool, executor, endPoint, parser, session, inputBufferSize);
65 this.listener = listener;
66 this.httpConfig = httpConfig;
67 }
68
69 @Override
70 protected ServerParser getParser()
71 {
72 return (ServerParser)super.getParser();
73 }
74
75 @Override
76 public void onUpgradeTo(ByteBuffer buffer)
77 {
78 if (LOG.isDebugEnabled())
79 LOG.debug("HTTP2 onUpgradeTo {} {}", this, BufferUtil.toDetailString(buffer));
80 setInputBuffer(buffer);
81 }
82
83 @Override
84 public void onOpen()
85 {
86 notifyAccept(getSession());
87 for (Frame frame : upgradeFrames)
88 getSession().onFrame(frame);
89 super.onOpen();
90 }
91
92 private void notifyAccept(ISession session)
93 {
94 try
95 {
96 listener.onAccept(session);
97 }
98 catch (Throwable x)
99 {
100 LOG.info("Failure while notifying listener " + listener, x);
101 }
102 }
103
104 public void onNewStream(Connector connector, IStream stream, HeadersFrame frame)
105 {
106 if (LOG.isDebugEnabled())
107 LOG.debug("Processing {} on {}", frame, stream);
108 HttpChannelOverHTTP2 channel = provideHttpChannel(connector, stream);
109 Runnable task = channel.onRequest(frame);
110 if (task != null)
111 offerTask(task, false);
112 }
113
114 public void onData(IStream stream, DataFrame frame, Callback callback)
115 {
116 if (LOG.isDebugEnabled())
117 LOG.debug("Processing {} on {}", frame, stream);
118 HttpChannelOverHTTP2 channel = (HttpChannelOverHTTP2)stream.getAttribute(IStream.CHANNEL_ATTRIBUTE);
119 Runnable task = channel.requestContent(frame, callback);
120 if (task != null)
121 offerTask(task, false);
122 }
123
124 public void push(Connector connector, IStream stream, MetaData.Request request)
125 {
126 if (LOG.isDebugEnabled())
127 LOG.debug("Processing push {} on {}", request, stream);
128 HttpChannelOverHTTP2 channel = provideHttpChannel(connector, stream);
129 Runnable task = channel.onPushRequest(request);
130 if (task != null)
131 offerTask(task, true);
132 }
133
134 private HttpChannelOverHTTP2 provideHttpChannel(Connector connector, IStream stream)
135 {
136 HttpChannelOverHTTP2 channel = channels.poll();
137 if (channel != null)
138 {
139 channel.getHttpTransport().setStream(stream);
140 if (LOG.isDebugEnabled())
141 LOG.debug("Recycling channel {} for {}", channel, this);
142 }
143 else
144 {
145 HttpTransportOverHTTP2 transport = new HttpTransportOverHTTP2(connector, this);
146 transport.setStream(stream);
147 channel = new ServerHttpChannelOverHTTP2(connector, httpConfig, getEndPoint(), transport);
148 if (LOG.isDebugEnabled())
149 LOG.debug("Creating channel {} for {}", channel, this);
150 }
151 stream.setAttribute(IStream.CHANNEL_ATTRIBUTE, channel);
152 return channel;
153 }
154
155 public boolean upgrade(Request request)
156 {
157 if (HttpMethod.PRI.is(request.getMethod()))
158 {
159 getParser().directUpgrade();
160 }
161 else
162 {
163 HttpField settingsField = request.getFields().getField(HttpHeader.HTTP2_SETTINGS);
164 if (settingsField == null)
165 throw new BadMessageException("Missing " + HttpHeader.HTTP2_SETTINGS + " header");
166 String value = settingsField.getValue();
167 final byte[] settings = B64Code.decodeRFC4648URL(value == null ? "" : value);
168
169 if (LOG.isDebugEnabled())
170 LOG.debug("{} settings {}",this,TypeUtil.toHexString(settings));
171
172 SettingsFrame settingsFrame = SettingsBodyParser.parseBody(BufferUtil.toBuffer(settings));
173 if (settingsFrame == null)
174 {
175 LOG.warn("Invalid {} header value: {}", HttpHeader.HTTP2_SETTINGS, value);
176 throw new BadMessageException();
177 }
178
179 getParser().standardUpgrade();
180
181 upgradeFrames.add(new PrefaceFrame());
182 upgradeFrames.add(settingsFrame);
183
184 upgradeFrames.add(new HeadersFrame(1, request, null, true));
185 }
186 return true;
187 }
188
189 private class ServerHttpChannelOverHTTP2 extends HttpChannelOverHTTP2
190 {
191 public ServerHttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransportOverHTTP2 transport)
192 {
193 super(connector, configuration, endPoint, transport);
194 }
195
196 @Override
197 public void onCompleted()
198 {
199 super.onCompleted();
200 recycle();
201 channels.offer(this);
202 }
203 }
204 }