Issue #417 (HttpClient: review support for OPTIONS *)

Implemented support for OPTIONS * HTTP/1.1 requests.
This commit is contained in:
Simone Bordet 2016-03-10 14:31:50 +01:00
parent 8cefb38788
commit e6c2c81bea
12 changed files with 149 additions and 24 deletions

View File

@ -117,12 +117,12 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
Authentication authentication = null;
Authentication.HeaderInfo headerInfo = null;
URI uri = getAuthenticationURI(request);
if (uri != null)
URI authURI = getAuthenticationURI(request);
if (authURI != null)
{
for (Authentication.HeaderInfo element : headerInfos)
{
authentication = client.getAuthenticationStore().findAuthentication(element.getType(), uri, element.getRealm());
authentication = client.getAuthenticationStore().findAuthentication(element.getType(), authURI, element.getRealm());
if (authentication != null)
{
headerInfo = element;
@ -151,7 +151,21 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
conversation.setAttribute(authenticationAttribute, true);
Request newRequest = client.copyRequest(request, request.getURI());
URI requestURI = request.getURI();
String path = null;
if (requestURI == null)
{
String uri = request.getScheme() + "://" + request.getHost();
int port = request.getPort();
if (port > 0)
uri += ":" + port;
requestURI = URI.create(uri);
path = request.getPath();
}
Request newRequest = client.copyRequest(request, requestURI);
if (path != null)
newRequest.path(path);
authnResult.apply(newRequest);
// Copy existing, explicitly set, authorization headers.
copyIfAbsent(request, newRequest, HttpHeader.AUTHORIZATION);

View File

@ -408,9 +408,9 @@ public class HttpClient extends ContainerLifeCycle
}
/**
* Creates a new request with the specified URI.
* Creates a new request with the specified absolute URI in string format.
*
* @param uri the URI to request
* @param uri the request absolute URI
* @return the request just created
*/
public Request newRequest(String uri)
@ -419,9 +419,9 @@ public class HttpClient extends ContainerLifeCycle
}
/**
* Creates a new request with the specified URI.
* Creates a new request with the specified absolute URI.
*
* @param uri the URI to request
* @param uri the request absolute URI
* @return the request just created
*/
public Request newRequest(URI uri)

View File

@ -102,9 +102,12 @@ public abstract class HttpConnection implements Connection
path = "/";
request.path(path);
}
if (proxy != null && !HttpMethod.CONNECT.is(method))
URI uri = request.getURI();
if (proxy != null && !HttpMethod.CONNECT.is(method) && uri != null)
{
path = request.getURI().toString();
path = uri.toString();
request.path(path);
}
@ -144,7 +147,6 @@ public abstract class HttpConnection implements Connection
CookieStore cookieStore = getHttpClient().getCookieStore();
if (cookieStore != null)
{
URI uri = request.getURI();
StringBuilder cookies = null;
if (uri != null)
cookies = convertCookies(cookieStore.get(uri), null);
@ -155,7 +157,7 @@ public abstract class HttpConnection implements Connection
// Authentication
applyAuthentication(request, proxy != null ? proxy.getURI() : null);
applyAuthentication(request, request.getURI());
applyAuthentication(request, uri);
}
private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder)

View File

@ -184,7 +184,9 @@ public abstract class HttpReceiver
case SET_COOKIE:
case SET_COOKIE2:
{
storeCookie(exchange.getRequest().getURI(), field);
URI uri = exchange.getRequest().getURI();
if (uri != null)
storeCookie(uri, field);
break;
}
default:

View File

@ -229,7 +229,18 @@ public class HttpRedirector
private Request redirect(Request request, Response response, Response.CompleteListener listener, URI newURI)
{
if (!newURI.isAbsolute())
newURI = request.getURI().resolve(newURI);
{
URI requestURI = request.getURI();
if (requestURI == null)
{
String uri = request.getScheme() + "://" + request.getHost();
int port = request.getPort();
if (port > 0)
uri += ":" + port;
requestURI = URI.create(uri);
}
newURI = requestURI.resolve(newURI);
}
int status = response.getStatus();
switch (status)

View File

@ -92,6 +92,7 @@ public class HttpRequest implements Request
path = uri.getRawPath();
query = uri.getRawQuery();
extractParams(query);
followRedirects(client.isFollowRedirects());
idleTimeout = client.getIdleTimeout();
HttpField acceptEncodingField = client.getAcceptEncodingField();
@ -797,12 +798,15 @@ public class HttpRequest implements Request
{
try
{
// Handle specially the "OPTIONS *" case, since it is possible to create a URI from "*" (!).
if ("*".equals(uri))
return null;
return new URI(uri);
}
catch (URISyntaxException x)
{
// The "path" of a HTTP request may not be a URI,
// for example for CONNECT 127.0.0.1:8080 or OPTIONS *.
// for example for CONNECT 127.0.0.1:8080.
return null;
}
}

View File

@ -208,7 +208,8 @@ public class DigestAuthentication extends AbstractAuthentication
String A1 = user + ":" + realm + ":" + password;
String hashA1 = toHexString(digester.digest(A1.getBytes(StandardCharsets.ISO_8859_1)));
String A2 = request.getMethod() + ":" + request.getURI();
URI uri = request.getURI();
String A2 = request.getMethod() + ":" + uri;
if ("auth-int".equals(qop))
A2 += ":" + toHexString(digester.digest(content));
String hashA2 = toHexString(digester.digest(A2.getBytes(StandardCharsets.ISO_8859_1)));
@ -237,7 +238,7 @@ public class DigestAuthentication extends AbstractAuthentication
if (opaque != null)
value.append(", opaque=\"").append(opaque).append("\"");
value.append(", algorithm=\"").append(algorithm).append("\"");
value.append(", uri=\"").append(request.getURI()).append("\"");
value.append(", uri=\"").append(uri).append("\"");
if (qop != null)
{
value.append(", qop=\"").append(qop).append("\"");

View File

@ -29,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.Fields;
@ -472,4 +473,35 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
@Test
public void testAsteriskFormTarget() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
Assert.assertEquals("*", target);
Assert.assertEquals("*", request.getPathInfo());
}
});
Request request = client.newRequest("localhost", connector.getLocalPort())
.method(HttpMethod.OPTIONS)
.scheme(scheme)
.path("*")
.timeout(5, TimeUnit.SECONDS);
Assert.assertEquals("*", request.getPath());
Assert.assertNull(request.getQuery());
Fields params = request.getParams();
Assert.assertEquals(0, params.getSize());
Assert.assertNull(request.getURI());
ContentResponse response = request.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
}

View File

@ -64,9 +64,9 @@ public class HttpSenderOverFCGI extends HttpSender
// FastCGI headers based on the URI
URI uri = request.getURI();
String path = uri.getRawPath();
String path = uri == null ? request.getPath() : uri.getRawPath();
fcgiHeaders.put(FCGI.Headers.DOCUMENT_URI, path);
String query = uri.getRawQuery();
String query = uri == null ? null : uri.getRawQuery();
fcgiHeaders.put(FCGI.Headers.QUERY_STRING, query == null ? "" : query);
// FastCGI headers based on HTTP headers

View File

@ -188,8 +188,8 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
fastCGIHeaders.put(FCGI.Headers.HTTPS, "on");
URI proxyRequestURI = proxyRequest.getURI();
String rawPath = proxyRequestURI.getRawPath();
String rawQuery = proxyRequestURI.getRawQuery();
String rawPath = proxyRequestURI == null ? proxyRequest.getPath() : proxyRequestURI.getRawPath();
String rawQuery = proxyRequestURI == null ? null : proxyRequestURI.getRawQuery();
String requestURI = (String)proxyRequest.getAttributes().get(REQUEST_URI_ATTRIBUTE);
if (requestURI == null)

View File

@ -192,22 +192,27 @@ public abstract class AbstractTest
return http2Client;
}
protected String newURI()
protected String getScheme()
{
switch (transport)
{
case HTTP:
case H2C:
case FCGI:
return "http://localhost:" + connector.getLocalPort();
return "http";
case HTTPS:
case H2:
return "https://localhost:" + connector.getLocalPort();
return "https";
default:
throw new IllegalArgumentException();
}
}
protected String newURI()
{
return getScheme() + "://localhost:" + connector.getLocalPort();
}
@After
public void stop() throws Exception
{

View File

@ -316,6 +316,60 @@ public class HttpClientTest extends AbstractTest
.send();
}
@Test
public void testOPTIONS() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
Assert.assertTrue(HttpMethod.OPTIONS.is(request.getMethod()));
Assert.assertEquals("*", target);
Assert.assertEquals("*", request.getPathInfo());
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(getScheme())
.method(HttpMethod.OPTIONS)
.path("*")
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
@Test
public void testOPTIONSWithRelativeRedirect() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
if ("*".equals(target))
{
// Be nasty and send a relative redirect.
// Code 303 will change the method to GET.
response.setStatus(HttpStatus.SEE_OTHER_303);
response.setHeader("Location", "/");
}
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(getScheme())
.method(HttpMethod.OPTIONS)
.path("*")
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
private void sleep(long time) throws IOException
{
try