Skip to content
This repository was archived by the owner on May 28, 2018. It is now read-only.

Commit 086b8a9

Browse files
committed
Experimental container support for HTTP/2.
Change-Id: Ieaa5926c23720fe86cdbfd9e469aa744de94291a (cherry picked from commit f7f10e9)
1 parent ed8995c commit 086b8a9

File tree

6 files changed

+674
-15
lines changed

6 files changed

+674
-15
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
41+
package org.glassfish.jersey.netty.httpserver;
42+
43+
import java.net.URI;
44+
45+
import io.netty.channel.ChannelHandlerContext;
46+
import io.netty.handler.codec.http.HttpServerCodec;
47+
import io.netty.handler.codec.http2.Http2Codec;
48+
import io.netty.handler.ssl.ApplicationProtocolNames;
49+
import io.netty.handler.ssl.ApplicationProtocolNegotiationHandler;
50+
import io.netty.handler.stream.ChunkedWriteHandler;
51+
52+
/**
53+
* Choose the handler implementation based on Http protocol.
54+
*
55+
* @author Pavel Bucek (pavel.bucek at oracle.com)
56+
*/
57+
class HttpVersionChooser extends ApplicationProtocolNegotiationHandler {
58+
59+
private final URI baseUri;
60+
private final NettyHttpContainer container;
61+
62+
HttpVersionChooser(URI baseUri, NettyHttpContainer container) {
63+
super(ApplicationProtocolNames.HTTP_1_1);
64+
65+
this.baseUri = baseUri;
66+
this.container = container;
67+
}
68+
69+
@Override
70+
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception {
71+
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
72+
ctx.pipeline().addLast(new Http2Codec(true, new JerseyHttp2ServerHandler(baseUri, container)));
73+
return;
74+
}
75+
76+
if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
77+
ctx.pipeline().addLast(new HttpServerCodec(),
78+
new ChunkedWriteHandler(),
79+
new JerseyServerHandler(baseUri, container));
80+
return;
81+
}
82+
83+
throw new IllegalStateException("Unknown protocol: " + protocol);
84+
}
85+
}
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
5+
*
6+
* The contents of this file are subject to the terms of either the GNU
7+
* General Public License Version 2 only ("GPL") or the Common Development
8+
* and Distribution License("CDDL") (collectively, the "License"). You
9+
* may not use this file except in compliance with the License. You can
10+
* obtain a copy of the License at
11+
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
12+
* or packager/legal/LICENSE.txt. See the License for the specific
13+
* language governing permissions and limitations under the License.
14+
*
15+
* When distributing the software, include this License Header Notice in each
16+
* file and include the License file at packager/legal/LICENSE.txt.
17+
*
18+
* GPL Classpath Exception:
19+
* Oracle designates this particular file as subject to the "Classpath"
20+
* exception as provided by Oracle in the GPL Version 2 section of the License
21+
* file that accompanied this code.
22+
*
23+
* Modifications:
24+
* If applicable, add the following below the License Header, with the fields
25+
* enclosed by brackets [] replaced by your own identifying information:
26+
* "Portions Copyright [year] [name of copyright owner]"
27+
*
28+
* Contributor(s):
29+
* If you wish your version of this file to be governed by only the CDDL or
30+
* only the GPL Version 2, indicate your decision by adding "[Contributor]
31+
* elects to include this software in this distribution under the [CDDL or GPL
32+
* Version 2] license." If you don't indicate a single choice of license, a
33+
* recipient has the option to distribute your version of this file under
34+
* either the CDDL, the GPL Version 2 or to extend the choice of license to
35+
* its licensees as provided above. However, if you add GPL Version 2 code
36+
* and therefore, elected the GPL Version 2 license, then the option applies
37+
* only if the new code is made subject to such option by the copyright
38+
* holder.
39+
*/
40+
41+
package org.glassfish.jersey.netty.httpserver;
42+
43+
import java.io.IOException;
44+
import java.io.InputStream;
45+
import java.net.URI;
46+
import java.security.Principal;
47+
import java.util.ArrayList;
48+
import java.util.Collection;
49+
import java.util.HashMap;
50+
import java.util.List;
51+
import java.util.Map;
52+
import java.util.concurrent.LinkedBlockingDeque;
53+
54+
import javax.ws.rs.core.SecurityContext;
55+
56+
import io.netty.buffer.ByteBufInputStream;
57+
import io.netty.channel.ChannelDuplexHandler;
58+
import io.netty.channel.ChannelHandler;
59+
import io.netty.channel.ChannelHandlerContext;
60+
import io.netty.handler.codec.http.HttpRequest;
61+
import io.netty.handler.codec.http2.Http2DataFrame;
62+
import io.netty.handler.codec.http2.Http2HeadersFrame;
63+
import io.netty.util.concurrent.Future;
64+
import io.netty.util.concurrent.GenericFutureListener;
65+
import org.glassfish.jersey.internal.PropertiesDelegate;
66+
import org.glassfish.jersey.netty.connector.internal.NettyInputStream;
67+
import org.glassfish.jersey.server.ContainerRequest;
68+
import org.glassfish.jersey.server.internal.ContainerUtils;
69+
70+
/**
71+
* Jersey Netty HTTP/2 handler.
72+
* <p>
73+
* Note that this implementation cannot be more experimental. Any contributions / feedback is welcomed.
74+
*
75+
* @author Pavel Bucek (pavel.bucek at oracle.com)
76+
*/
77+
@ChannelHandler.Sharable
78+
class JerseyHttp2ServerHandler extends ChannelDuplexHandler {
79+
80+
private final URI baseUri;
81+
private final LinkedBlockingDeque<InputStream> isList = new LinkedBlockingDeque<>();
82+
private final NettyHttpContainer container;
83+
84+
/**
85+
* Constructor.
86+
*
87+
* @param baseUri base {@link URI} of the container (includes context path, if any).
88+
* @param container Netty container implementation.
89+
*/
90+
JerseyHttp2ServerHandler(URI baseUri, NettyHttpContainer container) {
91+
this.baseUri = baseUri;
92+
this.container = container;
93+
}
94+
95+
@Override
96+
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
97+
ctx.close();
98+
}
99+
100+
@Override
101+
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
102+
if (msg instanceof Http2HeadersFrame) {
103+
onHeadersRead(ctx, (Http2HeadersFrame) msg);
104+
} else if (msg instanceof Http2DataFrame) {
105+
onDataRead(ctx, (Http2DataFrame) msg);
106+
} else {
107+
super.channelRead(ctx, msg);
108+
}
109+
}
110+
111+
/**
112+
* Process incoming data.
113+
*/
114+
private void onDataRead(ChannelHandlerContext ctx, Http2DataFrame data) throws Exception {
115+
isList.add(new ByteBufInputStream(data.content()));
116+
if (data.isEndStream()) {
117+
isList.add(NettyInputStream.END_OF_INPUT);
118+
}
119+
}
120+
121+
/**
122+
* Process incoming request (just a headers in this case, entity is processed separately).
123+
*/
124+
private void onHeadersRead(ChannelHandlerContext ctx, Http2HeadersFrame headers) throws Exception {
125+
126+
final ContainerRequest requestContext = createContainerRequest(ctx, headers);
127+
128+
requestContext.setWriter(new NettyHttp2ResponseWriter(ctx, headers, container));
129+
130+
// must be like this, since there is a blocking read from Jersey
131+
container.getExecutorService().execute(new Runnable() {
132+
@Override
133+
public void run() {
134+
container.getApplicationHandler().handle(requestContext);
135+
}
136+
});
137+
}
138+
139+
/**
140+
* Create Jersey {@link ContainerRequest} based on Netty {@link HttpRequest}.
141+
*
142+
* @param ctx Netty channel context.
143+
* @param http2Headers Netty Http/2 headers.
144+
* @return created Jersey Container Request.
145+
*/
146+
private ContainerRequest createContainerRequest(ChannelHandlerContext ctx, Http2HeadersFrame http2Headers) {
147+
148+
String path = http2Headers.headers().path().toString();
149+
150+
String s = path.startsWith("/") ? path.substring(1) : path;
151+
URI requestUri = URI.create(baseUri + ContainerUtils.encodeUnsafeCharacters(s));
152+
153+
ContainerRequest requestContext = new ContainerRequest(
154+
baseUri, requestUri, http2Headers.headers().method().toString(), getSecurityContext(),
155+
new PropertiesDelegate() {
156+
157+
private final Map<String, Object> properties = new HashMap<>();
158+
159+
@Override
160+
public Object getProperty(String name) {
161+
return properties.get(name);
162+
}
163+
164+
@Override
165+
public Collection<String> getPropertyNames() {
166+
return properties.keySet();
167+
}
168+
169+
@Override
170+
public void setProperty(String name, Object object) {
171+
properties.put(name, object);
172+
}
173+
174+
@Override
175+
public void removeProperty(String name) {
176+
properties.remove(name);
177+
}
178+
});
179+
180+
// request entity handling.
181+
if (!http2Headers.isEndStream()) {
182+
183+
ctx.channel().closeFuture().addListener(new GenericFutureListener<Future<? super Void>>() {
184+
@Override
185+
public void operationComplete(Future<? super Void> future) throws Exception {
186+
isList.add(NettyInputStream.END_OF_INPUT_ERROR);
187+
}
188+
});
189+
190+
requestContext.setEntityStream(new NettyInputStream(isList));
191+
} else {
192+
requestContext.setEntityStream(new InputStream() {
193+
@Override
194+
public int read() throws IOException {
195+
return -1;
196+
}
197+
});
198+
}
199+
200+
// copying headers from netty request to jersey container request context.
201+
for (CharSequence name : http2Headers.headers().names()) {
202+
requestContext.headers(name.toString(), mapToString(http2Headers.headers().getAll(name)));
203+
}
204+
205+
return requestContext;
206+
}
207+
208+
private List<String> mapToString(List<CharSequence> list) {
209+
ArrayList<String> result = new ArrayList<>(list.size());
210+
211+
for (CharSequence sequence : list) {
212+
result.add(sequence.toString());
213+
}
214+
215+
return result;
216+
}
217+
218+
private SecurityContext getSecurityContext() {
219+
return new SecurityContext() {
220+
221+
@Override
222+
public boolean isUserInRole(final String role) {
223+
return false;
224+
}
225+
226+
@Override
227+
public boolean isSecure() {
228+
return false;
229+
}
230+
231+
@Override
232+
public Principal getUserPrincipal() {
233+
return null;
234+
}
235+
236+
@Override
237+
public String getAuthenticationScheme() {
238+
return null;
239+
}
240+
};
241+
}
242+
}

0 commit comments

Comments
 (0)