Uploaded image for project: 'RESTEasy'
  1. RESTEasy
  2. RESTEASY-2505

Client Proxy methods returning void eagerly close body even in error conditions

    XMLWordPrintable

Details

    • Bug
    • Resolution: Not a Bug
    • Minor
    • None
    • 4.4.2.Final
    • jaxrs
    • None

    Description

      We write a JAX-RS client method, like

      @Produces(JSON)
      @PUT
      void storeSomething(Something value);
      

      Due to an error, the server returns 400 Bad Request unconditionally. This correctly causes the client to throw BadRequestException.

      We have a post-processor that attempts to inspect WebApplicationExceptions (including this BadRequestException) and attach diagnostic info for the operator:

          public static <X extends WebApplicationException> X summarize(final X wae) {
              wae.addSuppressed(new BodySummary(wae.getResponse()));
              throw wae;
          }
      
          @SuppressWarnings("PMD.DoNotExtendJavaLangThrowable")
          static class BodySummary extends Throwable {
              private static final long serialVersionUID = 1L;
      
              BodySummary(final Response response) {
                  super("Failing body is: " + abbreviate(response));
              }
      
              private static String abbreviate(final Response response) {
                  if (response instanceof BuiltResponse && ((BuiltResponse) response).isClosed()) {
                      return "<closed>";
                  }
                  return StringUtils.abbreviate(response.readEntity(String.class), 5120);
              }
          }
      

      This normally works just fine. We noticed it does not work at all on methods returning void, however. This seems to be due to DefaultEntityExtractorFactory:

         public static final EntityExtractor createVoidExtractor()
         {
            return new EntityExtractor()
            {
               public Object extractEntity(ClientContext context, Object... args)
               {
                  ClientResponse response = context.getClientResponse();
                  int status = response.getStatus();
                  if (status >= 400)
                  {
                     response.bufferEntity();
                     response.close();
                     ClientInvocation.handleErrorStatus(response);
                  }
                  response.close();
                  return null;
               }
            };
         }
      

      The extractor takes care to ensure the response is closed before returning it. This is fine in the normal case – obviously void methods don't have data returned – but in case of an exception, there may in fact be a body present describing the error.

      In this case it is extremely tricky to inspect the returned body via WebApplicationException.getResponse(). While it is buffered explicitly, since the response is already closed, any attempt to check e.g. hasEntity throws. And in fact the spec says:

      Buffering the message entity data allows for multiple invocations of readEntity(...) methods on the response instance. Note however, that once the response instance itself is closed, the implementations are expected to release the buffered message entity data too. Therefore any subsequent attempts to read a message entity stream on such closed response will result in an IllegalStateException being thrown. 
      

      So, if you have a void-returning method in a proxied interface, it seems to be impossible to read an error body in a spec-compliant manner.

      Please let me know if I can clarify further or if I'm "holding it wrong". Thanks!

      Attachments

        Activity

          People

            weli@redhat.com Weinan Li
            stevenschlansker Steven Schlansker
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: