hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject svn commit: r158844 - in jakarta/httpclient/trunk/http-common/src: java/org/apache/http/impl/ java/org/apache/http/impl/NIOHttpDataReceiver.java java/org/apache/http/impl/NIOHttpDataTransmitter.java test/org/apache/http/impl/ test/org/apache/http/impl/TestNIOHttpTransmitterAndReceiver.java
Date Wed, 23 Mar 2005 22:14:52 GMT
Author: olegk
Date: Wed Mar 23 14:14:51 2005
New Revision: 158844

URL: http://svn.apache.org/viewcvs?view=rev&rev=158844
Log:
NIO based implementation of HttpDataReceiver and HttpDataTransmitter interfaces (initial import)

Added:
    jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/
    jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataReceiver.java
  (with props)
    jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataTransmitter.java
  (with props)
    jakarta/httpclient/trunk/http-common/src/test/org/apache/http/impl/
    jakarta/httpclient/trunk/http-common/src/test/org/apache/http/impl/TestNIOHttpTransmitterAndReceiver.java
  (with props)

Added: jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataReceiver.java
URL: http://svn.apache.org/viewcvs/jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataReceiver.java?view=auto&rev=158844
==============================================================================
--- jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataReceiver.java
(added)
+++ jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataReceiver.java
Wed Mar 23 14:14:51 2005
@@ -0,0 +1,204 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+
+import org.apache.http.HttpDataReceiver;
+
+/**
+ * <p>
+ * </p>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision$
+ * 
+ * @since 4.0
+ */
+public class NIOHttpDataReceiver implements HttpDataReceiver {
+
+    private static final int CR = 13;
+    private static final int LF = 10;
+    
+    private final ReadableByteChannel channel;
+    private final ByteBuffer buffer;
+    private final Charset charset;
+    
+    protected NIOHttpDataReceiver(final ReadableByteChannel channel, int buffersize) {
+        if (channel == null) {
+            throw new IllegalArgumentException("Channel may not be null");
+        }
+        this.channel = channel;
+        
+        if ((buffersize > 2048) || (buffersize <= 0)) {
+            buffersize = 2048;
+        }
+        this.buffer = ByteBuffer.allocateDirect(buffersize);
+        
+        this.charset = Charset.forName("US-ASCII");
+    }
+
+    private CharsetDecoder createCharDecoder() {
+        CharsetDecoder chardecoder = this.charset.newDecoder();
+        chardecoder.onMalformedInput(CodingErrorAction.REPLACE); 
+        chardecoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
+        return chardecoder;
+    }
+    
+    private int fillBuffer() throws IOException {
+        int i = this.channel.read(this.buffer);
+        this.buffer.flip();
+        return i;
+    }
+    
+    public int read(final byte[] b, int off, int len) throws IOException {
+        if (b == null) {
+            return 0;
+        }
+        if (!this.buffer.hasRemaining()) {
+            this.buffer.clear();
+            int noRead = fillBuffer();
+            if (noRead == -1) {
+                return noRead; 
+            }
+        }
+        int chunk = len;
+        if (chunk > this.buffer.remaining()) {
+            chunk = this.buffer.remaining();
+        }
+        this.buffer.get(b, off, chunk);
+        return chunk;
+    }
+    
+    public int read(final byte[] b) throws IOException {
+        if (b == null) {
+            return 0;
+        }
+        return read(b, 0, b.length);
+    }
+    
+    private int locateLF() {
+        for (int i = this.buffer.position(); i < this.buffer.limit(); i++) {
+            int b = this.buffer.get(i);
+            if (b == LF) {
+                return i;
+            }
+        }
+        return -1;
+    }
+    
+    public String readLine() throws IOException {
+        int noRead = 0;
+        // prefill the buffer if necessary
+        if (this.buffer.position() == 0) {
+            noRead = fillBuffer();
+            if (noRead == -1) {
+                // end of stream
+                return null;
+            }
+        }
+        CharsetDecoder chardecoder = createCharDecoder();
+        StringBuffer line = new StringBuffer(); 
+        CharBuffer tmp = CharBuffer.allocate(128);
+        boolean retry = true;
+        while (retry) {
+            // attempt to find end of line (LF)
+            int i = locateLF(); 
+            if (i != -1) {
+                // end of line found. 
+                retry = false;
+                // read up to the end of line
+                int origLimit = this.buffer.limit();
+                this.buffer.limit(i + 1);
+                chardecoder.decode(this.buffer, tmp, true);
+                this.buffer.limit(origLimit);
+            } else {
+                // end of line not found
+                if (this.buffer.hasRemaining()) {
+                    // decode the entire buffer content
+                    chardecoder.decode(this.buffer, tmp, false);
+                }
+                // discard the decoded content
+                this.buffer.compact();
+                noRead = fillBuffer();
+                if (noRead == -1) {
+                    retry = false;
+                    // terminate the decoding process
+                    chardecoder.decode(this.buffer, tmp, true);
+                    this.buffer.clear();
+                }
+            }
+            // append the decoded content to the line buffer
+            tmp.flip();
+            if (tmp.hasRemaining()) {
+                line.append(tmp.array(), tmp.position(), tmp.remaining());
+            }
+            tmp.clear();
+        }
+        // flush the decoder
+        chardecoder.flush(tmp);
+        tmp.flip();
+        // append the decoded content to the line buffer
+        if (tmp.hasRemaining()) {
+            line.append(tmp.array(), tmp.position(), tmp.remaining());
+        }
+        if (noRead == -1 && line.length() == 0) {
+            // indicate the end of stream
+            return null;
+        }
+        // discard LF if found
+        int l = line.length(); 
+        if (l > 0) {
+            if (line.charAt(l - 1) == LF) {
+                l--;
+                line.setLength(l);
+            }
+            // discard CR if found
+            if (l > 0) {
+                if (line.charAt(l - 1) == CR) {
+                    l--;
+                    line.setLength(l);
+                }
+            }
+        }
+        return line.toString();
+    }
+    
+    public boolean isDataAvailable(int timeout) throws IOException {
+        return this.channel.isOpen();
+    }    
+    
+}

Propchange: jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataReceiver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataReceiver.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataReceiver.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataTransmitter.java
URL: http://svn.apache.org/viewcvs/jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataTransmitter.java?view=auto&rev=158844
==============================================================================
--- jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataTransmitter.java
(added)
+++ jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataTransmitter.java
Wed Mar 23 14:14:51 2005
@@ -0,0 +1,184 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ *
+ * ====================================================================
+ *
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+import org.apache.http.HttpDataTransmitter;
+
+/**
+ * <p>
+ * </p>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ *
+ * @version $Revision$
+ * 
+ * @since 4.0
+ */
+public class NIOHttpDataTransmitter implements HttpDataTransmitter {
+
+    private static final int CR = 13;
+    private static final int LF = 10;
+    private static final byte[] CRLF = new byte[] {CR, LF};
+
+    private final WritableByteChannel channel;
+    private final ByteBuffer buffer;
+    private final Charset charset;
+
+    protected NIOHttpDataTransmitter(final WritableByteChannel channel, int buffersize) {
+        super();
+        if (channel == null) {
+            throw new IllegalArgumentException("Channel may not be null");
+        }
+        this.channel = channel;
+        
+        if ((buffersize > 2048) || (buffersize <= 0)) {
+            buffersize = 2048;
+        }
+        this.buffer = ByteBuffer.allocateDirect(buffersize);
+        this.charset = Charset.forName("US-ASCII");
+    }
+
+    private void flushBuffer() throws IOException {
+        this.buffer.flip();
+        this.channel.write(this.buffer);
+        this.buffer.compact();
+    }
+    
+    public void flush() throws IOException {
+        this.buffer.flip();
+        while (this.buffer.hasRemaining()) {
+            this.channel.write(this.buffer);
+        }
+        this.buffer.clear();
+    }
+    
+    private CharsetEncoder createCharEncoder() {
+        CharsetEncoder charencoder = this.charset.newEncoder();
+        charencoder.onMalformedInput(CodingErrorAction.REPLACE); 
+        charencoder.onUnmappableCharacter(CodingErrorAction.REPLACE); 
+        return charencoder; 
+    }
+    
+    private void write(
+            final CharsetEncoder charencoder, 
+            final CharBuffer charbuffer, 
+            boolean endOfInput) throws IOException {
+        boolean retry = true;
+        while (retry) {
+            CoderResult result = charencoder.encode(charbuffer, this.buffer, endOfInput);
+            if (result.isOverflow()) {
+                flushBuffer();
+            }
+            retry = !result.isUnderflow();
+        }
+    }
+
+    public void write(final byte[] b, int off, int len) throws IOException {
+        if (b == null) {
+            return;
+        }
+        int i = off;
+        while (i < len) {
+            if (this.buffer.hasRemaining()) {
+                int chunk = len - i;
+                if (chunk > this.buffer.remaining()) {
+                    chunk = this.buffer.remaining();
+                }
+                this.buffer.put(b, i, chunk);
+                i += chunk;
+            } else {
+                flushBuffer();
+            }
+        }
+    }
+
+    public void write(final byte[] b) throws IOException {
+        if (b == null) {
+            return;
+        }
+        write(b, 0, b.length);
+    }
+
+    private void writeCRLF() throws IOException {
+        write(CRLF);
+    }
+
+    private void flushCharEncoder(final CharsetEncoder charencoder) throws IOException {
+        boolean retry = true;
+        while (retry) {
+            CoderResult result = charencoder.flush(this.buffer);
+            if (result.isOverflow()) {
+                flushBuffer();
+            }
+            retry = !result.isUnderflow();
+        }
+    }
+    
+    public void writeLine(final String s) throws IOException {
+        if (s == null) {
+            return;
+        }
+        // Do not bother if the string is empty
+        if (s.length() > 0 ) {
+            CharsetEncoder charencoder = createCharEncoder();
+            CharBuffer tmp = CharBuffer.allocate(128);
+            // transfer the string in small chunks
+            int remaining = s.length();
+            int offset = 0;
+            while (remaining > 0) {
+                int l = tmp.remaining();
+                boolean eol = false;
+                if (remaining < l) {
+                    l = remaining;
+                    // terminate the encoding process
+                    eol = true;
+                }
+                tmp.put(s, offset, offset + l);
+                tmp.flip();
+                write(charencoder, tmp, eol);
+                tmp.compact();
+                offset += l;
+                remaining -= l;
+            }
+            // flush the encoder
+            flushCharEncoder(charencoder);
+        }
+        writeCRLF();
+    }
+
+}

Propchange: jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataTransmitter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataTransmitter.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/httpclient/trunk/http-common/src/java/org/apache/http/impl/NIOHttpDataTransmitter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jakarta/httpclient/trunk/http-common/src/test/org/apache/http/impl/TestNIOHttpTransmitterAndReceiver.java
URL: http://svn.apache.org/viewcvs/jakarta/httpclient/trunk/http-common/src/test/org/apache/http/impl/TestNIOHttpTransmitterAndReceiver.java?view=auto&rev=158844
==============================================================================
--- jakarta/httpclient/trunk/http-common/src/test/org/apache/http/impl/TestNIOHttpTransmitterAndReceiver.java
(added)
+++ jakarta/httpclient/trunk/http-common/src/test/org/apache/http/impl/TestNIOHttpTransmitterAndReceiver.java
Wed Mar 23 14:14:51 2005
@@ -0,0 +1,155 @@
+/*
+ * $HeadURL$
+ * $Revision$
+ * $Date$
+ * ====================================================================
+ *
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.channels.Channels;
+
+import org.apache.http.HttpDataReceiver;
+import org.apache.http.HttpDataTransmitter;
+
+import junit.framework.*;
+
+/**
+ * Simple tests for {@link NIOHttpDataTransmitter} and {@link NIOHttpDataReceiver}.
+ *
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * 
+ * @version $Id$
+ */
+public class TestNIOHttpTransmitterAndReceiver extends TestCase {
+
+    // ------------------------------------------------------------ Constructor
+    public TestNIOHttpTransmitterAndReceiver(String testName) {
+        super(testName);
+    }
+
+    // ------------------------------------------------------------------- Main
+    public static void main(String args[]) {
+        String[] testCaseName = { TestNIOHttpTransmitterAndReceiver.class.getName() };
+        junit.textui.TestRunner.main(testCaseName);
+    }
+
+    // ------------------------------------------------------- TestCase Methods
+
+    public static Test suite() {
+        return new TestSuite(TestNIOHttpTransmitterAndReceiver.class);
+    }
+    
+    public void testBasicReadWriteLine() throws Exception {
+        
+        String[] teststrs = new String[5];
+        teststrs[0] = "Hello";
+        teststrs[1] = "This string should be much longer than the size of the output buffer
" +
+                "which is only 16 bytes for this test";
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < 15; i++) {
+            buffer.append("123456789 ");
+        }
+        buffer.append("and stuff like that");
+        teststrs[2] = buffer.toString();
+        teststrs[3] = "";
+        teststrs[4] = "And goodbye";
+        
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        HttpDataTransmitter transmitter = 
+            new NIOHttpDataTransmitter(Channels.newChannel(out), 16); 
+        for (int i = 0; i < teststrs.length; i++) {
+            transmitter.writeLine(teststrs[i]);
+        }
+        transmitter.flush();
+        
+        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+        HttpDataReceiver receiver = 
+            new NIOHttpDataReceiver(Channels.newChannel(in), 16);
+
+        for (int i = 0; i < teststrs.length; i++) {
+            assertEquals(teststrs[i], receiver.readLine());
+        }
+        assertNull(receiver.readLine());
+        assertNull(receiver.readLine());
+    }
+
+    public void testComplexReadWriteLine() throws Exception {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        HttpDataTransmitter transmitter = 
+            new NIOHttpDataTransmitter(Channels.newChannel(out), 16); 
+        transmitter.write(new byte[] {'a', '\n'});
+        transmitter.write(new byte[] {'\r', '\n'});
+        transmitter.write(new byte[] {'\r', '\r', '\n'});
+        transmitter.write(new byte[] {'\n'});
+        transmitter.flush();
+
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < 14; i++) {
+            buffer.append("a");
+        }
+        String s1 = buffer.toString();
+        buffer.append("\r\n");
+        transmitter.write(buffer.toString().getBytes("US-ASCII"));
+        transmitter.flush();
+
+        buffer.setLength(0);
+        for (int i = 0; i < 15; i++) {
+            buffer.append("a");
+        }
+        String s2 = buffer.toString();
+        buffer.append("\r\n");
+        transmitter.write(buffer.toString().getBytes("US-ASCII"));
+        transmitter.flush();
+
+        buffer.setLength(0);
+        for (int i = 0; i < 16; i++) {
+            buffer.append("a");
+        }
+        String s3 = buffer.toString();
+        buffer.append("\r\n");
+        transmitter.write(buffer.toString().getBytes("US-ASCII"));
+        transmitter.flush();
+
+        transmitter.write(new byte[] {'a'});
+        transmitter.flush();
+        
+        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+        HttpDataReceiver receiver = 
+            new NIOHttpDataReceiver(Channels.newChannel(in), 16);
+
+        assertEquals("a", receiver.readLine());
+        assertEquals("", receiver.readLine());
+        assertEquals("\r", receiver.readLine());
+        assertEquals("", receiver.readLine());
+        assertEquals(s1, receiver.readLine());
+        assertEquals(s2, receiver.readLine());
+        assertEquals(s3, receiver.readLine());
+        assertEquals("a", receiver.readLine());
+        assertNull(receiver.readLine());
+        assertNull(receiver.readLine());
+    }
+}

Propchange: jakarta/httpclient/trunk/http-common/src/test/org/apache/http/impl/TestNIOHttpTransmitterAndReceiver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/httpclient/trunk/http-common/src/test/org/apache/http/impl/TestNIOHttpTransmitterAndReceiver.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/httpclient/trunk/http-common/src/test/org/apache/http/impl/TestNIOHttpTransmitterAndReceiver.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message