Skip to content

Commit

Permalink
Support Jetty error page handling of PUT requests
Browse files Browse the repository at this point in the history
Update JettyEmbeddedServletContainerFactory so that requests other than
just GET, POST and HEAD are handled by the ErrorHandler.

Fixes spring-projectsgh-5367
  • Loading branch information
philwebb committed Apr 10, 2016
1 parent 084b288 commit 02764b8
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.context.embedded.jetty;

import java.io.IOException;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.ErrorHandler;

/**
* Variation of Jetty's {@link ErrorHandler} that supports all {@link HttpMethod
* HttpMethods} rather than just {@code GET}, {@code POST} and {@code HEAD}. Jetty
* <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=446039">intentionally only
* supports a limited set of HTTP methods</a> for error pages, however, Spring Boot
* prefers Tomcat, Jetty and Undertow to all behave in the same way.
*
* @author Phillip Webb
*/
class JettyEmbeddedErrorHandler extends ErrorHandler {

private final ErrorHandler delegate;

JettyEmbeddedErrorHandler(ErrorHandler delegate) {
this.delegate = delegate;
}

@Override
public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException {
String method = request.getMethod();
if (!HttpMethod.GET.is(method) && !HttpMethod.POST.is(method)
&& !HttpMethod.HEAD.is(method)) {
request = new ErrorHttpServletRequest(request);
}
this.delegate.handle(target, baseRequest, request, response);
}

private static class ErrorHttpServletRequest extends HttpServletRequestWrapper {

private boolean simulateGetMethod = true;

ErrorHttpServletRequest(HttpServletRequest request) {
super(request);
}

@Override
public String getMethod() {
return (this.simulateGetMethod ? HttpMethod.GET.toString()
: super.getMethod());
}

@Override
public ServletContext getServletContext() {
this.simulateGetMethod = false;
return super.getServletContext();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ private Configuration getErrorPageConfiguration() {
@Override
public void configure(WebAppContext context) throws Exception {
ErrorHandler errorHandler = context.getErrorHandler();
context.setErrorHandler(new JettyEmbeddedErrorHandler(errorHandler));
addJettyErrorPages(errorHandler, getErrorPages());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -343,6 +343,19 @@ public void errorPage() throws Exception {
assertThat(getResponse(getLocalUrl("/bang")), equalTo("Hello World"));
}

@Test
public void errorPageFromPutRequest() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
factory.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/hello"));
this.container = factory.getEmbeddedServletContainer(exampleServletRegistration(),
errorServletRegistration());
this.container.start();
assertThat(getResponse(getLocalUrl("/hello"), HttpMethod.PUT),
equalTo("Hello World"));
assertThat(getResponse(getLocalUrl("/bang"), HttpMethod.PUT),
equalTo("Hello World"));
}

@Test
public void basicSslFromClassPath() throws Exception {
testBasicSslWithKeyStore("classpath:test.jks");
Expand Down Expand Up @@ -792,7 +805,12 @@ protected String getLocalUrl(int port, String resourcePath) {

protected String getResponse(String url, String... headers)
throws IOException, URISyntaxException {
ClientHttpResponse response = getClientResponse(url, headers);
return getResponse(url, HttpMethod.GET, headers);
}

protected String getResponse(String url, HttpMethod method, String... headers)
throws IOException, URISyntaxException {
ClientHttpResponse response = getClientResponse(url, method, headers);
try {
return StreamUtils.copyToString(response.getBody(), Charset.forName("UTF-8"));
}
Expand All @@ -804,7 +822,14 @@ protected String getResponse(String url, String... headers)
protected String getResponse(String url,
HttpComponentsClientHttpRequestFactory requestFactory, String... headers)
throws IOException, URISyntaxException {
ClientHttpResponse response = getClientResponse(url, requestFactory, headers);
return getResponse(url, HttpMethod.GET, requestFactory, headers);
}

protected String getResponse(String url, HttpMethod method,
HttpComponentsClientHttpRequestFactory requestFactory, String... headers)
throws IOException, URISyntaxException {
ClientHttpResponse response = getClientResponse(url, method, requestFactory,
headers);
try {
return StreamUtils.copyToString(response.getBody(), Charset.forName("UTF-8"));
}
Expand All @@ -815,21 +840,27 @@ protected String getResponse(String url,

protected ClientHttpResponse getClientResponse(String url, String... headers)
throws IOException, URISyntaxException {
return getClientResponse(url, new HttpComponentsClientHttpRequestFactory() {
return getClientResponse(url, HttpMethod.GET, headers);
}

@Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
return AbstractEmbeddedServletContainerFactoryTests.this.httpClientContext;
}
protected ClientHttpResponse getClientResponse(String url, HttpMethod method,
String... headers) throws IOException, URISyntaxException {
return getClientResponse(url, method,
new HttpComponentsClientHttpRequestFactory() {

@Override
protected HttpContext createHttpContext(HttpMethod httpMethod,
URI uri) {
return AbstractEmbeddedServletContainerFactoryTests.this.httpClientContext;
}

}, headers);
}, headers);
}

protected ClientHttpResponse getClientResponse(String url,
protected ClientHttpResponse getClientResponse(String url, HttpMethod method,
HttpComponentsClientHttpRequestFactory requestFactory, String... headers)
throws IOException, URISyntaxException {
ClientHttpRequest request = requestFactory.createRequest(new URI(url),
HttpMethod.GET);
ClientHttpRequest request = requestFactory.createRequest(new URI(url), method);
request.getHeaders().add("Cookie", "JSESSIONID=" + "123");
for (String header : headers) {
String[] parts = header.split(":");
Expand Down

0 comments on commit 02764b8

Please sign in to comment.