Merge remote-tracking branch 'origin/jetty-12.0.x' into jetty-12.0.x-HttpContent-Caching-Refactor
This commit is contained in:
commit
6facedba75
|
@ -71,7 +71,7 @@ jobs:
|
|||
# queries: security-extended,security-and-quality
|
||||
|
||||
- name: Set up Maven
|
||||
uses: stCarolas/setup-maven@v4
|
||||
uses: stCarolas/setup-maven@v.4.5
|
||||
with:
|
||||
maven-version: 3.8.6
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ pipeline {
|
|||
options {
|
||||
skipDefaultCheckout()
|
||||
durabilityHint('PERFORMANCE_OPTIMIZED')
|
||||
buildDiscarder logRotator( numToKeepStr: '60' )
|
||||
}
|
||||
stages {
|
||||
stage("Parallel Stage") {
|
||||
|
@ -13,34 +14,32 @@ pipeline {
|
|||
stage("Build / Test - JDK17") {
|
||||
agent { node { label 'linux' } }
|
||||
steps {
|
||||
container('jetty-build') {
|
||||
timeout( time: 180, unit: 'MINUTES' ) {
|
||||
checkout scm
|
||||
mavenBuild( "jdk17", "clean install javadoc:javadoc -Perrorprone", "maven3")
|
||||
// Collect up the jacoco execution results (only on main build)
|
||||
jacoco inclusionPattern: '**/org/eclipse/jetty/**/*.class',
|
||||
exclusionPattern: '' +
|
||||
// build tools
|
||||
'**/org/eclipse/jetty/ant/**' +
|
||||
',*/org/eclipse/jetty/maven/its/**' +
|
||||
',**/org/eclipse/jetty/its/**' +
|
||||
// example code / documentation
|
||||
',**/org/eclipse/jetty/embedded/**' +
|
||||
',**/org/eclipse/jetty/asyncrest/**' +
|
||||
',**/org/eclipse/jetty/demo/**' +
|
||||
// special environments / late integrations
|
||||
',**/org/eclipse/jetty/gcloud/**' +
|
||||
',**/org/eclipse/jetty/infinispan/**' +
|
||||
',**/org/eclipse/jetty/osgi/**' +
|
||||
',**/org/eclipse/jetty/http/spi/**' +
|
||||
// test classes
|
||||
',**/org/eclipse/jetty/tests/**' +
|
||||
',**/org/eclipse/jetty/test/**',
|
||||
execPattern: '**/target/jacoco.exec',
|
||||
classPattern: '**/target/classes',
|
||||
sourcePattern: '**/src/main/java'
|
||||
recordIssues id: "jdk17", name: "Static Analysis jdk17", aggregatingResults: true, enabledForFailure: true, tools: [mavenConsole(), java(), checkStyle(), errorProne(), spotBugs()]
|
||||
}
|
||||
timeout( time: 180, unit: 'MINUTES' ) {
|
||||
checkout scm
|
||||
mavenBuild( "jdk17", "clean install javadoc:javadoc -Perrorprone", "maven3")
|
||||
// Collect up the jacoco execution results (only on main build)
|
||||
jacoco inclusionPattern: '**/org/eclipse/jetty/**/*.class',
|
||||
exclusionPattern: '' +
|
||||
// build tools
|
||||
'**/org/eclipse/jetty/ant/**' +
|
||||
',*/org/eclipse/jetty/maven/its/**' +
|
||||
',**/org/eclipse/jetty/its/**' +
|
||||
// example code / documentation
|
||||
',**/org/eclipse/jetty/embedded/**' +
|
||||
',**/org/eclipse/jetty/asyncrest/**' +
|
||||
',**/org/eclipse/jetty/demo/**' +
|
||||
// special environments / late integrations
|
||||
',**/org/eclipse/jetty/gcloud/**' +
|
||||
',**/org/eclipse/jetty/infinispan/**' +
|
||||
',**/org/eclipse/jetty/osgi/**' +
|
||||
',**/org/eclipse/jetty/http/spi/**' +
|
||||
// test classes
|
||||
',**/org/eclipse/jetty/tests/**' +
|
||||
',**/org/eclipse/jetty/test/**',
|
||||
execPattern: '**/target/jacoco.exec',
|
||||
classPattern: '**/target/classes',
|
||||
sourcePattern: '**/src/main/java'
|
||||
recordIssues id: "jdk17", name: "Static Analysis jdk17", aggregatingResults: true, enabledForFailure: true, tools: [mavenConsole(), java(), checkStyle(), errorProne(), spotBugs()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -659,7 +659,7 @@ public class HTTPClientDocs
|
|||
|
||||
// Add the new proxy to the list of proxies already registered.
|
||||
ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
|
||||
proxyConfig.getProxies().add(proxy);
|
||||
proxyConfig.addProxy(proxy);
|
||||
|
||||
ContentResponse response = httpClient.GET("http://domain.com/path");
|
||||
// end::proxy[]
|
||||
|
@ -684,7 +684,7 @@ public class HTTPClientDocs
|
|||
// Proxy configuration.
|
||||
ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
|
||||
HttpProxy proxy = new HttpProxy("proxy.net", 8080);
|
||||
proxyConfig.getProxies().add(proxy);
|
||||
proxyConfig.addProxy(proxy);
|
||||
|
||||
ContentResponse response = httpClient.newRequest(serverURI).send();
|
||||
// end::proxyAuthentication[]
|
||||
|
|
|
@ -54,7 +54,7 @@ public class WebSocketClientDocs
|
|||
// Instantiate and configure HttpClient.
|
||||
HttpClient httpClient = new HttpClient();
|
||||
// For example, configure a proxy.
|
||||
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", 8888));
|
||||
httpClient.getProxyConfiguration().addProxy(new HttpProxy("localhost", 8888));
|
||||
|
||||
// Instantiate WebSocketClient, passing HttpClient to the constructor.
|
||||
WebSocketClient webSocketClient = new WebSocketClient(httpClient);
|
||||
|
|
|
@ -165,7 +165,7 @@ public class HandlerDocs
|
|||
public Request.Processor handle(Request request) throws Exception
|
||||
{
|
||||
if (HttpMethod.GET.is(request.getMethod()) &&
|
||||
"greeting".equals(request.getPathInContext()))
|
||||
"greeting".equals(Request.getPathInContext(request)))
|
||||
return this;
|
||||
return null;
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ public class HandlerDocs
|
|||
{
|
||||
String name = handler.getClass().getSimpleName().replace("Handler", "");
|
||||
String path = "/" + name;
|
||||
if (request.getPathInContext().equals(name))
|
||||
if (Request.getPathInContext(request).equals(name))
|
||||
{
|
||||
Request.Processor processor = handler.handle(request);
|
||||
if (processor != null)
|
||||
|
|
|
@ -14,13 +14,14 @@
|
|||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.io.ClientConnectionFactory;
|
||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
||||
import org.eclipse.jetty.util.HostPort;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
|
||||
|
@ -30,23 +31,40 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
* Applications add subclasses of {@link Proxy} to this configuration via:
|
||||
* <pre>
|
||||
* ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
|
||||
* proxyConfig.getProxies().add(new HttpProxy(proxyHost, 8080));
|
||||
* proxyConfig.addProxy(new HttpProxy(proxyHost, 8080));
|
||||
* </pre>
|
||||
*
|
||||
* @see HttpClient#getProxyConfiguration()
|
||||
*/
|
||||
public class ProxyConfiguration
|
||||
{
|
||||
private final List<Proxy> proxies = new ArrayList<>();
|
||||
private final List<Proxy> proxies = new BlockingArrayQueue<>();
|
||||
|
||||
public List<Proxy> getProxies()
|
||||
/**
|
||||
* Adds a proxy.
|
||||
*
|
||||
* @param proxy a proxy
|
||||
* @throws NullPointerException if {@code proxy} is null
|
||||
*/
|
||||
public void addProxy(Proxy proxy)
|
||||
{
|
||||
return proxies;
|
||||
proxies.add(Objects.requireNonNull(proxy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a proxy.
|
||||
*
|
||||
* @param proxy a proxy
|
||||
* @return true if a match is found
|
||||
*/
|
||||
public boolean removeProxy(Proxy proxy)
|
||||
{
|
||||
return proxies.remove(proxy);
|
||||
}
|
||||
|
||||
public Proxy match(Origin origin)
|
||||
{
|
||||
for (Proxy proxy : getProxies())
|
||||
for (Proxy proxy : proxies)
|
||||
{
|
||||
if (proxy.matches(origin))
|
||||
return proxy;
|
||||
|
|
|
@ -95,7 +95,7 @@ public class HttpClientCustomProxyTest
|
|||
// Setup the custom proxy
|
||||
int proxyPort = connector.getLocalPort();
|
||||
int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
|
||||
client.getProxyConfiguration().getProxies().add(new CAFEBABEProxy(new Origin.Address("localhost", proxyPort), false));
|
||||
client.getProxyConfiguration().addProxy(new CAFEBABEProxy(new Origin.Address("localhost", proxyPort), false));
|
||||
|
||||
ContentResponse response = client.newRequest(serverHost, serverPort)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
|
|
|
@ -172,7 +172,7 @@ public class HttpClientProxyProtocolTest
|
|||
EndPoint endPoint = request.getConnectionMetaData().getConnection().getEndPoint();
|
||||
assertTrue(endPoint instanceof ProxyConnectionFactory.ProxyEndPoint);
|
||||
ProxyConnectionFactory.ProxyEndPoint proxyEndPoint = (ProxyConnectionFactory.ProxyEndPoint)endPoint;
|
||||
if (request.getPathInContext().equals("/tls_version"))
|
||||
if (Request.getPathInContext(request).equals("/tls_version"))
|
||||
{
|
||||
assertNotNull(proxyEndPoint.getTLV(typeTLS));
|
||||
assertEquals(tlsVersion, proxyEndPoint.getAttribute(ProxyConnectionFactory.TLS_VERSION));
|
||||
|
@ -231,7 +231,7 @@ public class HttpClientProxyProtocolTest
|
|||
|
||||
int proxyPort = connector.getLocalPort();
|
||||
int serverPort = proxyPort + 1; // Any port will do.
|
||||
client.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new HttpProxy("localhost", proxyPort));
|
||||
|
||||
// We are simulating to be a HttpClient inside a proxy.
|
||||
// The server is configured with the PROXY protocol to know the socket address of clients.
|
||||
|
|
|
@ -57,7 +57,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
|
|||
|
||||
int proxyPort = connector.getLocalPort();
|
||||
int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
|
||||
client.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new HttpProxy("localhost", proxyPort));
|
||||
|
||||
ContentResponse response = client.newRequest(serverHost, serverPort)
|
||||
.scheme(scenario.getScheme())
|
||||
|
@ -104,7 +104,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
|
|||
String proxyHost = "localhost";
|
||||
int proxyPort = connector.getLocalPort();
|
||||
int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
|
||||
client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new HttpProxy(proxyHost, proxyPort));
|
||||
|
||||
ContentResponse response1 = client.newRequest(serverHost, serverPort)
|
||||
.scheme(scenario.getScheme())
|
||||
|
@ -163,7 +163,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void process(org.eclipse.jetty.server.Request request, Response response, Callback callback)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = org.eclipse.jetty.server.Request.getPathInContext(request);
|
||||
if (target.startsWith("/proxy"))
|
||||
{
|
||||
String authorization = request.getHeaders().get(HttpHeader.PROXY_AUTHORIZATION.asString());
|
||||
|
@ -202,7 +202,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
|
|||
});
|
||||
|
||||
int proxyPort = connector.getLocalPort();
|
||||
client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new HttpProxy(proxyHost, proxyPort));
|
||||
|
||||
ContentResponse response1 = client.newRequest(serverHost, serverPort)
|
||||
.scheme(scenario.getScheme())
|
||||
|
@ -289,7 +289,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
|
|||
client.getAuthenticationStore().addAuthentication(new BasicAuthentication(proxyURI, proxyRealm, "proxyUser", "proxyPassword"));
|
||||
URI serverURI = URI.create(scenario.getScheme() + "://" + serverHost + ":" + serverPort);
|
||||
client.getAuthenticationStore().addAuthentication(new BasicAuthentication(serverURI, serverRealm, "serverUser", "serverPassword"));
|
||||
client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new HttpProxy(proxyHost, proxyPort));
|
||||
final AtomicInteger requests = new AtomicInteger();
|
||||
client.getRequestListeners().add(new Request.Listener.Adapter()
|
||||
{
|
||||
|
@ -359,7 +359,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
|
|||
int serverPort = proxyPort + 1;
|
||||
URI proxyURI = URI.create(scenario.getScheme() + "://" + proxyHost + ":" + proxyPort);
|
||||
client.getAuthenticationStore().addAuthentication(new BasicAuthentication(proxyURI, proxyRealm, "proxyUser", "proxyPassword"));
|
||||
client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new HttpProxy(proxyHost, proxyPort));
|
||||
final AtomicInteger requests = new AtomicInteger();
|
||||
client.getRequestListeners().add(new Request.Listener.Adapter()
|
||||
{
|
||||
|
|
|
@ -445,7 +445,7 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void process(Request request, org.eclipse.jetty.server.Response response, Callback callback) throws Exception
|
||||
{
|
||||
if (request.getPathInContext().startsWith("/redirect"))
|
||||
if (Request.getPathInContext(request).startsWith("/redirect"))
|
||||
{
|
||||
response.setStatus(HttpStatus.SEE_OTHER_303);
|
||||
response.getHeaders().put(HttpHeader.LOCATION, scenario.getScheme() + "://localhost:" + connector.getLocalPort() + "/ok");
|
||||
|
@ -537,7 +537,7 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(Request request, org.eclipse.jetty.server.Response response) throws Exception
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
if ("/one".equals(target))
|
||||
{
|
||||
response.setStatus(HttpStatus.SEE_OTHER_303);
|
||||
|
@ -588,7 +588,7 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
protected void service(Request request, org.eclipse.jetty.server.Response response) throws Exception
|
||||
{
|
||||
String serverURI = scenario.getScheme() + "://localhost:" + connector.getLocalPort();
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
if ("/one".equals(target))
|
||||
{
|
||||
Thread.sleep(timeout);
|
||||
|
@ -676,7 +676,7 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
|||
{
|
||||
Fields fields = Request.extractQueryParameters(request);
|
||||
|
||||
String[] paths = request.getPathInContext().split("/", 4);
|
||||
String[] paths = Request.getPathInContext(request).split("/", 4);
|
||||
|
||||
int status = Integer.parseInt(paths[1]);
|
||||
response.setStatus(status);
|
||||
|
|
|
@ -484,7 +484,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(org.eclipse.jetty.server.Request request, org.eclipse.jetty.server.Response response)
|
||||
{
|
||||
if (request.getPathInContext().endsWith("/one"))
|
||||
if (org.eclipse.jetty.server.Request.getPathInContext(request).endsWith("/one"))
|
||||
request.getConnectionMetaData().getConnection().getEndPoint().close();
|
||||
}
|
||||
});
|
||||
|
@ -904,7 +904,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
protected void service(org.eclipse.jetty.server.Request request, org.eclipse.jetty.server.Response response)
|
||||
{
|
||||
List<String> userAgents = request.getHeaders().getValuesList(HttpHeader.USER_AGENT);
|
||||
if ("/ua".equals(request.getPathInContext()))
|
||||
if ("/ua".equals(org.eclipse.jetty.server.Request.getPathInContext(request)))
|
||||
assertEquals(1, userAgents.size());
|
||||
else
|
||||
assertEquals(0, userAgents.size());
|
||||
|
|
|
@ -203,7 +203,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(org.eclipse.jetty.server.Request request, Response response)
|
||||
{
|
||||
assertEquals(path, request.getPathInContext());
|
||||
assertEquals(path, org.eclipse.jetty.server.Request.getPathInContext(request));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -236,7 +236,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(org.eclipse.jetty.server.Request request, Response response)
|
||||
{
|
||||
assertEquals(path, request.getPathInContext());
|
||||
assertEquals(path, org.eclipse.jetty.server.Request.getPathInContext(request));
|
||||
assertEquals(query, request.getHttpURI().getQuery());
|
||||
}
|
||||
});
|
||||
|
@ -273,7 +273,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(org.eclipse.jetty.server.Request request, Response response)
|
||||
{
|
||||
assertEquals(path, request.getPathInContext());
|
||||
assertEquals(path, org.eclipse.jetty.server.Request.getPathInContext(request));
|
||||
assertEquals(query, request.getHttpURI().getQuery());
|
||||
}
|
||||
});
|
||||
|
@ -312,7 +312,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(org.eclipse.jetty.server.Request request, Response response)
|
||||
{
|
||||
assertEquals(path, request.getPathInContext());
|
||||
assertEquals(path, org.eclipse.jetty.server.Request.getPathInContext(request));
|
||||
assertEquals(query, request.getHttpURI().getQuery());
|
||||
}
|
||||
});
|
||||
|
@ -354,7 +354,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(org.eclipse.jetty.server.Request request, Response response)
|
||||
{
|
||||
assertEquals(path, request.getPathInContext());
|
||||
assertEquals(path, org.eclipse.jetty.server.Request.getPathInContext(request));
|
||||
assertEquals(query, request.getHttpURI().getQuery());
|
||||
Fields fields = org.eclipse.jetty.server.Request.extractQueryParameters(request);
|
||||
assertEquals(value1, fields.getValue(name1));
|
||||
|
@ -393,7 +393,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(org.eclipse.jetty.server.Request request, Response response)
|
||||
{
|
||||
assertEquals(path, request.getPathInContext());
|
||||
assertEquals(path, org.eclipse.jetty.server.Request.getPathInContext(request));
|
||||
assertEquals(query, request.getHttpURI().getQuery());
|
||||
}
|
||||
});
|
||||
|
@ -426,7 +426,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(org.eclipse.jetty.server.Request request, Response response)
|
||||
{
|
||||
assertEquals(path, request.getPathInContext());
|
||||
assertEquals(path, org.eclipse.jetty.server.Request.getPathInContext(request));
|
||||
assertEquals(query, request.getHttpURI().getQuery());
|
||||
}
|
||||
});
|
||||
|
@ -607,7 +607,7 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
|
|||
protected void service(org.eclipse.jetty.server.Request request, Response response)
|
||||
{
|
||||
assertEquals("*", request.getHttpURI().getPath());
|
||||
assertEquals("*", request.getPathInContext());
|
||||
assertEquals("*", org.eclipse.jetty.server.Request.getPathInContext(request));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -179,7 +179,7 @@ public class HttpCookieTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(Request request, org.eclipse.jetty.server.Response response)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
int r = (int)request.getHeaders().getLongField(headerName);
|
||||
if ("/foo".equals(target) && r == 0)
|
||||
{
|
||||
|
@ -234,7 +234,7 @@ public class HttpCookieTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(Request request, org.eclipse.jetty.server.Response response)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
int r = (int)request.getHeaders().getLongField(headerName);
|
||||
if ("/foo/bar".equals(target) && r == 0)
|
||||
{
|
||||
|
@ -290,7 +290,7 @@ public class HttpCookieTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(Request request, org.eclipse.jetty.server.Response response)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
int r = (int)request.getHeaders().getLongField(headerName);
|
||||
if ("/foo".equals(target) && r == 0)
|
||||
{
|
||||
|
@ -346,7 +346,7 @@ public class HttpCookieTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(Request request, org.eclipse.jetty.server.Response response)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
int r = (int)request.getHeaders().getLongField(headerName);
|
||||
if ("/foo/bar".equals(target) && r == 0)
|
||||
{
|
||||
|
@ -403,7 +403,7 @@ public class HttpCookieTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(Request request, org.eclipse.jetty.server.Response response)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
int r = (int)request.getHeaders().getLongField(headerName);
|
||||
if ("/foo".equals(target) && r == 0)
|
||||
{
|
||||
|
@ -462,7 +462,7 @@ public class HttpCookieTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(Request request, org.eclipse.jetty.server.Response response)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
int r = (int)request.getHeaders().getLongField(headerName);
|
||||
if ("/foo".equals(target) && r == 0)
|
||||
{
|
||||
|
@ -528,7 +528,7 @@ public class HttpCookieTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(Request request, org.eclipse.jetty.server.Response response)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
int r = (int)request.getHeaders().getLongField(headerName);
|
||||
if ("/foo".equals(target) && r == 0)
|
||||
{
|
||||
|
@ -598,7 +598,7 @@ public class HttpCookieTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(Request request, org.eclipse.jetty.server.Response response)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
int r = (int)request.getHeaders().getLongField(headerName);
|
||||
if ("/foo/bar".equals(target) && r == 0)
|
||||
{
|
||||
|
|
|
@ -485,7 +485,7 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
public void process(org.eclipse.jetty.server.Request request, Response response, Callback callback)
|
||||
{
|
||||
if ("/done".equals(request.getPathInContext()))
|
||||
if ("/done".equals(org.eclipse.jetty.server.Request.getPathInContext(request)))
|
||||
callback.succeeded();
|
||||
else
|
||||
Response.sendRedirect(request, response, callback, "/done");
|
||||
|
|
|
@ -54,7 +54,7 @@ public class Socks4ProxyTest
|
|||
public void prepare() throws Exception
|
||||
{
|
||||
proxy = ServerSocketChannel.open();
|
||||
proxy.bind(new InetSocketAddress("localhost", 0));
|
||||
proxy.bind(new InetSocketAddress("127.0.0.1", 0));
|
||||
|
||||
ClientConnector connector = new ClientConnector();
|
||||
QueuedThreadPool clientThreads = new QueuedThreadPool();
|
||||
|
@ -77,7 +77,7 @@ public class Socks4ProxyTest
|
|||
public void testSocks4Proxy() throws Exception
|
||||
{
|
||||
int proxyPort = proxy.socket().getLocalPort();
|
||||
client.getProxyConfiguration().getProxies().add(new Socks4Proxy("localhost", proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new Socks4Proxy("127.0.0.1", proxyPort));
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
|
@ -139,7 +139,7 @@ public class Socks4ProxyTest
|
|||
public void testSocks4ProxyWithSplitResponse() throws Exception
|
||||
{
|
||||
int proxyPort = proxy.socket().getLocalPort();
|
||||
client.getProxyConfiguration().getProxies().add(new Socks4Proxy("localhost", proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new Socks4Proxy("127.0.0.1", proxyPort));
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
|
@ -196,7 +196,6 @@ public class Socks4ProxyTest
|
|||
@Test
|
||||
public void testSocks4ProxyWithTLSServer() throws Exception
|
||||
{
|
||||
String proxyHost = "localhost";
|
||||
int proxyPort = proxy.socket().getLocalPort();
|
||||
|
||||
String serverHost = "127.0.0.13"; // Server host different from proxy host.
|
||||
|
@ -215,7 +214,7 @@ public class Socks4ProxyTest
|
|||
// The hostname must be that of the server, not of the proxy.
|
||||
ssl.setHostnameVerifier((hostname, session) -> serverHost.equals(hostname));
|
||||
});
|
||||
client.getProxyConfiguration().getProxies().add(new Socks4Proxy(proxyHost, proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new Socks4Proxy("127.0.0.1", proxyPort));
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest(serverHost, serverPort)
|
||||
|
@ -281,12 +280,15 @@ public class Socks4ProxyTest
|
|||
@Test
|
||||
public void testRequestTimeoutWhenSocksProxyDoesNotRespond() throws Exception
|
||||
{
|
||||
String proxyHost = "localhost";
|
||||
int proxyPort = proxy.socket().getLocalPort();
|
||||
client.getProxyConfiguration().getProxies().add(new Socks4Proxy(proxyHost, proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new Socks4Proxy("127.0.0.1", proxyPort));
|
||||
|
||||
long timeout = 1000;
|
||||
Request request = client.newRequest("localhost", proxyPort + 1)
|
||||
|
||||
// Use an address to avoid resolution of "localhost" to multiple addresses.
|
||||
String serverHost = "127.0.0.13";
|
||||
int serverPort = proxyPort + 1; // Any port will do
|
||||
Request request = client.newRequest(serverHost, serverPort)
|
||||
.timeout(timeout, TimeUnit.MILLISECONDS);
|
||||
FutureResponseListener listener = new FutureResponseListener(request);
|
||||
request.send(listener);
|
||||
|
@ -303,13 +305,15 @@ public class Socks4ProxyTest
|
|||
@Test
|
||||
public void testIdleTimeoutWhenSocksProxyDoesNotRespond() throws Exception
|
||||
{
|
||||
String proxyHost = "localhost";
|
||||
int proxyPort = proxy.socket().getLocalPort();
|
||||
client.getProxyConfiguration().getProxies().add(new Socks4Proxy(proxyHost, proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new Socks4Proxy("127.0.0.1", proxyPort));
|
||||
long idleTimeout = 1000;
|
||||
client.setIdleTimeout(idleTimeout);
|
||||
|
||||
Request request = client.newRequest("localhost", proxyPort + 1);
|
||||
// Use an address to avoid resolution of "localhost" to multiple addresses.
|
||||
String serverHost = "127.0.0.13";
|
||||
int serverPort = proxyPort + 1; // Any port will do
|
||||
Request request = client.newRequest(serverHost, serverPort);
|
||||
FutureResponseListener listener = new FutureResponseListener(request);
|
||||
request.send(listener);
|
||||
|
||||
|
@ -325,11 +329,13 @@ public class Socks4ProxyTest
|
|||
@Test
|
||||
public void testSocksProxyClosesConnectionImmediately() throws Exception
|
||||
{
|
||||
String proxyHost = "localhost";
|
||||
int proxyPort = proxy.socket().getLocalPort();
|
||||
client.getProxyConfiguration().getProxies().add(new Socks4Proxy(proxyHost, proxyPort));
|
||||
client.getProxyConfiguration().addProxy(new Socks4Proxy("127.0.0.1", proxyPort));
|
||||
|
||||
Request request = client.newRequest("localhost", proxyPort + 1);
|
||||
// Use an address to avoid resolution of "localhost" to multiple addresses.
|
||||
String serverHost = "127.0.0.13";
|
||||
int serverPort = proxyPort + 1; // Any port will do
|
||||
Request request = client.newRequest(serverHost, serverPort);
|
||||
FutureResponseListener listener = new FutureResponseListener(request);
|
||||
request.send(listener);
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ public class ValidatingConnectionPoolTest extends AbstractHttpClientServerTest
|
|||
@Override
|
||||
protected void service(org.eclipse.jetty.server.Request request, Response response) throws Throwable
|
||||
{
|
||||
if (request.getPathInContext().endsWith("/redirect"))
|
||||
if (org.eclipse.jetty.server.Request.getPathInContext(request).endsWith("/redirect"))
|
||||
{
|
||||
response.setStatus(HttpStatus.TEMPORARY_REDIRECT_307);
|
||||
response.getHeaders().putLongField(HttpHeader.CONTENT_LENGTH, 0);
|
||||
|
|
|
@ -23,13 +23,7 @@
|
|||
<Arg>starting</Arg>
|
||||
<Arg>customise</Arg>
|
||||
</Call>
|
||||
<Call name="addLifeCycleBinding">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.deploy.bindings.DebugBinding">
|
||||
<Arg>customise</Arg>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call> -->
|
||||
-->
|
||||
|
||||
</New>
|
||||
</Arg>
|
||||
|
|
|
@ -91,7 +91,7 @@ public class FastCGIProxyHandlerTest
|
|||
{
|
||||
HttpURI httpURI = request.getHttpURI();
|
||||
HttpURI.Mutable newHttpURI = HttpURI.build(httpURI)
|
||||
.path(appContextPath + request.getPathInContext());
|
||||
.path(appContextPath + Request.getPathInContext(request));
|
||||
newHttpURI.port(unixDomainPath == null ? ((ServerConnector)serverConnector).getLocalPort() : 0);
|
||||
return newHttpURI;
|
||||
}, "/scriptRoot");
|
||||
|
@ -152,7 +152,7 @@ public class FastCGIProxyHandlerTest
|
|||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
assertNotEquals(proxyContext.getContextPath(), request.getContext().getContextPath());
|
||||
assertEquals(path, request.getPathInContext());
|
||||
assertEquals(path, Request.getPathInContext(request));
|
||||
response.getHeaders().putLongField(HttpHeader.CONTENT_LENGTH, data.length);
|
||||
response.write(true, ByteBuffer.wrap(data), callback);
|
||||
}
|
||||
|
@ -198,7 +198,7 @@ public class FastCGIProxyHandlerTest
|
|||
{
|
||||
assertThat((String)request.getAttribute(FCGI.Headers.REQUEST_URI), startsWith(originalPath));
|
||||
assertEquals(originalQuery, request.getAttribute(FCGI.Headers.QUERY_STRING));
|
||||
assertThat(request.getPathInContext(), endsWith(remotePath));
|
||||
assertThat(Request.getPathInContext(request), endsWith(remotePath));
|
||||
callback.succeeded();
|
||||
}
|
||||
});
|
||||
|
@ -211,7 +211,7 @@ public class FastCGIProxyHandlerTest
|
|||
@Override
|
||||
public Request.Processor handle(Request request) throws Exception
|
||||
{
|
||||
if (request.getPathInContext().startsWith("/remote/"))
|
||||
if (Request.getPathInContext(request).startsWith("/remote/"))
|
||||
{
|
||||
request.setAttribute(pathAttribute, originalPath);
|
||||
request.setAttribute(queryAttribute, originalQuery);
|
||||
|
|
|
@ -875,7 +875,16 @@ public interface HttpURI
|
|||
{
|
||||
_param = param;
|
||||
if (_path != null && _param != null)
|
||||
{
|
||||
int lastSlash = _path.lastIndexOf('/');
|
||||
if (lastSlash >= 0)
|
||||
{
|
||||
int trailingParam = _path.indexOf(';', lastSlash + 1);
|
||||
if (trailingParam >= 0)
|
||||
_path = _path.substring(0, trailingParam);
|
||||
}
|
||||
_path += ";" + _param;
|
||||
}
|
||||
|
||||
_uri = null;
|
||||
return this;
|
||||
|
@ -889,9 +898,22 @@ public interface HttpURI
|
|||
{
|
||||
if (hasAuthority() && !isPathValidForAuthority(path))
|
||||
throw new IllegalArgumentException("Relative path with authority");
|
||||
if (!URIUtil.isPathValid(path))
|
||||
throw new IllegalArgumentException("Path not correctly encoded: " + path);
|
||||
_uri = null;
|
||||
_path = path;
|
||||
_canonicalPath = null;
|
||||
|
||||
// If the passed path does not have a parameter, then keep the current parameter
|
||||
// else delete the current parameter
|
||||
if (_param != null)
|
||||
{
|
||||
if (path.indexOf(';') >= 0)
|
||||
_param = null;
|
||||
else
|
||||
_path = _path + ';' + _param;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.util.Index;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
|
@ -88,6 +89,11 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
|||
_suffixMap.clear();
|
||||
}
|
||||
|
||||
public Stream<MappedResource<E>> streamResources()
|
||||
{
|
||||
return _mappings.stream();
|
||||
}
|
||||
|
||||
public void removeIf(Predicate<MappedResource<E>> predicate)
|
||||
{
|
||||
_mappings.removeIf(predicate);
|
||||
|
|
|
@ -916,4 +916,17 @@ public class HttpURITest
|
|||
.path("");
|
||||
assertEquals("//host", uri.asString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeepParam()
|
||||
{
|
||||
HttpURI orig = HttpURI.from("http://localhost/context/info;param=value");
|
||||
HttpURI built = HttpURI.build(orig).path("/context/info").asImmutable();
|
||||
assertThat(built.getParam(), is(orig.getParam()));
|
||||
assertThat(built.toString(), is(orig.toString()));
|
||||
|
||||
built = HttpURI.build(orig).path("/context/info").param("param=value").asImmutable();
|
||||
assertThat(built.getParam(), is(orig.getParam()));
|
||||
assertThat(built.toString(), is(orig.toString()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -519,6 +519,10 @@ public class MultiPartTest
|
|||
{
|
||||
byte[] random = new byte[8192];
|
||||
ThreadLocalRandom.current().nextBytes(random);
|
||||
// Make sure the last 2 bytes are not \r\n,
|
||||
// otherwise the multipart parser gets confused.
|
||||
random[random.length - 2] = 0;
|
||||
random[random.length - 1] = 0;
|
||||
|
||||
TestPartsListener listener = new TestPartsListener();
|
||||
MultiPart.Parser parser = new MultiPart.Parser("BOUNDARY", listener);
|
||||
|
@ -526,7 +530,7 @@ public class MultiPartTest
|
|||
String preamble = "Blah blah blah\r\n--BOUNDARY\r\n\r\n";
|
||||
parser.parse(Content.Chunk.from(BufferUtil.toBuffer(preamble), false));
|
||||
parser.parse(Content.Chunk.from(ByteBuffer.wrap(random), false));
|
||||
String epilogue = "\r\n--BOUNDARY\r\nBlah blah blah!\r\n";
|
||||
String epilogue = "\r\n--BOUNDARY--\r\nBlah blah blah!\r\n";
|
||||
ByteBuffer epilogueBuffer = BufferUtil.toBuffer(epilogue);
|
||||
parser.parse(Content.Chunk.from(epilogueBuffer, true));
|
||||
|
||||
|
|
|
@ -25,8 +25,6 @@ import org.eclipse.jetty.util.Promise;
|
|||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class ConnectTimeoutTest extends AbstractTest
|
||||
|
@ -49,7 +47,6 @@ public class ConnectTimeoutTest extends AbstractTest
|
|||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
assertThat(x, instanceOf(SocketTimeoutException.class));
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -67,7 +67,7 @@ public class HTTP2CServer extends Server
|
|||
response.getHeaders().put("Custom", "Value");
|
||||
response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/plain");
|
||||
String content = "Hello from Jetty using " + request.getConnectionMetaData().getProtocol() + "\n";
|
||||
content += "uri=" + request.getPathInContext() + "\n";
|
||||
content += "uri=" + Request.getPathInContext(request) + "\n";
|
||||
content += "date=" + new Date() + "\n";
|
||||
response.getHeaders().putLongField(HttpHeader.CONTENT_LENGTH, content.length());
|
||||
Content.Sink.write(response, true, content, callback);
|
||||
|
|
|
@ -422,7 +422,7 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest
|
|||
});
|
||||
|
||||
int proxyPort = connector.getLocalPort();
|
||||
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(new Origin.Address("localhost", proxyPort), false, new Origin.Protocol(List.of("h2c"), false)));
|
||||
httpClient.getProxyConfiguration().addProxy(new HttpProxy(new Origin.Address("localhost", proxyPort), false, new Origin.Protocol(List.of("h2c"), false)));
|
||||
|
||||
int serverPort = proxyPort + 1; // Any port will do, just not the same as the proxy.
|
||||
ContentResponse response = httpClient.newRequest("localhost", serverPort)
|
||||
|
|
|
@ -421,7 +421,7 @@ public class MaxConcurrentStreamsTest extends AbstractTest
|
|||
@Override
|
||||
public void process(Request request, org.eclipse.jetty.server.Response response, Callback callback)
|
||||
{
|
||||
if (request.getPathInContext().endsWith("/1"))
|
||||
if (Request.getPathInContext(request).endsWith("/1"))
|
||||
sleep(2 * timeout);
|
||||
callback.succeeded();
|
||||
}
|
||||
|
|
|
@ -135,7 +135,7 @@ public class MultiplexedConnectionPoolTest
|
|||
@Override
|
||||
public void process(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
int req = Integer.parseInt(request.getPathInContext().substring(1));
|
||||
int req = Integer.parseInt(Request.getPathInContext(request).substring(1));
|
||||
reqExecutingLatches[req].countDown();
|
||||
Thread.sleep(250);
|
||||
reqExecutedLatches[req].countDown();
|
||||
|
@ -231,7 +231,7 @@ public class MultiplexedConnectionPoolTest
|
|||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
int req = Integer.parseInt(request.getPathInContext().substring(1));
|
||||
int req = Integer.parseInt(Request.getPathInContext(request).substring(1));
|
||||
Content.Sink.write(response, true, "req " + req + " executed", callback);
|
||||
}
|
||||
}, 64, 1L);
|
||||
|
@ -380,7 +380,7 @@ public class MultiplexedConnectionPoolTest
|
|||
@Override
|
||||
public void process(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
if (request.getPathInContext().equals("/block"))
|
||||
if (Request.getPathInContext(request).equals("/block"))
|
||||
{
|
||||
handlerSignalingSemaphore.release();
|
||||
handlerWaitingSemaphore.acquire();
|
||||
|
|
|
@ -108,7 +108,7 @@ public class PushedResourcesTest extends AbstractTest
|
|||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
if (target.equals(path1))
|
||||
{
|
||||
response.write(true, ByteBuffer.wrap(pushBytes1), callback);
|
||||
|
@ -173,7 +173,7 @@ public class PushedResourcesTest extends AbstractTest
|
|||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
if (target.equals(oldPath))
|
||||
{
|
||||
Response.sendRedirect(request, response, callback, newPath);
|
||||
|
|
|
@ -928,7 +928,7 @@ public class StreamResetTest extends AbstractTest
|
|||
@Override
|
||||
public void process(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
if (target.equals("/1"))
|
||||
service1(request, response, callback);
|
||||
else if (target.equals("/2"))
|
||||
|
|
|
@ -480,7 +480,7 @@ public abstract class ProxyHandler extends Handler.Processor
|
|||
* <p>Forward proxies are configured in client applications that use
|
||||
* {@link HttpClient} in this way:</p>
|
||||
* <pre>{@code
|
||||
* httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
|
||||
* httpClient.getProxyConfiguration().addProxy(new HttpProxy(proxyHost, proxyPort));
|
||||
* }</pre>
|
||||
*
|
||||
* @see org.eclipse.jetty.client.ProxyConfiguration
|
||||
|
|
|
@ -257,7 +257,7 @@ public class ForwardProxyWithDynamicTransportTest
|
|||
int proxyPort = proxySecure ? proxyTLSConnector.getLocalPort() : proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy proxy = new HttpProxy(proxyAddress, proxySecure, proxyProtocol);
|
||||
client.getProxyConfiguration().getProxies().add(proxy);
|
||||
client.getProxyConfiguration().addProxy(proxy);
|
||||
|
||||
String scheme = serverSecure ? "https" : "http";
|
||||
int serverPort = serverSecure ? serverTLSConnector.getLocalPort() : serverConnector.getLocalPort();
|
||||
|
@ -293,7 +293,7 @@ public class ForwardProxyWithDynamicTransportTest
|
|||
int proxyPort = proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy proxy = new HttpProxy(proxyAddress, false, new Origin.Protocol(List.of("h2c"), false));
|
||||
client.getProxyConfiguration().getProxies().add(proxy);
|
||||
client.getProxyConfiguration().addProxy(proxy);
|
||||
|
||||
long idleTimeout = 1000;
|
||||
http2Client.setStreamIdleTimeout(idleTimeout);
|
||||
|
@ -334,7 +334,7 @@ public class ForwardProxyWithDynamicTransportTest
|
|||
int proxyPort = proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy httpProxy = new HttpProxy(proxyAddress, false, new Origin.Protocol(List.of("h2c"), false));
|
||||
client.getProxyConfiguration().getProxies().add(httpProxy);
|
||||
client.getProxyConfiguration().addProxy(httpProxy);
|
||||
proxy.stop();
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
@ -369,7 +369,7 @@ public class ForwardProxyWithDynamicTransportTest
|
|||
int proxyPort = proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy httpProxy = new HttpProxy(proxyAddress, false, new Origin.Protocol(List.of("h2c"), false));
|
||||
client.getProxyConfiguration().getProxies().add(httpProxy);
|
||||
client.getProxyConfiguration().addProxy(httpProxy);
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", serverConnector.getLocalPort())
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jetty-quic-quiche-foreign-incubator</artifactId>
|
||||
<name>Jetty Core :: QUIC :: Quiche :: Foreign Binding (incubator)</name>
|
||||
<name>Jetty Core :: QUIC :: Quiche :: Foreign (Java 17)</name>
|
||||
|
||||
<properties>
|
||||
<bundle-symbolic-name>${project.groupId}.quic-quiche-foreign-incubator</bundle-symbolic-name>
|
||||
|
@ -20,9 +20,6 @@
|
|||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<release>17</release>
|
||||
<compilerArgs>
|
||||
<arg>--add-modules</arg>
|
||||
<arg>jdk.incubator.foreign</arg>
|
||||
|
|
|
@ -14,17 +14,6 @@
|
|||
<modules>
|
||||
<module>jetty-quic-quiche-common</module>
|
||||
<module>jetty-quic-quiche-jna</module>
|
||||
<module>jetty-quic-quiche-foreign-incubator</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>jdk17</id>
|
||||
<activation>
|
||||
<jdk>17</jdk>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>jetty-quic-quiche-foreign-incubator</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<profile>
|
||||
<id>jdk17</id>
|
||||
<activation>
|
||||
<jdk>[17,)</jdk>
|
||||
<jdk>17</jdk>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.rewrite.handler;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
||||
|
@ -25,20 +26,22 @@ import org.eclipse.jetty.util.URIUtil;
|
|||
public class CompactPathRule extends Rule
|
||||
{
|
||||
@Override
|
||||
public Request.WrapperProcessor matchAndApply(Request.WrapperProcessor input) throws IOException
|
||||
public Request.WrapperProcessor matchAndApply(Request.WrapperProcessor request) throws IOException
|
||||
{
|
||||
String path = input.getPathInContext();
|
||||
String path = request.getHttpURI().getCanonicalPath();
|
||||
String compacted = URIUtil.compactPath(path);
|
||||
|
||||
if (path.equals(compacted))
|
||||
return null;
|
||||
|
||||
return new Request.WrapperProcessor(input)
|
||||
HttpURI uri = Request.newHttpURIFrom(request, compacted);
|
||||
|
||||
return new Request.WrapperProcessor(request)
|
||||
{
|
||||
@Override
|
||||
public String getPathInContext()
|
||||
public HttpURI getHttpURI()
|
||||
{
|
||||
return compacted;
|
||||
return uri;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public class CookiePatternRuleTest extends AbstractRuleTest
|
|||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/plain;charset=utf-8");
|
||||
Content.Sink.write(response, false, "pathInContext=%s%n".formatted(request.getPathInContext()), Callback.NOOP);
|
||||
Content.Sink.write(response, false, "pathInContext=%s%n".formatted(Request.getPathInContext(request)), Callback.NOOP);
|
||||
Content.Sink.write(response, false, "path=%s%n".formatted(request.getHttpURI().getPath()), Callback.NOOP);
|
||||
Content.Sink.write(response, false, "query=%s%n".formatted(request.getHttpURI().getQuery()), Callback.NOOP);
|
||||
Request original = Request.unWrap(request);
|
||||
|
|
|
@ -78,83 +78,81 @@ public interface ConnectionMetaData extends Attributes
|
|||
|
||||
class Wrapper extends Attributes.Wrapper implements ConnectionMetaData
|
||||
{
|
||||
private final ConnectionMetaData _wrapped;
|
||||
|
||||
public Wrapper(ConnectionMetaData wrapped)
|
||||
{
|
||||
super(wrapped);
|
||||
_wrapped = wrapped;
|
||||
}
|
||||
|
||||
protected ConnectionMetaData getWrappedConnectionMetaData()
|
||||
@Override
|
||||
public ConnectionMetaData getWrapped()
|
||||
{
|
||||
return _wrapped;
|
||||
return (ConnectionMetaData)super.getWrapped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId()
|
||||
{
|
||||
return _wrapped.getId();
|
||||
return getWrapped().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpConfiguration getHttpConfiguration()
|
||||
{
|
||||
return _wrapped.getHttpConfiguration();
|
||||
return getWrapped().getHttpConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpVersion getHttpVersion()
|
||||
{
|
||||
return _wrapped.getHttpVersion();
|
||||
return getWrapped().getHttpVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol()
|
||||
{
|
||||
return _wrapped.getProtocol();
|
||||
return getWrapped().getProtocol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection()
|
||||
{
|
||||
return _wrapped.getConnection();
|
||||
return getWrapped().getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connector getConnector()
|
||||
{
|
||||
return _wrapped.getConnector();
|
||||
return getWrapped().getConnector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPersistent()
|
||||
{
|
||||
return _wrapped.isPersistent();
|
||||
return getWrapped().isPersistent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure()
|
||||
{
|
||||
return _wrapped.isSecure();
|
||||
return getWrapped().isSecure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteSocketAddress()
|
||||
{
|
||||
return _wrapped.getRemoteSocketAddress();
|
||||
return getWrapped().getRemoteSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalSocketAddress()
|
||||
{
|
||||
return _wrapped.getLocalSocketAddress();
|
||||
return getWrapped().getLocalSocketAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostPort getServerAuthority()
|
||||
{
|
||||
return _wrapped.getServerAuthority();
|
||||
return getWrapped().getServerAuthority();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ import org.eclipse.jetty.util.resource.Resource;
|
|||
public interface Context extends Attributes, Decorator, Executor
|
||||
{
|
||||
/**
|
||||
* @return The URI path prefix of the context, which may be null for the server context, or "/" for the root context.
|
||||
* @return the context path of this Context
|
||||
*/
|
||||
String getContextPath();
|
||||
|
||||
|
@ -56,4 +56,15 @@ public interface Context extends Attributes, Decorator, Executor
|
|||
|
||||
/** scope the calling thread to the context and request and run the runnable. */
|
||||
void run(Runnable runnable, Request request);
|
||||
|
||||
/**
|
||||
* <p>Returns a URI path scoped to this Context.</p>
|
||||
* <p>For example, if the context path is {@code /ctx} then a
|
||||
* full path of {@code /ctx/foo/bar} will return {@code /foo/bar}.</p>
|
||||
*
|
||||
* @param fullPath A full URI path
|
||||
* @return The URI path scoped to this Context, or {@code null} if the full path does not match this Context.
|
||||
* The empty string is returned if the full path is exactly the context path.
|
||||
*/
|
||||
String getPathInContext(String fullPath);
|
||||
}
|
||||
|
|
|
@ -1139,7 +1139,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
|
|||
Resource baseResource = context.getBaseResource();
|
||||
if (baseResource != null)
|
||||
{
|
||||
String fileName = baseResource.resolve(request.getPathInContext()).getName();
|
||||
String fileName = baseResource.resolve(Request.getPathInContext(request)).getName();
|
||||
append(b, fileName);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -597,7 +597,7 @@ public class ForwardedRequestCustomizer implements HttpConfiguration.Customizer
|
|||
getId(),
|
||||
remote,
|
||||
authority,
|
||||
getWrappedConnectionMetaData()
|
||||
getWrapped()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
/**
|
||||
* Abstraction of the outbound HTTP transport.
|
||||
*/
|
||||
public interface HttpTransport
|
||||
{
|
||||
String UPGRADE_CONNECTION_ATTRIBUTE = HttpTransport.class.getName() + ".UPGRADE";
|
||||
|
||||
/**
|
||||
* Asynchronous call to send a response (or part) over the transport
|
||||
*
|
||||
* @param request True if the response if for a HEAD request (and the data should not be sent).
|
||||
* @param response The header info to send, or null if just sending more data.
|
||||
* The first call to send for a response must have a non null info.
|
||||
* @param content A buffer of content to be sent.
|
||||
* @param lastContent True if the content is the last content for the current response.
|
||||
* @param callback The Callback instance that success or failure of the send is notified on
|
||||
*/
|
||||
void send(MetaData.Request request, MetaData.Response response, ByteBuffer content, boolean lastContent, Callback callback);
|
||||
|
||||
/**
|
||||
* @return true if responses can be pushed over this transport
|
||||
*/
|
||||
boolean isPushSupported();
|
||||
|
||||
/**
|
||||
* @param request A request to use as the basis for generating a pushed response.
|
||||
*/
|
||||
void push(MetaData.Request request);
|
||||
|
||||
/**
|
||||
* Called to indicated the end of the current request/response cycle (which may be
|
||||
* some time after the last content is sent).
|
||||
*/
|
||||
void onCompleted();
|
||||
|
||||
/**
|
||||
* Aborts this transport.
|
||||
* <p>
|
||||
* This method should terminate the transport in a way that
|
||||
* can indicate an abnormal response to the client, for example
|
||||
* by abruptly close the connection.
|
||||
* <p>
|
||||
* This method is called when an error response needs to be sent,
|
||||
* but the response is already committed, or when a write failure
|
||||
* is detected. If abort is called, {@link #onCompleted()} is not
|
||||
* called
|
||||
*
|
||||
* @param failure the failure that caused the abort.
|
||||
*/
|
||||
void abort(Throwable failure);
|
||||
}
|
|
@ -121,6 +121,7 @@ public interface Request extends Attributes, Content.Source
|
|||
/**
|
||||
* an ID unique within the lifetime scope of the {@link ConnectionMetaData#getId()}).
|
||||
* This may be a protocol ID (eg HTTP/2 stream ID) or it may be unrelated to the protocol.
|
||||
*
|
||||
* @see HttpStream#getId()
|
||||
*/
|
||||
String getId();
|
||||
|
@ -142,6 +143,8 @@ public interface Request extends Attributes, Content.Source
|
|||
|
||||
/**
|
||||
* @return the HTTP URI of this request
|
||||
* @see #getContextPath(Request)
|
||||
* @see #getPathInContext(Request)
|
||||
*/
|
||||
HttpURI getHttpURI();
|
||||
|
||||
|
@ -151,10 +154,31 @@ public interface Request extends Attributes, Content.Source
|
|||
Context getContext();
|
||||
|
||||
/**
|
||||
* TODO see discussion in #7713, as this path should probably be canonically encoded - ie everything but %25 and %2F decoded
|
||||
* @return The part of the decoded path of the URI after any context path prefix has been removed.
|
||||
* <p>Returns the context path of this Request.</p>
|
||||
* <p>This is equivalent to {@code request.getContext().getContextPath()}.</p>
|
||||
*
|
||||
* @param request The request to get the context path from.
|
||||
* @return The contextPath of the request.
|
||||
* @see Context#getContextPath()
|
||||
*/
|
||||
String getPathInContext();
|
||||
static String getContextPath(Request request)
|
||||
{
|
||||
return request.getContext().getContextPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the canonically encoded path of the URI, scoped to the current context.</p>
|
||||
* <p>For example, when the request has a {@link Context} with {@code contextPath=/ctx} and the request's
|
||||
* {@link HttpURI} canonical path is {@code canonicalPath=/ctx/foo}, then {@code pathInContext=/foo}.</p>
|
||||
*
|
||||
* @return The part of the canonically encoded path of the URI after any context path prefix has been removed.
|
||||
* @see HttpURI#getCanonicalPath()
|
||||
* @see Context#getContextPath()
|
||||
*/
|
||||
static String getPathInContext(Request request)
|
||||
{
|
||||
return request.getContext().getPathInContext(request.getHttpURI().getCanonicalPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the HTTP headers of this request
|
||||
|
@ -509,12 +533,6 @@ public interface Request extends Attributes, Content.Source
|
|||
return getWrapped().getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathInContext()
|
||||
{
|
||||
return getWrapped().getPathInContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpFields getHeaders()
|
||||
{
|
||||
|
@ -689,4 +707,21 @@ public interface Request extends Attributes, Content.Source
|
|||
processor.process(this, response, callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a new {@link HttpURI} from the given Request's HttpURI and the given path in context.</p>
|
||||
* <p>For example, for {@code contextPath=/ctx}, {@code request.httpURI=http://host/ctx/path?a=b}, and
|
||||
* {@code newPathInContext=/newPath}, the returned HttpURI is {@code http://host/ctx/newPath?a=b}.</p>
|
||||
*
|
||||
* @param request The request to base the new HttpURI on.
|
||||
* @param newPathInContext The new path in context for the new HttpURI
|
||||
* @return A new immutable HttpURI with the path in context replaced, but query string and path
|
||||
* parameters retained.
|
||||
*/
|
||||
static HttpURI newHttpURIFrom(Request request, String newPathInContext)
|
||||
{
|
||||
return HttpURI.build(request.getHttpURI())
|
||||
.path(URIUtil.addPaths(getContextPath(request), newPathInContext))
|
||||
.asImmutable();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ public class ResourceService
|
|||
|
||||
public void doGet(Request request, Response response, Callback callback, HttpContent content) throws Exception
|
||||
{
|
||||
String pathInContext = request.getPathInContext();
|
||||
String pathInContext = Request.getPathInContext(request);
|
||||
|
||||
// Is this a Range request?
|
||||
List<String> reqRanges = request.getHeaders().getValuesList(HttpHeader.RANGE.asString());
|
||||
|
@ -457,10 +457,7 @@ public class ResourceService
|
|||
HttpURI.Mutable uri = HttpURI.build(request.getHttpURI());
|
||||
if (!uri.getCanonicalPath().endsWith("/"))
|
||||
{
|
||||
// TODO need URI util that handles param and query without reconstructing entire URI with scheme and authority
|
||||
String parameter = uri.getParam();
|
||||
uri.path(uri.getCanonicalPath() + "/");
|
||||
uri.param(parameter);
|
||||
response.getHeaders().putLongField(HttpHeader.CONTENT_LENGTH, 0);
|
||||
// TODO: can writeRedirect (override) also work for WelcomeActionType.REDIRECT?
|
||||
sendRedirect(request, response, callback, uri.getPathQuery());
|
||||
|
@ -544,11 +541,8 @@ public class ResourceService
|
|||
{
|
||||
// Redirect to the index
|
||||
response.getHeaders().putLongField(HttpHeader.CONTENT_LENGTH, 0);
|
||||
// TODO need URI util that handles param and query without reconstructing entire URI with scheme and authority
|
||||
HttpURI.Mutable uri = HttpURI.build(request.getHttpURI());
|
||||
String parameter = uri.getParam();
|
||||
uri.path(URIUtil.addPaths(contextPath, welcomeTarget));
|
||||
uri.param(parameter);
|
||||
return new WelcomeAction(WelcomeActionType.REDIRECT, uri.getPathQuery());
|
||||
}
|
||||
|
||||
|
|
|
@ -712,7 +712,7 @@ public class Server extends Handler.Wrapper implements Attributes
|
|||
|
||||
private static class DynamicErrorProcessor extends ErrorProcessor {}
|
||||
|
||||
private class ServerContext extends Attributes.Wrapper implements Context
|
||||
class ServerContext extends Attributes.Wrapper implements Context
|
||||
{
|
||||
private ServerContext()
|
||||
{
|
||||
|
@ -785,6 +785,12 @@ public class Server extends Handler.Wrapper implements Attributes
|
|||
if (factory != null)
|
||||
factory.destroy(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathInContext(String fullPath)
|
||||
{
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
|
||||
private class ServerEnvironment extends Attributes.Wrapper implements Environment
|
||||
|
|
|
@ -129,7 +129,7 @@ public class BufferedResponseHandler extends Handler.Wrapper
|
|||
if (processor == null)
|
||||
return null;
|
||||
|
||||
final String path = request.getPathInContext();
|
||||
final String path = Request.getPathInContext(request);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} handle {} in {}", this, request, request.getContext());
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.EventListener;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
@ -94,6 +95,7 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
|
|||
|
||||
private String _displayName;
|
||||
private String _contextPath = "/";
|
||||
private boolean _rootContext = true;
|
||||
private Resource _baseResource;
|
||||
private ClassLoader _classLoader;
|
||||
private Request.Processor _errorProcessor;
|
||||
|
@ -593,20 +595,6 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
|
|||
return false;
|
||||
}
|
||||
|
||||
protected String getPathInContext(Request request)
|
||||
{
|
||||
String path = request.getPathInContext();
|
||||
if (!path.startsWith(_context.getContextPath()))
|
||||
return null;
|
||||
if ("/".equals(_context.getContextPath()))
|
||||
return path;
|
||||
if (path.length() == _context.getContextPath().length())
|
||||
return "";
|
||||
if (path.charAt(_context.getContextPath().length()) != '/')
|
||||
return null;
|
||||
return path.substring(_context.getContextPath().length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
|
@ -622,27 +610,45 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
|
|||
if (!checkVirtualHost(request))
|
||||
return null;
|
||||
|
||||
String pathInContext = getPathInContext(request);
|
||||
if (pathInContext == null)
|
||||
return null;
|
||||
// The root context handles all requests.
|
||||
if (!_rootContext)
|
||||
{
|
||||
// Otherwise match the path.
|
||||
String path = request.getHttpURI().getCanonicalPath();
|
||||
if (path == null || !path.startsWith(_contextPath))
|
||||
return null;
|
||||
|
||||
if (pathInContext.isEmpty() && !getAllowNullPathInContext())
|
||||
return this::processMovedPermanently;
|
||||
if (path.length() == _contextPath.length())
|
||||
{
|
||||
if (!getAllowNullPathInContext())
|
||||
return this::processMovedPermanently;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (path.charAt(_contextPath.length()) != '/')
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO check availability and maybe return a 503
|
||||
if (!isAvailable() && isStarted())
|
||||
return this::processUnavailable;
|
||||
|
||||
ContextRequest contextRequest = wrap(request, pathInContext);
|
||||
ContextRequest contextRequest = wrap(request);
|
||||
// wrap might fail (eg ServletContextHandler could not match a servlet)
|
||||
if (contextRequest == null)
|
||||
return null;
|
||||
|
||||
// Does this handler want to process the request itself?
|
||||
Request.Processor processor = processByContextHandler(contextRequest);
|
||||
if (processor != null)
|
||||
return processor;
|
||||
|
||||
return contextRequest.wrapProcessor(_context.get(contextRequest, contextRequest));
|
||||
// The contextRequest is-a Supplier<Processor> that calls effectively calls getHandler().handle(request).
|
||||
// Call this supplier in the scope of the context.
|
||||
Request.Processor contextScopedProcessor = _context.get(contextRequest, contextRequest);
|
||||
// Wrap the contextScopedProcessor with a wrapper that uses the wrapped request
|
||||
return contextRequest.wrapProcessor(contextScopedProcessor);
|
||||
}
|
||||
|
||||
protected void processMovedPermanently(Request request, Response response, Callback callback)
|
||||
|
@ -665,7 +671,7 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
|
|||
|
||||
protected Request.Processor processByContextHandler(ContextRequest contextRequest)
|
||||
{
|
||||
if (!_allowNullPathInContext && StringUtil.isEmpty(contextRequest.getPathInContext()))
|
||||
if (!_allowNullPathInContext && StringUtil.isEmpty(Request.getPathInContext(contextRequest)))
|
||||
{
|
||||
return (request, response, callback) ->
|
||||
{
|
||||
|
@ -687,7 +693,8 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
|
|||
{
|
||||
if (isStarted())
|
||||
throw new IllegalStateException(getState());
|
||||
_contextPath = URIUtil.canonicalPath(contextPath);
|
||||
_contextPath = URIUtil.canonicalPath(Objects.requireNonNull(contextPath));
|
||||
_rootContext = "/".equals(contextPath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -777,9 +784,9 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
|
|||
_errorProcessor = errorProcessor;
|
||||
}
|
||||
|
||||
protected ContextRequest wrap(Request request, String pathInContext)
|
||||
protected ContextRequest wrap(Request request)
|
||||
{
|
||||
return new ContextRequest(this, _context, request, pathInContext);
|
||||
return new ContextRequest(this, _context, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1163,6 +1170,20 @@ public class ContextHandler extends Handler.Wrapper implements Attributes, Grace
|
|||
if (factory != null)
|
||||
factory.destroy(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathInContext(String fullPath)
|
||||
{
|
||||
if (_rootContext)
|
||||
return fullPath;
|
||||
if (!fullPath.startsWith(_contextPath))
|
||||
return null;
|
||||
if (fullPath.length() == _contextPath.length())
|
||||
return "";
|
||||
if (fullPath.charAt(_contextPath.length()) != '/')
|
||||
return null;
|
||||
return fullPath.substring(_contextPath.length());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -140,7 +140,7 @@ public class ContextHandlerCollection extends Handler.Collection
|
|||
if (pathBranches == null)
|
||||
return null;
|
||||
|
||||
String path = request.getPathInContext();
|
||||
String path = Request.getPathInContext(request);
|
||||
if (!path.startsWith("/"))
|
||||
{
|
||||
super.handle(request);
|
||||
|
|
|
@ -28,16 +28,14 @@ import org.slf4j.LoggerFactory;
|
|||
public class ContextRequest extends Request.WrapperProcessor implements Invocable, Supplier<Request.Processor>, Runnable
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ContextRequest.class);
|
||||
private final String _pathInContext;
|
||||
private final ContextHandler _contextHandler;
|
||||
private final ContextHandler.Context _context;
|
||||
private Response _response;
|
||||
private Callback _callback;
|
||||
|
||||
protected ContextRequest(ContextHandler contextHandler, ContextHandler.Context context, Request wrapped, String pathInContext)
|
||||
protected ContextRequest(ContextHandler contextHandler, ContextHandler.Context context, Request wrapped)
|
||||
{
|
||||
super(wrapped);
|
||||
_pathInContext = pathInContext;
|
||||
_contextHandler = contextHandler;
|
||||
_context = context;
|
||||
}
|
||||
|
@ -115,11 +113,6 @@ public class ContextRequest extends Request.WrapperProcessor implements Invocabl
|
|||
return _context;
|
||||
}
|
||||
|
||||
public String getPathInContext()
|
||||
{
|
||||
return _pathInContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String name)
|
||||
{
|
||||
|
@ -127,7 +120,7 @@ public class ContextRequest extends Request.WrapperProcessor implements Invocabl
|
|||
return switch (name)
|
||||
{
|
||||
case "o.e.j.s.h.ScopedRequest.contextPath" -> _context.getContextPath();
|
||||
case "o.e.j.s.h.ScopedRequest.pathInContext" -> _pathInContext;
|
||||
case "o.e.j.s.h.ScopedRequest.pathInContext" -> Request.getPathInContext(this);
|
||||
default -> super.getAttribute(name);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ public class DefaultHandler extends Handler.Processor
|
|||
String method = request.getMethod();
|
||||
|
||||
// little cheat for common request
|
||||
if (isServeIcon() && _favicon != null && HttpMethod.GET.is(method) && request.getPathInContext().equals("/favicon.ico"))
|
||||
if (isServeIcon() && _favicon != null && HttpMethod.GET.is(method) && Request.getPathInContext(request).equals("/favicon.ico"))
|
||||
{
|
||||
ByteBuffer content = BufferUtil.EMPTY_BUFFER;
|
||||
if (_faviconModifiedMs > 0 && request.getHeaders().getDateField(HttpHeader.IF_MODIFIED_SINCE) == _faviconModifiedMs)
|
||||
|
@ -127,7 +127,7 @@ public class DefaultHandler extends Handler.Processor
|
|||
return;
|
||||
}
|
||||
|
||||
if (!isShowContexts() || !HttpMethod.GET.is(method) || !request.getPathInContext().equals("/"))
|
||||
if (!isShowContexts() || !HttpMethod.GET.is(method) || !Request.getPathInContext(request).equals("/"))
|
||||
{
|
||||
Response.writeError(request, response, callback, HttpStatus.NOT_FOUND_404, null);
|
||||
return;
|
||||
|
|
|
@ -110,7 +110,7 @@ public class ErrorProcessor implements Request.Processor
|
|||
|
||||
try
|
||||
{
|
||||
generateAcceptableResponse(request, response, code, message, cause, callback);
|
||||
generateResponse(request, response, code, message, cause, callback);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
|
@ -120,7 +120,7 @@ public class ErrorProcessor implements Request.Processor
|
|||
}
|
||||
}
|
||||
|
||||
protected void generateAcceptableResponse(Request request, Response response, int code, String message, Throwable cause, Callback callback) throws IOException
|
||||
protected void generateResponse(Request request, Response response, int code, String message, Throwable cause, Callback callback) throws IOException
|
||||
{
|
||||
List<String> acceptable = request.getHeaders().getQualityCSV(HttpHeader.ACCEPT, QuotedQualityCSV.MOST_SPECIFIC_MIME_ORDERING);
|
||||
if (acceptable.isEmpty())
|
||||
|
|
|
@ -222,7 +222,7 @@ public class InetAccessHandler extends Handler.Wrapper
|
|||
protected boolean isAllowed(InetAddress addr, Request request)
|
||||
{
|
||||
String connectorName = request.getConnectionMetaData().getConnector().getName();
|
||||
String path = request.getPathInContext();
|
||||
String path = Request.getPathInContext(request);
|
||||
return _set.test(new AccessTuple(connectorName, addr, path));
|
||||
}
|
||||
|
||||
|
|
|
@ -102,8 +102,9 @@ public class MovedContextHandler extends ContextHandler
|
|||
return;
|
||||
|
||||
String path = _newContextURL;
|
||||
if (!_discardPathInfo && request.getPathInContext() != null)
|
||||
path = URIUtil.addPaths(path, request.getPathInContext());
|
||||
String pathInContext = Request.getPathInContext(request);
|
||||
if (!_discardPathInfo && pathInContext != null)
|
||||
path = URIUtil.addPaths(path, pathInContext);
|
||||
|
||||
HttpURI uri = request.getHttpURI();
|
||||
StringBuilder location = new StringBuilder();
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.eclipse.jetty.http.pathmap.MappedResource;
|
||||
import org.eclipse.jetty.http.pathmap.MatchedResource;
|
||||
import org.eclipse.jetty.http.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A Handler that delegates to other handlers through a configured {@link PathMappings}.
|
||||
*/
|
||||
|
||||
public class PathMappingsHandler extends Handler.AbstractContainer
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(PathMappingsHandler.class);
|
||||
|
||||
private final PathMappings<Handler> mappings = new PathMappings<>();
|
||||
|
||||
@Override
|
||||
public void addHandler(Handler handler)
|
||||
{
|
||||
throw new UnsupportedOperationException("Arbitrary addHandler() not supported, use addMapping() instead");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHandler(Supplier<Handler> supplier)
|
||||
{
|
||||
throw new UnsupportedOperationException("Arbitrary addHandler() not supported, use addMapping() instead");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Handler> getHandlers()
|
||||
{
|
||||
return mappings.streamResources().map(MappedResource::getResource).toList();
|
||||
}
|
||||
|
||||
public void addMapping(PathSpec pathSpec, Handler handler)
|
||||
{
|
||||
if (isStarted())
|
||||
throw new IllegalStateException("Cannot add mapping: " + this);
|
||||
|
||||
// check that self isn't present
|
||||
if (handler == this || handler instanceof Handler.Container container && container.getDescendants().contains(this))
|
||||
throw new IllegalStateException("Unable to addHandler of self: " + handler);
|
||||
|
||||
// check existing mappings
|
||||
for (MappedResource<Handler> entry : mappings)
|
||||
{
|
||||
Handler entryHandler = entry.getResource();
|
||||
|
||||
if (entryHandler == this ||
|
||||
entryHandler == handler ||
|
||||
(entryHandler instanceof Handler.Container container && container.getDescendants().contains(this)))
|
||||
throw new IllegalStateException("addMapping loop detected: " + handler);
|
||||
}
|
||||
|
||||
mappings.put(pathSpec, handler);
|
||||
addBean(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
Dumpable.dumpObjects(out, indent, this, mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request.Processor handle(Request request) throws Exception
|
||||
{
|
||||
String pathInContext = Request.getPathInContext(request);
|
||||
MatchedResource<Handler> matchedResource = mappings.getMatched(pathInContext);
|
||||
if (matchedResource == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("No match on pathInContext of {}", pathInContext);
|
||||
return null;
|
||||
}
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Matched pathInContext of {} to {} -> {}", pathInContext, matchedResource.getPathSpec(), matchedResource.getResource());
|
||||
return matchedResource.getResource().handle(request);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.ExceptionUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* An ErrorProcessor that can re-handle a request at an error page location.
|
||||
*/
|
||||
public abstract class ReHandlingErrorProcessor extends ErrorProcessor
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReHandlingErrorProcessor.class);
|
||||
|
||||
private final Handler _handler;
|
||||
|
||||
protected ReHandlingErrorProcessor(Handler handler)
|
||||
{
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvocationType getInvocationType()
|
||||
{
|
||||
return _handler.getInvocationType();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateResponse(Request request, Response response, int code, String message, Throwable cause, Callback callback) throws IOException
|
||||
{
|
||||
if (request.getAttribute(ReHandlingErrorProcessor.class.getName()) == null)
|
||||
{
|
||||
String pathInContext = getReHandlePathInContext(request, code, cause);
|
||||
|
||||
if (pathInContext != null)
|
||||
{
|
||||
request.setAttribute(ReHandlingErrorProcessor.class.getName(), pathInContext);
|
||||
HttpURI uri = Request.newHttpURIFrom(request, pathInContext);
|
||||
Request.Wrapper wrapper = new ReHandleRequestWrapper(request, uri);
|
||||
|
||||
try
|
||||
{
|
||||
Request.Processor processor = _handler.handle(wrapper);
|
||||
if (processor != null)
|
||||
{
|
||||
response.setStatus(200);
|
||||
processor.process(wrapper, response, callback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Unable to process error {}", wrapper, e);
|
||||
if (cause != null && ExceptionUtil.areNotAssociated(cause, e))
|
||||
cause.addSuppressed(e);
|
||||
response.setStatus(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.generateResponse(request, response, code, message, cause, callback);
|
||||
}
|
||||
|
||||
protected abstract String getReHandlePathInContext(Request request, int code, Throwable cause);
|
||||
|
||||
/**
|
||||
* An ErrorPageErrorProcessor that uses a map of error codes to select a page.
|
||||
*/
|
||||
public static class ByHttpStatus extends ReHandlingErrorProcessor
|
||||
{
|
||||
private final Map<Integer, String> _statusMap = new ConcurrentHashMap<>();
|
||||
|
||||
public ByHttpStatus(Handler handler)
|
||||
{
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getReHandlePathInContext(Request request, int code, Throwable cause)
|
||||
{
|
||||
return get(code);
|
||||
}
|
||||
|
||||
public String put(int code, String pathInContext)
|
||||
{
|
||||
return _statusMap.put(code, pathInContext);
|
||||
}
|
||||
|
||||
public String get(int code)
|
||||
{
|
||||
return _statusMap.get(code);
|
||||
}
|
||||
|
||||
public String remove(int code)
|
||||
{
|
||||
return _statusMap.remove(code);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReHandleRequestWrapper extends Request.Wrapper
|
||||
{
|
||||
private final HttpURI _uri;
|
||||
|
||||
public ReHandleRequestWrapper(Request request, HttpURI uri)
|
||||
{
|
||||
super(request);
|
||||
_uri = uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpURI getHttpURI()
|
||||
{
|
||||
return _uri;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -101,8 +101,9 @@ public class ResourceHandler extends Handler.Wrapper
|
|||
|
||||
for (String welcome : _welcomes)
|
||||
{
|
||||
String welcomeInContext = URIUtil.addPaths(request.getPathInContext(), welcome);
|
||||
Resource welcomePath = _resourceBase.resolve(request.getPathInContext()).resolve(welcome);
|
||||
String pathInContext = Request.getPathInContext(request);
|
||||
String welcomeInContext = URIUtil.addPaths(pathInContext, welcome);
|
||||
Resource welcomePath = _resourceBase.resolve(pathInContext).resolve(welcome);
|
||||
if (Resources.isReadableFile(welcomePath))
|
||||
return welcomeInContext;
|
||||
}
|
||||
|
@ -120,7 +121,7 @@ public class ResourceHandler extends Handler.Wrapper
|
|||
return super.handle(request);
|
||||
}
|
||||
|
||||
HttpContent content = _resourceService.getContent(request.getPathInContext(), request);
|
||||
HttpContent content = _resourceService.getContent(Request.getPathInContext(request), request);
|
||||
if (content == null)
|
||||
return super.handle(request); // no content - try other handlers
|
||||
|
||||
|
|
|
@ -15,35 +15,111 @@ package org.eclipse.jetty.server.handler;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.server.Context;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
||||
/**
|
||||
* <p>Inspired by nginx's {@code try_files} functionality.</p>
|
||||
* <p> This handler can be configured with a list of URI paths.
|
||||
* The special token {@code $path} represents the current request URI
|
||||
* path (the portion after the context path).</p>
|
||||
*
|
||||
* <p>This handler can be configured with a list of rewrite URI paths.
|
||||
* The special token {@code $path} represents the current request
|
||||
* {@code pathInContext} (the portion after the context path).</p>
|
||||
*
|
||||
* <p>Typical example of how this handler can be configured is the following:</p>
|
||||
* <pre>{@code
|
||||
* TryPathsHandler tryPaths = new TryPathsHandler();
|
||||
* tryPaths.setPaths("/maintenance.html", "$path", "/index.php?p=$path");
|
||||
* TryPathsHandler tryPathsHandler = new TryPathsHandler();
|
||||
* tryPathsHandler.setPaths("/maintenance.html", "$path", "/index.php?p=$path");
|
||||
*
|
||||
* PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
|
||||
* tryPathsHandler.setHandler(pathMappingsHandler);
|
||||
*
|
||||
* pathMappingsHandler.addMapping(new ServletPathSpec("*.php"), new PHPHandler());
|
||||
* pathMappingsHandler.addMapping(new ServletPathSpec("/"), new ResourceHandler());
|
||||
* }</pre>
|
||||
* <p>For a request such as {@code /context/path/to/resource.ext}, this
|
||||
* handler will try to serve the {@code /maintenance.html} file if it finds
|
||||
* it; failing that, it will try to serve the {@code /path/to/resource.ext}
|
||||
* file if it finds it; failing that it will forward the request to
|
||||
* {@code /index.php?p=/path/to/resource.ext} to the next handler.</p>
|
||||
* <p>The last URI path specified in the list is therefore the "fallback" to
|
||||
* which the request is forwarded to in case no previous files can be found.</p>
|
||||
* <p>The file paths are resolved against {@link Context#getBaseResource()}
|
||||
* to make sure that only files visible to the application are served.</p>
|
||||
*
|
||||
* <p>For a request such as {@code /context/path/to/resource.ext}:</p>
|
||||
* <ul>
|
||||
* <li>This handler rewrites the request {@code pathInContext} to
|
||||
* {@code /maintenance.html} and forwards the request to the next handler,
|
||||
* where it matches the {@code /} mapping, hitting the {@code ResourceHandler}
|
||||
* that serves the file if it exists.</li>
|
||||
* <li>Otherwise, this handler rewrites the request {@code pathInContext} to
|
||||
* {@code /path/to/resource.ext} and forwards the request to the next handler,
|
||||
* where it matches the {@code /} mapping, hitting the {@code ResourceHandler}
|
||||
* that serves the file if it exists.</li>
|
||||
* <li>Otherwise, this handler rewrites the request {@code pathInContext} to
|
||||
* {@code /index.php?p=/path/to/resource.ext} and forwards the request to
|
||||
* the next handler, where it matches the {@code *.php} mapping, hitting
|
||||
* the {@code PHPHandler}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>The original path and query may be stored as request attributes,
|
||||
* under the names specified by {@link #setOriginalPathAttribute(String)}
|
||||
* and {@link #setOriginalQueryAttribute(String)}.</p>
|
||||
*/
|
||||
public class TryPathsHandler extends Handler.Wrapper
|
||||
{
|
||||
private String originalPathAttribute;
|
||||
private String originalQueryAttribute;
|
||||
private List<String> paths;
|
||||
|
||||
/**
|
||||
* @return the attribute name of the original request path
|
||||
*/
|
||||
public String getOriginalPathAttribute()
|
||||
{
|
||||
return originalPathAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the request attribute name to use to
|
||||
* retrieve the original request path.</p>
|
||||
*
|
||||
* @param originalPathAttribute the attribute name of the original
|
||||
* request path
|
||||
*/
|
||||
public void setOriginalPathAttribute(String originalPathAttribute)
|
||||
{
|
||||
this.originalPathAttribute = originalPathAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the attribute name of the original request query
|
||||
*/
|
||||
public String getOriginalQueryAttribute()
|
||||
{
|
||||
return originalQueryAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the request attribute name to use to
|
||||
* retrieve the original request query.</p>
|
||||
*
|
||||
* @param originalQueryAttribute the attribute name of the original
|
||||
* request query
|
||||
*/
|
||||
public void setOriginalQueryAttribute(String originalQueryAttribute)
|
||||
{
|
||||
this.originalQueryAttribute = originalQueryAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the rewrite URI paths
|
||||
*/
|
||||
public List<String> getPaths()
|
||||
{
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets a list of rewrite URI paths.</p>
|
||||
* <p>The special token {@code $path} represents the current request
|
||||
* {@code pathInContext} (the portion after the context path).</p>
|
||||
*
|
||||
* @param paths the rewrite URI paths
|
||||
*/
|
||||
public void setPaths(List<String> paths)
|
||||
{
|
||||
this.paths = paths;
|
||||
|
@ -52,49 +128,60 @@ public class TryPathsHandler extends Handler.Wrapper
|
|||
@Override
|
||||
public Request.Processor handle(Request request) throws Exception
|
||||
{
|
||||
String interpolated = interpolate(request, "$path");
|
||||
Resource rootResource = request.getContext().getBaseResource();
|
||||
if (rootResource != null)
|
||||
for (String path : paths)
|
||||
{
|
||||
for (String path : paths)
|
||||
{
|
||||
interpolated = interpolate(request, path);
|
||||
Resource resource = rootResource.resolve(interpolated);
|
||||
if (resource != null && resource.exists())
|
||||
break;
|
||||
}
|
||||
String interpolated = interpolate(request, path);
|
||||
Request.WrapperProcessor result = new Request.WrapperProcessor(new TryPathsRequest(request, interpolated));
|
||||
Request.Processor childProcessor = super.handle(result);
|
||||
if (childProcessor != null)
|
||||
return result.wrapProcessor(childProcessor);
|
||||
}
|
||||
Request.WrapperProcessor result = new Request.WrapperProcessor(new TryPathsRequest(request, interpolated));
|
||||
return result.wrapProcessor(super.handle(result));
|
||||
}
|
||||
|
||||
private Request.Processor fallback(Request request) throws Exception
|
||||
{
|
||||
String fallback = paths.isEmpty() ? "$path" : paths.get(paths.size() - 1);
|
||||
String interpolated = interpolate(request, fallback);
|
||||
return super.handle(new TryPathsRequest(request, interpolated));
|
||||
return null;
|
||||
}
|
||||
|
||||
private String interpolate(Request request, String value)
|
||||
{
|
||||
String path = request.getPathInContext();
|
||||
String path = Request.getPathInContext(request);
|
||||
return value.replace("$path", path);
|
||||
}
|
||||
|
||||
private static class TryPathsRequest extends Request.Wrapper
|
||||
private class TryPathsRequest extends Request.Wrapper
|
||||
{
|
||||
private final String pathInContext;
|
||||
private final HttpURI _uri;
|
||||
|
||||
public TryPathsRequest(Request wrapped, String pathInContext)
|
||||
public TryPathsRequest(Request wrapped, String newPathQuery)
|
||||
{
|
||||
super(wrapped);
|
||||
this.pathInContext = pathInContext;
|
||||
|
||||
HttpURI originalURI = wrapped.getHttpURI();
|
||||
|
||||
String originalPathAttribute = getOriginalPathAttribute();
|
||||
if (originalPathAttribute != null)
|
||||
setAttribute(originalPathAttribute, Request.getPathInContext(wrapped));
|
||||
String originalQueryAttribute = getOriginalQueryAttribute();
|
||||
if (originalQueryAttribute != null)
|
||||
setAttribute(originalQueryAttribute, originalURI.getQuery());
|
||||
|
||||
String originalContextPath = Request.getContextPath(wrapped);
|
||||
HttpURI.Mutable rewrittenURI = HttpURI.build(originalURI);
|
||||
int queryIdx = newPathQuery.indexOf('?');
|
||||
if (queryIdx >= 0)
|
||||
{
|
||||
String path = newPathQuery.substring(0, queryIdx);
|
||||
rewrittenURI.path(URIUtil.addPaths(originalContextPath, path));
|
||||
rewrittenURI.query(newPathQuery.substring(queryIdx + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
rewrittenURI.path(URIUtil.addPaths(originalContextPath, newPathQuery));
|
||||
}
|
||||
_uri = rewrittenURI.asImmutable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathInContext()
|
||||
public HttpURI getHttpURI()
|
||||
{
|
||||
return pathInContext;
|
||||
return _uri;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -523,7 +523,7 @@ public class GzipHandler extends Handler.Wrapper implements GzipFactory
|
|||
@Override
|
||||
public Request.Processor handle(Request request) throws Exception
|
||||
{
|
||||
final String path = request.getPathInContext();
|
||||
final String path = Request.getPathInContext(request);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} handle {}", this, request);
|
||||
|
|
|
@ -605,7 +605,7 @@ public class HttpChannelState implements HttpChannel, Components
|
|||
{
|
||||
if (!HttpMethod.PRI.is(request.getMethod()) &&
|
||||
!HttpMethod.CONNECT.is(request.getMethod()) &&
|
||||
!_request.getPathInContext().startsWith("/") &&
|
||||
!Request.getPathInContext(_request).startsWith("/") &&
|
||||
!HttpMethod.OPTIONS.is(request.getMethod()))
|
||||
{
|
||||
_processState = ProcessState.PROCESSING;
|
||||
|
@ -796,7 +796,7 @@ public class HttpChannelState implements HttpChannel, Components
|
|||
return _loggedRequest == null ? this : _loggedRequest;
|
||||
}
|
||||
|
||||
HttpStream getStream()
|
||||
HttpStream getHttpStream()
|
||||
{
|
||||
return getHttpChannel()._stream;
|
||||
}
|
||||
|
@ -903,12 +903,6 @@ public class HttpChannelState implements HttpChannel, Components
|
|||
return getConnectionMetaData().getConnector().getServer().getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathInContext()
|
||||
{
|
||||
return _metaData.getURI().getCanonicalPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpFields getHeaders()
|
||||
{
|
||||
|
@ -1013,7 +1007,7 @@ public class HttpChannelState implements HttpChannel, Components
|
|||
@Override
|
||||
public void push(MetaData.Request request)
|
||||
{
|
||||
getStream().push(request);
|
||||
getHttpStream().push(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1047,7 +1041,7 @@ public class HttpChannelState implements HttpChannel, Components
|
|||
@Override
|
||||
public TunnelSupport getTunnelSupport()
|
||||
{
|
||||
return getStream().getTunnelSupport();
|
||||
return getHttpStream().getTunnelSupport();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1468,7 +1462,7 @@ public class HttpChannelState implements HttpChannel, Components
|
|||
public InvocationType getInvocationType()
|
||||
{
|
||||
// TODO review this as it is probably not correct
|
||||
return _request.getStream().getInvocationType();
|
||||
return _request.getHttpStream().getInvocationType();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -614,7 +614,7 @@ public class CustomRequestLogTest
|
|||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
if (request.getPathInContext().equals("/abort"))
|
||||
if (Request.getPathInContext(request).equals("/abort"))
|
||||
{
|
||||
Callback cbk = Callback.from(() -> callback.failed(new QuietException.Exception("test fail")), callback::failed);
|
||||
Content.Sink.write(response, false, "data", cbk);
|
||||
|
|
|
@ -25,11 +25,13 @@ import javax.xml.parsers.DocumentBuilderFactory;
|
|||
import org.eclipse.jetty.http.BadMessageException;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.io.Content;
|
||||
import org.eclipse.jetty.io.QuietException;
|
||||
import org.eclipse.jetty.logging.StacklessLogging;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.ErrorProcessor;
|
||||
import org.eclipse.jetty.server.handler.ReHandlingErrorProcessor;
|
||||
import org.eclipse.jetty.server.internal.HttpChannelState;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
@ -71,21 +73,22 @@ public class ErrorProcessorTest
|
|||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
if (request.getPathInContext().startsWith("/badmessage/"))
|
||||
String pathInContext = Request.getPathInContext(request);
|
||||
if (pathInContext.startsWith("/badmessage/"))
|
||||
{
|
||||
int code = Integer.parseInt(request.getPathInContext().substring(request.getPathInContext().lastIndexOf('/') + 1));
|
||||
int code = Integer.parseInt(pathInContext.substring(pathInContext.lastIndexOf('/') + 1));
|
||||
throw new BadMessageException(code);
|
||||
}
|
||||
|
||||
// produce an exception with an JSON formatted cause message
|
||||
if (request.getPathInContext().startsWith("/jsonmessage/"))
|
||||
if (pathInContext.startsWith("/jsonmessage/"))
|
||||
{
|
||||
String message = "\"}, \"glossary\": {\n \"title\": \"example\"\n }\n {\"";
|
||||
throw new TestException(message);
|
||||
}
|
||||
|
||||
// produce an exception with an XML cause message
|
||||
if (request.getPathInContext().startsWith("/xmlmessage/"))
|
||||
if (pathInContext.startsWith("/xmlmessage/"))
|
||||
{
|
||||
String message =
|
||||
"<!DOCTYPE glossary PUBLIC \"-//OASIS//DTD DocBook V3.1//EN\">\n" +
|
||||
|
@ -96,14 +99,14 @@ public class ErrorProcessorTest
|
|||
}
|
||||
|
||||
// produce an exception with an HTML cause message
|
||||
if (request.getPathInContext().startsWith("/htmlmessage/"))
|
||||
if (pathInContext.startsWith("/htmlmessage/"))
|
||||
{
|
||||
String message = "<hr/><script>alert(42)</script>%3Cscript%3E";
|
||||
throw new TestException(message);
|
||||
}
|
||||
|
||||
// produce an exception with a UTF-8 cause message
|
||||
if (request.getPathInContext().startsWith("/utf8message/"))
|
||||
if (pathInContext.startsWith("/utf8message/"))
|
||||
{
|
||||
// @checkstyle-disable-check : AvoidEscapedUnicodeCharacters
|
||||
String message = "Euro is € and \u20AC and %E2%82%AC";
|
||||
|
@ -111,6 +114,17 @@ public class ErrorProcessorTest
|
|||
throw new TestException(message);
|
||||
}
|
||||
|
||||
// 200 response
|
||||
if (pathInContext.startsWith("/ok/"))
|
||||
{
|
||||
Content.Sink.write(
|
||||
response,
|
||||
true,
|
||||
"%s Error %s : %s%n".formatted(pathInContext, request.getAttribute(ErrorProcessor.ERROR_STATUS), request.getAttribute(ErrorProcessor.ERROR_MESSAGE)),
|
||||
callback);
|
||||
return;
|
||||
}
|
||||
|
||||
Response.writeError(request, response, callback, 404);
|
||||
}
|
||||
});
|
||||
|
@ -689,6 +703,104 @@ public class ErrorProcessorTest
|
|||
assertThat(response, containsString("Server Error"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRootReHandlingErrorProcessor() throws Exception
|
||||
{
|
||||
ReHandlingErrorProcessor.ByHttpStatus errorProcessor = new ReHandlingErrorProcessor.ByHttpStatus(server);
|
||||
errorProcessor.put(400, "/ok/badMessage");
|
||||
server.setErrorProcessor(errorProcessor);
|
||||
|
||||
String rawResponse = connector.getResponse("""
|
||||
GET /no/host HTTP/1.1
|
||||
|
||||
""");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
|
||||
assertThat(response.getStatus(), is(200));
|
||||
assertThat(response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
|
||||
assertThat(response.getContent(), containsString("/ok/badMessage Error 400 : No Host"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRootReHandlingErrorProcessorLoop() throws Exception
|
||||
{
|
||||
ReHandlingErrorProcessor.ByHttpStatus errorProcessor = new ReHandlingErrorProcessor.ByHttpStatus(server);
|
||||
errorProcessor.put(404, "/not/found");
|
||||
server.setErrorProcessor(errorProcessor);
|
||||
|
||||
String rawResponse = connector.getResponse("""
|
||||
GET /not/found HTTP/1.1
|
||||
Host: localhost
|
||||
|
||||
""");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
|
||||
assertThat(response.getStatus(), is(404));
|
||||
assertThat(response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
|
||||
assertThat(response.getContent(), containsString("<title>Error 404 Not Found</title>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRootReHandlingErrorProcessorExceptionLoop() throws Exception
|
||||
{
|
||||
ReHandlingErrorProcessor.ByHttpStatus errorProcessor = new ReHandlingErrorProcessor.ByHttpStatus(server);
|
||||
errorProcessor.put(444, "/badmessage/444");
|
||||
server.setErrorProcessor(errorProcessor);
|
||||
|
||||
String rawResponse = connector.getResponse("""
|
||||
GET /badmessage/444 HTTP/1.1
|
||||
Host: localhost
|
||||
|
||||
""");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
|
||||
assertThat(response.getStatus(), is(444));
|
||||
assertThat(response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
|
||||
assertThat(response.getContent(), containsString("<title>Error 444</title>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContextReHandlingErrorProcessor() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
|
||||
ContextHandler context = new ContextHandler("/ctx");
|
||||
context.setHandler(server.getHandler());
|
||||
|
||||
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||
contexts.addHandler(context);
|
||||
server.setHandler(contexts);
|
||||
|
||||
server.setErrorProcessor(new ErrorProcessor()
|
||||
{
|
||||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
});
|
||||
|
||||
server.start();
|
||||
ReHandlingErrorProcessor.ByHttpStatus errorProcessor = new ReHandlingErrorProcessor.ByHttpStatus(context);
|
||||
errorProcessor.put(444, "/ok/badMessage");
|
||||
context.setErrorProcessor(errorProcessor);
|
||||
|
||||
String rawResponse = connector.getResponse("""
|
||||
GET /ctx/badmessage/444 HTTP/1.1
|
||||
Host: localhost
|
||||
|
||||
""");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
|
||||
assertThat(response.getStatus(), is(200));
|
||||
assertThat(response.getField(HttpHeader.CONTENT_LENGTH).getIntValue(), greaterThan(0));
|
||||
assertThat(response.getContent(), containsString("/ok/badMessage Error 444"));
|
||||
}
|
||||
|
||||
static class TestException extends RuntimeException implements QuietException
|
||||
{
|
||||
public TestException(String message)
|
||||
|
|
|
@ -654,7 +654,7 @@ public class HttpConfigurationAuthorityOverrideTest
|
|||
@Override
|
||||
public Request.Processor handle(Request request) throws Exception
|
||||
{
|
||||
if (!request.getPathInContext().startsWith("/dump"))
|
||||
if (!Request.getPathInContext(request).startsWith("/dump"))
|
||||
return null;
|
||||
return (rq, rs, cb) ->
|
||||
{
|
||||
|
@ -679,7 +679,7 @@ public class HttpConfigurationAuthorityOverrideTest
|
|||
@Override
|
||||
public Request.Processor handle(Request request) throws Exception
|
||||
{
|
||||
if (!request.getPathInContext().startsWith("/redirect"))
|
||||
if (!Request.getPathInContext(request).startsWith("/redirect"))
|
||||
return null;
|
||||
|
||||
return (rq, rs, cb) ->
|
||||
|
@ -696,7 +696,7 @@ public class HttpConfigurationAuthorityOverrideTest
|
|||
@Override
|
||||
public Request.Processor handle(Request request) throws Exception
|
||||
{
|
||||
if (!request.getPathInContext().startsWith("/error"))
|
||||
if (!Request.getPathInContext(request).startsWith("/error"))
|
||||
return null;
|
||||
return super.handle(request);
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ public class SlowClientWithPipelinedRequestTest
|
|||
@Override
|
||||
public void process(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
if ("/content".equals(request.getPathInContext()))
|
||||
if ("/content".equals(Request.getPathInContext(request)))
|
||||
{
|
||||
// TODO is this still a valid test?
|
||||
// We simulate what the DefaultServlet does, bypassing the blocking
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ContextHandlerDeepTest
|
||||
{
|
||||
private Server server;
|
||||
private LocalConnector connector;
|
||||
|
||||
private void startServer(Handler handler) throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = new LocalConnector(server);
|
||||
server.addConnector(connector);
|
||||
|
||||
server.setHandler(handler);
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void stopServer()
|
||||
{
|
||||
LifeCycle.stop(server);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedThreeDeepContextHandler() throws Exception
|
||||
{
|
||||
ContextHandler contextHandlerA = new ContextHandler();
|
||||
contextHandlerA.setContextPath("/a");
|
||||
ContextHandler contextHandlerB = new ContextHandler();
|
||||
contextHandlerB.setContextPath("/a/b");
|
||||
ContextHandler contextHandlerC = new ContextHandler();
|
||||
contextHandlerC.setContextPath("/a/b/c");
|
||||
|
||||
contextHandlerA.setHandler(contextHandlerB);
|
||||
contextHandlerB.setHandler(contextHandlerC);
|
||||
contextHandlerC.setHandler(new Handler.Processor()
|
||||
{
|
||||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/plain; charset=utf-8");
|
||||
String msg = """
|
||||
contextPath=%s
|
||||
pathInContext=%s
|
||||
httpURI.getPath=%s
|
||||
"""
|
||||
.formatted(
|
||||
Request.getContextPath(request),
|
||||
Request.getPathInContext(request),
|
||||
request.getHttpURI().getPath()
|
||||
);
|
||||
|
||||
response.write(true, BufferUtil.toBuffer(msg), callback);
|
||||
}
|
||||
});
|
||||
|
||||
startServer(contextHandlerA);
|
||||
|
||||
String rawRequest = """
|
||||
GET /a/b/c/d HTTP/1.1\r
|
||||
Host: local\r
|
||||
Connection: close\r
|
||||
|
||||
""";
|
||||
HttpTester.Response response = HttpTester.parseResponse(connector.getResponse(rawRequest));
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertThat(response.getContent(), containsString("contextPath=/a/b/c\n"));
|
||||
assertThat(response.getContent(), containsString("pathInContext=/d\n"));
|
||||
assertThat(response.getContent(), containsString("httpURI.getPath=/a/b/c/d\n"));
|
||||
}
|
||||
}
|
|
@ -60,6 +60,7 @@ import static org.hamcrest.Matchers.nullValue;
|
|||
import static org.hamcrest.Matchers.sameInstance;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class ContextHandlerTest
|
||||
|
@ -188,7 +189,7 @@ public class ContextHandlerTest
|
|||
{
|
||||
if (request != null)
|
||||
{
|
||||
assertThat(request.getPathInContext(), equalTo("/path"));
|
||||
assertThat(Request.getPathInContext(request), equalTo("/path"));
|
||||
assertThat(request.getContext(), sameInstance(_context));
|
||||
}
|
||||
assertThat(ContextHandler.getCurrentContext(), sameInstance(_context));
|
||||
|
@ -586,6 +587,31 @@ public class ContextHandlerTest
|
|||
assertThat(result.get(), equalTo("OK"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetHandlerLoopSelf()
|
||||
{
|
||||
ContextHandler contextHandlerA = new ContextHandler();
|
||||
assertThrows(IllegalStateException.class, () -> contextHandlerA.setHandler(contextHandlerA));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetHandlerLoopDeepWrapper()
|
||||
{
|
||||
ContextHandler contextHandlerA = new ContextHandler();
|
||||
Handler.Wrapper handlerWrapper = new Handler.Wrapper();
|
||||
contextHandlerA.setHandler(handlerWrapper);
|
||||
assertThrows(IllegalStateException.class, () -> handlerWrapper.setHandler(contextHandlerA));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddHandlerLoopDeep()
|
||||
{
|
||||
ContextHandler contextHandlerA = new ContextHandler();
|
||||
Handler.Collection handlerCollection = new Handler.Collection();
|
||||
contextHandlerA.setHandler(handlerCollection);
|
||||
assertThrows(IllegalStateException.class, () -> handlerCollection.addHandler(contextHandlerA));
|
||||
}
|
||||
|
||||
private static class ScopeListener implements ContextHandler.ContextScopeListener
|
||||
{
|
||||
private static final Request NULL = new Request.Wrapper(null);
|
||||
|
|
|
@ -150,7 +150,7 @@ public class DumpHandler extends Handler.Processor.Blocking
|
|||
writer.write("<pre>httpURI.path=" + httpURI.getPath() + "</pre><br/>\n");
|
||||
writer.write("<pre>httpURI.query=" + httpURI.getQuery() + "</pre><br/>\n");
|
||||
writer.write("<pre>httpURI.pathQuery=" + httpURI.getPathQuery() + "</pre><br/>\n");
|
||||
writer.write("<pre>pathInContext=" + request.getPathInContext() + "</pre><br/>\n");
|
||||
writer.write("<pre>pathInContext=" + Request.getPathInContext(request) + "</pre><br/>\n");
|
||||
writer.write("<pre>contentType=" + request.getHeaders().get(HttpHeader.CONTENT_TYPE) + "</pre><br/>\n");
|
||||
writer.write("<pre>servername=" + Request.getServerName(request) + "</pre><br/>\n");
|
||||
writer.write("<pre>local=" + Request.getLocalAddr(request) + ":" + Request.getLocalPort(request) + "</pre><br/>\n");
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class PathMappingsHandlerTest
|
||||
{
|
||||
private Server server;
|
||||
private LocalConnector connector;
|
||||
|
||||
public void startServer(Handler handler) throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = new LocalConnector(server);
|
||||
server.addConnector(connector);
|
||||
|
||||
server.addHandler(handler);
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void stopServer()
|
||||
{
|
||||
LifeCycle.stop(server);
|
||||
}
|
||||
|
||||
public HttpTester.Response executeRequest(String rawRequest) throws Exception
|
||||
{
|
||||
String rawResponse = connector.getResponse(rawRequest);
|
||||
return HttpTester.parseResponse(rawResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test where there are no mappings, and no wrapper.
|
||||
*/
|
||||
@Test
|
||||
public void testEmpty() throws Exception
|
||||
{
|
||||
ContextHandler contextHandler = new ContextHandler();
|
||||
contextHandler.setContextPath("/");
|
||||
|
||||
PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
|
||||
contextHandler.setHandler(pathMappingsHandler);
|
||||
|
||||
startServer(contextHandler);
|
||||
|
||||
HttpTester.Response response = executeRequest("""
|
||||
GET / HTTP/1.1\r
|
||||
Host: local\r
|
||||
Connection: close\r
|
||||
|
||||
""");
|
||||
assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test where there is only a single mapping, and no wrapper.
|
||||
*/
|
||||
@Test
|
||||
public void testOnlyMappingSuffix() throws Exception
|
||||
{
|
||||
ContextHandler contextHandler = new ContextHandler();
|
||||
contextHandler.setContextPath("/");
|
||||
|
||||
PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("*.php"), new SimpleHandler("PhpExample Hit"));
|
||||
contextHandler.setHandler(pathMappingsHandler);
|
||||
|
||||
startServer(contextHandler);
|
||||
|
||||
HttpTester.Response response = executeRequest("""
|
||||
GET /hello HTTP/1.1\r
|
||||
Host: local\r
|
||||
Connection: close\r
|
||||
|
||||
""");
|
||||
assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus());
|
||||
|
||||
response = executeRequest("""
|
||||
GET /hello.php HTTP/1.1\r
|
||||
Host: local\r
|
||||
Connection: close\r
|
||||
|
||||
""");
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertEquals("PhpExample Hit", response.getContent());
|
||||
}
|
||||
|
||||
public static Stream<Arguments> severalMappingsInput()
|
||||
{
|
||||
return Stream.of(
|
||||
Arguments.of("/hello", HttpStatus.OK_200, "FakeResourceHandler Hit"),
|
||||
Arguments.of("/index.html", HttpStatus.OK_200, "FakeSpecificStaticHandler Hit"),
|
||||
Arguments.of("/index.php", HttpStatus.OK_200, "PhpHandler Hit"),
|
||||
Arguments.of("/config.php", HttpStatus.OK_200, "PhpHandler Hit"),
|
||||
Arguments.of("/css/main.css", HttpStatus.OK_200, "FakeResourceHandler Hit")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test where there are a few mappings, with a root mapping, and no wrapper.
|
||||
* This means the wrapper would not ever be hit, as all inputs would match at
|
||||
* least 1 mapping.
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("severalMappingsInput")
|
||||
public void testSeveralMappingAndNoWrapper(String requestPath, int expectedStatus, String expectedResponseBody) throws Exception
|
||||
{
|
||||
ContextHandler contextHandler = new ContextHandler();
|
||||
contextHandler.setContextPath("/");
|
||||
|
||||
PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("/"), new SimpleHandler("FakeResourceHandler Hit"));
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("/index.html"), new SimpleHandler("FakeSpecificStaticHandler Hit"));
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("*.php"), new SimpleHandler("PhpHandler Hit"));
|
||||
contextHandler.setHandler(pathMappingsHandler);
|
||||
|
||||
startServer(contextHandler);
|
||||
|
||||
HttpTester.Response response = executeRequest("""
|
||||
GET %s HTTP/1.1\r
|
||||
Host: local\r
|
||||
Connection: close\r
|
||||
|
||||
""".formatted(requestPath));
|
||||
assertEquals(expectedStatus, response.getStatus());
|
||||
assertEquals(expectedResponseBody, response.getContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDump() throws Exception
|
||||
{
|
||||
ContextHandler contextHandler = new ContextHandler();
|
||||
contextHandler.setContextPath("/");
|
||||
|
||||
PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("/"), new SimpleHandler("FakeResourceHandler Hit"));
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("/index.html"), new SimpleHandler("FakeSpecificStaticHandler Hit"));
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("*.php"), new SimpleHandler("PhpHandler Hit"));
|
||||
contextHandler.setHandler(pathMappingsHandler);
|
||||
|
||||
startServer(contextHandler);
|
||||
|
||||
String dump = contextHandler.dump();
|
||||
assertThat(dump, containsString("FakeResourceHandler"));
|
||||
assertThat(dump, containsString("FakeSpecificStaticHandler"));
|
||||
assertThat(dump, containsString("PhpHandler"));
|
||||
assertThat(dump, containsString("PathMappings[size=3]"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDescendantsSimple()
|
||||
{
|
||||
ContextHandler contextHandler = new ContextHandler();
|
||||
contextHandler.setContextPath("/");
|
||||
|
||||
PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("/"), new SimpleHandler("default"));
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("/index.html"), new SimpleHandler("specific"));
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("*.php"), new SimpleHandler("php"));
|
||||
|
||||
List<String> actualHandlers = pathMappingsHandler.getDescendants().stream().map(Objects::toString).toList();
|
||||
|
||||
String[] expectedHandlers = {
|
||||
"SimpleHandler[msg=\"default\"]",
|
||||
"SimpleHandler[msg=\"specific\"]",
|
||||
"SimpleHandler[msg=\"php\"]"
|
||||
};
|
||||
assertThat(actualHandlers, containsInAnyOrder(expectedHandlers));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetDescendantsDeep()
|
||||
{
|
||||
ContextHandler contextHandler = new ContextHandler();
|
||||
contextHandler.setContextPath("/");
|
||||
|
||||
Handler.Collection handlerCollection = new Handler.Collection();
|
||||
handlerCollection.addHandler(new SimpleHandler("phpIndex"));
|
||||
Handler.Wrapper handlerWrapper = new Handler.Wrapper(new SimpleHandler("other"));
|
||||
handlerCollection.addHandler(handlerWrapper);
|
||||
|
||||
PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("/"), new SimpleHandler("default"));
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("/index.html"), new SimpleHandler("specific"));
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("*.php"), handlerCollection);
|
||||
|
||||
List<String> actualHandlers = pathMappingsHandler.getDescendants().stream().map(Objects::toString).toList();
|
||||
|
||||
String[] expectedHandlers = {
|
||||
"SimpleHandler[msg=\"default\"]",
|
||||
"SimpleHandler[msg=\"specific\"]",
|
||||
handlerCollection.toString(),
|
||||
handlerWrapper.toString(),
|
||||
"SimpleHandler[msg=\"phpIndex\"]",
|
||||
"SimpleHandler[msg=\"other\"]"
|
||||
};
|
||||
assertThat(actualHandlers, containsInAnyOrder(expectedHandlers));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLoopSelf()
|
||||
{
|
||||
PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
|
||||
assertThrows(IllegalStateException.class, () -> pathMappingsHandler.addMapping(new ServletPathSpec("/self"), pathMappingsHandler));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLoopContext()
|
||||
{
|
||||
ContextHandler contextHandler = new ContextHandler();
|
||||
PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
|
||||
contextHandler.setHandler(pathMappingsHandler);
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> pathMappingsHandler.addMapping(new ServletPathSpec("/loop"), contextHandler));
|
||||
}
|
||||
|
||||
private static class SimpleHandler extends Handler.Processor
|
||||
{
|
||||
private final String message;
|
||||
|
||||
public SimpleHandler(String message)
|
||||
{
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
assertTrue(isStarted());
|
||||
response.setStatus(HttpStatus.OK_200);
|
||||
response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/plain; charset=utf-8");
|
||||
response.write(true, BufferUtil.toBuffer(message, StandardCharsets.UTF_8), callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[msg=\"%s\"]", SimpleHandler.class.getSimpleName(), message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -199,7 +199,7 @@ public class ThreadLimitHandlerTest
|
|||
public void process(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
response.setStatus(HttpStatus.OK_200);
|
||||
if (!"/other".equals(request.getPathInContext()))
|
||||
if (!"/other".equals(Request.getPathInContext(request)))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -22,16 +22,18 @@ import java.nio.file.StandardOpenOption;
|
|||
import java.util.List;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||
import org.eclipse.jetty.io.Content;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
|
@ -41,18 +43,20 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class TryPathsHandlerTest
|
||||
{
|
||||
public WorkDir workDir;
|
||||
private static final String CONTEXT_PATH = "/ctx";
|
||||
private Server server;
|
||||
private SslContextFactory.Server sslContextFactory;
|
||||
private ServerConnector connector;
|
||||
private ServerConnector sslConnector;
|
||||
private Path rootPath;
|
||||
private String contextPath;
|
||||
|
||||
private void start(List<String> paths, Handler handler) throws Exception
|
||||
{
|
||||
|
@ -66,22 +70,18 @@ public class TryPathsHandlerTest
|
|||
sslConnector = new ServerConnector(server, 1, 1, sslContextFactory);
|
||||
server.addConnector(sslConnector);
|
||||
|
||||
contextPath = "/ctx";
|
||||
ContextHandler context = new ContextHandler(contextPath);
|
||||
rootPath = Files.createDirectories(MavenTestingUtils.getTargetTestingPath(getClass().getSimpleName()));
|
||||
FS.cleanDirectory(rootPath);
|
||||
ContextHandler context = new ContextHandler(CONTEXT_PATH);
|
||||
rootPath = workDir.getEmptyPathDir();
|
||||
context.setBaseResourceAsPath(rootPath);
|
||||
server.setHandler(context);
|
||||
|
||||
TryPathsHandler tryPaths = new TryPathsHandler();
|
||||
context.setHandler(tryPaths);
|
||||
|
||||
tryPaths.setPaths(paths);
|
||||
tryPaths.setHandler(handler);
|
||||
|
||||
ResourceHandler resourceHandler = new ResourceHandler();
|
||||
tryPaths.setHandler(resourceHandler);
|
||||
|
||||
resourceHandler.setHandler(handler);
|
||||
|
||||
server.setDumpAfterStart(true);
|
||||
server.start();
|
||||
}
|
||||
|
||||
|
@ -94,47 +94,155 @@ public class TryPathsHandlerTest
|
|||
@Test
|
||||
public void testTryPaths() throws Exception
|
||||
{
|
||||
start(List.of("/maintenance.txt", "$path", "/forward?p=$path"), new Handler.Processor()
|
||||
ResourceHandler resourceHandler = new ResourceHandler();
|
||||
resourceHandler.setDirAllowed(false);
|
||||
resourceHandler.setHandler(new Handler.Abstract()
|
||||
{
|
||||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
public Request.Processor handle(Request request)
|
||||
{
|
||||
assertThat(request.getPathInContext(), equalTo("/forward?p=/last"));
|
||||
response.setStatus(HttpStatus.NO_CONTENT_204);
|
||||
callback.succeeded();
|
||||
if (!Request.getPathInContext(request).startsWith("/forward"))
|
||||
return null;
|
||||
|
||||
return new Handler.Processor()
|
||||
{
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
assertThat(Request.getPathInContext(request), equalTo("/forward"));
|
||||
assertThat(request.getHttpURI().getQuery(), equalTo("p=/last"));
|
||||
response.setStatus(HttpStatus.NO_CONTENT_204);
|
||||
callback.succeeded();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
start(List.of("/maintenance.txt", "$path", "/forward?p=$path"), resourceHandler);
|
||||
|
||||
try (SocketChannel channel = SocketChannel.open())
|
||||
{
|
||||
channel.connect(new InetSocketAddress("localhost", connector.getLocalPort()));
|
||||
|
||||
// Make a first request without existing file paths.
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setURI(contextPath + "/last");
|
||||
request.setURI(CONTEXT_PATH + "/last");
|
||||
channel.write(request.generate());
|
||||
HttpTester.Response response = HttpTester.parseResponse(channel);
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.NO_CONTENT_204, response.getStatus());
|
||||
|
||||
// Create the specific file that is requested.
|
||||
// Create the specific static file that is requested.
|
||||
String path = "idx.txt";
|
||||
Files.writeString(rootPath.resolve(path), "hello", StandardOpenOption.CREATE);
|
||||
// Make a second request with the specific file.
|
||||
request = HttpTester.newRequest();
|
||||
request.setURI(contextPath + "/" + path);
|
||||
request.setURI(CONTEXT_PATH + "/" + path);
|
||||
channel.write(request.generate());
|
||||
response = HttpTester.parseResponse(channel);
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertEquals("hello", response.getContent());
|
||||
|
||||
// Create the "maintenance" file, it should be served first.
|
||||
path = "maintenance.txt";
|
||||
Files.writeString(rootPath.resolve(path), "maintenance", StandardOpenOption.CREATE);
|
||||
// Make a third request with any path, we should get the maintenance file.
|
||||
request = HttpTester.newRequest();
|
||||
request.setURI(CONTEXT_PATH + "/whatever");
|
||||
channel.write(request.generate());
|
||||
response = HttpTester.parseResponse(channel);
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertEquals("maintenance", response.getContent());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTryPathsWithPathMappings() throws Exception
|
||||
{
|
||||
ResourceHandler resourceHandler = new ResourceHandler();
|
||||
resourceHandler.setDirAllowed(false);
|
||||
|
||||
PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("/"), resourceHandler);
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("*.php"), new Handler.Abstract()
|
||||
{
|
||||
@Override
|
||||
public Request.Processor handle(Request request)
|
||||
{
|
||||
return new Processor()
|
||||
{
|
||||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
response.setStatus(HttpStatus.OK_200);
|
||||
response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/plain; charset=utf-8");
|
||||
String message = "PHP: pathInContext=%s, query=%s".formatted(Request.getPathInContext(request), request.getHttpURI().getQuery());
|
||||
Content.Sink.write(response, true, message, callback);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
pathMappingsHandler.addMapping(new ServletPathSpec("/forward"), new Handler.Abstract()
|
||||
{
|
||||
@Override
|
||||
public Request.Processor handle(Request request)
|
||||
{
|
||||
return new Handler.Processor()
|
||||
{
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
assertThat(Request.getPathInContext(request), equalTo("/forward"));
|
||||
assertThat(request.getHttpURI().getQuery(), equalTo("p=/last"));
|
||||
response.setStatus(HttpStatus.NO_CONTENT_204);
|
||||
callback.succeeded();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
start(List.of("/maintenance.txt", "$path", "/forward?p=$path"), pathMappingsHandler);
|
||||
|
||||
try (SocketChannel channel = SocketChannel.open())
|
||||
{
|
||||
channel.connect(new InetSocketAddress("localhost", connector.getLocalPort()));
|
||||
|
||||
// Make a first request without existing file paths.
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setURI(CONTEXT_PATH + "/last");
|
||||
channel.write(request.generate());
|
||||
HttpTester.Response response = HttpTester.parseResponse(channel);
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.NO_CONTENT_204, response.getStatus());
|
||||
|
||||
// Create the specific static file that is requested.
|
||||
String path = "idx.txt";
|
||||
Files.writeString(rootPath.resolve(path), "hello", StandardOpenOption.CREATE);
|
||||
// Make a second request with the specific file.
|
||||
request = HttpTester.newRequest();
|
||||
request.setURI(CONTEXT_PATH + "/" + path);
|
||||
channel.write(request.generate());
|
||||
response = HttpTester.parseResponse(channel);
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertEquals("hello", response.getContent());
|
||||
|
||||
// Request an existing PHP file.
|
||||
Files.writeString(rootPath.resolve("index.php"), "raw-php-contents", StandardOpenOption.CREATE);
|
||||
request = HttpTester.newRequest();
|
||||
request.setURI(CONTEXT_PATH + "/index.php");
|
||||
channel.write(request.generate());
|
||||
response = HttpTester.parseResponse(channel);
|
||||
assertNotNull(response);
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertThat(response.getContent(), startsWith("PHP: pathInContext=/index.php"));
|
||||
|
||||
// Create the "maintenance" file, it should be served first.
|
||||
path = "maintenance.txt";
|
||||
Files.writeString(rootPath.resolve(path), "maintenance", StandardOpenOption.CREATE);
|
||||
// Make a second request with any path, we should get the maintenance file.
|
||||
request = HttpTester.newRequest();
|
||||
request.setURI(contextPath + "/whatever");
|
||||
request.setURI(CONTEXT_PATH + "/whatever");
|
||||
channel.write(request.generate());
|
||||
response = HttpTester.parseResponse(channel);
|
||||
assertNotNull(response);
|
||||
|
@ -155,7 +263,7 @@ public class TryPathsHandlerTest
|
|||
HttpURI httpURI = request.getHttpURI();
|
||||
assertEquals("https", httpURI.getScheme());
|
||||
assertTrue(request.isSecure());
|
||||
assertEquals(path, request.getPathInContext());
|
||||
assertEquals(path, Request.getPathInContext(request));
|
||||
callback.succeeded();
|
||||
}
|
||||
});
|
||||
|
@ -165,7 +273,7 @@ public class TryPathsHandlerTest
|
|||
sslSocket.connect(new InetSocketAddress("localhost", sslConnector.getLocalPort()));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setURI(contextPath + path);
|
||||
request.setURI(CONTEXT_PATH + path);
|
||||
OutputStream output = sslSocket.getOutputStream();
|
||||
output.write(BufferUtil.toArray(request.generate()));
|
||||
output.flush();
|
||||
|
|
|
@ -148,7 +148,7 @@ public class GzipHandlerTest
|
|||
@Override
|
||||
public void process(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
String pathInfo = request.getPathInContext();
|
||||
String pathInfo = Request.getPathInContext(request);
|
||||
response.getHeaders().put(HttpHeader.CONTENT_TYPE, getContentTypeFromRequest(pathInfo, request));
|
||||
Content.Sink.write(response, true, "This is content for " + pathInfo + "\n", callback);
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ public class SniSslConnectionFactoryTest
|
|||
public void process(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
response.setStatus(200);
|
||||
response.getHeaders().put("X-URL", request.getPathInContext());
|
||||
response.getHeaders().put("X-URL", Request.getPathInContext(request));
|
||||
response.getHeaders().put("X-HOST", Request.getServerName(request));
|
||||
callback.succeeded();
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public class SimpleSessionHandlerTest
|
|||
@Override
|
||||
public void process(Request request, Response response, Callback callback)
|
||||
{
|
||||
String pathInContext = request.getPathInContext();
|
||||
String pathInContext = Request.getPathInContext(request);
|
||||
String[] split = pathInContext.substring(1).split("/");
|
||||
|
||||
SessionRequest sessionRequest = Request.as(request, SessionRequest.class);
|
||||
|
|
|
@ -96,12 +96,6 @@ public class TestableRequest implements Request
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathInContext()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpFields getHeaders()
|
||||
{
|
||||
|
|
|
@ -261,7 +261,7 @@ public class ForwardProxyWithDynamicTransportTest
|
|||
int proxyPort = proxySecure ? proxyTLSConnector.getLocalPort() : proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy proxy = new HttpProxy(proxyAddress, proxySecure, proxyProtocol);
|
||||
client.getProxyConfiguration().getProxies().add(proxy);
|
||||
client.getProxyConfiguration().addProxy(proxy);
|
||||
|
||||
String scheme = serverSecure ? "https" : "http";
|
||||
int serverPort = serverSecure ? serverTLSConnector.getLocalPort() : serverConnector.getLocalPort();
|
||||
|
@ -298,7 +298,7 @@ public class ForwardProxyWithDynamicTransportTest
|
|||
int proxyPort = proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy proxy = new HttpProxy(proxyAddress, false, new Origin.Protocol(List.of("h2c"), false));
|
||||
client.getProxyConfiguration().getProxies().add(proxy);
|
||||
client.getProxyConfiguration().addProxy(proxy);
|
||||
|
||||
long idleTimeout = 1000;
|
||||
http2Client.setStreamIdleTimeout(idleTimeout);
|
||||
|
@ -339,7 +339,7 @@ public class ForwardProxyWithDynamicTransportTest
|
|||
int proxyPort = proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy httpProxy = new HttpProxy(proxyAddress, false, new Origin.Protocol(List.of("h2c"), false));
|
||||
client.getProxyConfiguration().getProxies().add(httpProxy);
|
||||
client.getProxyConfiguration().addProxy(httpProxy);
|
||||
proxy.stop();
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
@ -376,7 +376,7 @@ public class ForwardProxyWithDynamicTransportTest
|
|||
int proxyPort = proxyConnector.getLocalPort();
|
||||
Origin.Address proxyAddress = new Origin.Address("localhost", proxyPort);
|
||||
HttpProxy httpProxy = new HttpProxy(proxyAddress, false, new Origin.Protocol(List.of("h2c"), false));
|
||||
client.getProxyConfiguration().getProxies().add(httpProxy);
|
||||
client.getProxyConfiguration().addProxy(httpProxy);
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", serverConnector.getLocalPort())
|
||||
|
|
|
@ -42,7 +42,7 @@ public class HttpClientIdleTimeoutTest extends AbstractTest
|
|||
public void process(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
// Do not succeed the callback if it's a timeout request.
|
||||
if (!request.getPathInContext().equals("/timeout"))
|
||||
if (!Request.getPathInContext(request).equals("/timeout"))
|
||||
callback.succeeded();
|
||||
}
|
||||
});
|
||||
|
@ -76,7 +76,7 @@ public class HttpClientIdleTimeoutTest extends AbstractTest
|
|||
public void process(Request request, Response response, Callback callback) throws Exception
|
||||
{
|
||||
// Do not succeed the callback if it's a timeout request.
|
||||
if (!request.getPathInContext().equals("/timeout"))
|
||||
if (!Request.getPathInContext(request).equals("/timeout"))
|
||||
callback.succeeded();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1069,7 +1069,7 @@ public class HttpClientStreamTest extends AbstractTest
|
|||
@Override
|
||||
public void process(Request request, org.eclipse.jetty.server.Response response, Callback callback)
|
||||
{
|
||||
if (request.getPathInContext().startsWith("/303"))
|
||||
if (Request.getPathInContext(request).startsWith("/303"))
|
||||
org.eclipse.jetty.server.Response.sendRedirect(request, response, callback, "/200");
|
||||
callback.succeeded();
|
||||
}
|
||||
|
|
|
@ -369,7 +369,7 @@ public class HttpClientTest extends AbstractTest
|
|||
public void process(Request request, org.eclipse.jetty.server.Response response, Callback callback)
|
||||
{
|
||||
assertTrue(HttpMethod.OPTIONS.is(request.getMethod()));
|
||||
assertEquals("*", request.getPathInContext());
|
||||
assertEquals("*", Request.getPathInContext(request));
|
||||
callback.succeeded();
|
||||
}
|
||||
});
|
||||
|
@ -392,7 +392,7 @@ public class HttpClientTest extends AbstractTest
|
|||
@Override
|
||||
public void process(Request request, org.eclipse.jetty.server.Response response, Callback callback)
|
||||
{
|
||||
if ("*".equals(request.getPathInContext()))
|
||||
if ("*".equals(Request.getPathInContext(request)))
|
||||
{
|
||||
// Be nasty and send a relative redirect.
|
||||
// Code 303 will change the method to GET.
|
||||
|
@ -566,7 +566,7 @@ public class HttpClientTest extends AbstractTest
|
|||
@Override
|
||||
public void process(Request request, org.eclipse.jetty.server.Response response, Callback callback)
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
if ("/notMapped".equals(target))
|
||||
org.eclipse.jetty.server.Response.writeError(request, response, callback, HttpStatus.NOT_FOUND_404);
|
||||
else
|
||||
|
@ -762,7 +762,7 @@ public class HttpClientTest extends AbstractTest
|
|||
@Override
|
||||
public void process(Request request, org.eclipse.jetty.server.Response response, Callback callback) throws Exception
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
if (target.equals("/1"))
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
else if (target.equals("/2"))
|
||||
|
|
|
@ -424,7 +424,7 @@ public class HttpClientTimeoutTest extends AbstractTest
|
|||
@Override
|
||||
public void process(org.eclipse.jetty.server.Request request, org.eclipse.jetty.server.Response response, Callback callback) throws Exception
|
||||
{
|
||||
if (request.getPathInContext().startsWith("/one"))
|
||||
if (org.eclipse.jetty.server.Request.getPathInContext(request).startsWith("/one"))
|
||||
Thread.sleep(3 * timeout);
|
||||
callback.succeeded();
|
||||
}
|
||||
|
@ -460,7 +460,7 @@ public class HttpClientTimeoutTest extends AbstractTest
|
|||
@Override
|
||||
public void process(org.eclipse.jetty.server.Request request, org.eclipse.jetty.server.Response response, Callback callback) throws Exception
|
||||
{
|
||||
if (request.getPathInContext().startsWith("/one"))
|
||||
if (org.eclipse.jetty.server.Request.getPathInContext(request).startsWith("/one"))
|
||||
serverLatch.await();
|
||||
callback.succeeded();
|
||||
}
|
||||
|
|
|
@ -585,7 +585,7 @@ public class HttpClientTransportDynamicTest
|
|||
int proxyPort = connector.getLocalPort();
|
||||
// The proxy speaks both http/1.1 and h2c.
|
||||
Origin.Protocol proxyProtocol = new Origin.Protocol(List.of("http/1.1", "h2c"), false);
|
||||
client.getProxyConfiguration().getProxies().add(new HttpProxy(new Origin.Address("localhost", proxyPort), false, proxyProtocol));
|
||||
client.getProxyConfiguration().addProxy(new HttpProxy(new Origin.Address("localhost", proxyPort), false, proxyProtocol));
|
||||
|
||||
// Make an upgrade request from HTTP/1.1 to H2C.
|
||||
int serverPort = proxyPort + 1; // Any port will do.
|
||||
|
|
|
@ -174,7 +174,7 @@ public class UnixDomainTest
|
|||
ClientConnector clientConnector = ClientConnector.forUnixDomain(unixDomainPath);
|
||||
|
||||
HttpClient httpClient = new HttpClient(new HttpClientTransportDynamic(clientConnector));
|
||||
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", fakeProxyPort));
|
||||
httpClient.getProxyConfiguration().addProxy(new HttpProxy("localhost", fakeProxyPort));
|
||||
httpClient.start();
|
||||
try
|
||||
{
|
||||
|
@ -205,7 +205,7 @@ public class UnixDomainTest
|
|||
assertThat(endPoint, Matchers.instanceOf(ProxyConnectionFactory.ProxyEndPoint.class));
|
||||
assertThat(endPoint.getLocalSocketAddress(), Matchers.instanceOf(unixDomainSocketAddressClass));
|
||||
assertThat(endPoint.getRemoteSocketAddress(), Matchers.instanceOf(unixDomainSocketAddressClass));
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
if ("/v1".equals(target))
|
||||
{
|
||||
// As PROXYv1 does not support UNIX, the wrapped EndPoint data is used.
|
||||
|
|
|
@ -170,60 +170,60 @@ public interface Attributes
|
|||
*/
|
||||
class Wrapper implements Attributes
|
||||
{
|
||||
protected final Attributes _attributes;
|
||||
private final Attributes _wrapped;
|
||||
|
||||
public Wrapper(Attributes attributes)
|
||||
public Wrapper(Attributes wrapped)
|
||||
{
|
||||
_attributes = attributes;
|
||||
_wrapped = wrapped;
|
||||
}
|
||||
|
||||
public Attributes getWrapped()
|
||||
{
|
||||
return _attributes;
|
||||
return _wrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object removeAttribute(String name)
|
||||
{
|
||||
return _attributes.removeAttribute(name);
|
||||
return getWrapped().removeAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object setAttribute(String name, Object attribute)
|
||||
{
|
||||
return _attributes.setAttribute(name, attribute);
|
||||
return getWrapped().setAttribute(name, attribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String name)
|
||||
{
|
||||
return _attributes.getAttribute(name);
|
||||
return getWrapped().getAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAttributeNameSet()
|
||||
{
|
||||
return _attributes.getAttributeNameSet();
|
||||
return getWrapped().getAttributeNameSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAttributes()
|
||||
{
|
||||
_attributes.clearAttributes();
|
||||
getWrapped().clearAttributes();
|
||||
}
|
||||
|
||||
// TODO: remove? or fix (don't want the wrapped and wrapper to match)
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return _attributes.hashCode();
|
||||
return getWrapped().hashCode();
|
||||
}
|
||||
|
||||
// TODO: remove? or fix (don't want the wrapped and wrapper to match)
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
return _attributes.equals(obj);
|
||||
return getWrapped().equals(obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -697,6 +697,29 @@ public final class URIUtil
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path The path to check for validity
|
||||
* @return True if the path does not contain any invalid path characters
|
||||
*/
|
||||
public static boolean isPathValid(String path)
|
||||
{
|
||||
if (path == null)
|
||||
return true;
|
||||
|
||||
int end = path.length();
|
||||
for (int i = 0; i < end; i++)
|
||||
{
|
||||
char c = path.charAt(i);
|
||||
switch (c)
|
||||
{
|
||||
case '?' :
|
||||
case '#' :
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if codepoint is safe and unambiguous to pass as input to {@link URI}
|
||||
*
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.ee9.quickstart;
|
||||
package org.eclipse.jetty.util.resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -22,18 +22,15 @@ import java.nio.file.FileSystems;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -165,81 +162,79 @@ public class AttributeNormalizer
|
|||
}
|
||||
}
|
||||
|
||||
private static Comparator<Attribute> attrComparator = new Comparator<Attribute>()
|
||||
private static final Comparator<Attribute> attrComparator = (o1, o2) ->
|
||||
{
|
||||
@Override
|
||||
public int compare(Attribute o1, Attribute o2)
|
||||
if ((o1.value == null) && (o2.value != null))
|
||||
{
|
||||
if ((o1.value == null) && (o2.value != null))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((o1.value != null) && (o2.value == null))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((o1.value == null) && (o2.value == null))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Different lengths?
|
||||
int diff = o2.value.length() - o1.value.length();
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Different names?
|
||||
diff = o2.value.compareTo(o1.value);
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// The paths are the same, base now on weight
|
||||
return o2.weight - o1.weight;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((o1.value != null) && (o2.value == null))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (o1.value == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Different lengths?
|
||||
int diff = o2.value.length() - o1.value.length();
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Different names?
|
||||
diff = o2.value.compareTo(o1.value);
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// The paths are the same, base now on weight
|
||||
return o2.weight - o1.weight;
|
||||
};
|
||||
|
||||
private URI warURI;
|
||||
private Map<String, Attribute> attributes = new HashMap<>();
|
||||
private List<PathAttribute> paths = new ArrayList<>();
|
||||
private List<URIAttribute> uris = new ArrayList<>();
|
||||
private final List<PathAttribute> paths = new ArrayList<>();
|
||||
private final List<URIAttribute> uris = new ArrayList<>();
|
||||
|
||||
public AttributeNormalizer(Resource baseResource)
|
||||
{
|
||||
if (baseResource == null)
|
||||
throw new IllegalArgumentException("No base resource!");
|
||||
|
||||
warURI = toCanonicalURI(baseResource.getURI());
|
||||
if (!warURI.isAbsolute())
|
||||
throw new IllegalArgumentException("WAR URI is not absolute: " + warURI);
|
||||
|
||||
addSystemProperty("jetty.base", 9);
|
||||
addSystemProperty("jetty.home", 8);
|
||||
addSystemProperty("user.home", 7);
|
||||
addSystemProperty("user.dir", 6);
|
||||
|
||||
if (warURI.getScheme().equalsIgnoreCase("file"))
|
||||
paths.add(new PathAttribute("WAR.path", toCanonicalPath(new File(warURI).toString()), 10));
|
||||
uris.add(new URIAttribute("WAR.uri", warURI, 9)); // preferred encoding
|
||||
uris.add(new URIAttribute("WAR", warURI, 8)); // legacy encoding
|
||||
Set<Path> rootPaths = new HashSet<>();
|
||||
for (Resource r : baseResource)
|
||||
{
|
||||
if (r instanceof MountedPathResource mpr && rootPaths.contains(mpr.getContainerPath()))
|
||||
return;
|
||||
|
||||
Collections.sort(paths, attrComparator);
|
||||
Collections.sort(uris, attrComparator);
|
||||
URI warURI = toCanonicalURI(r.getURI());
|
||||
if (!warURI.isAbsolute())
|
||||
throw new IllegalArgumentException("WAR URI is not absolute: " + warURI);
|
||||
|
||||
Stream.concat(paths.stream(), uris.stream()).forEach(a -> attributes.put(a.key, a));
|
||||
Path path = r.getPath();
|
||||
if (path != null)
|
||||
{
|
||||
rootPaths.add(path);
|
||||
paths.add(new PathAttribute("WAR.path", toCanonicalPath(path), 10));
|
||||
}
|
||||
uris.add(new URIAttribute("WAR.uri", warURI, 9)); // preferred encoding
|
||||
uris.add(new URIAttribute("WAR", warURI, 8)); // legacy encoding
|
||||
}
|
||||
|
||||
paths.sort(attrComparator);
|
||||
uris.sort(attrComparator);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
for (Attribute attr : attributes.values())
|
||||
{
|
||||
LOG.debug(attr.toString());
|
||||
}
|
||||
}
|
||||
Stream.concat(paths.stream(), uris.stream()).map(Object::toString).forEach(LOG::debug);
|
||||
}
|
||||
|
||||
private void addSystemProperty(String key, int weight)
|
||||
|
@ -352,7 +347,7 @@ public class AttributeNormalizer
|
|||
return String.format("${%s}", a.key);
|
||||
|
||||
String s = uPath.substring(aPath.length());
|
||||
if (s.length() > 0 && s.charAt(0) != '/')
|
||||
if (s.charAt(0) != '/')
|
||||
continue;
|
||||
|
||||
return String.format("${%s}%s", a.key, s);
|
||||
|
@ -375,95 +370,79 @@ public class AttributeNormalizer
|
|||
}
|
||||
|
||||
if (path.startsWith(a.path))
|
||||
return String.format("${%s}%c%s", a.key, File.separatorChar, a.path.relativize(path).toString());
|
||||
return String.format("${%s}%c%s", a.key, File.separatorChar, a.path.relativize(path));
|
||||
}
|
||||
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
public String expand(String str)
|
||||
{
|
||||
return expand(str, new Stack<String>());
|
||||
}
|
||||
|
||||
public String expand(String str, Stack<String> seenStack)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
if (str.indexOf("${") < 0)
|
||||
if (!str.contains("${"))
|
||||
{
|
||||
// Contains no potential expressions.
|
||||
return str;
|
||||
}
|
||||
|
||||
Matcher mat = __propertyPattern.matcher(str);
|
||||
StringBuilder expanded = new StringBuilder();
|
||||
int offset = 0;
|
||||
String property;
|
||||
String value;
|
||||
|
||||
while (mat.find(offset))
|
||||
if (mat.find(0))
|
||||
{
|
||||
property = mat.group(1);
|
||||
|
||||
// Loop detection
|
||||
if (seenStack.contains(property))
|
||||
{
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Property expansion loop detected: ");
|
||||
int idx = seenStack.lastIndexOf(property);
|
||||
for (int i = idx; i < seenStack.size(); i++)
|
||||
{
|
||||
err.append(seenStack.get(i));
|
||||
err.append(" -> ");
|
||||
}
|
||||
err.append(property);
|
||||
throw new RuntimeException(err.toString());
|
||||
}
|
||||
|
||||
seenStack.push(property);
|
||||
|
||||
// find property name
|
||||
expanded.append(str.subSequence(offset, mat.start()));
|
||||
// get property value
|
||||
value = getString(property);
|
||||
if (value == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Unable to expand: {}", property);
|
||||
expanded.append(mat.group());
|
||||
}
|
||||
else
|
||||
{
|
||||
// recursively expand
|
||||
value = expand(value, seenStack);
|
||||
expanded.append(value);
|
||||
}
|
||||
// update offset
|
||||
offset = mat.end();
|
||||
String prefix = str.substring(0, mat.start());
|
||||
String property = mat.group(1);
|
||||
String suffix = str.substring(mat.end());
|
||||
str = expand(prefix, property, suffix);
|
||||
}
|
||||
|
||||
// leftover
|
||||
expanded.append(str.substring(offset));
|
||||
|
||||
return StringUtil.replace(expanded.toString(), "$$", "$");
|
||||
return StringUtil.replace(str, "$$", "$");
|
||||
}
|
||||
|
||||
private String getString(String property)
|
||||
private String expand(String prefix, String property, String suffix)
|
||||
{
|
||||
if (property == null)
|
||||
{
|
||||
return null;
|
||||
|
||||
for (URIAttribute attr : uris)
|
||||
{
|
||||
if (property.equals(attr.key))
|
||||
{
|
||||
try
|
||||
{
|
||||
String uri = prefix + attr.value + suffix;
|
||||
Resource resource = ResourceFactory.root().newResource(uri);
|
||||
if (resource.exists())
|
||||
return uri;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.trace("ignored", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Attribute a = attributes.get(property);
|
||||
if (a != null)
|
||||
return a.value;
|
||||
for (PathAttribute attr : paths)
|
||||
{
|
||||
if (property.equals(attr.key))
|
||||
{
|
||||
String path = prefix + attr.value + suffix;
|
||||
if (Files.exists(Path.of(path)))
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
// Use system properties next
|
||||
return System.getProperty(property);
|
||||
String system = System.getProperty(property);
|
||||
if (system != null)
|
||||
return prefix + system + suffix;
|
||||
|
||||
String unexpanded = prefix + "${" + property + "}" + suffix;
|
||||
LOG.warn("Cannot expand: {}", unexpanded);
|
||||
return unexpanded;
|
||||
}
|
||||
}
|
|
@ -13,11 +13,8 @@
|
|||
|
||||
package org.eclipse.jetty.util.resource;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
|
@ -30,32 +27,34 @@ import java.util.stream.Collectors;
|
|||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
||||
/**
|
||||
* A collection of Resources.
|
||||
* Allows webapps to have multiple sources.
|
||||
* The first resource in the collection is the main resource.
|
||||
* If a resource is not found in the main resource, it looks it up in
|
||||
* the order the provided in the constructors
|
||||
* Multiple resource directories presented as a single Resource.
|
||||
*/
|
||||
public class ResourceCollection extends Resource
|
||||
public class CombinedResource extends Resource
|
||||
{
|
||||
private final List<Resource> _resources;
|
||||
|
||||
/**
|
||||
* Instantiates a new resource collection.
|
||||
*
|
||||
* @param resources the resources to be added to collection
|
||||
* <p>Make a Resource containing a combination of other resources</p>
|
||||
* @param resources multiple resources to combine as a single resource. Typically, they are directories.
|
||||
* @return A Resource of multiple resources or a single resource if only 1 is passed, or null if none are passed
|
||||
* @see CombinedResource
|
||||
*/
|
||||
ResourceCollection(List<Resource> resources)
|
||||
static Resource combine(List<Resource> resources)
|
||||
{
|
||||
List<Resource> res = new ArrayList<>();
|
||||
gatherUniqueFlatResourceList(res, resources);
|
||||
_resources = Collections.unmodifiableList(res);
|
||||
resources = CombinedResource.gatherUniqueFlatResourceList(resources);
|
||||
|
||||
if (resources == null || resources.isEmpty())
|
||||
return null;
|
||||
if (resources.size() == 1)
|
||||
return resources.get(0);
|
||||
|
||||
return new CombinedResource(resources);
|
||||
}
|
||||
|
||||
private static void gatherUniqueFlatResourceList(List<Resource> unique, List<Resource> resources)
|
||||
static List<Resource> gatherUniqueFlatResourceList(List<Resource> resources)
|
||||
{
|
||||
if (resources == null || resources.isEmpty())
|
||||
throw new IllegalArgumentException("Empty Resource collection");
|
||||
return null;
|
||||
|
||||
List<Resource> unique = new ArrayList<>(resources.size());
|
||||
|
||||
for (Resource r : resources)
|
||||
{
|
||||
|
@ -64,9 +63,9 @@ public class ResourceCollection extends Resource
|
|||
throw new IllegalArgumentException("Null Resource entry encountered");
|
||||
}
|
||||
|
||||
if (r instanceof ResourceCollection resourceCollection)
|
||||
if (r instanceof CombinedResource resourceCollection)
|
||||
{
|
||||
gatherUniqueFlatResourceList(unique, resourceCollection.getResources());
|
||||
unique.addAll(gatherUniqueFlatResourceList(resourceCollection.getResources()));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -89,6 +88,19 @@ public class ResourceCollection extends Resource
|
|||
unique.add(r);
|
||||
}
|
||||
}
|
||||
return unique;
|
||||
}
|
||||
|
||||
private final List<Resource> _resources;
|
||||
|
||||
/**
|
||||
* Instantiates a new resource collection.
|
||||
*
|
||||
* @param resources the resources to be added to collection
|
||||
*/
|
||||
CombinedResource(List<Resource> resources)
|
||||
{
|
||||
_resources = Collections.unmodifiableList(resources);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,127 +139,67 @@ public class ResourceCollection extends Resource
|
|||
ArrayList<Resource> resources = null;
|
||||
|
||||
// Attempt a simple (single) Resource lookup that exists
|
||||
Resource addedResource = null;
|
||||
Resource resolved = null;
|
||||
for (Resource res : _resources)
|
||||
{
|
||||
addedResource = res.resolve(subUriPath);
|
||||
if (Resources.missing(addedResource))
|
||||
resolved = res.resolve(subUriPath);
|
||||
if (Resources.missing(resolved))
|
||||
continue; // skip, doesn't exist
|
||||
if (!addedResource.isDirectory())
|
||||
return addedResource; // Return simple (non-directory) Resource
|
||||
if (!resolved.isDirectory())
|
||||
return resolved; // Return simple (non-directory) Resource
|
||||
if (resources == null)
|
||||
resources = new ArrayList<>();
|
||||
resources.add(addedResource);
|
||||
resources.add(resolved);
|
||||
}
|
||||
|
||||
if (resources == null)
|
||||
return addedResource; // This will not exist
|
||||
return resolved; // This will not exist
|
||||
|
||||
if (resources.size() == 1)
|
||||
return resources.get(0);
|
||||
|
||||
return new ResourceCollection(resources);
|
||||
return new CombinedResource(resources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists()
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
if (r.exists())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return _resources.stream().anyMatch(Resource::exists);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPath()
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
Path p = r.getPath();
|
||||
if (p != null)
|
||||
return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream newInputStream() throws IOException
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
if (!r.exists())
|
||||
{
|
||||
// Skip, cannot open anyway
|
||||
continue;
|
||||
}
|
||||
InputStream is = r.newInputStream();
|
||||
if (is != null)
|
||||
{
|
||||
return is;
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileNotFoundException("Resource does not exist");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableByteChannel newReadableByteChannel() throws IOException
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
ReadableByteChannel channel = r.newReadableByteChannel();
|
||||
if (channel != null)
|
||||
{
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
String name = r.getName();
|
||||
if (name != null)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName()
|
||||
{
|
||||
String filename = null;
|
||||
// return a non-null filename only if all resources agree on the same name.
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
String filename = r.getFileName();
|
||||
if (filename != null)
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
String fn = r.getFileName();
|
||||
if (fn == null)
|
||||
return null;
|
||||
if (filename == null)
|
||||
filename = fn;
|
||||
else if (!filename.equals(fn))
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI()
|
||||
{
|
||||
for (Resource r : _resources)
|
||||
{
|
||||
URI uri = r.getURI();
|
||||
if (uri != null)
|
||||
{
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -326,7 +278,7 @@ public class ResourceCollection extends Resource
|
|||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
ResourceCollection other = (ResourceCollection)o;
|
||||
CombinedResource other = (CombinedResource)o;
|
||||
return Objects.equals(_resources, other._resources);
|
||||
}
|
||||
|
||||
|
@ -343,7 +295,7 @@ public class ResourceCollection extends Resource
|
|||
public String toString()
|
||||
{
|
||||
return _resources.stream()
|
||||
.map(Resource::getName)
|
||||
.map(Resource::toString)
|
||||
.collect(Collectors.joining(", ", "[", "]"));
|
||||
}
|
||||
|
|
@ -113,11 +113,8 @@ public abstract class Resource implements Iterable<Resource>
|
|||
public abstract boolean isContainedIn(Resource r);
|
||||
|
||||
/**
|
||||
* Return an Iterator of all Resource's referenced in this Resource.
|
||||
*
|
||||
* <p>
|
||||
* This is meaningful if you have a Composite Resource, otherwise it will be a single entry Iterator.
|
||||
* </p>
|
||||
* <p>Return an Iterator of all Resource's referenced in this Resource.</p>
|
||||
* <p>This is meaningful if you have a Composite Resource, otherwise it will be a single entry Iterator of this resource.</p>
|
||||
*
|
||||
* @return the iterator of Resources.
|
||||
*/
|
||||
|
@ -198,22 +195,28 @@ public abstract class Resource implements Iterable<Resource>
|
|||
/**
|
||||
* Creates a new input stream to the resource.
|
||||
*
|
||||
* @return an input stream to the resource
|
||||
* @throws IOException if unable to open the input stream
|
||||
* @return an input stream to the resource or null if one is not available.
|
||||
* @throws IOException if there is a problem opening the input stream
|
||||
*/
|
||||
public InputStream newInputStream() throws IOException
|
||||
{
|
||||
return Files.newInputStream(getPath(), StandardOpenOption.READ);
|
||||
Path path = getPath();
|
||||
if (path == null)
|
||||
return null;
|
||||
return Files.newInputStream(path, StandardOpenOption.READ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Readable ByteChannel for the resource.
|
||||
*
|
||||
* @return an readable bytechannel to the resource or null if one is not available.
|
||||
* @return a readable {@link java.nio.channels.ByteChannel} to the resource or null if one is not available.
|
||||
* @throws IOException if unable to open the readable bytechannel for the resource.
|
||||
*/
|
||||
public ReadableByteChannel newReadableByteChannel() throws IOException
|
||||
{
|
||||
Path path = getPath();
|
||||
if (path == null)
|
||||
return null;
|
||||
return Files.newByteChannel(getPath(), StandardOpenOption.READ);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,27 +35,23 @@ public interface ResourceFactory
|
|||
/**
|
||||
* <p>Make a Resource containing a collection of other resources</p>
|
||||
* @param resources multiple resources to combine as a single resource. Typically, they are directories.
|
||||
* @return A Resource of multiple resources.
|
||||
* @see ResourceCollection
|
||||
* @return A Resource of multiple resources or a single resource if only 1 is passed, or null if none are passed
|
||||
* @see CombinedResource
|
||||
*/
|
||||
static ResourceCollection combine(List<Resource> resources)
|
||||
static Resource combine(List<Resource> resources)
|
||||
{
|
||||
if (resources == null || resources.isEmpty())
|
||||
throw new IllegalArgumentException("No resources");
|
||||
return new ResourceCollection(resources);
|
||||
return CombinedResource.combine(resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Make a Resource containing a collection of other resources</p>
|
||||
* @param resources multiple resources to combine as a single resource. Typically, they are directories.
|
||||
* @return A Resource of multiple resources.
|
||||
* @see ResourceCollection
|
||||
* @see CombinedResource
|
||||
*/
|
||||
static ResourceCollection combine(Resource... resources)
|
||||
static Resource combine(Resource... resources)
|
||||
{
|
||||
if (resources == null || resources.length == 0)
|
||||
throw new IllegalArgumentException("No resources");
|
||||
return new ResourceCollection(List.of(resources));
|
||||
return CombinedResource.combine(List.of(resources));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -203,12 +199,12 @@ public interface ResourceFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* Construct a ResourceCollection from a list of URIs
|
||||
* Construct a possible {@link CombinedResource} from a list of URIs
|
||||
*
|
||||
* @param uris the URIs
|
||||
* @return the Resource for the provided path
|
||||
*/
|
||||
default ResourceCollection newResource(List<URI> uris)
|
||||
default Resource newResource(List<URI> uris)
|
||||
{
|
||||
if ((uris == null) || (uris.isEmpty()))
|
||||
throw new IllegalArgumentException("List of URIs is invalid");
|
||||
|
|
|
@ -20,13 +20,13 @@ import java.util.concurrent.Executor;
|
|||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
import org.eclipse.jetty.util.AtomicBiInteger;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.VirtualThreads;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.thread.AutoLock;
|
||||
import org.eclipse.jetty.util.thread.ExecutionStrategy;
|
||||
import org.eclipse.jetty.util.thread.Invocable;
|
||||
import org.eclipse.jetty.util.thread.TryExecutor;
|
||||
|
@ -98,12 +98,9 @@ public class AdaptiveExecutionStrategy extends ContainerLifeCycle implements Exe
|
|||
/**
|
||||
* The production state of the strategy.
|
||||
*/
|
||||
private enum State
|
||||
{
|
||||
IDLE, // No tasks or producers.
|
||||
PRODUCING, // There is an active producing thread.
|
||||
REPRODUCING // There is an active producing thread and demand for more production.
|
||||
}
|
||||
static final int IDLE = 0; // No tasks or producers.
|
||||
static final int PRODUCING = 1; // There is an active producing thread.
|
||||
static final int REPRODUCING = 2; // There is an active producing thread and demand for more production.
|
||||
|
||||
/**
|
||||
* The sub-strategies used by the strategy to consume tasks that are produced.
|
||||
|
@ -128,7 +125,6 @@ public class AdaptiveExecutionStrategy extends ContainerLifeCycle implements Exe
|
|||
EXECUTE_PRODUCE_CONSUME
|
||||
}
|
||||
|
||||
private final AutoLock _lock = new AutoLock();
|
||||
private final LongAdder _pcMode = new LongAdder();
|
||||
private final LongAdder _picMode = new LongAdder();
|
||||
private final LongAdder _pecMode = new LongAdder();
|
||||
|
@ -138,8 +134,7 @@ public class AdaptiveExecutionStrategy extends ContainerLifeCycle implements Exe
|
|||
private final TryExecutor _tryExecutor;
|
||||
private final Runnable _runPendingProducer = () -> tryProduce(true);
|
||||
private boolean _useVirtualThreads;
|
||||
private State _state = State.IDLE;
|
||||
private boolean _pending;
|
||||
private final AtomicBiInteger _state = new AtomicBiInteger();
|
||||
|
||||
/**
|
||||
* @param producer The producer of tasks to be consumed.
|
||||
|
@ -167,26 +162,33 @@ public class AdaptiveExecutionStrategy extends ContainerLifeCycle implements Exe
|
|||
public void dispatch()
|
||||
{
|
||||
boolean execute = false;
|
||||
try (AutoLock l = _lock.lock())
|
||||
loop: while (true)
|
||||
{
|
||||
switch (_state)
|
||||
long biState = _state.get();
|
||||
int state = AtomicBiInteger.getLo(biState);
|
||||
int pending = AtomicBiInteger.getHi(biState);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case IDLE:
|
||||
if (!_pending)
|
||||
if (pending <= 0)
|
||||
{
|
||||
_pending = true;
|
||||
if (!_state.compareAndSet(biState, pending + 1, state))
|
||||
continue;
|
||||
execute = true;
|
||||
}
|
||||
break;
|
||||
break loop;
|
||||
|
||||
case PRODUCING:
|
||||
_state = State.REPRODUCING;
|
||||
break;
|
||||
if (!_state.compareAndSet(biState, pending, REPRODUCING))
|
||||
continue;
|
||||
break loop;
|
||||
|
||||
default:
|
||||
break;
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} dispatch {}", this, execute);
|
||||
if (execute)
|
||||
|
@ -209,39 +211,47 @@ public class AdaptiveExecutionStrategy extends ContainerLifeCycle implements Exe
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} tryProduce {}", this, wasPending);
|
||||
|
||||
// Takes the lock to atomically check if the thread can produce.
|
||||
try (AutoLock l = _lock.lock())
|
||||
// check if the thread can produce.
|
||||
loop: while (true)
|
||||
{
|
||||
long biState = _state.get();
|
||||
int state = AtomicBiInteger.getLo(biState);
|
||||
int pending = AtomicBiInteger.getHi(biState);
|
||||
|
||||
// If the calling thread was the pending producer, there is no longer one pending.
|
||||
if (wasPending)
|
||||
_pending = false;
|
||||
pending--;
|
||||
|
||||
switch (_state)
|
||||
switch (state)
|
||||
{
|
||||
case IDLE:
|
||||
// The strategy was IDLE, so this thread can become the producer.
|
||||
_state = State.PRODUCING;
|
||||
break;
|
||||
if (!_state.compareAndSet(biState, pending, PRODUCING))
|
||||
continue;
|
||||
break loop;
|
||||
|
||||
case PRODUCING:
|
||||
// The strategy is already producing, so another thread must be the producer.
|
||||
// However, it may be just about to stop being the producer so we set the
|
||||
// REPRODUCING state to force it to produce at least once more.
|
||||
_state = State.REPRODUCING;
|
||||
if (!_state.compareAndSet(biState, pending, REPRODUCING))
|
||||
continue;
|
||||
return;
|
||||
|
||||
case REPRODUCING:
|
||||
// Another thread is already producing and will already try again to produce.
|
||||
if (!_state.compareAndSet(biState, pending, state))
|
||||
continue;
|
||||
return;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(toStringLocked());
|
||||
throw new IllegalStateException(toString(biState));
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the thread's invocation type once, outside of the production loop.
|
||||
boolean nonBlocking = Invocable.isNonBlockingInvocation();
|
||||
while (isRunning())
|
||||
running: while (isRunning())
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -250,24 +260,30 @@ public class AdaptiveExecutionStrategy extends ContainerLifeCycle implements Exe
|
|||
// If we did not produce a task
|
||||
if (task == null)
|
||||
{
|
||||
// take the lock to atomically determine if we should keep producing.
|
||||
try (AutoLock l = _lock.lock())
|
||||
// determine if we should keep producing.
|
||||
while (true)
|
||||
{
|
||||
switch (_state)
|
||||
long biState = _state.get();
|
||||
int state = AtomicBiInteger.getLo(biState);
|
||||
int pending = AtomicBiInteger.getHi(biState);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case PRODUCING:
|
||||
// The calling thread was the only producer, so it is now IDLE and we stop producing.
|
||||
_state = State.IDLE;
|
||||
if (!_state.compareAndSet(biState, pending, IDLE))
|
||||
continue;
|
||||
return;
|
||||
|
||||
case REPRODUCING:
|
||||
// Another thread may have queued a task and tried to produce
|
||||
// so the calling thread should continue to produce.
|
||||
_state = State.PRODUCING;
|
||||
continue;
|
||||
if (!_state.compareAndSet(biState, pending, PRODUCING))
|
||||
continue;
|
||||
continue running;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(toStringLocked());
|
||||
throw new IllegalStateException(toString(biState));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -303,53 +319,90 @@ public class AdaptiveExecutionStrategy extends ContainerLifeCycle implements Exe
|
|||
return SubStrategy.PRODUCE_CONSUME;
|
||||
|
||||
case EITHER:
|
||||
{
|
||||
// The produced task may be run either as blocking or non blocking.
|
||||
|
||||
// If the calling producing thread is already non-blocking, use PC.
|
||||
if (nonBlocking)
|
||||
return SubStrategy.PRODUCE_CONSUME;
|
||||
|
||||
// Take the lock to atomically check if a pending producer is available.
|
||||
try (AutoLock l = _lock.lock())
|
||||
// check if a pending producer is available.
|
||||
int executed = 0;
|
||||
while (true)
|
||||
{
|
||||
long biState = _state.get();
|
||||
int state = AtomicBiInteger.getLo(biState);
|
||||
int pending = AtomicBiInteger.getHi(biState);
|
||||
|
||||
// If a pending producer is available or one can be started
|
||||
if (_pending || _tryExecutor.tryExecute(_runPendingProducer))
|
||||
pending += executed;
|
||||
if (pending <= 0 && _tryExecutor.tryExecute(_runPendingProducer))
|
||||
{
|
||||
executed++;
|
||||
pending++;
|
||||
}
|
||||
|
||||
if (pending > 0)
|
||||
{
|
||||
// Use EPC: the producer directly consumes the task, which may block
|
||||
// and then races with the pending producer to resume production.
|
||||
_pending = true;
|
||||
_state = State.IDLE;
|
||||
if (!_state.compareAndSet(biState, pending, IDLE))
|
||||
continue;
|
||||
return SubStrategy.EXECUTE_PRODUCE_CONSUME;
|
||||
}
|
||||
|
||||
if (!_state.compareAndSet(biState, pending, state))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise use PIC: the producer consumes the task
|
||||
// in non-blocking mode and then resumes production.
|
||||
return SubStrategy.PRODUCE_INVOKE_CONSUME;
|
||||
}
|
||||
|
||||
case BLOCKING:
|
||||
{
|
||||
// The produced task may block.
|
||||
|
||||
// If the calling producing thread may also block
|
||||
if (!nonBlocking)
|
||||
{
|
||||
// Take the lock to atomically check if a pending producer is available.
|
||||
try (AutoLock l = _lock.lock())
|
||||
// check if a pending producer is available.
|
||||
int executed = 0;
|
||||
while (true)
|
||||
{
|
||||
long biState = _state.get();
|
||||
int state = AtomicBiInteger.getLo(biState);
|
||||
int pending = AtomicBiInteger.getHi(biState);
|
||||
|
||||
// If a pending producer is available or one can be started
|
||||
if (_pending || _tryExecutor.tryExecute(_runPendingProducer))
|
||||
pending += executed;
|
||||
if (pending <= 0 && _tryExecutor.tryExecute(_runPendingProducer))
|
||||
{
|
||||
executed++;
|
||||
pending++;
|
||||
}
|
||||
|
||||
// If a pending producer is available or one can be started
|
||||
if (pending > 0)
|
||||
{
|
||||
// use EPC: The producer directly consumes the task, which may block
|
||||
// and then races with the pending producer to resume production.
|
||||
_pending = true;
|
||||
_state = State.IDLE;
|
||||
if (!_state.compareAndSet(biState, pending, IDLE))
|
||||
continue;
|
||||
return SubStrategy.EXECUTE_PRODUCE_CONSUME;
|
||||
}
|
||||
|
||||
if (!_state.compareAndSet(biState, pending, state))
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise use PEC: the task is consumed by the executor and the producer continues to produce.
|
||||
return SubStrategy.PRODUCE_EXECUTE_CONSUME;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(String.format("taskType=%s %s", taskType, this));
|
||||
|
@ -390,19 +443,25 @@ public class AdaptiveExecutionStrategy extends ContainerLifeCycle implements Exe
|
|||
runTask(task);
|
||||
|
||||
// Race the pending producer to produce again.
|
||||
try (AutoLock l = _lock.lock())
|
||||
while (true)
|
||||
{
|
||||
if (_state == State.IDLE)
|
||||
long biState = _state.get();
|
||||
int state = AtomicBiInteger.getLo(biState);
|
||||
int pending = AtomicBiInteger.getHi(biState);
|
||||
|
||||
if (state == IDLE)
|
||||
{
|
||||
// We beat the pending producer, so we will become the producer instead.
|
||||
// The pending produce will become a noop if it arrives whilst we are producing,
|
||||
// or it may take over if we subsequently do another EPC consumption.
|
||||
_state = State.PRODUCING;
|
||||
if (!_state.compareAndSet(biState, pending, PRODUCING))
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
// The pending producer is now producing, so this thread no longer produces.
|
||||
return false;
|
||||
}
|
||||
// The pending producer is now producing, so this thread no longer produces.
|
||||
return false;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(String.format("ss=%s %s", subStrategy, this));
|
||||
|
@ -521,10 +580,7 @@ public class AdaptiveExecutionStrategy extends ContainerLifeCycle implements Exe
|
|||
@ManagedAttribute(value = "whether this execution strategy is idle", readonly = true)
|
||||
public boolean isIdle()
|
||||
{
|
||||
try (AutoLock l = _lock.lock())
|
||||
{
|
||||
return _state == State.IDLE;
|
||||
}
|
||||
return _state.getLo() == IDLE;
|
||||
}
|
||||
|
||||
@ManagedOperation(value = "resets the task counts", impact = "ACTION")
|
||||
|
@ -539,17 +595,14 @@ public class AdaptiveExecutionStrategy extends ContainerLifeCycle implements Exe
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
try (AutoLock l = _lock.lock())
|
||||
{
|
||||
return toStringLocked();
|
||||
}
|
||||
return toString(_state.get());
|
||||
}
|
||||
|
||||
public String toStringLocked()
|
||||
public String toString(long biState)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
getString(builder);
|
||||
getState(builder);
|
||||
getState(builder, biState);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
@ -563,11 +616,20 @@ public class AdaptiveExecutionStrategy extends ContainerLifeCycle implements Exe
|
|||
builder.append('/');
|
||||
}
|
||||
|
||||
private void getState(StringBuilder builder)
|
||||
private void getState(StringBuilder builder, long biState)
|
||||
{
|
||||
builder.append(_state);
|
||||
int state = AtomicBiInteger.getLo(biState);
|
||||
int pending = AtomicBiInteger.getHi(biState);
|
||||
builder.append(
|
||||
switch (state)
|
||||
{
|
||||
case IDLE -> "IDLE";
|
||||
case PRODUCING -> "PRODUCING";
|
||||
case REPRODUCING -> "REPRODUCING";
|
||||
default -> "UNKNOWN(%d)".formatted(state);
|
||||
});
|
||||
builder.append("/p=");
|
||||
builder.append(_pending);
|
||||
builder.append(pending);
|
||||
builder.append('/');
|
||||
builder.append(_tryExecutor);
|
||||
builder.append("[pc=");
|
||||
|
|
|
@ -11,8 +11,9 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.ee10.quickstart;
|
||||
package org.eclipse.jetty.util.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
@ -26,11 +27,9 @@ import java.util.stream.Stream;
|
|||
import org.eclipse.jetty.toolchain.test.FS;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.resource.FileSystemPool;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
|
@ -43,7 +42,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
|
||||
public class AttributeNormalizerTest
|
||||
{
|
||||
public static Stream<Arguments> scenarios()
|
||||
public static Stream<Arguments> scenarios() throws IOException
|
||||
{
|
||||
final List<Arguments> data = new ArrayList<>();
|
||||
final String arch = String.format("%s/%s", System.getProperty("os.name"), System.getProperty("os.arch"));
|
||||
|
@ -55,18 +54,22 @@ public class AttributeNormalizerTest
|
|||
|
||||
// ------
|
||||
title = "Typical Setup";
|
||||
jettyHome = asTargetPath(title, "jetty-distro");
|
||||
jettyBase = asTargetPath(title, "jetty-distro/demo.base");
|
||||
war = asTargetPath(title, "jetty-distro/demo.base/webapps/FOO");
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
jettyHome = asTargetPath(title, "jetty-typical");
|
||||
jettyBase = asTargetPath(title, "jetty-typical/demo.base");
|
||||
war = asTargetPath(title, "jetty-typical/demo.base/webapps/FOO");
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, resourceFactory.newResource(war))));
|
||||
|
||||
// ------
|
||||
title = "Old Setup";
|
||||
jettyHome = asTargetPath(title, "jetty-distro");
|
||||
jettyBase = asTargetPath(title, "jetty-distro");
|
||||
war = asTargetPath(title, "jetty-distro/webapps/FOO");
|
||||
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
jettyHome = asTargetPath(title, "jetty-old");
|
||||
jettyBase = asTargetPath(title, "jetty-old");
|
||||
war = asTargetPath(title, "jetty-old/webapps/FOO");
|
||||
if (!Files.exists(war.resolve("index.html")))
|
||||
{
|
||||
Files.createFile(war.resolve("index.html"));
|
||||
Files.createFile(war.resolve("favicon.ico"));
|
||||
}
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, resourceFactory.newResource(war))));
|
||||
|
||||
// ------
|
||||
// This puts the jetty.home inside the jetty.base
|
||||
|
@ -74,8 +77,7 @@ public class AttributeNormalizerTest
|
|||
jettyHome = asTargetPath(title, "app/dist");
|
||||
jettyBase = asTargetPath(title, "app");
|
||||
war = asTargetPath(title, "app/webapps/FOO");
|
||||
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, resourceFactory.newResource(war))));
|
||||
|
||||
// ------
|
||||
// This tests a path scenario often seen on various automatic deployments tooling
|
||||
|
@ -84,8 +86,16 @@ public class AttributeNormalizerTest
|
|||
jettyHome = asTargetPath(title, "app%2Fnasty/dist");
|
||||
jettyBase = asTargetPath(title, "app%2Fnasty/base");
|
||||
war = asTargetPath(title, "app%2Fnasty/base/webapps/FOO");
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, resourceFactory.newResource(war))));
|
||||
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase, war)));
|
||||
// ------
|
||||
title = "ResourceCollection Setup";
|
||||
jettyHome = asTargetPath(title, "jetty-collection");
|
||||
jettyBase = asTargetPath(title, "jetty-collection/demo.base");
|
||||
Path warA = asTargetPath(title, "jetty-collection/demo.base/webapps/WarA");
|
||||
Path warB = asTargetPath(title, "jetty-collection/demo.base/webapps/WarB");
|
||||
data.add(Arguments.of(new Scenario(arch, title, jettyHome, jettyBase,
|
||||
ResourceFactory.combine(resourceFactory.newResource(warA), resourceFactory.newResource(warB)))));
|
||||
|
||||
return data.stream();
|
||||
}
|
||||
|
@ -166,7 +176,9 @@ public class AttributeNormalizerTest
|
|||
public void testNormalizeWarAsString(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR as String path
|
||||
assertNormalize(scenario, scenario.war.toString(), scenario.war.toString());
|
||||
Path path = scenario.war.getPath();
|
||||
Assumptions.assumeTrue(path != null);
|
||||
assertNormalize(scenario, path.toString(), path.toString());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
|
@ -259,12 +271,22 @@ public class AttributeNormalizerTest
|
|||
assertExpandPath(scenario, "${jetty.home}", scenario.jettyHome.toString());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testExpandWebInfAsURI(final Scenario scenario)
|
||||
{
|
||||
// Expand
|
||||
assertExpandURI(scenario, "${WAR.uri}/WEB-INF/web.xml", scenario.webXml.toUri());
|
||||
assertExpandURI(scenario, "${WAR.uri}/WEB-INF/test.tld", scenario.testTld.toUri());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testNormalizeWarAsURI(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR as URI
|
||||
URI testWarURI = scenario.war.toUri();
|
||||
URI testWarURI = scenario.war.getURI();
|
||||
Assumptions.assumeTrue(testWarURI != null);
|
||||
assertNormalize(scenario, testWarURI, "${WAR.uri}");
|
||||
}
|
||||
|
||||
|
@ -273,7 +295,7 @@ public class AttributeNormalizerTest
|
|||
public void testNormalizeWarDeepAsPath(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR deep path as File
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref").getPath();
|
||||
assertNormalize(scenario, testWarDeep, "${WAR.path}" + FS.separators("/deep/ref"));
|
||||
}
|
||||
|
||||
|
@ -282,7 +304,7 @@ public class AttributeNormalizerTest
|
|||
public void testNormalizeWarDeepAsString(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR deep path as String
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref").getPath();
|
||||
assertNormalize(scenario, testWarDeep.toString(), testWarDeep.toString());
|
||||
}
|
||||
|
||||
|
@ -291,30 +313,22 @@ public class AttributeNormalizerTest
|
|||
public void testNormalizeWarDeepAsURI(final Scenario scenario)
|
||||
{
|
||||
// Normalize WAR deep path as URI
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref").getPath();
|
||||
assertNormalize(scenario, testWarDeep.toUri(), "${WAR.uri}/deep/ref");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("scenarios")
|
||||
public void testExpandWarDeep(final Scenario scenario)
|
||||
{
|
||||
// Expand WAR deep path
|
||||
Path testWarDeep = scenario.war.resolve("deep/ref");
|
||||
URI uri = URI.create("jar:" + testWarDeep.toUri().toASCIIString() + "!/other/file");
|
||||
assertExpandURI(scenario, "jar:${WAR.uri}/deep/ref!/other/file", uri);
|
||||
}
|
||||
|
||||
public static class Scenario
|
||||
{
|
||||
private final Path jettyHome;
|
||||
private final Path jettyBase;
|
||||
private final Path war;
|
||||
private final Resource war;
|
||||
private Path webXml;
|
||||
private Path testTld;
|
||||
private final String arch;
|
||||
private final String title;
|
||||
private final AttributeNormalizer normalizer;
|
||||
|
||||
public Scenario(String arch, String title, Path jettyHome, Path jettyBase, Path war)
|
||||
public Scenario(String arch, String title, Path jettyHome, Path jettyBase, Resource war)
|
||||
{
|
||||
this.arch = arch;
|
||||
this.title = title;
|
||||
|
@ -326,15 +340,48 @@ public class AttributeNormalizerTest
|
|||
|
||||
assertTrue(Files.exists(this.jettyHome));
|
||||
assertTrue(Files.exists(this.jettyBase));
|
||||
assertTrue(Files.exists(this.war));
|
||||
assertTrue(war.exists());
|
||||
|
||||
// Set some System Properties that AttributeNormalizer expects
|
||||
System.setProperty("jetty.home", jettyHome.toString());
|
||||
System.setProperty("jetty.base", jettyBase.toString());
|
||||
|
||||
for (Resource w : war)
|
||||
{
|
||||
try
|
||||
{
|
||||
Path webinf = w.getPath().resolve("WEB-INF");
|
||||
if (!Files.exists(webinf))
|
||||
Files.createDirectory(webinf);
|
||||
Path deep = w.getPath().resolve("deep");
|
||||
if (!Files.exists(deep))
|
||||
Files.createDirectory(deep);
|
||||
Path ref = deep.resolve("ref");
|
||||
if (!Files.exists(ref))
|
||||
Files.createFile(ref);
|
||||
|
||||
if (w.getFileName().equals("FOO") || w.getFileName().equals("WarA"))
|
||||
{
|
||||
webXml = webinf.resolve("web.xml");
|
||||
if (!Files.exists(webXml))
|
||||
Files.createFile(webXml);
|
||||
}
|
||||
|
||||
if (w.getFileName().equals("FOO") || w.getFileName().equals("WarB"))
|
||||
{
|
||||
testTld = webinf.resolve("test.tld");
|
||||
if (!Files.exists(testTld))
|
||||
Files.createFile(testTld);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup normalizer
|
||||
Resource webresource = resourceFactory.newResource(war);
|
||||
this.normalizer = new AttributeNormalizer(webresource);
|
||||
this.normalizer = new AttributeNormalizer(war);
|
||||
}
|
||||
|
||||
@Override
|
|
@ -11,7 +11,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.ee9.quickstart;
|
||||
package org.eclipse.jetty.util.resource;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
|
@ -52,7 +52,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
|
|||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(WorkDirExtension.class)
|
||||
public class ResourceCollectionTest
|
||||
public class CombinedResourceTest
|
||||
{
|
||||
private final ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable();
|
||||
public WorkDir workDir;
|
||||
|
@ -78,7 +78,7 @@ public class ResourceCollectionTest
|
|||
Path two = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two");
|
||||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
|
||||
ResourceCollection rc = ResourceFactory.combine(
|
||||
Resource rc = ResourceFactory.combine(
|
||||
resourceFactory.newResource(one),
|
||||
resourceFactory.newResource(two),
|
||||
resourceFactory.newResource(three)
|
||||
|
@ -127,7 +127,7 @@ public class ResourceCollectionTest
|
|||
Path two = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two");
|
||||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
|
||||
ResourceCollection rc = ResourceFactory.combine(
|
||||
Resource rc = ResourceFactory.combine(
|
||||
resourceFactory.newResource(one),
|
||||
resourceFactory.newResource(two),
|
||||
resourceFactory.newResource(three)
|
||||
|
@ -135,8 +135,8 @@ public class ResourceCollectionTest
|
|||
|
||||
// This should return a ResourceCollection with 3 `/dir/` sub-directories.
|
||||
Resource r = rc.resolve("dir");
|
||||
assertTrue(r instanceof ResourceCollection);
|
||||
rc = (ResourceCollection)r;
|
||||
assertTrue(r instanceof CombinedResource);
|
||||
rc = (CombinedResource)r;
|
||||
assertEquals(getContent(rc, "1.txt"), "1 - one (in dir)");
|
||||
assertEquals(getContent(rc, "2.txt"), "2 - two (in dir)");
|
||||
assertEquals(getContent(rc, "3.txt"), "3 - three (in dir)");
|
||||
|
@ -149,7 +149,7 @@ public class ResourceCollectionTest
|
|||
Path two = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two");
|
||||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
|
||||
ResourceCollection rc = ResourceFactory.combine(
|
||||
Resource rc = ResourceFactory.combine(
|
||||
resourceFactory.newResource(one),
|
||||
resourceFactory.newResource(two),
|
||||
resourceFactory.newResource(three)
|
||||
|
@ -175,7 +175,7 @@ public class ResourceCollectionTest
|
|||
Path three = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/three");
|
||||
Path twoDir = MavenTestingUtils.getTestResourcePathDir("org/eclipse/jetty/util/resource/two/dir");
|
||||
|
||||
ResourceCollection rc1 = ResourceFactory.combine(
|
||||
Resource rc1 = ResourceFactory.combine(
|
||||
List.of(
|
||||
resourceFactory.newResource(one),
|
||||
resourceFactory.newResource(two),
|
||||
|
@ -183,7 +183,7 @@ public class ResourceCollectionTest
|
|||
)
|
||||
);
|
||||
|
||||
ResourceCollection rc2 = ResourceFactory.combine(
|
||||
Resource rc2 = ResourceFactory.combine(
|
||||
List.of(
|
||||
// the original ResourceCollection
|
||||
rc1,
|
||||
|
@ -202,9 +202,11 @@ public class ResourceCollectionTest
|
|||
};
|
||||
|
||||
List<URI> actual = new ArrayList<>();
|
||||
for (Resource res: rc2.getResources())
|
||||
assertThat(rc2, instanceOf(CombinedResource.class));
|
||||
if (rc2 instanceof CombinedResource combinedResource)
|
||||
{
|
||||
actual.add(res.getURI());
|
||||
for (Resource res : combinedResource.getResources())
|
||||
actual.add(res.getURI());
|
||||
}
|
||||
assertThat(actual, contains(expected));
|
||||
}
|
||||
|
@ -307,7 +309,7 @@ public class ResourceCollectionTest
|
|||
// Since this is user space, we cannot know ahead of time what
|
||||
// this list contains, so we mount because we assume there
|
||||
// will be necessary things to mount
|
||||
ResourceCollection rc = resourceFactory.newResource(uris);
|
||||
Resource rc = resourceFactory.newResource(uris);
|
||||
assertThat(getContent(rc, "test.txt"), is("Test"));
|
||||
}
|
||||
|
||||
|
@ -333,7 +335,7 @@ public class ResourceCollectionTest
|
|||
// Since this is user space, we cannot know ahead of time what
|
||||
// this list contains, so we mount because we assume there
|
||||
// will be necessary things to mount
|
||||
ResourceCollection rc = resourceFactory.newResource(uris);
|
||||
Resource rc = resourceFactory.newResource(uris);
|
||||
assertThat(getContent(rc, "test.txt"), is("Test inside lib-foo.jar"));
|
||||
assertThat(getContent(rc, "testZed.txt"), is("TestZed inside lib-zed.jar"));
|
||||
}
|
|
@ -246,7 +246,7 @@ public class WebSocketMappings implements Dumpable, LifeCycle.Listener
|
|||
*/
|
||||
public boolean upgrade(Request request, Response response, Callback callback, Configuration.Customizer defaultCustomizer) throws IOException
|
||||
{
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
WebSocketNegotiator negotiator = getMatchedNegotiator(target, pathSpec ->
|
||||
{
|
||||
// Store PathSpec resource mapping as request attribute,
|
||||
|
|
|
@ -68,7 +68,7 @@ public class WebSocketUpgradeHandler extends Handler.Wrapper
|
|||
if (processor == null)
|
||||
return null;
|
||||
|
||||
String target = request.getPathInContext();
|
||||
String target = Request.getPathInContext(request);
|
||||
WebSocketNegotiator negotiator = mappings.getMatchedNegotiator(target, pathSpec ->
|
||||
{
|
||||
// Store PathSpec resource mapping as request attribute,
|
||||
|
|
|
@ -83,7 +83,7 @@ public class WebSocketProxyTest
|
|||
{
|
||||
if (request.getHeaders().get("Upgrade") != null)
|
||||
{
|
||||
if (blockServerUpgradeRequests && request.getPathInContext().startsWith("/server"))
|
||||
if (blockServerUpgradeRequests && Request.getPathInContext(request).startsWith("/server"))
|
||||
{
|
||||
return (req, resp, cb) -> Response.writeError(req, resp, cb, HttpStatus.INTERNAL_SERVER_ERROR_500);
|
||||
}
|
||||
|
|
|
@ -13,176 +13,240 @@
|
|||
|
||||
package org.eclipse.jetty.ee10.demos;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.eclipse.jetty.deploy.DeploymentManager;
|
||||
import org.eclipse.jetty.deploy.providers.ContextProvider;
|
||||
import org.eclipse.jetty.ee.Deployable;
|
||||
import org.eclipse.jetty.ee10.annotations.AnnotationConfiguration;
|
||||
import org.eclipse.jetty.ee10.plus.webapp.EnvConfiguration;
|
||||
import org.eclipse.jetty.ee10.plus.webapp.PlusConfiguration;
|
||||
import org.eclipse.jetty.ee10.servlet.DebugListener;
|
||||
import org.eclipse.jetty.ee10.servlet.security.HashLoginService;
|
||||
import org.eclipse.jetty.ee10.webapp.Configurations;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.io.ConnectionStatistics;
|
||||
import org.eclipse.jetty.jmx.MBeanContainer;
|
||||
import org.eclipse.jetty.rewrite.handler.InvalidURIRule;
|
||||
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
|
||||
import org.eclipse.jetty.server.AsyncRequestLogWriter;
|
||||
import org.eclipse.jetty.server.CustomRequestLog;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.LowResourceMonitor;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||
import org.eclipse.jetty.util.component.Environment;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.eclipse.jetty.util.resource.Resources;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
||||
import org.eclipse.jetty.xml.EnvironmentBuilder;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Starts the Jetty Distribution's demo-base directory using entirely
|
||||
* embedded jetty techniques.
|
||||
*/
|
||||
public class LikeJettyXml
|
||||
{
|
||||
// TODO
|
||||
// public static Server createServer(int port, int securePort, boolean addDebugListener) throws Exception
|
||||
// {
|
||||
// Path configDir = Paths.get("src/main/resources/demo").toAbsolutePath();
|
||||
// Path runtimeDir = Paths.get("target/embedded/" + LikeJettyXml.class.getSimpleName()).toAbsolutePath();
|
||||
// mkdir(runtimeDir);
|
||||
//
|
||||
// // === jetty.xml ===
|
||||
// // Setup Threadpool
|
||||
// QueuedThreadPool threadPool = new QueuedThreadPool();
|
||||
// threadPool.setMaxThreads(500);
|
||||
//
|
||||
// // Server
|
||||
// Server server = new Server(threadPool);
|
||||
//
|
||||
// // Scheduler
|
||||
// server.addBean(new ScheduledExecutorScheduler(null, false));
|
||||
//
|
||||
// // HTTP Configuration
|
||||
// HttpConfiguration httpConfig = new HttpConfiguration();
|
||||
// httpConfig.setSecureScheme("https");
|
||||
// httpConfig.setSecurePort(securePort);
|
||||
// httpConfig.setOutputBufferSize(32768);
|
||||
// httpConfig.setRequestHeaderSize(8192);
|
||||
// httpConfig.setResponseHeaderSize(8192);
|
||||
// httpConfig.setSendServerVersion(true);
|
||||
// httpConfig.setSendDateHeader(false);
|
||||
// // httpConfig.addCustomizer(new ForwardedRequestCustomizer());
|
||||
//
|
||||
// // Handler Structure
|
||||
// ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||
// server.setHandler(new HandlerList(contexts, new DefaultHandler()));
|
||||
//
|
||||
// // === jetty-jmx.xml ===
|
||||
// MBeanContainer mbContainer = new MBeanContainer(
|
||||
// ManagementFactory.getPlatformMBeanServer());
|
||||
// server.addBean(mbContainer);
|
||||
//
|
||||
// // === jetty-http.xml ===
|
||||
// ServerConnector http = new ServerConnector(server,
|
||||
// new HttpConnectionFactory(httpConfig));
|
||||
// http.setPort(port);
|
||||
// http.setIdleTimeout(30000);
|
||||
// server.addConnector(http);
|
||||
//
|
||||
// // === jetty-https.xml ===
|
||||
// // SSL Context Factory
|
||||
// Path keystorePath = Paths.get("src/main/resources/etc/keystore.p12").toAbsolutePath();
|
||||
// if (!Files.exists(keystorePath))
|
||||
// throw new FileNotFoundException(keystorePath.toString());
|
||||
// SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
|
||||
// sslContextFactory.setKeyStorePath(keystorePath.toString());
|
||||
// sslContextFactory.setKeyStorePassword("storepwd");
|
||||
// sslContextFactory.setTrustStorePath(keystorePath.toString());
|
||||
// sslContextFactory.setTrustStorePassword("storepwd");
|
||||
//
|
||||
// // SSL HTTP Configuration
|
||||
// HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
|
||||
// httpsConfig.addCustomizer(new SecureRequestCustomizer());
|
||||
//
|
||||
// // SSL Connector
|
||||
// ServerConnector sslConnector = new ServerConnector(server,
|
||||
// new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
|
||||
// new HttpConnectionFactory(httpsConfig));
|
||||
// sslConnector.setPort(securePort);
|
||||
// server.addConnector(sslConnector);
|
||||
//
|
||||
// // === jetty-deploy.xml ===
|
||||
// DeploymentManager deployer = new DeploymentManager();
|
||||
// if (addDebugListener)
|
||||
// {
|
||||
// DebugListener debug = new DebugListener(System.err, true, true, true);
|
||||
// server.addBean(debug);
|
||||
// deployer.addLifeCycleBinding(new DebugListenerBinding(debug));
|
||||
// }
|
||||
// deployer.setContexts(contexts);
|
||||
// deployer.setContextAttribute(
|
||||
// "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
|
||||
// ".*/jetty-jakarta-servlet-api-[^/]*\\.jar$|.*/jakarta.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$");
|
||||
//
|
||||
// Path webappsDir = runtimeDir.resolve("webapps");
|
||||
// mkdir(webappsDir);
|
||||
//
|
||||
// Path testWebapp = webappsDir.resolve("test.war");
|
||||
// if (!Files.exists(testWebapp))
|
||||
// {
|
||||
// Path testWebappSrc = JettyDemos.find("demo-simple-webapp/target/demo-simple-webapp-@VER@.war");
|
||||
// Files.copy(testWebappSrc, testWebapp);
|
||||
// }
|
||||
//
|
||||
// WebAppProvider webAppProvider = new WebAppProvider();
|
||||
// webAppProvider.setMonitoredDirName(webappsDir.toString());
|
||||
// webAppProvider.setDefaultsDescriptor(configDir.resolve("webdefault-ee10.xml").toString());
|
||||
// webAppProvider.setScanInterval(1);
|
||||
// webAppProvider.setExtractWars(true);
|
||||
// webAppProvider.setConfigurationManager(new PropertiesConfigurationManager());
|
||||
//
|
||||
// deployer.addAppProvider(webAppProvider);
|
||||
// server.addBean(deployer);
|
||||
//
|
||||
// // === setup jetty plus ==
|
||||
// Configurations.setServerDefault(server).add(new EnvConfiguration(), new PlusConfiguration(), new AnnotationConfiguration());
|
||||
//
|
||||
// // === jetty-stats.xml ===
|
||||
// StatisticsHandler stats = new StatisticsHandler();
|
||||
// stats.setHandler(server.getHandler());
|
||||
// server.setHandler(stats);
|
||||
// server.addBeanToAllConnectors(new ConnectionStatistics());
|
||||
//
|
||||
// // === Rewrite Handler
|
||||
// RewriteHandler rewrite = new RewriteHandler();
|
||||
// rewrite.setHandler(server.getHandler());
|
||||
// server.setHandler(rewrite);
|
||||
// rewrite.addRule(new InvalidURIRule());
|
||||
//
|
||||
// // === jetty-requestlog.xml ===
|
||||
// Path logsDir = runtimeDir.resolve("logs");
|
||||
// mkdir(logsDir);
|
||||
// AsyncRequestLogWriter logWriter = new AsyncRequestLogWriter(logsDir.resolve("yyyy_mm_dd.request.log").toString());
|
||||
// logWriter.setFilenameDateFormat("yyyy_MM_dd");
|
||||
// logWriter.setRetainDays(90);
|
||||
// logWriter.setTimeZone("GMT");
|
||||
// CustomRequestLog requestLog = new CustomRequestLog(logWriter, CustomRequestLog.EXTENDED_NCSA_FORMAT + " \"%C\"");
|
||||
// server.setRequestLog(requestLog);
|
||||
//
|
||||
// // === jetty-lowresources.xml ===
|
||||
// LowResourceMonitor lowResourcesMonitor = new LowResourceMonitor(server);
|
||||
// lowResourcesMonitor.setPeriod(1000);
|
||||
// lowResourcesMonitor.setLowResourcesIdleTimeout(200);
|
||||
// lowResourcesMonitor.setMonitorThreads(true);
|
||||
// lowResourcesMonitor.setMaxMemory(0);
|
||||
// lowResourcesMonitor.setMaxLowResourcesTime(5000);
|
||||
// server.addBean(lowResourcesMonitor);
|
||||
//
|
||||
// // === test-realm.xml ===
|
||||
// HashLoginService login = new HashLoginService();
|
||||
// login.setName("Test Realm");
|
||||
// login.setConfig(configDir.resolve("demo-realm.properties").toString());
|
||||
// login.setHotReload(false);
|
||||
// server.addBean(login);
|
||||
//
|
||||
// return server;
|
||||
// }
|
||||
//
|
||||
// private static void mkdir(Path path) throws IOException
|
||||
// {
|
||||
// if (Files.exists(path))
|
||||
// return;
|
||||
// Files.createDirectories(path);
|
||||
// }
|
||||
//
|
||||
// public static void main(String[] args) throws Exception
|
||||
// {
|
||||
// int port = ExampleUtil.getPort(args, "jetty.http.port", 8080);
|
||||
// int securePort = ExampleUtil.getPort(args, "jetty.https.port", 8443);
|
||||
// Server server = createServer(port, securePort, true);
|
||||
//
|
||||
// // Extra options
|
||||
// server.setDumpAfterStart(true);
|
||||
// server.setDumpBeforeStop(false);
|
||||
// server.setStopAtShutdown(true);
|
||||
//
|
||||
// // Start the server
|
||||
// server.start();
|
||||
// server.join();
|
||||
// }
|
||||
public static Server createServer(int port, int securePort, boolean addDebugListener) throws Exception
|
||||
{
|
||||
Path configDir = Path.of("src/main/resources/demo").toAbsolutePath();
|
||||
Path runtimeDir = Path.of("target/embedded/" + LikeJettyXml.class.getSimpleName()).toAbsolutePath();
|
||||
mkdir(runtimeDir);
|
||||
|
||||
// === jetty.xml ===
|
||||
// Setup Threadpool
|
||||
QueuedThreadPool threadPool = new QueuedThreadPool();
|
||||
threadPool.setMaxThreads(500);
|
||||
|
||||
// Server
|
||||
Server server = new Server(threadPool);
|
||||
|
||||
// Scheduler
|
||||
server.addBean(new ScheduledExecutorScheduler(null, false, -1));
|
||||
|
||||
// HTTP Configuration
|
||||
HttpConfiguration httpConfig = new HttpConfiguration();
|
||||
httpConfig.setSecureScheme("https");
|
||||
httpConfig.setSecurePort(securePort);
|
||||
httpConfig.setOutputBufferSize(32768);
|
||||
httpConfig.setRequestHeaderSize(8192);
|
||||
httpConfig.setResponseHeaderSize(8192);
|
||||
httpConfig.setSendServerVersion(true);
|
||||
httpConfig.setSendDateHeader(false);
|
||||
// httpConfig.addCustomizer(new ForwardedRequestCustomizer());
|
||||
|
||||
// Handler Structure
|
||||
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||
DefaultHandler defaultHandler = new DefaultHandler();
|
||||
|
||||
Handler.Collection handlers = new Handler.Collection();
|
||||
handlers.setHandlers(contexts, defaultHandler);
|
||||
server.setHandler(handlers);
|
||||
|
||||
// === jetty-jmx.xml ===
|
||||
MBeanContainer mbContainer = new MBeanContainer(ManagementFactory.getPlatformMBeanServer());
|
||||
mbContainer.beanAdded(null, LoggerFactory.getILoggerFactory());
|
||||
server.addBean(mbContainer);
|
||||
|
||||
// === jetty-http.xml ===
|
||||
ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfig));
|
||||
http.setHost("0.0.0.0");
|
||||
http.setPort(port);
|
||||
http.setIdleTimeout(30000);
|
||||
server.addConnector(http);
|
||||
|
||||
// === jetty-ssl-context.xml ===
|
||||
// SSL Context Factory
|
||||
Path keystorePath = Paths.get("src/main/resources/etc/keystore.p12").toAbsolutePath();
|
||||
if (!Files.exists(keystorePath))
|
||||
throw new FileNotFoundException(keystorePath.toString());
|
||||
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
|
||||
sslContextFactory.setKeyStorePath(keystorePath.toString());
|
||||
sslContextFactory.setKeyStorePassword("storepwd");
|
||||
sslContextFactory.setTrustStorePath(keystorePath.toString());
|
||||
sslContextFactory.setTrustStorePassword("storepwd");
|
||||
|
||||
// === jetty-ssl.xml ===
|
||||
// SSL HTTP Configuration
|
||||
HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
|
||||
httpsConfig.addCustomizer(new SecureRequestCustomizer());
|
||||
|
||||
// === jetty-https.xml ===
|
||||
// SSL Connector
|
||||
ServerConnector sslConnector = new ServerConnector(server,
|
||||
new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
|
||||
new HttpConnectionFactory(httpsConfig));
|
||||
sslConnector.setPort(securePort);
|
||||
server.addConnector(sslConnector);
|
||||
|
||||
// === jetty-deploy.xml ===
|
||||
DeploymentManager deployer = new DeploymentManager();
|
||||
if (addDebugListener)
|
||||
{
|
||||
DebugListener debug = new DebugListener(System.err, true, true, true);
|
||||
server.addBean(debug);
|
||||
}
|
||||
deployer.setContexts(contexts);
|
||||
|
||||
Path webappsDir = runtimeDir.resolve("webapps");
|
||||
mkdir(webappsDir);
|
||||
|
||||
Path testWebapp = webappsDir.resolve("test.war");
|
||||
if (!Files.exists(testWebapp))
|
||||
{
|
||||
Path testWebappSrc = JettyDemos.find("jetty-ee10-demo-simple-webapp/target/jetty-ee10-demo-simple-webapp-@VER@.war");
|
||||
Files.copy(testWebappSrc, testWebapp);
|
||||
}
|
||||
|
||||
// == Build ee10 Environment ==
|
||||
// Support for environment specific classpath / modulepath goes here
|
||||
String environmentName = "ee10";
|
||||
Environment ee10 = Environment.get(environmentName);
|
||||
if (ee10 == null)
|
||||
{
|
||||
ee10 = new EnvironmentBuilder(environmentName).build();
|
||||
Environment.set(ee10);
|
||||
}
|
||||
|
||||
// === jetty-ee10-deploy.xml ===
|
||||
ee10.setAttribute("contextHandlerClass", org.eclipse.jetty.ee10.webapp.WebAppContext.class.getName());
|
||||
ContextProvider webAppProvider = new ContextProvider();
|
||||
webAppProvider.setEnvironmentName(environmentName);
|
||||
webAppProvider.setMonitoredDirName(webappsDir.toString());
|
||||
webAppProvider.setDefaultsDescriptor(configDir.resolve("webdefault-ee10.xml").toString());
|
||||
webAppProvider.setScanInterval(1);
|
||||
webAppProvider.setExtractWars(true);
|
||||
webAppProvider.getProperties().put(Deployable.CONTAINER_SCAN_JARS,
|
||||
".*/jakarta.servlet-api-[^/]*\\.jar$|.*jakarta.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$");
|
||||
|
||||
deployer.addAppProvider(webAppProvider);
|
||||
server.addBean(deployer);
|
||||
|
||||
// === setup jetty plus ==
|
||||
Configurations.setServerDefault(server)
|
||||
.add(new EnvConfiguration(), new PlusConfiguration(), new AnnotationConfiguration());
|
||||
|
||||
// === jetty-stats.xml ===
|
||||
StatisticsHandler stats = new StatisticsHandler();
|
||||
stats.setHandler(server.getHandler());
|
||||
server.setHandler(stats);
|
||||
server.addBeanToAllConnectors(new ConnectionStatistics());
|
||||
|
||||
// === Rewrite Handler
|
||||
RewriteHandler rewrite = new RewriteHandler();
|
||||
rewrite.setHandler(server.getHandler());
|
||||
server.setHandler(rewrite);
|
||||
rewrite.addRule(new InvalidURIRule());
|
||||
|
||||
// === jetty-requestlog.xml ===
|
||||
Path logsDir = runtimeDir.resolve("logs");
|
||||
mkdir(logsDir);
|
||||
AsyncRequestLogWriter logWriter = new AsyncRequestLogWriter(logsDir.resolve("yyyy_mm_dd.request.log").toString());
|
||||
logWriter.setFilenameDateFormat("yyyy_MM_dd");
|
||||
logWriter.setRetainDays(90);
|
||||
logWriter.setTimeZone("GMT");
|
||||
CustomRequestLog requestLog = new CustomRequestLog(logWriter, CustomRequestLog.EXTENDED_NCSA_FORMAT + " \"%C\"");
|
||||
server.setRequestLog(requestLog);
|
||||
|
||||
// === jetty-lowresources.xml ===
|
||||
LowResourceMonitor lowResourcesMonitor = new LowResourceMonitor(server);
|
||||
lowResourcesMonitor.setPeriod(1000);
|
||||
lowResourcesMonitor.setLowResourcesIdleTimeout(200);
|
||||
lowResourcesMonitor.setMonitorThreads(true);
|
||||
lowResourcesMonitor.setMaxMemory(0);
|
||||
lowResourcesMonitor.setMaxLowResourcesTime(5000);
|
||||
server.addBean(lowResourcesMonitor);
|
||||
|
||||
// === Realm ===
|
||||
HashLoginService login = new HashLoginService();
|
||||
login.setName("Test Realm");
|
||||
Path realmFile = configDir.resolve("ee10-demo-realm.properties");
|
||||
Resource realmResource = ResourceFactory.of(server).newResource(realmFile);
|
||||
if (!Resources.isReadableFile(realmResource))
|
||||
throw new FileNotFoundException("Unable to find config: " + realmFile);
|
||||
login.setConfig(realmResource);
|
||||
login.setHotReload(false);
|
||||
server.addBean(login);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
private static void mkdir(Path path) throws IOException
|
||||
{
|
||||
if (Files.exists(path))
|
||||
return;
|
||||
Files.createDirectories(path);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
int port = ExampleUtil.getPort(args, "jetty.http.port", 8080);
|
||||
int securePort = ExampleUtil.getPort(args, "jetty.https.port", 8443);
|
||||
Server server = createServer(port, securePort, true);
|
||||
|
||||
// Extra options
|
||||
server.setDumpAfterStart(true);
|
||||
server.setDumpBeforeStop(false);
|
||||
server.setStopAtShutdown(true);
|
||||
|
||||
// Start the server
|
||||
server.start();
|
||||
server.join();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,14 +29,6 @@
|
|||
This file is applied to a Web application before its own WEB_INF/web.xml file
|
||||
</description>
|
||||
|
||||
<!-- ==================================================================== -->
|
||||
<!-- Removes static references to beans from javax.el.BeanELResolver to -->
|
||||
<!-- ensure webapp classloader can be released on undeploy -->
|
||||
<!-- ==================================================================== -->
|
||||
<listener>
|
||||
<listener-class>org.eclipse.jetty.ee10.servlet.listener.ELContextCleaner</listener-class>
|
||||
</listener>
|
||||
|
||||
<!-- ==================================================================== -->
|
||||
<!-- Removes static cache of Methods from java.beans.Introspector to -->
|
||||
<!-- ensure webapp classloader can be released on undeploy -->
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
@ -39,8 +38,7 @@ public class LikeJettyXmlTest extends AbstractEmbeddedTest
|
|||
@BeforeEach
|
||||
public void startServer() throws Exception
|
||||
{
|
||||
//TODO fix me
|
||||
//server = LikeJettyXml.createServer(0, 0, false);
|
||||
server = LikeJettyXml.createServer(0, 0, false);
|
||||
server.start();
|
||||
|
||||
Map<String, Integer> ports = ServerUtil.fixDynamicPortConfigurations(server);
|
||||
|
@ -57,7 +55,6 @@ public class LikeJettyXmlTest extends AbstractEmbeddedTest
|
|||
LifeCycle.stop(server);
|
||||
}
|
||||
|
||||
@Disabled //TODO
|
||||
@Test
|
||||
public void testGetTest() throws Exception
|
||||
{
|
||||
|
@ -74,7 +71,6 @@ public class LikeJettyXmlTest extends AbstractEmbeddedTest
|
|||
assertThat("Response Content", responseBody, containsString("Hello"));
|
||||
}
|
||||
|
||||
@Disabled //TODO
|
||||
@Test
|
||||
public void testGetTestSsl() throws Exception
|
||||
{
|
||||
|
|
|
@ -42,7 +42,7 @@ public class ProxyServerTest extends AbstractEmbeddedTest
|
|||
server.start();
|
||||
|
||||
URI uri = server.getURI();
|
||||
client.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", uri.getPort()));
|
||||
client.getProxyConfiguration().addProxy(new HttpProxy("localhost", uri.getPort()));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
|
|
@ -17,8 +17,8 @@ import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration;
|
|||
import org.eclipse.jetty.ee10.webapp.Configuration;
|
||||
import org.eclipse.jetty.ee10.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -47,9 +47,9 @@ public class MavenQuickStartConfiguration extends QuickStartConfiguration
|
|||
//Iterate over all of the resource bases and ignore any that were original bases, just
|
||||
//deleting the overlays
|
||||
Resource res = context.getBaseResource();
|
||||
if (res instanceof ResourceCollection)
|
||||
if (res instanceof CombinedResource)
|
||||
{
|
||||
for (Resource r : ((ResourceCollection)res).getResources())
|
||||
for (Resource r : ((CombinedResource)res).getResources())
|
||||
{
|
||||
if (originalBaseStr.contains(r.toString()))
|
||||
continue;
|
||||
|
|
|
@ -39,8 +39,8 @@ import org.eclipse.jetty.ee10.webapp.WebAppContext;
|
|||
import org.eclipse.jetty.util.FileID;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.util.resource.Resources;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -218,7 +218,7 @@ public class MavenWebAppContext extends WebAppContext
|
|||
* configuration
|
||||
*
|
||||
* @param resourceBases Array of resources strings to set as a
|
||||
* {@link ResourceCollection}.
|
||||
* {@link CombinedResource}.
|
||||
*/
|
||||
public void setResourceBases(String[] resourceBases)
|
||||
{
|
||||
|
|
|
@ -30,8 +30,8 @@ import org.eclipse.jetty.ee10.quickstart.QuickStartConfiguration;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.eclipse.jetty.xml.XmlConfiguration;
|
||||
|
||||
/**
|
||||
|
@ -106,8 +106,8 @@ public class WebAppPropertyConverter
|
|||
|
||||
//send over the calculated resource bases that includes unpacked overlays
|
||||
Resource baseResource = webApp.getBaseResource();
|
||||
if (baseResource instanceof ResourceCollection)
|
||||
props.put(BASE_DIRS, toCSV(((ResourceCollection)webApp.getBaseResource()).getResources()));
|
||||
if (baseResource instanceof CombinedResource)
|
||||
props.put(BASE_DIRS, toCSV(((CombinedResource)webApp.getBaseResource()).getResources()));
|
||||
else if (baseResource instanceof Resource)
|
||||
props.put(BASE_DIRS, webApp.getBaseResource().toString());
|
||||
|
||||
|
|
|
@ -16,17 +16,17 @@ package org.eclipse.jetty.ee10.maven.plugin;
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.eclipse.jetty.ee10.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.resource.CombinedResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceCollection;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
@ -152,10 +152,13 @@ public class TestWebAppPropertyConverter
|
|||
assertEquals(true, webApp.isPersistTempDirectory());
|
||||
assertEquals(war.getAbsolutePath(), webApp.getWar());
|
||||
assertEquals(webXml.getAbsolutePath(), webApp.getDescriptor());
|
||||
assertThat(webApp.getBaseResource(), instanceOf(ResourceCollection.class));
|
||||
assertThat(webApp.getBaseResource(), instanceOf(CombinedResource.class));
|
||||
|
||||
ResourceCollection resourceCollection = (ResourceCollection)webApp.getBaseResource();
|
||||
List<URI> actual = resourceCollection.getResources().stream().filter(Objects::nonNull).map(Resource::getURI).toList();
|
||||
Resource combinedResource = webApp.getBaseResource();
|
||||
List<URI> actual = new ArrayList<>();
|
||||
for (Resource r : combinedResource)
|
||||
if (r != null)
|
||||
actual.add(r.getURI());
|
||||
URI[] expected = new URI[]{base1.toURI(), base2.toURI()};
|
||||
assertThat(actual, containsInAnyOrder(expected));
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue