Fixes #11223 - WebSocketClient.connect with URI including query parameters don't work for HTTP2 connector

Fixed concatenation of path and query for HTTP/2 and HTTP/3 when creating `MetaData.ConnectRequest` to "tunnel" the WebSocket upgrade.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2024-01-05 11:42:11 +01:00
parent 4853da2f07
commit 9b2cfc4483
4 changed files with 31 additions and 13 deletions

View File

@ -224,26 +224,35 @@ public class MetaData implements Iterable<HttpField>
{ {
private final String _protocol; private final String _protocol;
public ConnectRequest(HttpScheme scheme, HostPortHttpField authority, String path, HttpFields fields, String protocol) public ConnectRequest(HttpScheme scheme, HostPortHttpField authority, String pathQuery, HttpFields fields, String protocol)
{ {
this(scheme == null ? null : scheme.asString(), authority, path, fields, protocol); this(scheme == null ? null : scheme.asString(), authority, pathQuery, fields, protocol);
} }
public ConnectRequest(long beginNanoTime, HttpScheme scheme, HostPortHttpField authority, String path, HttpFields fields, String protocol) public ConnectRequest(long beginNanoTime, HttpScheme scheme, HostPortHttpField authority, String pathQuery, HttpFields fields, String protocol)
{ {
this(beginNanoTime, scheme == null ? null : scheme.asString(), authority, path, fields, protocol); this(beginNanoTime, scheme == null ? null : scheme.asString(), authority, pathQuery, fields, protocol);
} }
public ConnectRequest(String scheme, HostPortHttpField authority, String path, HttpFields fields, String protocol) public ConnectRequest(String scheme, HostPortHttpField authority, String pathQuery, HttpFields fields, String protocol)
{ {
this(NanoTime.now(), scheme, authority, path, fields, protocol); this(NanoTime.now(), scheme, authority, pathQuery, fields, protocol);
} }
public ConnectRequest(long beginNanoTime, String scheme, HostPortHttpField authority, String path, HttpFields fields, String protocol) public ConnectRequest(long beginNanoTime, String scheme, HostPortHttpField authority, String pathQuery, HttpFields fields, String protocol)
{ {
super(beginNanoTime, HttpMethod.CONNECT.asString(), super(beginNanoTime,
HttpURI.build().scheme(scheme).host(authority == null ? null : authority.getHost()).port(authority == null ? -1 : authority.getPort()).pathQuery(path), HttpMethod.CONNECT.asString(),
HttpVersion.HTTP_2, fields, Long.MIN_VALUE, null); HttpURI.build()
.scheme(scheme)
.host(authority == null ? null : authority.getHost())
.port(authority == null ? -1 : authority.getPort())
.pathQuery(pathQuery),
HttpVersion.HTTP_2,
fields,
Long.MIN_VALUE,
null
);
_protocol = protocol; _protocol = protocol;
} }

View File

@ -33,6 +33,7 @@ import org.eclipse.jetty.http2.frames.HeadersFrame;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.URIUtil;
public class HttpSenderOverHTTP2 extends HttpSender public class HttpSenderOverHTTP2 extends HttpSender
{ {
@ -63,7 +64,8 @@ public class HttpSenderOverHTTP2 extends HttpSender
else else
{ {
HostPortHttpField authority = new HostPortHttpField(request.getHost(), request.getPort()); HostPortHttpField authority = new HostPortHttpField(request.getHost(), request.getPort());
metaData = new MetaData.ConnectRequest(request.getScheme(), authority, request.getPath(), request.getHeaders(), upgradeProtocol); String pathQuery = URIUtil.addPathQuery(request.getPath(), request.getQuery());
metaData = new MetaData.ConnectRequest(request.getScheme(), authority, pathQuery, request.getHeaders(), upgradeProtocol);
} }
} }
else else

View File

@ -33,6 +33,7 @@ import org.eclipse.jetty.http3.frames.HeadersFrame;
import org.eclipse.jetty.http3.internal.HTTP3Stream; import org.eclipse.jetty.http3.internal.HTTP3Stream;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.URIUtil;
public class HttpSenderOverHTTP3 extends HttpSender public class HttpSenderOverHTTP3 extends HttpSender
{ {
@ -63,7 +64,8 @@ public class HttpSenderOverHTTP3 extends HttpSender
else else
{ {
HostPortHttpField authority = new HostPortHttpField(request.getHost(), request.getPort()); HostPortHttpField authority = new HostPortHttpField(request.getHost(), request.getPort());
metaData = new MetaData.ConnectRequest(request.getScheme(), authority, request.getPath(), request.getHeaders(), upgradeProtocol); String pathQuery = URIUtil.addPathQuery(request.getPath(), request.getQuery());
metaData = new MetaData.ConnectRequest(request.getScheme(), authority, pathQuery, request.getHeaders(), upgradeProtocol);
} }
} }
else else

View File

@ -162,7 +162,7 @@ public class WebSocketOverHTTP2Test
startClient(protocolFn); startClient(protocolFn);
EventSocket wsEndPoint = new EventSocket(); EventSocket wsEndPoint = new EventSocket();
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/ws/echo"); URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/ws/echo/query?param=value");
Session session = wsClient.connect(wsEndPoint, uri).get(5, TimeUnit.SECONDS); Session session = wsClient.connect(wsEndPoint, uri).get(5, TimeUnit.SECONDS);
String text = "websocket"; String text = "websocket";
@ -382,6 +382,11 @@ public class WebSocketOverHTTP2Test
protected void configure(JettyWebSocketServletFactory factory) protected void configure(JettyWebSocketServletFactory factory)
{ {
factory.addMapping("/ws/echo", (request, response) -> new EchoSocket()); factory.addMapping("/ws/echo", (request, response) -> new EchoSocket());
factory.addMapping("/ws/echo/query", (request, response) ->
{
assertNotNull(request.getQueryString());
return new EchoSocket();
});
factory.addMapping("/ws/null", (request, response) -> null); factory.addMapping("/ws/null", (request, response) -> null);
factory.addMapping("/ws/throw", (request, response) -> factory.addMapping("/ws/throw", (request, response) ->
{ {