5

I have a web application built using Spring Boot with Apache Camel and I'm implementing a REST interface. Currently, using either Camel default Servlet or Restlet component, I'm not getting the HTTP Status code reason in the response.

Here is an example response I'm getting while setting the HTTP Status code to 403:

< HTTP/1.1 403 
< Date: Mon, 19 Feb 2018 10:01:21 GMT
< Server: Restlet-Framework/2.4.0
< Content-Type: application/json
< Content-Length: 75

How it should be:

< HTTP/1.1 403 Forbidden
< Date: Mon, 19 Feb 2018 10:01:21 GMT
< Server: Restlet-Framework/2.4.0
< Content-Type: application/json
< Content-Length: 75

How can I configure Camel/Restlet/Servlet to include the reason on the HTTP Status code?

My current configuration:

Application.java

@SpringBootApplication
public class Application extends SpringBootServletInitializer {
    private static final Logger appLogger = LoggerFactory.getLogger(Application.class);
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        appLogger.info("--Application Started--");
    }

    @Bean
    public ServletRegistrationBean servletRegistrationBean() {

        SpringServerServlet serverServlet = new SpringServerServlet();
        ServletRegistrationBean regBean = new ServletRegistrationBean(serverServlet, "/*");

        Map<String,String> params = new HashMap<>();
        params.put("org.restlet.component", "restletComponent");

        regBean.setInitParameters(params);

        return regBean;
    }


    @Bean
    public Component restletComponent() {
        return new Component();
    }

    @Bean
    public RestletComponent restletComponentService() {
        return new RestletComponent(restletComponent());
    }

}

Route Configuration:

@Component
public class RestRouteBuilder extends RouteBuilder {
    private static final Logger appLogger = LoggerFactory.getLogger(RestRouteBuilder.class);
    private Predicate isAuthorizedRequest = header(HttpHeaders.AUTHORIZATION).isNotNull();

    @Override
    public void configure() throws Exception {
        restConfiguration().component("restlet")
                           .contextPath("/overlay")
                           .bindingMode(RestBindingMode.json)
                           .skipBindingOnErrorCode(false)
                           .dataFormatProperty("prettyPrint", "true");

        rest("/")
                .get()
                .route()
                .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403))
                .setBody(constant("Forbidden"))
                .endRest();
    }
}

I also tried adding .setHeader(Exchange.HTTP_RESPONSE_TEXT, constant("Forbidden")) but the result was the same.

Tiago Ferreira
  • 255
  • 2
  • 5
  • 12
  • try withouth this part `.component("restlet")` – pvpkiran Feb 19 '18 at 11:20
  • @pvpkiran Exactly the same result – Tiago Ferreira Feb 19 '18 at 11:39
  • for me it works. without the `setBody`. This is the result `curl --verbose http://localhost:8080/overlay/ * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8080 (#0) > GET /overlay/ HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.47.0 > Accept: */* > < HTTP/1.1 403 Forbidden < Server: Restlet-Framework/2.3.6 < Breadcrumbid: ID-Boss-36986-1519039039690-0-5 < Date: Mon, 19 Feb 2018 11:19:34 GMT < Content-type: application/json; charset=UTF-8 < Content-length: 0 ` – pvpkiran Feb 19 '18 at 11:48
  • @pvpkiran Could you please share your test code and package versions so that I can compare? Thank you. – Tiago Ferreira Feb 19 '18 at 13:26
  • I have the exact code as you. except `.component("restlet")` and also I don't have ServletRegistrationBean, Component, RestletComponent bean declarations. We don't need this – pvpkiran Feb 19 '18 at 13:46
  • @pvpkiran If I don't add the ServletRegistrationBean, Component, RestletComponent, I get a 404 for every route. – Tiago Ferreira Feb 19 '18 at 13:59
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/165420/discussion-between-tiago-ferreira-and-pvpkiran). – Tiago Ferreira Feb 19 '18 at 14:00
  • @pvpkiran Could you check chat? – Tiago Ferreira Feb 20 '18 at 16:40

3 Answers3

9
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403))
.setHeader(Exchange.CONTENT_TYPE, constant("text/plain"))
.setBody(constant("Forbidden"))
codebrane
  • 4,290
  • 2
  • 18
  • 27
4

How can I configure Camel/Restlet/Servlet to include the reason on the HTTP Status code?

Without a custom core, I believe you can't:

The response is being sent at org.restlet.engine.adapter.ServerCall.sendResponse(), where the response head and body are written to the OutputStream:

writeResponseHead(response); // <--
if (responseEntity != null) {
    responseEntityStream = getResponseEntityStream();
    writeResponseBody(responseEntity, responseEntityStream);
}

... and writeResponseHead(response) does nothing by default, check it:

protected void writeResponseHead(Response response) throws IOException {
    // Do nothing by default
}

Update: ... the HttpStatus(value, reasonPhrase) has the reasonPhrase, but isn't used to stringify:

HttpStatus(int value, String reasonPhrase) {
    this.value = value;
    this.reasonPhrase = reasonPhrase;
}
...
@Override
public String toString() {
    return Integer.toString(this.value);
}

Update 2: ... the DefaultRestletBinding.populateRestletResponseFromExchange does the following:

// get response code
Integer responseCode = out.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
if (responseCode != null) {
    response.setStatus(Status.valueOf(responseCode));
}

... it only uses the Status.valueOf.

Although there is a Status.reasonPhrase, it isn't accessible.


Answer:

Without custom core, (I believe) you can't!


... what isn't inappropriate, given that:

6.1.1 Status Code and Reason Phrase

(...) The client is not required to examine or display the Reason-Phrase.

(...) The reason phrases (...) MAY be replaced by local equivalents without affecting the protocol.

3.1.2. Status Line

(...) A client SHOULD ignore the reason-phrase content.

8.1.2.4. Response Pseudo-Header Fields

(...) HTTP/2 does not define a way to carry the version or reason phrase that is included in an HTTP/1.1 status line.

Need to know the meaning of a status code?

See the complete list of status codes maintained by IANA.

Community
  • 1
  • 1
mo7ty
  • 159
  • 10
  • Answer updated with [HttpStatus](https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/http/HttpStatus.java)[.toString()](https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/http/HttpStatus.java#L505) behaviour. – mo7ty Apr 01 '18 at 10:08
  • Answer updated (2) with [DefaultRestletBinding.populateRestletResponseFromExchange](https://github.com/apache/camel/blob/master/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/DefaultRestletBinding.java#L395) behaviour... and, believe, answered. – mo7ty Apr 11 '18 at 23:22
0

TLDR - use .setHeader(Exchange.HTTP_RESPONSE_CODE, 403)

I found out that

.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403)) does not work but

.setHeader(Exchange.HTTP_RESPONSE_CODE, 403) DOES.

Mind the constant keyword difference. You need to use constant when setting values directly in the route, but e.g. in Processor you do not. Therefore you can set response status code whenever exception occurs in code, not in the route builder config.

GTessa
  • 1
  • 1