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.io.IOException;
22 import java.nio.ByteBuffer;
23
24 import org.eclipse.jetty.http.HttpField;
25 import org.eclipse.jetty.http.HttpFields;
26 import org.eclipse.jetty.http.HttpGenerator;
27 import org.eclipse.jetty.http.HttpHeader;
28 import org.eclipse.jetty.http.HttpHeaderValue;
29 import org.eclipse.jetty.http.MetaData;
30 import org.eclipse.jetty.http.PreEncodedHttpField;
31 import org.eclipse.jetty.http2.IStream;
32 import org.eclipse.jetty.http2.api.Stream;
33 import org.eclipse.jetty.http2.frames.DataFrame;
34 import org.eclipse.jetty.http2.frames.HeadersFrame;
35 import org.eclipse.jetty.io.ByteBufferPool;
36 import org.eclipse.jetty.io.EndPoint;
37 import org.eclipse.jetty.server.Connector;
38 import org.eclipse.jetty.server.HttpChannel;
39 import org.eclipse.jetty.server.HttpConfiguration;
40 import org.eclipse.jetty.server.HttpInput;
41 import org.eclipse.jetty.util.BufferUtil;
42 import org.eclipse.jetty.util.Callback;
43 import org.eclipse.jetty.util.log.Log;
44 import org.eclipse.jetty.util.log.Logger;
45
46 public class HttpChannelOverHTTP2 extends HttpChannel
47 {
48 private static final Logger LOG = Log.getLogger(HttpChannelOverHTTP2.class);
49 private static final HttpField SERVER_VERSION = new PreEncodedHttpField(HttpHeader.SERVER, HttpConfiguration.SERVER_VERSION);
50 private static final HttpField POWERED_BY = new PreEncodedHttpField(HttpHeader.X_POWERED_BY, HttpConfiguration.SERVER_VERSION);
51
52 private boolean _expect100Continue;
53 private boolean _delayedUntilContent;
54
55 public HttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransportOverHTTP2 transport)
56 {
57 super(connector, configuration, endPoint, transport);
58 }
59
60 private IStream getStream()
61 {
62 return getHttpTransport().getStream();
63 }
64
65 @Override
66 public boolean isExpecting100Continue()
67 {
68 return _expect100Continue;
69 }
70
71 public Runnable onRequest(HeadersFrame frame)
72 {
73 MetaData.Request request = (MetaData.Request)frame.getMetaData();
74 HttpFields fields = request.getFields();
75
76
77
78 if (!fields.contains(HttpHeader.HOST))
79 {
80 String authority = request.getURI().getAuthority();
81 if (authority != null)
82 {
83
84 fields.put("host", authority);
85 }
86 }
87
88 _expect100Continue = fields.contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
89
90 HttpFields response = getResponse().getHttpFields();
91 if (getHttpConfiguration().getSendServerVersion())
92 response.add(SERVER_VERSION);
93 if (getHttpConfiguration().getSendXPoweredBy())
94 response.add(POWERED_BY);
95
96 onRequest(request);
97
98 boolean endStream = frame.isEndStream();
99 if (endStream)
100 onRequestComplete();
101
102 _delayedUntilContent = getHttpConfiguration().isDelayDispatchUntilContent() &&
103 !endStream && !_expect100Continue;
104
105 if (LOG.isDebugEnabled())
106 {
107 Stream stream = getStream();
108 LOG.debug("HTTP2 Request #{}/{}, delayed={}:{}{} {} {}{}{}",
109 stream.getId(), Integer.toHexString(stream.getSession().hashCode()),
110 _delayedUntilContent, System.lineSeparator(),
111 request.getMethod(), request.getURI(), request.getVersion(),
112 System.lineSeparator(), fields);
113 }
114
115 return _delayedUntilContent ? null : this;
116 }
117
118 public Runnable onPushRequest(MetaData.Request request)
119 {
120 onRequest(request);
121 getRequest().setAttribute("org.eclipse.jetty.pushed", Boolean.TRUE);
122 onRequestComplete();
123
124 if (LOG.isDebugEnabled())
125 {
126 Stream stream = getStream();
127 LOG.debug("HTTP2 PUSH Request #{}/{}:{}{} {} {}{}{}",
128 stream.getId(), Integer.toHexString(stream.getSession().hashCode()), System.lineSeparator(),
129 request.getMethod(), request.getURI(), request.getVersion(),
130 System.lineSeparator(), request.getFields());
131 }
132
133 return this;
134 }
135
136 @Override
137 public HttpTransportOverHTTP2 getHttpTransport()
138 {
139 return (HttpTransportOverHTTP2)super.getHttpTransport();
140 }
141
142 @Override
143 public void recycle()
144 {
145 _expect100Continue = false;
146 _delayedUntilContent = false;
147 super.recycle();
148 getHttpTransport().recycle();
149 }
150
151 @Override
152 protected void commit(MetaData.Response info)
153 {
154 super.commit(info);
155 if (LOG.isDebugEnabled())
156 {
157 Stream stream = getStream();
158 LOG.debug("HTTP2 Commit Response #{}/{}:{}{} {} {}{}{}",
159 stream.getId(), Integer.toHexString(stream.getSession().hashCode()), System.lineSeparator(), info.getVersion(), info.getStatus(), info.getReason(),
160 System.lineSeparator(), info.getFields());
161 }
162 }
163
164 public Runnable requestContent(DataFrame frame, final Callback callback)
165 {
166
167
168
169
170 final ByteBufferPool byteBufferPool = getByteBufferPool();
171 ByteBuffer original = frame.getData();
172 int length = original.remaining();
173 final ByteBuffer copy = byteBufferPool.acquire(length, original.isDirect());
174 BufferUtil.clearToFill(copy);
175 copy.put(original).flip();
176
177 boolean handle = onContent(new HttpInput.Content(copy)
178 {
179 @Override
180 public boolean isNonBlocking()
181 {
182 return callback.isNonBlocking();
183 }
184
185 @Override
186 public void succeeded()
187 {
188 byteBufferPool.release(copy);
189 callback.succeeded();
190 }
191
192 @Override
193 public void failed(Throwable x)
194 {
195 byteBufferPool.release(copy);
196 callback.failed(x);
197 }
198 });
199
200 boolean endStream = frame.isEndStream();
201 if (endStream)
202 handle |= onRequestComplete();
203
204 if (LOG.isDebugEnabled())
205 {
206 Stream stream = getStream();
207 LOG.debug("HTTP2 Request #{}/{}: {} bytes of {} content, handle: {}",
208 stream.getId(),
209 Integer.toHexString(stream.getSession().hashCode()),
210 length,
211 endStream ? "last" : "some",
212 handle);
213 }
214
215 boolean delayed = _delayedUntilContent;
216 _delayedUntilContent = false;
217
218 return handle || delayed ? this : null;
219 }
220
221
222
223
224
225
226
227
228 @Override
229 public void continue100(int available) throws IOException
230 {
231
232
233 if (isExpecting100Continue())
234 {
235 _expect100Continue = false;
236
237
238 if (available == 0)
239 {
240 if (getResponse().isCommitted())
241 throw new IOException("Committed before 100 Continues");
242
243 boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false);
244 if (!committed)
245 throw new IOException("Concurrent commit while trying to send 100-Continue");
246 }
247 }
248 }
249 }