jetty.project
jetty.project copied to clipboard
Error 400 - Ambiguous URI Empty Segment
Jetty Version 12.0.3
Jetty Environment ee8
Java Version JavaSE-17
Question I just migrated from Jetty 10.0.15 to 12.0.3 and I keep getting the following error: URI: /badURI STATUS: 400 MESSAGE: Ambiguous URI empty segment CAUSED BY: org.eclipse.jetty.http.BadMessageException: 400: Ambiguous URI empty segment
Any ideas what could be causing this?
What is your URI?
@Justvuur It means your URI has an empty segment (//
) which makes it ambiguous. This is because it could be an attempt to bypass some security constraints.
You can set the URI compliance mode in the HttpConfiguration
.
See https://eclipse.dev/jetty/javadoc/jetty-12/org/eclipse/jetty/http/UriCompliance.Violation.html#AMBIGUOUS_EMPTY_SEGMENT
When allowing this Violation, the application developer/deployer must ensure that the application behaves as desired when it receives a URI path containing //. Specifically, any URI pattern matching for security concerns needs to be carefully audited.
If any vaiolation of UriCompliance exists at entry point of ServletContextRequest , Jetty12 using Servlet returns 400 Bad Request.
So, Whether HttpConfiguration is set or not, any violation is not allowed implicitly with using Servlet.
It might be good that UriCompliance check should be taken into account about value of HttpConfiguration#getHttpCompliance
https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextRequest.java#L195-L208
If you need shorthand workaround, make EmptySegmentHandler and make it handle before ServletContextHandler
public class UriComplianceCheckHandler extends Handler.Wrapper {
public UriComplianceCheckHandler(Handler handler) {
super(handler);
}
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception {
if (request.getHttpURI().hasViolations()) {
return super.handle(rewriteRequest(request), response, callback);
} else {
return super.handle(request, response, callback);
}
}
Request rewriteRequest(Request request) {
log.warn("There are HttpURI Violation exists. HttpURI:{}. Violations:{}", request.getHttpURI(),
request.getHttpURI().getViolations());
if (request.getHttpURI().hasViolation(UriCompliance.Violation.AMBIGUOUS_EMPTY_SEGMENT)) {
HttpURI rewrite = rewriteHttpURL(request);
return Request.serveAs(request, rewrite);
}
return request;
}
private HttpURI rewriteHttpURL(Request base) {
String param = base.getHttpURI().getParam();
String query = base.getHttpURI().getQuery();
List<String> segments = Arrays
.stream(base.getHttpURI().getPath().split("\\/"))
.filter(v -> !v.isEmpty())
.toList();
String newPath = "/" + StringUtils.join(segments, "/");
return HttpURI.build(base.getHttpURI(), newPath, param, query).asImmutable();
}
}
URI
http://localhost:9998/static/
Ok, I managed to get it working but using http.setUriCompliance(UriCompliance.LEGACY);
but I noticed that something somewhere is appending the extra "/". Even though I navigate to "http://localhost:9998/static/", it ends up being "http://localhost:9998/static//".
Any ideas why?
If you see http://localhost:9998/static//
with UriCompliance.LEGACY, then that means that is exactly the path as it was sent on the HTTP request from your User-Agent (HTTP Client).
That would also explain the "400: Ambiguous URI empty segment" you were seeing.
That last segment //
is valid per URI rules, and it means a segment with no value (hence ambiguous).
It also invalid per Servlet rules, as it cannot match a context-path, url-pattern, or constraint.
To address this problem you have to address what is going on with your HTTP Client. You can confirm this easily with a network traffic capturing tool like wireshark.
Jetty does not "clean up" these kinds of URIs as there are some libraries that rely on this ambiguity (esp security frameworks).