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

Commit a77cf1a

Browse files
Marek PotočiarGerrit Code Review
authored andcommitted
Merge "JERSEY-2525 Incoming URL components are parsed for bracket paths"
2 parents 062dbc0 + 98f9682 commit a77cf1a

File tree

6 files changed

+217
-7
lines changed

6 files changed

+217
-7
lines changed

containers/grizzly2-http/src/main/java/org/glassfish/jersey/grizzly2/httpserver/GrizzlyHttpContainer.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import org.glassfish.jersey.server.ResourceConfig;
7272
import org.glassfish.jersey.server.ServerProperties;
7373
import org.glassfish.jersey.server.internal.ConfigHelper;
74+
import org.glassfish.jersey.server.internal.ContainerUtils;
7475
import org.glassfish.jersey.server.spi.Container;
7576
import org.glassfish.jersey.server.spi.ContainerLifecycleListener;
7677
import org.glassfish.jersey.server.spi.ContainerResponseWriter;
@@ -453,7 +454,7 @@ private URI getRequestUri(URI baseUri, Request grizzlyRequest) {
453454

454455
String queryString = grizzlyRequest.getQueryString();
455456
if (queryString != null) {
456-
originalUri = originalUri + "?" + queryString;
457+
originalUri = originalUri + "?" + ContainerUtils.encodeUnsafeCharacters(queryString);
457458
}
458459

459460
return baseUri.resolve(originalUri);

containers/jersey-servlet-core/src/main/java/org/glassfish/jersey/servlet/ServletContainer.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import org.glassfish.jersey.server.ResourceConfig;
7070
import org.glassfish.jersey.server.ServerProperties;
7171
import org.glassfish.jersey.server.internal.ConfigHelper;
72+
import org.glassfish.jersey.server.internal.ContainerUtils;
7273
import org.glassfish.jersey.server.spi.Container;
7374
import org.glassfish.jersey.server.spi.ContainerLifecycleListener;
7475
import org.glassfish.jersey.servlet.internal.LocalizationMessages;
@@ -320,10 +321,8 @@ protected void service(HttpServletRequest request, HttpServletResponse response)
320321
final URI baseUri;
321322
final URI requestUri;
322323
try {
323-
baseUri = absoluteUriBuilder.replacePath(encodedBasePath).
324-
build();
325-
326-
String queryParameters = request.getQueryString();
324+
baseUri = absoluteUriBuilder.replacePath(encodedBasePath).build();
325+
String queryParameters = ContainerUtils.encodeUnsafeCharacters(request.getQueryString());
327326
if (queryParameters == null) {
328327
queryParameters = "";
329328
}

containers/jetty-http/src/main/java/org/glassfish/jersey/jetty/JettyHttpContainer.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import javax.servlet.http.HttpServletRequest;
6262
import javax.servlet.http.HttpServletResponse;
6363

64+
import org.eclipse.jetty.http.HttpURI;
6465
import org.glassfish.jersey.internal.MapPropertiesDelegate;
6566
import org.glassfish.jersey.internal.inject.ReferencingFactory;
6667
import org.glassfish.jersey.internal.util.ExtendedLogger;
@@ -74,6 +75,7 @@
7475
import org.glassfish.jersey.server.ResourceConfig;
7576
import org.glassfish.jersey.server.ServerProperties;
7677
import org.glassfish.jersey.server.internal.ConfigHelper;
78+
import org.glassfish.jersey.server.internal.ContainerUtils;
7779
import org.glassfish.jersey.server.spi.Container;
7880
import org.glassfish.jersey.server.spi.ContainerLifecycleListener;
7981
import org.glassfish.jersey.server.spi.ContainerResponseWriter;
@@ -164,8 +166,12 @@ public void handle(String target, final Request request, final HttpServletReques
164166
final Response response = Response.getResponse(httpServletResponse);
165167
final ResponseWriter responseWriter = new ResponseWriter(request, response, configSetStatusOverSendError);
166168
final URI baseUri = getBaseUri(request);
167-
final URI requestUri = baseUri.resolve(request.getUri().toString());
168169

170+
final String originalQuery = request.getUri().getQuery();
171+
final String encodedQuery = ContainerUtils.encodeUnsafeCharacters(originalQuery);
172+
final String uriString = (originalQuery == null || originalQuery.isEmpty() || originalQuery.equals(encodedQuery)) ?
173+
request.getUri().toString() : request.getUri().toString().replace(originalQuery, encodedQuery);
174+
final URI requestUri = baseUri.resolve(uriString);
169175
try {
170176
final ContainerRequest requestContext = new ContainerRequest(
171177
baseUri,

containers/simple-http/src/main/java/org/glassfish/jersey/simple/SimpleContainer.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import org.glassfish.jersey.server.ContainerResponse;
6969
import org.glassfish.jersey.server.ResourceConfig;
7070
import org.glassfish.jersey.server.internal.ConfigHelper;
71+
import org.glassfish.jersey.server.internal.ContainerUtils;
7172
import org.glassfish.jersey.server.spi.Container;
7273
import org.glassfish.jersey.server.spi.ContainerLifecycleListener;
7374
import org.glassfish.jersey.server.spi.ContainerResponseWriter;
@@ -232,7 +233,7 @@ private void rethrow(Throwable error) {
232233
public void handle(final Request request, final Response response) {
233234
final Writer responseWriter = new Writer(response);
234235
final URI baseUri = getBaseUri(request);
235-
final URI requestUri = baseUri.resolve(request.getTarget());
236+
final URI requestUri = baseUri.resolve(ContainerUtils.encodeUnsafeCharacters(request.getTarget()));
236237

237238
try {
238239
final ContainerRequest requestContext = new ContainerRequest(
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2010-2014 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+
package org.glassfish.jersey.server.internal;
41+
42+
import javax.ws.rs.core.UriBuilder;
43+
import java.net.URI;
44+
45+
/**
46+
* Utility methods used by container implementations.
47+
*
48+
* @author Adam Lindenthal (adam.lindenthal at oracle.com)
49+
*/
50+
public class ContainerUtils {
51+
52+
/**
53+
* Encodes (predefined subset of) unsafe/unwise URI characters with the percent-encoding.
54+
* <p/>
55+
* <p>Replaces the predefined set of unsafe URI characters in the query string with its percent-encoded counterparts. The
56+
* reserved characters (as defined by the RFC) are automatically encoded by browsers, but some characters are in the "gray
57+
* zone" - are not explicitly forbidden, but not recommended and known to cause issues.</p>
58+
* <p/>
59+
* <p>Currently, the method only encodes the curly brackets ({@code \{} and {@code \}}),
60+
* if any, to allow URI request parameters to contain JSON.</p>
61+
*
62+
* @param originalQueryString URI query string (the part behind the question mark character).
63+
* @return the same string with unsafe characters (currently only curly brackets) eventually percent encoded.
64+
*/
65+
public static String encodeUnsafeCharacters(String originalQueryString) {
66+
if (originalQueryString == null) {
67+
return null;
68+
}
69+
if (originalQueryString.contains("{") || originalQueryString.contains("}")) {
70+
return originalQueryString.replace("{", "%7B").replace("}", "%7D");
71+
}
72+
return originalQueryString;
73+
}
74+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3+
*
4+
* Copyright (c) 2014 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+
package org.glassfish.jersey.tests.api;
41+
42+
import org.glassfish.jersey.filter.LoggingFilter;
43+
import org.glassfish.jersey.server.ResourceConfig;
44+
import org.glassfish.jersey.test.JerseyTest;
45+
import org.glassfish.jersey.test.TestProperties;
46+
import org.junit.Test;
47+
48+
import javax.ws.rs.DefaultValue;
49+
import javax.ws.rs.GET;
50+
import javax.ws.rs.Path;
51+
import javax.ws.rs.QueryParam;
52+
import javax.ws.rs.core.Response;
53+
import java.io.BufferedReader;
54+
import java.io.BufferedWriter;
55+
import java.io.IOException;
56+
import java.io.InputStreamReader;
57+
import java.io.OutputStreamWriter;
58+
import java.io.PrintWriter;
59+
import java.net.Socket;
60+
import java.util.logging.Logger;
61+
62+
import static org.junit.Assert.assertEquals;
63+
64+
/**
65+
* Test if URI can contain a JSON in the query parameter.
66+
*
67+
* @author Adam Lindenthal (adam.lindenthal at oracle.com)
68+
*/
69+
public class JsonInUriTest extends JerseyTest {
70+
private static final Logger LOGGER = Logger.getLogger(JsonInUriTest.class.getName());
71+
72+
@Override
73+
protected ResourceConfig configure() {
74+
ResourceConfig rc = new ResourceConfig(JsonInUriTest.ResponseTest.class);
75+
return rc;
76+
}
77+
78+
/**
79+
* Test resource
80+
*/
81+
@Path(value = "/app")
82+
public static class ResponseTest {
83+
/**
84+
* Test resource method returning the content of the {@code json} query parameter.
85+
*
86+
* @return the {@code json} query parameter (as received)
87+
*/
88+
@GET
89+
@Path("test")
90+
public Response jsonQueryParamTest(@DefaultValue("") @QueryParam("json") String json) {
91+
return Response.ok().entity(json).build();
92+
}
93+
94+
}
95+
96+
/**
97+
* Test, that server can consume JSON sent in the query parameter
98+
*
99+
* @throws IOException
100+
*/
101+
@Test
102+
public void testJsonInUriWithSockets() throws IOException {
103+
// Low level approach with sockets is used, because common Java HTTP clients are using java.net.URI,
104+
// which fails when unencoded curly bracket is part of the URI
105+
Socket socket = new Socket(getBaseUri().getHost(), getBaseUri().getPort());
106+
PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
107+
108+
// quotes are encoded by browsers, curly brackets are not, so the quotes will be sent pre-encoded
109+
// HTTP 1.0 is used for simplicity
110+
pw.println("GET /app/test?json={%22foo%22:%22bar%22} HTTP/1.0");
111+
pw.println(); // http request should end with a blank line
112+
pw.flush();
113+
114+
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
115+
116+
String lastLine = null;
117+
String line;
118+
while ((line = br.readLine()) != null) {
119+
// read the response and remember the last line
120+
lastLine = line;
121+
}
122+
assertEquals("{\"foo\":\"bar\"}", lastLine);
123+
br.close();
124+
}
125+
}
126+
127+
128+
129+

0 commit comments

Comments
 (0)