You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by gh...@apache.org on 2006/05/17 12:23:02 UTC

svn commit: r407212 - in /incubator/harmony/enhanced/classlib/trunk/modules/luni/src: main/java/java/net/ main/java/org/apache/harmony/luni/internal/net/www/protocol/http/ main/java/org/apache/harmony/luni/util/ test/java/tests/api/java/net/

Author: gharley
Date: Wed May 17 03:22:57 2006
New Revision: 407212

URL: http://svn.apache.org/viewcvs?rev=407212&view=rev
Log:
HARMONY 328 : Java 5 Enhancement: 2 new methods in java.net.HttpURLConnection

Modified:
    incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/net/HttpURLConnection.java
    incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java
    incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/util/ExternalMessages.properties
    incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/net/HttpURLConnectionTest.java

Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/net/HttpURLConnection.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/net/HttpURLConnection.java?rev=407212&r1=407211&r2=407212&view=diff
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/net/HttpURLConnection.java (original)
+++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/net/HttpURLConnection.java Wed May 17 03:22:57 2006
@@ -1,4 +1,4 @@
-/* Copyright 1998, 2005 The Apache Software Foundation or its licensors, as applicable
+/* Copyright 1998, 2006 The Apache Software Foundation or its licensors, as applicable
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
 
 import java.net.ProtocolException;
 import java.net.URL;
+import org.apache.harmony.luni.util.Msg;
 
 /**
  * This abstract subclass of <code>URLConnection</code> defines method for
@@ -42,6 +43,12 @@
 	protected boolean instanceFollowRedirects = followRedirects;
 
 	private static boolean followRedirects = true;
+    
+    protected int chunkLength = -1;
+
+    protected int fixedContentLength = -1;
+    
+    private final static int DEFAULT_CHUNK_LENGTH = 1024;
 
 	// 2XX: generally "OK"
 	// 3XX: relocation/redirect
@@ -447,4 +454,57 @@
 	public long getHeaderFieldDate(String field, long defaultValue) {
 		return super.getHeaderFieldDate(field, defaultValue);
 	}
+    
+    
+    /**
+     * If length of a HTTP request body is known ahead, sets fixed length to
+     * enable streaming without buffering. Sets after connection will cause an
+     * exception.
+     * 
+     * @see <code>setChunkedStreamingMode</code>
+     * @param contentLength
+     *            the fixed length of the HTTP request body
+     * @throws IllegalStateException
+     *             if already connected or other mode already set
+     * @throws IllegalArgumentException
+     *             if contentLength is less than zero
+     */
+    public void setFixedLengthStreamingMode(int contentLength) {
+        if (super.connected) {
+            throw new IllegalStateException(Msg.getString("K0079"));
+        }
+        if (0 < chunkLength) {
+            throw new IllegalStateException(Msg.getString("KA003"));
+        }
+        if (0 > contentLength) {
+            throw new IllegalArgumentException(Msg.getString("K0051"));
+        }
+        this.fixedContentLength = contentLength;
+    }
+
+    /**
+     * If length of a HTTP request body is NOT known ahead, enable chunked
+     * transfer encoding to enable streaming without buffering. Notice that not
+     * all http servers support this mode. Sets after connection will cause an
+     * exception.
+     * 
+     * @see <code>setFixedLengthStreamingMode</code>
+     * @param chunklen
+     *            the length of a chunk
+     * @throws IllegalStateException
+     *             if already connected or other mode already set
+     */
+    public void setChunkedStreamingMode(int chunklen) {
+        if (super.connected) {
+            throw new IllegalStateException(Msg.getString("K0079"));
+        }
+        if (0 <= fixedContentLength) {
+            throw new IllegalStateException(Msg.getString("KA003"));
+        }
+        if (0 >= chunklen) {
+            chunkLength = DEFAULT_CHUNK_LENGTH;
+        } else {
+            chunkLength = chunklen;
+        }
+    }
 }

Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java?rev=407212&r1=407211&r2=407212&view=diff
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java (original)
+++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java Wed May 17 03:22:57 2006
@@ -238,21 +238,56 @@
     }
 
     private class HttpOutputStream extends OutputStream {
+        
         static final int MAX = 1024;
 
-        ByteArrayOutputStream cache = new ByteArrayOutputStream(MAX + 7);
+        int cacheLength;
+        
+        int defaultCacheSize = MAX;
+
+        ByteArrayOutputStream cache;
 
         boolean writeToSocket, closed = false;
 
         int limit;
 
         public HttpOutputStream() {
+            cacheLength = defaultCacheSize;
+            cache = new ByteArrayOutputStream(cacheLength);
             limit = -1;
         }
 
         public HttpOutputStream(int limit) {
             writeToSocket = true;
             this.limit = limit;
+            if (limit > 0){
+                cacheLength = limit;
+            } else {
+                // chunkLength must be larger than 3
+                defaultCacheSize = chunkLength > 3 ? chunkLength : MAX;
+                cacheLength = calculateChunkDataLength();
+            }
+            cache = new ByteArrayOutputStream(cacheLength);
+        }
+        
+        // calculates the exact size of chunk data, chunk data size is chunk
+        // size minus chunk head (which writes chunk data size in HEX and
+        // "\r\n") size. For example, a string "abcd" use chunk whose size is 5
+        // must be writen to socket as "2\r\nab","2\r\ncd" ...
+        private int calculateChunkDataLength() {
+            // chunk head size is the hex string length of the cache size plus 2
+            // (which is the length of "\r\n"), it must be suitable
+            // to express the size of chunk data, as short as possible.
+            // Notices that according to RI, if chunklength is 19, chunk head
+            // length is 4 (expressed as "10\r\n"), chunk data length is 16
+            // (which real sum is 20,not 19); while if chunklength is 18, chunk
+            // head length is 3. Thus the cacheSize = chunkdataSize + 
+            // sizeof(string length of chunk head in HEX) + sizeof("\r\n");
+            int bitSize = Integer.toHexString(defaultCacheSize).length();
+            // here is the calculated head size, not real size (for 19, it
+            // counts 3, not real size 4)
+            int headSize = (Integer.toHexString(defaultCacheSize - bitSize - 2).length()) + 2;
+            return defaultCacheSize - headSize;
         }
 
         private void output(String output) throws IOException {
@@ -265,19 +300,14 @@
                 if (limit < 0) {
                     if (size > 0) {
                         output(Integer.toHexString(size) + "\r\n");
-                        cache.write('\r');
-                        cache.write('\n');
+                        socketOut.write(cache.toByteArray());
+                        cache.reset();
+                        output("\r\n");
                     }
                     if (close) {
-                        cache.write('0');
-                        cache.write('\r');
-                        cache.write('\n');
-                        cache.write('\r');
-                        cache.write('\n');
+                        output("0\r\n\r\n");
                     }
                 }
-                socketOut.write(cache.toByteArray());
-                cache.reset();
             }
         }
 
@@ -310,8 +340,9 @@
                 limit--;
             }
             cache.write(data);
-            if (writeToSocket && cache.size() >= MAX)
+            if (writeToSocket && cache.size() >= cacheLength){
                 sendCache(false);
+            }
         }
 
         public synchronized void write(byte[] buffer, int offset, int count)
@@ -329,17 +360,31 @@
                 if (count > limit)
                     throw new IOException(Msg.getString("K00b2"));
                 limit -= count;
-            }
-            if (!writeToSocket || cache.size() + count < MAX) {
                 cache.write(buffer, offset, count);
+                if (limit == 0){
+                    socketOut.write(cache.toByteArray());                    
+                }
             } else {
-                if (limit < 0)
-                    output(Integer.toHexString(count + cache.size()) + "\r\n");
-                socketOut.write(cache.toByteArray());
-                cache.reset();
-                socketOut.write(buffer, offset, count);
-                if (limit < 0)
+                if (!writeToSocket || cache.size() + count < cacheLength) {
+                    cache.write(buffer, offset, count);
+                } else {
+                    output(Integer.toHexString(cacheLength) + "\r\n");
+                    int writeNum = cacheLength - cache.size();
+                    cache.write(buffer, offset, writeNum);
+                    socketOut.write(cache.toByteArray());
                     output("\r\n");
+                    cache.reset();
+                    int left = count - writeNum;
+                    int position = offset + writeNum;
+                    while (left > cacheLength) {
+                        output(Integer.toHexString(cacheLength) + "\r\n");
+                        socketOut.write(buffer, position, cacheLength);
+                        output("\r\n");
+                        left = left - cacheLength;
+                        position = position + cacheLength;
+                    }
+                    cache.write(buffer, position, left);
+                }
             }
         }
 
@@ -670,6 +715,14 @@
                 limit = -1;
             }
         }
+        // if user has set chunk/fixedLength mode, use that value
+        if (chunkLength > 0) {
+            sendChunked = true;
+            limit = -1;
+        }
+        if (fixedContentLength >= 0) {
+            limit = fixedContentLength;
+        }
         if ((httpVersion > 0 && sendChunked) || limit >= 0) {
             os = new HttpOutputStream(limit);
             doRequest();
@@ -895,6 +948,7 @@
                 output.append("Transfer-Encoding: chunked\r\n");
         }
 
+        boolean hasContentLength = false;
         // then the user-specified request headers, if any
         for (int i = 0; i < reqHeader.length(); i++) {
             String key = reqHeader.getKey(i);
@@ -909,10 +963,26 @@
                      * duplicates are allowed under certain conditions see
                      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
                      */
-                    output.append(reqHeader.get(i));
+                    if (lKey.equals("content-length")) {
+                        hasContentLength = true;
+                        // if both setFixedLengthStreamingMode and
+                        // content-length are set, use fixedContentLength
+                        // first
+                        output
+                                .append((fixedContentLength >= 0) ? String
+                                        .valueOf(fixedContentLength)
+                                        : reqHeader.get(i));
+                    } else {
+                        output.append(reqHeader.get(i));
+                    }
                     output.append("\r\n");
                 }
             }
+        }
+        if (fixedContentLength >= 0 && !hasContentLength) {
+            output.append("content-length: ");
+            output.append(String.valueOf(fixedContentLength));
+            output.append("\r\n");
         }
         // end the headers
         output.append("\r\n");

Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/util/ExternalMessages.properties
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/util/ExternalMessages.properties?rev=407212&r1=407211&r2=407212&view=diff
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/util/ExternalMessages.properties (original)
+++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/util/ExternalMessages.properties Wed May 17 03:22:57 2006
@@ -1,4 +1,4 @@
-# Copyright 2000, 2005 The Apache Software Foundation or its licensors, as applicable
+# Copyright 2000, 2006 The Apache Software Foundation or its licensors, as applicable
 #  
 #  Licensed under the Apache License, Version 2.0 (the "License");
 #  you may not use this file except in compliance with the License.
@@ -287,3 +287,4 @@
 KA000=Line too long
 KA001=Argument must not be null
 KA002=Unshared read of back reference
+KA003=different mode already set
\ No newline at end of file

Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/net/HttpURLConnectionTest.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/net/HttpURLConnectionTest.java?rev=407212&r1=407211&r2=407212&view=diff
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/net/HttpURLConnectionTest.java (original)
+++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/net/HttpURLConnectionTest.java Wed May 17 03:22:57 2006
@@ -222,6 +222,185 @@
 		}
 	}
 
+    /**
+     * @tests java.net.HttpURLConnection#setFixedLengthStreamingMode_I()
+     */
+    public void test_setFixedLengthStreamingModeI() throws Exception {
+        url = new URL("http://"
+                + Support_Configuration.InetTestAddress);
+        uc = (HttpURLConnection) url.openConnection();
+        try {
+            uc.setFixedLengthStreamingMode(-1);
+            fail("should throw IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // correct
+        }
+        uc.setFixedLengthStreamingMode(0);
+        uc.setFixedLengthStreamingMode(1);
+        try {
+            uc.setChunkedStreamingMode(1);
+            fail("should throw IllegalStateException");
+        } catch (IllegalStateException e) {
+            // correct
+        }
+        uc.connect();
+        try {
+            uc.setFixedLengthStreamingMode(-1);
+            fail("should throw IllegalStateException");
+        } catch (IllegalStateException e) {
+            // correct
+        }
+        try {
+            uc.setChunkedStreamingMode(-1);
+            fail("should throw IllegalStateException");
+        } catch (IllegalStateException e) {
+            // correct
+        }
+        MockHttpConnection mock = new MockHttpConnection(url);
+        assertEquals(-1, mock.getFixedLength());
+        mock.setFixedLengthStreamingMode(0);
+        assertEquals(0, mock.getFixedLength());
+        mock.setFixedLengthStreamingMode(1);
+        assertEquals(1, mock.getFixedLength());
+        mock.setFixedLengthStreamingMode(0);
+        assertEquals(0, mock.getFixedLength());
+    }
+
+    /**
+     * @tests java.net.HttpURLConnection#setChunkedStreamingMode_I()
+     */
+    public void test_setChunkedStreamingModeI() throws Exception {
+        url = new URL("http://"
+                + Support_Configuration.InetTestAddress);
+        uc = (HttpURLConnection) url.openConnection();
+        uc.setChunkedStreamingMode(0);
+        uc.setChunkedStreamingMode(-1);
+        uc.setChunkedStreamingMode(-2);
+
+        try {
+            uc.setFixedLengthStreamingMode(-1);
+            fail("should throw IllegalStateException");
+        } catch (IllegalStateException e) {
+            // correct
+        }
+        try {
+            uc.setFixedLengthStreamingMode(1);
+            fail("should throw IllegalStateException");
+        } catch (IllegalStateException e) {
+            // correct
+        }
+        uc.connect();
+        try {
+            uc.setFixedLengthStreamingMode(-1);
+            fail("should throw IllegalStateException");
+        } catch (IllegalStateException e) {
+            // correct
+        }
+        try {
+            uc.setChunkedStreamingMode(1);
+            fail("should throw IllegalStateException");
+        } catch (IllegalStateException e) {
+            // correct
+        }
+        MockHttpConnection mock = new MockHttpConnection(url);
+        assertEquals(-1, mock.getChunkLength());
+        mock.setChunkedStreamingMode(-1);
+        int defaultChunk = mock.getChunkLength();
+        assertTrue(defaultChunk > 0);
+        mock.setChunkedStreamingMode(0);
+        assertEquals(mock.getChunkLength(), defaultChunk);
+        mock.setChunkedStreamingMode(1);
+        assertEquals(1, mock.getChunkLength());
+    }
+
+    class MockHttpConnection extends HttpURLConnection {
+
+        protected MockHttpConnection(URL url) {
+            super(url);
+        }
+
+        public void disconnect() {
+            // do nothing
+        }
+
+        public boolean usingProxy() {
+            return false;
+        }
+
+        public void connect() throws IOException {
+            // do nothing
+        }
+
+        public int getChunkLength() {
+            return super.chunkLength;
+        }
+
+        public int getFixedLength() {
+            return super.fixedContentLength;
+        }
+
+    }
+
+    /**
+     * @tests java.net.HttpURLConnection#setFixedLengthStreamingMode_I()
+     */
+    public void test_setFixedLengthStreamingModeI_effect() throws Exception {
+        String posted = "just a test";
+        URL u = new URL("http://"
+                + Support_Configuration.InetTestAddress);
+        java.net.HttpURLConnection conn = (java.net.HttpURLConnection) u
+                .openConnection();
+        conn.setDoOutput(true);
+        conn.setRequestMethod("POST");
+        conn.setFixedLengthStreamingMode(posted.length() - 1);
+        assertNull(conn.getRequestProperty("Content-length"));
+        conn.setRequestProperty("Content-length", String.valueOf(posted
+                .length()));
+        assertEquals(String.valueOf(posted.length()), conn
+                .getRequestProperty("Content-length"));
+        OutputStream out = conn.getOutputStream();
+        try {
+            out.write(posted.getBytes());
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // correct, too many bytes written
+        }
+        try {
+            out.close();
+            fail("should throw IOException");
+        } catch (IOException e) {
+            // correct, too many bytes written
+        }
+    }
+
+    /**
+     * @tests java.net.HttpURLConnection#setChunkedStreamingMode_I()
+     */
+    public void test_setChunkedStreamingModeI_effect() throws Exception {
+        String posted = "just a test";
+        // for test, use half length of the string
+        int chunkSize = posted.length() / 2;
+        URL u = new URL("http://"
+                + Support_Configuration.InetTestAddress);
+        java.net.HttpURLConnection conn = (java.net.HttpURLConnection) u
+                .openConnection();
+        conn.setDoOutput(true);
+        conn.setRequestMethod("POST");
+        conn.setChunkedStreamingMode(chunkSize);
+        assertNull(conn.getRequestProperty("Transfer-Encoding"));
+        // does not take effect
+        conn.setRequestProperty("Content-length", String.valueOf(posted
+                .length() - 1));
+        assertEquals(conn.getRequestProperty("Content-length"), String
+                .valueOf(posted.length() - 1));
+        OutputStream out = conn.getOutputStream();
+        // no error occurs
+        out.write(posted.getBytes());
+        out.close();
+        // no assert here, pass if no exception thrown
+        assertTrue(conn.getResponseCode() > 0);
+    }
+    
 	protected void setUp() {
 		try {
 			url = new URL(Support_Resources
@@ -234,8 +413,5 @@
 
 	protected void tearDown() {
 		uc.disconnect();
-	}
-
-	protected void doneSuite() {
 	}
 }