You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by dp...@apache.org on 2011/09/13 18:20:42 UTC

svn commit: r1170230 - in /jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk: client/Client.java client/HttpExecutor.java client/Request.java util/ChunkedInputStream.java util/ChunkedOutputStream.java

Author: dpfister
Date: Tue Sep 13 16:20:42 2011
New Revision: 1170230

URL: http://svn.apache.org/viewvc?rev=1170230&view=rev
Log:
HTTP interface to microkernel
- simplify chunked transmittal

Added:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedOutputStream.java   (with props)
Modified:
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/Client.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/HttpExecutor.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/Request.java
    jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedInputStream.java

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/Client.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/Client.java?rev=1170230&r1=1170229&r2=1170230&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/Client.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/Client.java Tue Sep 13 16:20:42 2011
@@ -202,7 +202,7 @@ public class Client implements MicroKern
             request.addParameter("blob_id", blobId);
             request.addParameter("pos", pos);
             request.addParameter("length", length);
-            return request.getBytes(buff, off, length);
+            return request.read(buff, off, length);
         } catch (IOException e) {
             throw toMicroKernelException(e);
         } finally {

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/HttpExecutor.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/HttpExecutor.java?rev=1170230&r1=1170229&r2=1170230&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/HttpExecutor.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/HttpExecutor.java Tue Sep 13 16:20:42 2011
@@ -30,6 +30,7 @@ import java.util.Map;
 
 import org.apache.jackrabbit.mk.util.BoundedInputStream;
 import org.apache.jackrabbit.mk.util.ChunkedInputStream;
+import org.apache.jackrabbit.mk.util.ChunkedOutputStream;
 import org.apache.jackrabbit.mk.util.IOUtils;
 
 /**
@@ -37,13 +38,14 @@ import org.apache.jackrabbit.mk.util.IOU
  */
 class HttpExecutor implements Closeable {
 
+    // TODO should actually NEVER match some part of binary content
+    private static final String BOUNDARY = "------ClientFormBoundaryHB5WJrSAPZjfwtqt--";
+    
     private final Socket socket;
     
     private InputStream socketIn;
     
     private OutputStream socketOut;
-    
-    private final byte[] chunkLen = new byte[4];
 
     /**
      * Create a new instance of this class.
@@ -80,18 +82,24 @@ class HttpExecutor implements Closeable 
         writeLine("Transfer-Encoding: chunked");
         writeLine("");
         
+        OutputStream bodyOut = new ChunkedOutputStream(socketOut);
+        
         if (in != null) {
-            // TODO sendInputStream(in);
+            bodyOut.write(BOUNDARY.getBytes());
+            bodyOut.write("\r\n\r\n".getBytes());
+            IOUtils.copy(in, bodyOut);
+            bodyOut.write("\r\n".getBytes());
+            bodyOut.write(BOUNDARY.getBytes());
         } else {
             for (Map.Entry<String,String> param : params.entrySet()) {
                 String s = String.format("%s=%s&", 
                         URLEncoder.encode(param.getKey(), "8859_1"),
                         URLEncoder.encode(param.getValue(), "8859_1"));
-                writeChunk(s.getBytes());
+                bodyOut.write(s.getBytes());
             }
         }
         
-        writeChunk(new byte[0]);
+        bodyOut.close();
         socketOut.flush();
         
         // read response
@@ -177,29 +185,6 @@ class HttpExecutor implements Closeable 
     }
 
     /**
-     * Write a chunk of bytes in the request body. 
-     * 
-     * @param b bytes
-     * @throws IOException if an I/O error occurs
-     */
-    private void writeChunk(byte[] b) throws IOException {
-        int off = 0;
-        
-        do {
-            int len = Math.min(b.length - off, 65534);
-            
-            toHexString(len, chunkLen);
-            socketOut.write(chunkLen);
-            socketOut.write("\r\n".getBytes());
-            socketOut.write(b, off, len);
-            socketOut.write("\r\n".getBytes());
-            
-            off += len;
-            
-        } while (off < b.length);
-    }
-    
-    /**
      * Read a single line, terminated by a CR LF combination from an input.
      * 
      * @return line
@@ -223,24 +208,4 @@ class HttpExecutor implements Closeable 
             }
         }
     }
-
-    /**
-     * Convert an integer into a byte array, consisting of its hexadecimal
-     * representation.
-     *
-     * @param n integer
-     * @param b byte array
-     */
-    private static void toHexString(int n, byte[] b) {
-        for (int i = b.length - 1; i >= 0; i--) {
-            int c = n & 0x0f;
-            if (c >= 0 && c <= 9) {
-                c += '0';
-            } else {
-                c += 'A' - 10;
-            }
-            b[i] = (byte) c;
-            n >>= 4;
-        }
-    }
 }

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/Request.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/Request.java?rev=1170230&r1=1170229&r2=1170230&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/Request.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/client/Request.java Tue Sep 13 16:20:42 2011
@@ -101,7 +101,7 @@ class Request implements Closeable {
         return Long.parseLong(getString());
     }
 
-    public int getBytes(byte[] b, int off, int len) throws IOException {
+    public int read(byte[] b, int off, int len) throws IOException {
         execute();
         
         int count = 0;

Modified: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedInputStream.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedInputStream.java?rev=1170230&r1=1170229&r2=1170230&view=diff
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedInputStream.java (original)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedInputStream.java Tue Sep 13 16:20:42 2011
@@ -190,8 +190,14 @@ public class ChunkedInputStream extends 
      * @see java.io.FilterInputStream#close()
      */
     public void close() throws IOException {
-        while (!lastChunk) {
-            readChunk();
+        if (in != null) {
+            try {
+                while (!lastChunk) {
+                    readChunk();
+                }
+            } finally {
+                in = null;
+            }
         }
     }
 }

Added: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedOutputStream.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedOutputStream.java?rev=1170230&view=auto
==============================================================================
--- jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedOutputStream.java (added)
+++ jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedOutputStream.java Tue Sep 13 16:20:42 2011
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.jackrabbit.mk.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Output stream that encodes and writes HTTP chunks.
+ */
+public class ChunkedOutputStream extends FilterOutputStream {
+
+    /**
+     * Maximum chunk size.
+     */
+    private static final int MAX_CHUNK_SIZE = 65535;
+
+    /**
+     * CR + LF combination.
+     */
+    private static final byte[] CRLF = "\r\n".getBytes();
+
+    /**
+     * Last chunk.
+     */
+    private static final byte[] LAST_CHUNK = "0000\r\n\r\n".getBytes();
+
+    /**
+     * Chunk prefix (length encoded as hexadecimal string).
+     */
+    private final byte[] prefix = new byte[4];
+
+    /**
+     * Chunk data.
+     */
+    private final byte[] data;
+
+    /**
+     * Current offset.
+     */
+    private int offset;
+
+    /**
+     * Create a new instance of this class.
+     *
+     * @param out underlying output stream.
+     * @param size internal buffer size
+     * @throws IllegalArgumentException if <code>size</code> is smaller than 1
+     *         or bigger than <code>65535</code>
+     */
+    public ChunkedOutputStream(OutputStream out, int size) {
+        super(out);
+
+        if (size < 1 || size > MAX_CHUNK_SIZE) {
+            String msg = "Chunk size smaller than 1 or bigger than " + MAX_CHUNK_SIZE;
+            throw new IllegalArgumentException(msg);
+        }
+        this.data = new byte[size];
+    }
+
+    /**
+     * Create a new instance of this class.
+     *
+     * @param out underlying output stream.
+     */
+    public ChunkedOutputStream(OutputStream out) {
+        this(out, MAX_CHUNK_SIZE);
+    }
+
+    /* (non-Javadoc)
+     * @see java.io.FilterOutputStream#write(int)
+     */
+    public void write(int b) throws IOException {
+        if (offset == data.length) {
+            writeChunk();
+        }
+        data[offset++] = (byte) (b & 0xff);
+    }
+
+    /* (non-Javadoc)
+     * @see java.io.FilterOutputStream#write(byte[], int, int)
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+        int written = 0;
+        while (written < len) {
+            if (offset == data.length) {
+                writeChunk();
+            }
+            int available = Math.min(len - written, data.length - offset);
+            System.arraycopy(b, off + written, data, offset, available);
+            written += available;
+            offset += available;
+        }
+    }
+
+    /**
+     * Writes the contents of the internal buffer as chunk to the underlying
+     * output stream.
+     *
+     * @throws IOException if an error occurs
+     */
+    private void writeChunk() throws IOException {
+        toHexString(offset, prefix);
+        out.write(prefix);
+        out.write(CRLF);
+        out.write(data, 0, offset);
+        out.write(CRLF);
+        offset = 0;
+    }
+
+    /**
+     * Convert an integer into a byte array, consisting of its hexadecimal
+     * representation.
+     *
+     * @param n integer
+     * @param b byte array
+     */
+    private static void toHexString(int n, byte[] b) {
+        for (int i = b.length - 1; i >= 0; i--) {
+            int c = n & 0x0f;
+            if (c >= 0 && c <= 9) {
+                c += '0';
+            } else {
+                c += 'A' - 10;
+            }
+            b[i] = (byte) c;
+            n >>= 4;
+        }
+    }
+
+    /**
+     * Flush the contents of the internal buffer to the underlying output
+     * stream as a chunk if it is non-zero. Never do that for a zero-size
+     * chunk as this would indicate EOF.
+     *
+     * @see java.io.FilterOutputStream#flush()
+     */
+    public void flush() throws IOException {
+        if (offset > 0) {
+            writeChunk();
+        }
+        super.flush();
+    }
+
+    /**
+     * Close this output stream. Flush the contents of the internal buffer
+     * and writes the last chunk to the underlying output stream. Does
+     * <b>not</b> close the underlying output stream.
+     *
+     * @see java.io.FilterOutputStream#close()
+     */
+    public void close() throws IOException {
+        if (out == null) {
+            return;
+        }
+        try {
+            if (offset > 0) {
+                writeChunk();
+            }
+            out.write(LAST_CHUNK);
+        } finally {
+            out = null;
+        }
+    }
+}

Propchange: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedOutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/sandbox/microkernel/src/main/java/org/apache/jackrabbit/mk/util/ChunkedOutputStream.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url