You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2014/12/12 13:12:21 UTC

cxf git commit: [CXF-6149] Fixing ContainerRequestContext and ClientResponseContext hasEntity implementations

Repository: cxf
Updated Branches:
  refs/heads/master b18a2bad6 -> 3f1a3c7e8


[CXF-6149] Fixing ContainerRequestContext and ClientResponseContext hasEntity implementations


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/3f1a3c7e
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/3f1a3c7e
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/3f1a3c7e

Branch: refs/heads/master
Commit: 3f1a3c7e8317536e7e486fe79271c78e78a72b3b
Parents: b18a2ba
Author: Sergey Beryozkin <sb...@talend.com>
Authored: Fri Dec 12 12:12:05 2014 +0000
Committer: Sergey Beryozkin <sb...@talend.com>
Committed: Fri Dec 12 12:12:05 2014 +0000

----------------------------------------------------------------------
 .../java/org/apache/cxf/helpers/IOUtils.java    | 35 ++++++++++++++++++++
 .../jaxrs/impl/ContainerRequestContextImpl.java | 11 ++++--
 .../client/spec/ClientResponseContextImpl.java  |  9 ++++-
 .../apache/cxf/transport/http/HTTPConduit.java  |  8 ++---
 .../org/apache/cxf/transport/http/Headers.java  |  8 ++++-
 .../apache/cxf/systest/jaxrs/BookServer20.java  | 15 +++++++--
 .../org/apache/cxf/systest/jaxrs/BookStore.java |  8 +++--
 .../jaxrs/JAXRS20ClientServerBookTest.java      | 30 +++++++++++++----
 8 files changed, 102 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/3f1a3c7e/core/src/main/java/org/apache/cxf/helpers/IOUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/helpers/IOUtils.java b/core/src/main/java/org/apache/cxf/helpers/IOUtils.java
index 56c26c0..aab3ab9 100644
--- a/core/src/main/java/org/apache/cxf/helpers/IOUtils.java
+++ b/core/src/main/java/org/apache/cxf/helpers/IOUtils.java
@@ -27,6 +27,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
+import java.io.PushbackInputStream;
 import java.io.Reader;
 import java.io.UnsupportedEncodingException;
 import java.io.Writer;
@@ -43,6 +44,40 @@ public final class IOUtils {
 
     }
     
+    public static boolean isEmpty(InputStream is) throws IOException {
+        if (is == null) {
+            return true;
+        }
+        final byte[] bytes = new byte[1];
+        try {
+            if (is.markSupported()) {
+                is.mark(1);
+                try {
+                    return isEof(is.read(bytes));
+                } finally {
+                    is.reset();
+                }
+            }
+            // if available is 0 it does not mean it is empty; it can also throw IOException
+            if (is.available() > 0) {
+                return false;
+            }
+        } catch (IOException ex) {
+            // ignore
+        }
+        // it may be an attachment stream
+        @SuppressWarnings("resource")
+        PushbackInputStream pbStream = 
+            is instanceof PushbackInputStream ? (PushbackInputStream)is : new PushbackInputStream(is);
+        boolean isEmpty = isEof(pbStream.read(bytes));
+        if (!isEmpty) {
+            pbStream.unread(bytes);
+        }
+        return isEmpty;
+    }
+    private static boolean isEof(int result) {
+        return result == -1;
+    }
     /**
      * Use this function instead of new String(byte[], String) to avoid surprises from 
      * non-standard default encodings.

http://git-wip-us.apache.org/repos/asf/cxf/blob/3f1a3c7e/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ContainerRequestContextImpl.java
----------------------------------------------------------------------
diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ContainerRequestContextImpl.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ContainerRequestContextImpl.java
index 3bdc167..039b68c 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ContainerRequestContextImpl.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/ContainerRequestContextImpl.java
@@ -18,16 +18,18 @@
  */
 package org.apache.cxf.jaxrs.impl;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 
-import javax.ws.rs.HttpMethod;
 import javax.ws.rs.container.ContainerRequestContext;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Request;
 import javax.ws.rs.core.SecurityContext;
 import javax.ws.rs.core.UriInfo;
 
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
 import org.apache.cxf.jaxrs.utils.HttpUtils;
 import org.apache.cxf.message.Message;
 
@@ -66,8 +68,11 @@ public class ContainerRequestContextImpl extends AbstractRequestContextImpl
 
     @Override
     public boolean hasEntity() {
-        InputStream is = getEntityStream();
-        return is != null && !HttpMethod.GET.equals(getMethod());
+        try {
+            return !IOUtils.isEmpty(getEntityStream());
+        } catch (IOException ex) {
+            throw ExceptionUtils.toInternalServerErrorException(ex, null);
+        }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cxf/blob/3f1a3c7e/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientResponseContextImpl.java
----------------------------------------------------------------------
diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientResponseContextImpl.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientResponseContextImpl.java
index 57c7391..e32c46f 100644
--- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientResponseContextImpl.java
+++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/spec/ClientResponseContextImpl.java
@@ -18,13 +18,16 @@
  */
 package org.apache.cxf.jaxrs.client.spec;
 
+import java.io.IOException;
 import java.io.InputStream;
 
 import javax.ws.rs.client.ClientResponseContext;
 import javax.ws.rs.core.MultivaluedMap;
 
+import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.jaxrs.impl.AbstractResponseContextImpl;
 import org.apache.cxf.jaxrs.impl.ResponseImpl;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
 import org.apache.cxf.jaxrs.utils.HttpUtils;
 import org.apache.cxf.message.Message;
 
@@ -59,6 +62,10 @@ public class ClientResponseContextImpl extends AbstractResponseContextImpl
     
     @Override
     public boolean hasEntity() { 
-        return getEntityStream() != null;
+        try {
+            return !IOUtils.isEmpty(getEntityStream());
+        } catch (IOException ex) {
+            throw ExceptionUtils.toInternalServerErrorException(ex, null);
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/3f1a3c7e/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
index da1a31e..7664525 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
@@ -1290,12 +1290,8 @@ public abstract class HTTPConduit
             // Trust is okay, set up for writing the request.
             
             String method = getMethod();
-            if (KNOWN_HTTP_VERBS_WITH_NO_CONTENT.contains(method)) {
-                handleNoOutput();
-                return;
-            }
-
-            if (outMessage.get("org.apache.cxf.empty.request") != null) {
+            if (KNOWN_HTTP_VERBS_WITH_NO_CONTENT.contains(method)
+                || PropertyUtils.isTrue(outMessage.get(Headers.EMPTY_REQUEST_PROPERTY))) {
                 handleNoOutput();
                 return;
             }

http://git-wip-us.apache.org/repos/asf/cxf/blob/3f1a3c7e/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java
----------------------------------------------------------------------
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java
index 620deb1..baf8b71 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/Headers.java
@@ -43,6 +43,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.PropertyUtils;
 import org.apache.cxf.helpers.CastUtils;
 import org.apache.cxf.helpers.HttpHeaderHelper;
 import org.apache.cxf.message.Message;
@@ -66,6 +67,7 @@ public class Headers {
     public static final String PROTOCOL_HEADERS_CONTENT_TYPE = Message.CONTENT_TYPE.toLowerCase();
     public static final String HTTP_HEADERS_SETCOOKIE = "Set-Cookie";
     public static final String HTTP_HEADERS_LINK = "Link";
+    public static final String EMPTY_REQUEST_PROPERTY = "org.apache.cxf.empty.request";
     private static final TimeZone TIME_ZONE_GMT = TimeZone.getTimeZone("GMT");
     private static final Logger LOG = LogUtils.getL7dLogger(Headers.class);
     
@@ -293,8 +295,12 @@ public class Headers {
      * @throws IOException
      */
     public void setProtocolHeadersInConnection(HttpURLConnection connection) throws IOException {
-        String ct = determineContentType();
+        boolean emptyRequest = PropertyUtils.isTrue(message.get(EMPTY_REQUEST_PROPERTY));
+        // Apparently HttpUrlConnection sets a form Content-Type 
+        // if no Content-Type is set even for empty requests 
+        String ct = emptyRequest ? "*/*" : determineContentType();
         connection.setRequestProperty(HttpHeaderHelper.CONTENT_TYPE, ct);
+         
         transferProtocolHeadersToURLConnection(connection);
         logProtocolHeaders(Level.FINE);
     }

http://git-wip-us.apache.org/repos/asf/cxf/blob/3f1a3c7e/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookServer20.java
----------------------------------------------------------------------
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookServer20.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookServer20.java
index 15870bf..8fed831 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookServer20.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookServer20.java
@@ -50,6 +50,7 @@ import javax.ws.rs.container.PreMatching;
 import javax.ws.rs.container.ResourceInfo;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
@@ -60,6 +61,7 @@ import javax.ws.rs.ext.WriterInterceptorContext;
 
 import org.apache.cxf.Bus;
 import org.apache.cxf.BusFactory;
+import org.apache.cxf.common.util.StringUtils;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
 import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
@@ -126,13 +128,22 @@ public class BookServer20 extends AbstractBusTestServerBase {
 
         @Override
         public void filter(ContainerRequestContext context) throws IOException {
+            UriInfo ui = context.getUriInfo();
+            String path = ui.getPath(false);
+            
+            if (context.getMethod().equals("POST") 
+                && "bookstore/bookheaders/simple".equals(path) && !context.hasEntity()) {
+                byte[] bytes = StringUtils.toBytesUTF8("<Book><name>Book</name><id>126</id></Book>");
+                context.getHeaders().putSingle(HttpHeaders.CONTENT_LENGTH, Integer.toString(bytes.length));
+                context.getHeaders().putSingle("Content-Type", "application/xml");
+                context.getHeaders().putSingle("EmptyRequestStreamDetected", "true");
+                context.setEntityStream(new ByteArrayInputStream(bytes));
+            }
             if ("true".equals(context.getProperty("DynamicPrematchingFilter"))) {
                 throw new RuntimeException();
             }
             context.setProperty("FirstPrematchingFilter", "true");
             
-            UriInfo ui = context.getUriInfo();
-            String path = ui.getPath(false);
             if ("wrongpath".equals(path)) {
                 context.setRequestUri(URI.create("/bookstore/bookheaders/simple"));
             } else if ("throwException".equals(path)) {

http://git-wip-us.apache.org/repos/asf/cxf/blob/3f1a3c7e/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
----------------------------------------------------------------------
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
index 299afd8..418f484 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
@@ -720,7 +720,8 @@ public class BookStore {
         @HeaderParam("BOOK") String headerBook,
         @HeaderParam("Simple") String headerSimple,
         @HeaderParam("ServerReaderInterceptor") String serverInterceptorHeader,
-        @HeaderParam("ClientWriterInterceptor") String clientInterceptorHeader) throws Exception {
+        @HeaderParam("ClientWriterInterceptor") String clientInterceptorHeader,
+        @HeaderParam("EmptyRequestStreamDetected") String emptyStreamHeader) throws Exception {
         if (!"application/xml".equals(ct)) {
             throw new RuntimeException();
         }
@@ -731,6 +732,9 @@ public class BookStore {
         if (clientInterceptorHeader != null) {
             builder.header("ClientWriterInterceptor", clientInterceptorHeader);
         }
+        if (emptyStreamHeader != null) {
+            builder.header("EmptyRequestStreamDetected", emptyStreamHeader);
+        }
         return builder.build();
     }
     
@@ -773,7 +777,7 @@ public class BookStore {
         throws Exception {
         
         return echoBookByHeaderSimple(book, ct, headerBook, headerSimple, serverInterceptorHeader, 
-                                      clientInterceptorHeader);
+                                      clientInterceptorHeader, null);
     }
     
     private ResponseBuilder getBookByHeaderSimpleBuilder(@HeaderParam("BOOK") String headerBook,

http://git-wip-us.apache.org/repos/asf/cxf/blob/3f1a3c7e/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRS20ClientServerBookTest.java
----------------------------------------------------------------------
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRS20ClientServerBookTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRS20ClientServerBookTest.java
index 1cfccf4..d8f5cc6 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRS20ClientServerBookTest.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRS20ClientServerBookTest.java
@@ -525,7 +525,7 @@ public class JAXRS20ClientServerBookTest extends AbstractBusClientServerTestBase
         Book book = future.get();
         assertSame(book, holder.value);
         assertEquals(124L, book.getId());
-        validatePostResponse(wc, true);   
+        validatePostResponse(wc, true, false);   
     }
     
     private void doTestGetBookAsyncResponse(String address, boolean asyncInvoker) 
@@ -562,12 +562,16 @@ public class JAXRS20ClientServerBookTest extends AbstractBusClientServerTestBase
         assertEquals("http://localhost/redirect", response.getHeaderString(HttpHeaders.LOCATION));
     }
     
-    private void validatePostResponse(WebClient wc, boolean async) {
+    private void validatePostResponse(WebClient wc, boolean async, boolean bodyEmpty) {
         validateResponse(wc);
         Response response = wc.getResponse();
         assertEquals(!async ? "serverRead" : "serverReadAsync", 
             response.getHeaderString("ServerReaderInterceptor"));
-        assertEquals("clientWrite", response.getHeaderString("ClientWriterInterceptor"));
+        if (!bodyEmpty) {
+            assertEquals("clientWrite", response.getHeaderString("ClientWriterInterceptor"));
+        } else {
+            assertEquals("true", response.getHeaderString("EmptyRequestStreamDetected"));
+        }
         assertEquals("clientRead", response.getHeaderString("ClientReaderInterceptor"));
     }
     
@@ -592,7 +596,17 @@ public class JAXRS20ClientServerBookTest extends AbstractBusClientServerTestBase
         WebClient wc = createWebClientPost(address);
         Book book = wc.post(new Book("Book", 126L), Book.class);
         assertEquals(124L, book.getId());
-        validatePostResponse(wc, false);
+        validatePostResponse(wc, false, false);
+    }
+    
+    @Test
+    public void testPostEmptyBook() {
+        String address = "http://localhost:" + PORT + "/bookstore/bookheaders/simple";
+        WebClient wc = createWebClientPost(address);
+        WebClient.getConfig(wc).getHttpConduit().getClient().setReceiveTimeout(1000000);
+        Book book = wc.post(null, Book.class);
+        assertEquals(124L, book.getId());
+        validatePostResponse(wc, false, true);
     }
     
     @Test
@@ -602,7 +616,7 @@ public class JAXRS20ClientServerBookTest extends AbstractBusClientServerTestBase
         wc.header("newmediatype", "application/v1+xml");
         Book book = wc.post(new Book("Book", 126L), Book.class);
         assertEquals(124L, book.getId());
-        validatePostResponse(wc, false);
+        validatePostResponse(wc, false, false);
         assertEquals("application/v1+xml", wc.getResponse().getHeaderString("newmediatypeused"));
     }
     
@@ -628,7 +642,7 @@ public class JAXRS20ClientServerBookTest extends AbstractBusClientServerTestBase
         WebClient wc = createWebClientPost(address);
         Future<Book> future = wc.async().post(Entity.xml(new Book("Book", 126L)), Book.class);
         assertEquals(124L, future.get().getId());
-        validatePostResponse(wc, true);
+        validatePostResponse(wc, true, false);
     }
     
     @Test
@@ -717,7 +731,9 @@ public class JAXRS20ClientServerBookTest extends AbstractBusClientServerTestBase
         @Override
         public void filter(ClientRequestContext context) throws IOException {
             context.getHeaders().putSingle("Simple", "simple");
-            context.getHeaders().putSingle("Content-Type", MediaType.APPLICATION_XML_TYPE);
+            if (context.hasEntity()) {
+                context.getHeaders().putSingle("Content-Type", MediaType.APPLICATION_XML_TYPE);
+            }
         }
     }