javacommons/gcfprotocols/http/javasrc.s60/com/nokia/mj/impl/http/HttpConnectionNative.java
branchRCL_3
changeset 19 04becd199f91
child 24 0fd27995241b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javacommons/gcfprotocols/http/javasrc.s60/com/nokia/mj/impl/http/HttpConnectionNative.java	Tue Apr 27 16:30:29 2010 +0300
@@ -0,0 +1,1506 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+
+package com.nokia.mj.impl.http;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import javax.microedition.io.HttpConnection;
+import com.nokia.mj.impl.gcf.utils.URI;
+import com.nokia.mj.impl.rt.support.Jvm;
+import com.nokia.mj.impl.utils.Logger;
+import com.nokia.mj.impl.gcf.utils.DateUtil;
+import com.nokia.mj.impl.gcf.utils.NativeError;
+import com.nokia.mj.impl.http.BlockingOperation;
+import com.nokia.mj.impl.rt.support.Finalizer;
+import com.nokia.mj.impl.connectionmanager.ConnectionManager;
+import com.nokia.mj.impl.connectionmanager.AccessPoint;
+import com.nokia.mj.impl.rt.support.ApplicationUtils;
+import com.nokia.mj.impl.rt.support.ApplicationInfo;
+import com.nokia.mj.impl.rt.support.ShutdownListener;
+
+/**
+ * An HttpConection class implementing an HTTP 1.1 client connection using the
+ * native symbian http client ap
+ *
+ */
+public class HttpConnectionNative implements HttpConnection,
+        NativeHttpByteSource
+{
+    protected int iMode;
+    protected int iState;
+    protected boolean iClosed;
+    protected URI iUri;
+    protected String iRef;
+    protected String iRequestMethod;
+    protected Hashtable iRequestProperties;
+    protected int iResponseCode;
+    protected String iResponseMessage;
+    protected Hashtable iReplyHeaders;
+    protected Vector iReplyHeaderKeys;
+    protected long iLength;
+    protected DataInputStream iInputStream;
+    protected boolean iInputStreamOpened = false;
+    protected NativeHttpInputStream iNativeHttpInputStream;
+    protected ByteArrayOutputStream iPostedDataStream;
+    protected DataOutputStream iOutputStream;
+    protected boolean iOutputStreamOpened = false;
+    protected char[] iLineBuffer;
+    protected int iLineBufferLength;
+    protected int iLineLength;
+
+    // States
+    protected final static int INVALID_STATE = 0;
+    protected final static int SETUP = 1;
+    protected final static int CONNECTED = 2;
+    protected final static int REQUEST_HEADERS_SENT = 3;
+    protected final static int REQUEST_SENT = 4;
+    protected final static int REPLY_RECEIVED = 5;
+
+    // Character constants
+    protected final static char HASH = '#';
+    protected final static int QUESTION_MARK = 63;
+    protected final static int CR = 13;
+    protected final static int LF = 10;
+    protected final static int SP = 32;
+    protected final static int EOF = -1;
+
+    // Misc. Constants
+    protected final static byte[] CRLF = { CR, LF };
+    protected final static int DEFAULT_LINE_BUFFER_SIZE = 128;
+    protected final static String VERSION_1_1 = "1.1";
+    protected final static String VERSION_1_0 = "1.0";
+    protected final static String NATIVESEPERATOR = ";;";
+    protected final static String DEFAULT_PATH = "/"; // NOI8N
+    protected final static int DEFAULT_PORT = 80;
+    protected final static String PROTOCOL_SUFFIX = "://";
+
+    // 'Well-known' Header names
+    protected final static String CONNECTION = "Connection"; // NOI8N
+    protected final static String CONTENT_ENCODING = "Content-Encoding"; // NOI8N
+    protected final static String CONTENT_LENGTH = "Content-Length"; // NOI8N
+    protected final static String CONTENT_TYPE = "Content-Type"; // NOI8N
+    protected final static String DATE = "Date"; // NOI8N
+    protected final static String HOST = "Host"; // NOI8N
+    protected final static String LAST_MODIFIED = "Last-Modified"; // NOI8N
+    protected final static String EXPIRES = "Expires"; // NOI8N
+    protected final static String TRANSFER_ENCODING = "Transfer-Encoding"; // NOI8N
+    protected final static String USER_AGENT = "User-Agent";
+
+    // 'Well-known' Header values
+    protected final static String UNTRUSTED_1_0 = "UNTRUSTED/1.0";
+
+    // Connection value
+    protected final static String CLOSE = "close"; // NOI8N
+
+    // Http-native handle
+    private static int iNativeHttpSessionHandle = 0;
+
+    protected int iNativeTransactionHande = 0;
+    final BlockingOperation iTransactionBlock;
+
+    // Custom lock for synchronizing all read operations.
+    protected final BlockingOperation iNativeDataReadyForRead;
+
+    private Finalizer iFinalizer;
+    protected ConnectionManager iCmInstance = null;
+    protected AccessPoint iApn = null;
+    private static boolean iIsSessionDeleted = false;
+    private boolean iTrustedSuite = true;
+    private int iRespTimeOut = -1;
+    private int iRetries = 0;
+
+    static
+    {
+        try
+        {
+            Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                       "loading http from HttpConnectionNative.java");
+            Jvm.loadSystemLibrary("javahttp");
+            Logger.LOG(Logger.ESOCKET, Logger.EInfo, "javahttp loaded");
+        }
+        catch (Exception e)
+        {
+            Logger.ELOG(Logger.ESOCKET, e.toString());
+        }
+    }
+
+    public HttpConnectionNative(int aNativeHttpSessionHandle, String aName,
+                                int aMode, boolean aTimeouts, ConnectionManager cm,
+                                AccessPoint apnInfo, int aResponseTimeout) throws IOException
+    {
+        Logger.PLOG(Logger.ESOCKET, "New HTTP connection to URL: " + aName);
+        Logger.ILOG(Logger.ESOCKET, "+++++ HttpConnectionNative new, this ="
+                    + this);
+
+        int index;
+        iCmInstance = cm;
+        iApn = apnInfo;
+        index = aName.indexOf(HASH);
+        iRespTimeOut = aResponseTimeout; // setting of timeout value for http response.
+        // specific scenario identified in case of java installer.
+
+        // Handle to shared native RHttpSession
+        if (iNativeHttpSessionHandle == 0)
+        {
+            // only one session per application
+            creatHttpSession();
+        }
+
+        if (index != -1)
+        {
+            iRef = aName.substring(index + 1);
+            aName = aName.substring(0, index);
+        }
+
+        iUri = new URI(aName);
+        final String host = iUri.getHost();
+        if (host == null || host.equals(""))
+        {
+            throw new IllegalArgumentException("Invalid url: " + aName);
+        }
+
+        iMode = aMode;
+        iState = SETUP;
+        iRequestMethod = GET;
+        iRequestProperties = new Hashtable();
+        iReplyHeaders = new Hashtable();
+        iReplyHeaderKeys = new Vector();
+        iLength = -1;
+
+        // Blocking operations to allow async native transactions
+        iTransactionBlock = new BlockingOperation();
+        iNativeDataReadyForRead = new BlockingOperation();
+        iNativeDataReadyForRead.setResult(BlockingOperation.BLOCKED);
+        iFinalizer = registerForFinalization();        
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- HttpConnectionNative new ");
+
+        ApplicationInfo appInfo = ApplicationInfo.getInstance();
+        String runtime = appInfo.getRuntimeType();
+        boolean  midpRuntime = true;
+
+        if (runtime == ApplicationInfo.RUNTIME_INSTALLER)
+        {
+            midpRuntime = false;
+        }
+        else
+        {
+            // add the untrusted header for untrusted midlet suite
+            if ((ApplicationInfo.getInstance().getProtectionDomain()
+                    .equals(ApplicationInfo.UNIDENTIFIED_THIRD_PARTY_DOMAIN)) == true)
+            {
+                iTrustedSuite = false;
+                iRequestProperties.put(USER_AGENT, UNTRUSTED_1_0);
+            }
+        }
+        // get the default User-Agent headers and add to hashtable
+        final String agent = _getUserAgentHeaderValue(midpRuntime);
+        if (agent != null)
+        {
+            if (iTrustedSuite == false)
+            {
+                String result = agent + ' ' + UNTRUSTED_1_0;
+                iRequestProperties.put(USER_AGENT, result);
+            }
+            else
+            {
+                iRequestProperties.put(USER_AGENT, agent);
+            }
+        }
+    }
+
+    Finalizer registerForFinalization()
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "++HttpConnectionNative::registerForFinalization ");
+        return new Finalizer()
+        {
+            public void finalizeImpl()
+            {
+                doFinalize();
+            }
+        };
+    }
+
+
+    public void doFinalize()
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "++HttpConnectionNative::doFinalize ");
+        try
+        {
+            close();
+        }
+        catch(Exception e)
+        {
+ 	
+        }
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "--HttpConnectionNative::doFinalize ");
+    }
+
+    public void creatHttpSession() throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "++HttpConnectionNative::creatHttpSession()");
+        int[] retval = new int[2];
+        int handle;
+        int err;
+        if (iApn != null)
+        {
+            handle = _createHttpSession(0, iApn.getType(), iApn.getNapId(),
+                                        retval);
+        }
+        else
+        {
+            handle = _createHttpSession(0, -1, -1, retval);
+        }
+        iNativeHttpSessionHandle = NativeError.check(handle);
+        if (retval[0] < 0)
+        {
+            throw new IOException(
+                "Unable to open http connection.Creation of Native peer failed.");
+        }
+        if (retval[1] < 0)
+        {
+            // reset connection manager
+            iCmInstance.reset();
+        }
+
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "--HttpConnectionNative::creatHttpSession()");
+    }
+
+    /**
+     * Please refer to Jsr 118
+     */
+    public synchronized void close() throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "++++++++++ close()");
+        if (iClosed)
+        {
+            return;
+        }
+        iClosed = true;
+        if ((iInputStream == null) && (iOutputStream == null)
+                && (iState != SETUP))
+        {
+            if (iNativeHttpInputStream != null)
+            {
+                iNativeHttpInputStream.close();
+                iNativeHttpInputStream = null;
+            }
+            closeConnection();
+        }
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "------- close()");
+        return;
+    }
+
+    /**
+     * Please refer to Jsr 118
+     */
+    public synchronized DataInputStream openDataInputStream()
+    throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ openDataInputStream()");
+        // prevents multiple read
+        synchronized (iNativeDataReadyForRead.getLock())
+        {
+            ensureOpen("openDataInputStream");
+            if (iInputStreamOpened)
+            {
+                throwIOException("Input stream is already opened");
+            }
+            ensureResponse();
+            iInputStream = makeDataInputStream();
+            iInputStreamOpened = true;
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "-
+            // openDataInputStream()" );
+            return iInputStream;
+        }
+    }
+
+    /**
+     * Please refer to Jsr 118
+     */
+    public synchronized InputStream openInputStream() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ openInputStream()" );
+        final InputStream result = openDataInputStream();
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- openInputStream()" );
+        return result;
+    }
+
+
+    /**
+     * Please refer to Jsr 118
+     */
+
+    public synchronized DataOutputStream openDataOutputStream()
+    throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ openDataOutputStream()"
+        // );
+        ensureOpen("openDataOutputStream");
+        if (iOutputStreamOpened)
+        {
+            throwIOException("Output stream is already opened");
+        }
+        makeDataOutputStream();
+        iOutputStreamOpened = true;
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- openDataOutputStream()"
+        // );
+        return iOutputStream;
+    }
+
+    /**
+     * Please refer to Jsr 118
+     */
+    public synchronized OutputStream openOutputStream() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ openOutputStream()" );
+        final OutputStream result = openDataOutputStream();
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- openOutputStream()" );
+        return result;
+    }
+
+    /**
+     * Please refer to Jsr 118
+     */
+    public synchronized String getEncoding()
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getEncoding()" );
+        String result = null;
+        try
+        {
+            result = getHeaderField(CONTENT_ENCODING);
+        }
+        catch (IOException ex)
+        {
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getEncoding() returns
+            // null" );
+            return null;
+        }
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getEncoding()" );
+        return result;
+    }
+
+    /**
+     * Please refer to Jsr 118
+     */
+    public synchronized long getLength()
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getLength()");
+        try
+        {
+            ensureOpen("getLength");
+            ensureResponse();
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getLength() : " +
+            // iLength);
+            return iLength;
+        }
+        catch (IOException ex)
+        {
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getLength() set -1
+            // IOException: "+ ex.toString() );
+            return -1;
+        }
+    }
+
+    /**
+     * Please refer to Jsr 118
+     */
+    public synchronized String getType()
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getType()" );
+        String result = null;
+        try
+        {
+            result = getHeaderField(CONTENT_TYPE);
+        }
+        catch (IOException ex)
+        {
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getType() returns
+            // null" );
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getType() NULL");
+            return null;
+        }
+        //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getType()" + result);
+        return result;
+    }
+
+    // HttpConnection interface implementation
+
+    public synchronized long getDate() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getDate()" );
+        final long result = getHeaderFieldDate(DATE, 0);
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getDate()" );
+        return result;
+    }
+
+    public synchronized long getExpiration() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getExpiration()" );
+        final long result = getHeaderFieldDate(EXPIRES, 0);
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getExpiration()" );
+        return result;
+    }
+
+    public String getFile()
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getFile()" );
+        String result = iUri.getPath();
+        if (result.length() == 0)
+            result = null;
+        else
+            result = "/" + result;
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getFile: " + result );
+        return result;
+    }
+
+    public synchronized String getHeaderField(int aIndex) throws IOException
+    {
+        ensureOpen("getHeaderField");
+        ensureResponse();
+        if (aIndex >= iReplyHeaderKeys.size() || aIndex < 0)
+        {
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getHeaderField(int)
+            // returns null" );
+            return null;
+        }
+        final String result = (String) iReplyHeaders.get(iReplyHeaderKeys
+                              .elementAt(aIndex));
+        if (result != null)
+        {
+            int length = result.length();
+            String keys = (String) iReplyHeaderKeys.elementAt(aIndex);
+            if (keys.equalsIgnoreCase("Set-Cookie") == true)
+            {
+                // first cookie is discarded
+                if ((result.charAt(0)) == ',')
+                {
+                    throw new IOException(
+                        "cookie size too large - hence discarded");
+                }
+
+                // last cookie is discarded
+                if ((result.charAt(length - 2)) == ',')
+                {
+                    throw new IOException(
+                        "cookie size too large - hence discarded");
+                }
+
+                int index = result.indexOf(", ,", 0);
+                if (index != -1)
+                {
+                    throw new IOException(
+                        "cookie size too large - hence discarded");
+                }
+            }
+        }
+        //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getHeaderField(int)" );
+        //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getHeaderField(int): "  + result);
+        return result;
+    }
+
+    public synchronized String getHeaderField(String aName) throws IOException
+    {
+        ensureOpen("getHeaderField");
+        ensureResponse();
+        if (aName == null)
+        {
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "--
+            // getHeaderField(String) returns null" );
+            return null;
+        }
+        final String result = (String) iReplyHeaders.get(aName.toLowerCase());
+
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getHeaderField(String): "
+                   + result);
+        if (result != null)
+        {
+            int length = result.length();
+            if (aName.equalsIgnoreCase("Set-Cookie") == true)
+            {
+                // first cookie is discarded
+                if ((result.charAt(0)) == ',')
+                {
+                    throw new IOException(
+                        "cookie size too large - hence discarded");
+                }
+
+                // last cookie is discarded
+                if ((result.charAt(length - 2)) == ',')
+                {
+                    throw new IOException(
+                        "cookie size too large - hence discarded");
+                }
+
+                int index = result.indexOf(", ,", 0);
+                if (index != -1)
+                {
+                    throw new IOException(
+                        "cookie size too large - hence discarded");
+                }
+            }
+        }
+        return result;
+    }
+
+    public synchronized long getHeaderFieldDate(String aName, long aDfault)
+    throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "+ getHeaderFieldDate(String, long)");
+        String value;
+
+        value = getHeaderField(aName);
+        if (value != null)
+        {
+            final long result = parseDate(value);
+            Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                       "- getHeaderFieldDate(String, long)");
+            return result;
+        }
+        else
+        {
+            Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                       "- getHeaderFieldDate(String, long) return aDfault");
+            return aDfault;
+        }
+    }
+
+    public synchronized int getHeaderFieldInt(String aName, int aDfault)
+    throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "+ getHeaderFieldInt(String, int) , name = " + aName
+                   + "default = " + aDfault);
+        String value;
+
+        value = getHeaderField(aName);
+        if (value == null)
+        {
+            Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                       "- getHeaderFieldInt(String, int) returns aDfault");
+            return aDfault;
+        }
+        else
+        {
+            try
+            {
+                final int result = Integer.parseInt(value);
+                // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "-
+                // getHeaderFieldInt(String, int)" );
+                return result;
+            }
+            catch (NumberFormatException nfe)
+            {
+                //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getHeaderFieldInt (String, int): " + aDfault);
+                return aDfault;
+            }
+        }
+    }
+
+    public synchronized String getHeaderFieldKey(int aIndex) throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getHeaderFieldKey(int)"
+        // );
+        ensureOpen("getHeaderFieldKey");
+        ensureResponse();
+        if (aIndex >= iReplyHeaderKeys.size() || aIndex < 0)
+        {
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "-
+            // getHeaderFieldKey(int) returns null" );
+            return null;
+        }
+        final String result = (String) iReplyHeaderKeys.elementAt(aIndex);
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getHeaderFieldKey(int)"
+        // );
+        return result;
+    }
+
+    public String getHost()
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getHost()" );
+        final String result = iUri.getHost();
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getHost()" );
+        return result;
+    }
+
+    public synchronized long getLastModified() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getLastModified()" );
+        final long result = getHeaderFieldDate(LAST_MODIFIED, 0);
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getLastModified()" );
+        return result;
+    }
+
+    public int getPort()
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getPort()" );
+        final String temp = iUri.getPort();
+        if (temp.length() != 0)
+        {
+            try
+            {
+                final int result = Integer.parseInt(temp);
+                // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getPort()" );
+                return result;
+            }
+            catch (NumberFormatException ex)
+            {
+                // do nothing, return default port
+            }
+        }
+        //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getPort() returns default port" );
+        return DEFAULT_PORT;
+    }
+
+    public String getProtocol()
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getProtocol()" );
+        final String result = iUri.getProtocol();
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getProtocol()" );
+        return result;
+    }
+
+    public String getQuery()
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getQuery()" );
+        String result = iUri.getQuery();
+        if (result.length() == 0)
+            result = null;
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getQuery()" );
+        return result;
+    }
+
+    public String getRef()
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "* getRef()" );
+        return iRef;
+    }
+
+    public final String getRequestMethod()
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "* getRequestMethod()" );
+        return iRequestMethod;
+    }
+
+    public final String getRequestProperty(String aKey)
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+
+        // getRequestProperty(String)" );
+        final String result = (String) iRequestProperties.get(aKey);
+
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "-
+        // getRequestProperty(String)" );
+        return result;
+    }
+
+    public synchronized final int getResponseCode() throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getResponseCode()");
+        ensureOpen("getResponseCode");
+        ensureResponse();
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getResponseCode() : "
+                   + iResponseCode);
+        return iResponseCode;
+    }
+
+    public synchronized String getResponseMessage() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getResponseMessage()" );
+        ensureOpen("getResponseMessage");
+        ensureResponse();
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getResponseMessage() : "
+        // + iResponseMessage);
+        return iResponseMessage;
+    }
+
+    public String getURL()
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getURL()" );
+        final String result = iUri.toString();
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getURL()" );
+        return result;
+    }
+
+    /**
+         * Please refer to Jsr 118
+         */
+    public synchronized void setRequestMethod(String aMethod)
+    throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ setRequestMethod(String)"
+        // );
+        try
+        {
+            if (aMethod == null)
+            {
+                throw new NullPointerException("Request method is null");
+            }
+            ensureOpen("setRequestMethod");
+            if (iState != SETUP)
+            {
+                throwIOException("setRequestMethod method failed.Connection state is already connected");
+            }
+            if ((GET.equals(aMethod) == false)
+                    && (HEAD.equals(aMethod) == false)
+                    && (POST.equals(aMethod) == false))
+            {
+                throwIOException("Invalid or Unsupported request method: "
+                                 + aMethod);
+            }
+            // ignore if an output stream has already been open
+            if (iPostedDataStream == null)
+                iRequestMethod = aMethod;
+        }
+        catch (IOException ex)
+        {
+            Logger.WLOG(Logger.ESOCKET, "setRequestMethod failed", ex);
+            throw ex;
+        }
+        //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- setRequestMethod(String)" );
+        return;
+    }
+
+    /**
+         * Please refer to Jsr 118
+         */
+    public synchronized void setRequestProperty(String aKey, String aValue)
+    throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ setRequestProperty( " +
+        // aKey + " , " + aValue );
+        ensureOpen("setRequestProperty");
+        if (iState != SETUP)
+        {
+            throwIOException("setRequestProperty method failed.Connection state is already connected");
+        }
+
+        // ignore if an outputstream has been open
+        if (iPostedDataStream == null)
+        {
+            if (aKey.equals(USER_AGENT) && !iTrustedSuite)
+            {
+                String val = aValue + ' ' + UNTRUSTED_1_0;
+                iRequestProperties.put(aKey, val);
+            }
+            else
+            {
+                iRequestProperties.put(aKey, aValue);
+            }
+        }
+        //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- setRequestProperty(String, String)" );
+        return;
+    }
+
+    protected void closeConnection() throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "++++++++++++++ Java closeConnection()");
+        if ((iNativeTransactionHande != 0) &&(iNativeHttpSessionHandle!=0))
+        {
+            Logger.LOG(Logger.ESOCKET, Logger.EInfo, "########## Valid handle");
+            _closeTransaction(iNativeTransactionHande);
+            iNativeTransactionHande = 0;
+
+        }
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "-------------- Java closeConnection()");
+    }
+
+    protected void addHeader(String aKey, String aValue)
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ addHeader to reply enum(
+        // " + aKey + " , " + aValue );
+        // Should check for duplicate keys
+        aKey = aKey.toLowerCase();
+        iReplyHeaders.put(aKey, aValue);
+        iReplyHeaderKeys.addElement(aKey);
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- addHeader(String,
+        // String)" );
+        return;
+    }
+
+    protected synchronized void inputStreamClosed() throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "++++++ inputStreamClosed()");
+        synchronized (iNativeDataReadyForRead.getLock())
+        {
+            iInputStream = null;
+            iNativeHttpInputStream = null;
+            if (iClosed && (iOutputStream == null))
+            {
+                closeConnection();
+            }
+        }
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "-------- inputStreamClosed()");
+    }
+
+
+    protected synchronized void outputStreamFlushed() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ outputStreamFlushed()" );
+        switch (iState)
+        {
+        case SETUP:
+        case CONNECTED:
+        case REQUEST_HEADERS_SENT:
+            ensureResponse();
+
+        default:
+            // No-op
+            ;
+        }
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- outputStreamFlushed()" );
+    }
+
+    protected synchronized void outputStreamClosed() throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "++++++++ outputStreamClosed()");
+        iOutputStream = null;
+        iPostedDataStream = null;
+        if (iClosed && iInputStream == null)
+        {
+            closeConnection();
+        }
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "-------- outputStreamClosed()");
+    }
+
+    private synchronized void connect() throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+++++++ Java connect()");
+        if (iNativeTransactionHande <= 0)
+        {
+            final int handle = _createNativeTransaction(
+                                   iNativeHttpSessionHandle, iUri.toString(), iRequestMethod);
+            iNativeTransactionHande = NativeError.check(handle);
+        }
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "-------- connect()");
+    }
+
+    protected void ensureConnected() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ ensureConnected()" );
+        switch (iState)
+        {
+        case SETUP:
+
+            connect();
+            iState = CONNECTED;
+            break;
+
+        default:
+            ;
+        }
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- ensureConnected()" );
+        return;
+    }
+
+    protected void ensureOpen(String aMethodName) throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ ensureOpen()" );
+        if (iClosed)
+        {
+            throwIOException(aMethodName
+                             + " method failed.Connection state is already closed");
+        }
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- ensureOpen()" );
+        return;
+    }
+
+    protected void ensureResponse() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ ensureResponse()" );
+        ensureConnected();
+        switch (iState)
+        {
+        case CONNECTED:
+
+            sendRequest();
+            // Fall Through
+        case REQUEST_HEADERS_SENT:
+        case REQUEST_SENT:
+            getResponse();
+            iState = REPLY_RECEIVED;
+            break;
+
+        default:
+            // No-op
+            ;
+        }
+        //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- ensureResponse()" );
+        return;
+    }
+
+    protected synchronized void getResponse() throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ getResponse()");
+
+        boolean flag = true;
+        String[] rawHeaders = _getResponse(iNativeTransactionHande);
+        if (rawHeaders == null || rawHeaders.length == 0)
+            throwIOException("Invalid http response");
+
+        // First element is the Status Line
+        readStatusLine(rawHeaders[0]);
+        readHeaders(rawHeaders);
+        processReplyHeaders();
+        rawHeaders = null;
+        //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- getResponse()" );
+        return;
+    }
+
+    protected void readStatusLine(String aStatusLine) throws IOException
+    {
+        // Status-Line = HTTP-Version ;; Status-Code ;; Reason-Phrase
+        Logger.PLOG(Logger.ESOCKET, "HTTP response, status line: "
+                    + aStatusLine);
+        // HTTP-Version
+        final int fisrtSepIndex = aStatusLine.indexOf(NATIVESEPERATOR);
+        if (fisrtSepIndex <= 0)
+        {
+            throwIOException("Invalid http response");
+        }
+        String parse = aStatusLine.substring(0, fisrtSepIndex);
+
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, " Server Vesion : " + parse
+        // );
+
+        // Check the server we are connecting to is a http v1.0 or v1.1
+        if (parse.compareTo(VERSION_1_1) != 0
+                && parse.compareTo(VERSION_1_0) != 0)
+        {
+            throwIOException("Invalid http response.Http-version mismatch");
+        }
+
+        // Status Code
+        int nextSepIndex = 0;
+        nextSepIndex = aStatusLine.indexOf(NATIVESEPERATOR, fisrtSepIndex + 2);
+        if (nextSepIndex <= 0)
+        {
+            throwIOException("Invalid http response");
+        }
+        parse = null;
+        parse = aStatusLine.substring(fisrtSepIndex + 2, nextSepIndex);
+        iResponseCode = Integer.parseInt(parse);
+
+        // Reason-Phrase
+
+        iResponseMessage = aStatusLine.substring(nextSepIndex + 2, aStatusLine
+                           .length());
+        if (iResponseMessage.length() <= 0)
+        {
+            throwIOException("Invalid http response");
+        }
+        //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- readStatusLine(InputStream)" );
+    }
+
+    protected long parseDate(String aDateString)
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ parseDate(String)" );
+        final long result = DateUtil.epocDifference(aDateString);
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- parseDate(String)" );
+        return result;
+    }
+
+    protected void processReplyHeaders()
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ processReplyHeaders()" );
+        String value;
+        value = (String) iReplyHeaders.get(CONTENT_LENGTH.toLowerCase());
+        if (value != null)
+        {
+            iLength = Long.parseLong(value);
+        }
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- processReplyHeaders()" );
+    }
+
+    protected void readHeaders(String[] aHeaders) throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ readHeaders()" );
+        // Headers are of the form
+        // valueKey + NATIVESEPERATOR + property
+        String key;
+        String value;
+        String rawHeader = null;
+        final int headersCount = aHeaders.length;
+
+        // First element is status line which we ignore (parsed in
+        // readStatusLine)
+        int sepIndex = -1;
+        for (int ii = 1; ii < headersCount; ++ii)
+        {
+            rawHeader = aHeaders[ii];
+            sepIndex = rawHeader.indexOf(NATIVESEPERATOR);
+            key = rawHeader.substring(0, sepIndex);
+            value = rawHeader.substring(sepIndex + 2, rawHeader.length());
+            addHeader(key, value);
+            rawHeader = null;
+            key = null;
+            value = null;
+            sepIndex = -1;
+        }
+        //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- readHeaders(InputStream)" );
+    }
+
+    protected synchronized void sendRequest() throws IOException
+    {
+        ensureConnected();
+        final int count = iRequestProperties.size();
+        int headerCount = count;
+
+        final Enumeration keyEnum = iRequestProperties.keys();
+        String[] headers = null;
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- headerCount: " +
+        // headerCount );
+        if (headerCount > 0)
+            headers = new String[headerCount];
+
+        String valueKey = null;
+        String property = null;
+        String concat = null;
+        boolean untrustedAdded = false;
+        for (int ii = 0; ii < count; ++ii)
+        {
+            valueKey = (String) keyEnum.nextElement();
+            property = getRequestProperty(valueKey);
+            concat = valueKey + NATIVESEPERATOR + property;
+            headers[ii] = concat;
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "submit : " + ii + " : "
+            // + concat + "concat length:" + concat.length() );
+        }
+
+        byte[] postData = null;
+        int postDataLength = 0;
+        // Data to be sent native
+        if (iPostedDataStream != null)
+        {
+            postData = iPostedDataStream.toByteArray();
+            postDataLength = postData.length;
+        }
+
+        synchronized (iTransactionBlock.getLock())
+        {
+            // Submit the headers and the data at the same time
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ _submitTransaction
+            // headers count : " + (headers!=null ?headers.length:-1 ) );
+            synchronized (iTransactionBlock)
+            {
+                Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                           "before _submitTransaction ");
+                final int err = _submitTransaction(iNativeTransactionHande,
+                                                   headers, postData, postDataLength,iRespTimeOut);
+
+                Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                           "-_submitTransaction +err " + err);
+
+                // Clear tempory byte array and headers array
+                postData = null;
+                headers = null;
+                if (err != 0)
+                    throwIOException("Unable to connect to server.Symbian os error code: "
+                                     + err);
+                iState = REQUEST_SENT;
+                waitForTransaction();
+                Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                           "- sendRequest _submitTransaction");
+            }
+        }
+        if ((iTransactionBlock.getResult() == -18) &&(iRetries < 2))
+        {
+            iRetries++;
+            Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                       "- sendRequest KErrNotReady erroi, calling recursive sendRequest()");
+            sendRequest();
+            Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                       "- sendRequest KErrNotReady erroi, calling recursive sendRequest() returned");
+        }
+    }
+
+    protected void throwIOException(String aMessage) throws IOException
+    {
+        throw new IOException(aMessage);
+    }
+
+    protected void waitForTransaction() throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+waitForTransaction");
+
+        iTransactionBlock.waitForCompletion();
+        final int result = iTransactionBlock.getResult();
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "waitForTransaction result :"
+                   + result);
+        if ((result < 0))
+        {
+            final Integer error = new Integer(result);
+            if ((result == -18))
+            {
+                if (iRetries >=2)
+                {
+                    throwIOException(error.toString());
+                }
+            }
+            else
+            {
+                throwIOException(error.toString());
+            }
+
+        }
+
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "-waitForTransaction");
+    }
+
+    protected DataInputStream makeDataInputStream() throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> makeDataInputStream()<<<<<<<<<<<<<<<<<<<<<<<");
+        DataInputStream result = null;
+        final String transferEncoding;
+
+        final int bytesTotal = (int) getLength();
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "++++++ makeDataInputStream()  getLength():" + bytesTotal);
+
+        if (iNativeHttpInputStream == null)
+            iNativeHttpInputStream = new NativeHttpInputStream(this);
+        result = new PlainDataInputStream(iNativeHttpInputStream);
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, " makeDataInputStream()");
+        return result;
+    }
+
+    protected void makeDataOutputStream() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ makeDataOutputStream()"
+        // );
+        iPostedDataStream = new ByteArrayOutputStream();
+        iOutputStream = new PlainDataOutputStream(iPostedDataStream);
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- makeDataOutputStream()"
+        // );
+    }
+
+    /**
+     * From NativeHttpByteSoure
+     */
+    public void closeByteSource()
+    {
+    }
+
+    /*
+     * Returns the total number of bytes read into the buffer, or -1 if there is
+     * no more data because the end of the stream has been reached.
+     */
+    public synchronized int getBytes(byte[] aBytes, int aLength)
+    throws IOException
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+getBytes()");
+        int result = -1;
+
+        // Prevents multiple read
+        synchronized (iNativeDataReadyForRead.getLock())
+        {
+            synchronized (iNativeDataReadyForRead)
+            {
+                if (iNativeDataReadyForRead.getResult() == BlockingOperation.BLOCKED)
+                {
+                    Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                               "+ getBytes()- ReadOperation.waitForCompletion()");
+                    iNativeDataReadyForRead.waitForCompletion();
+                    Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                               "- getBytes()- ReadOperation.waitForCompletion()");
+                }
+            }
+
+            final int callBackResult = iNativeDataReadyForRead.getResult();
+            if (callBackResult == EOF)
+                return 0; // All data has been read
+            if (callBackResult < 0)
+                throwIOException("Unable to read http response.Symbian os error code: "
+                                 + result);
+            // Block until data has been read from native
+            iNativeDataReadyForRead.setResult(BlockingOperation.BLOCKED);
+            if (iNativeHttpSessionHandle!=0)
+            {
+                result = _getBytes(iNativeTransactionHande, aBytes, aLength);
+                Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ 2 getBytes()- result "
+                           + result);
+            }
+        }
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "-getBytes() + result "
+                   + result);
+        return result;
+    }
+
+    public int available() throws IOException
+    {
+        if ((iClosed == true) && (iInputStream == null)
+                && (iOutputStream == null))
+        {
+            throw new IOException(
+                "Available method failed.Connection is already closed");
+        }
+        return _available(iNativeTransactionHande);
+    }
+
+    /*
+     * Native call back methods
+     */
+    protected void transactionSubmitCallback(int aStatus)
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "+ transactionSubmitCallback Callback");
+        iTransactionBlock.notifyCompleted(aStatus);
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                   "- transactionSubmitCallback Callback");
+    }
+
+    protected void dataReadyForReadCallBack(int aStatus)
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ dataReadyForReadCallBack  "
+                   + aStatus);
+        iNativeDataReadyForRead.notifyCompleted(aStatus);
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- dataReadyForReadCallBack");
+    }
+
+    /*
+     * Native Calls
+     */
+    private native int _createHttpSession(int a, int aType, int aApn,
+                                          int[] retval);
+    private native int _createNativeTransaction(int aNativeHttpSessionHandle,
+            String aUri, String aRequestMethod);
+    private native int _submitTransaction(int aNativeTransactionHande,
+                                          String[] aHeaders, byte[] aPostData, int aPostLength,
+                                          int aRespTimeOut);
+    private native String[] _getResponse(int aNativeTransactionHande);
+    private native int _getBytes(int aNativeTransactionHande, byte[] aBuffer,
+                                 int aLength);
+    private native void _closeTransaction(int aNativeTransactionHande);
+    private native int _available(int aNativeTransactionHande);
+    private native String _getUserAgentHeaderValue(boolean aMidpRuntime);
+
+    /*
+     *
+     * class PlainDataInputStream
+     *
+     */
+    protected class PlainDataInputStream extends DataInputStream
+    {
+        public void close() throws IOException
+        {
+            super.close();
+            inputStreamClosed();
+        }
+
+        PlainDataInputStream(InputStream aIs)
+        {
+            super(aIs);
+        }
+    }
+
+    /*
+     *
+     * class PlainDataOutputStream
+     *
+     */
+    protected class PlainDataOutputStream extends DataOutputStream
+    {
+        private boolean iFlushed = false;
+
+        public void close() throws IOException
+        {
+            if (!iFlushed)
+                flush();
+            super.close();
+            outputStreamClosed();
+        }
+
+        public void flush() throws IOException
+        {
+            super.flush();
+            outputStreamFlushed();
+            iFlushed = true;
+        }
+
+        PlainDataOutputStream(OutputStream aOs)
+        {
+            super(aOs);
+        }
+    }
+
+    class NativeHttpInputStream extends InputStream
+    {
+        private NativeHttpByteSource iByteSource;
+        private byte[] iBuffer;
+        private int iOffset;
+        private int iLength;
+        // 6K buffer to read from native http
+        private final static int BUFFER_SIZE = 6144;
+        private boolean iIsClosed;
+
+        NativeHttpInputStream(NativeHttpByteSource aByteSource)
+        {
+            // Bytesource length may be -1 indicating CHUNKED (size unknown)
+            iByteSource = aByteSource;
+            iBuffer = new byte[BUFFER_SIZE];
+            iIsClosed = false;
+        }
+
+        public synchronized void close() throws IOException
+        {
+            iIsClosed = true;
+            iByteSource.closeByteSource();
+        }
+
+        public synchronized int read() throws IOException
+        {
+            final int BYTEPADDER = 0xFF;
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo,"NativeInputStream.read()
+            // iOffset, "+ iOffset + "iLength " + iLength);
+            throwIfClosed();
+
+            if (iOffset == iLength)
+            {
+                // Logger.LOG(Logger.ESOCKET,
+                // Logger.EInfo,"NativeInputStream.read() iOffset == iLength
+                // getBytes: iLength " + iLength );
+                getBytes();
+            }
+
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo,"NativeInputStream.read()
+            // 2 iOffset, "+ iOffset + " iLength " + iLength);
+            if (iLength > 0)
+            {
+                return iBuffer[iOffset++] & BYTEPADDER;
+            }
+            else
+            {
+                Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                           "NativeInputStream.read(): return -1 ");
+                return EOF;
+            }
+        }
+
+        public synchronized int read(byte[] aBytes, int aOffset, int aLength)
+        throws IOException
+        {
+            // Logger.LOG(Logger.ESOCKET,
+            // Logger.EInfo,"+NativeInputStream.read(,,,) aOffset, "+ aOffset +
+            // "aLength" + aLength);
+
+            throwIfClosed();
+
+            int nBytes = 0;
+            while (nBytes < aLength)
+            {
+                // Logger.LOG(Logger.ESOCKET,
+                // Logger.EInfo,"NativeInputStream.read(,,,) nBytes, "+ nBytes);
+                int count = readInner(aBytes, aOffset + nBytes, aLength
+                                      - nBytes);
+                // Logger.LOG(Logger.ESOCKET,
+                // Logger.EInfo,"NativeInputStream.read(,,,) count = readInner,
+                // "+ count);
+                if (count < 0)
+                {
+                    nBytes = (nBytes == 0) ? -1 : nBytes;
+                    break;
+                }
+                nBytes += count;
+            }
+            Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                       "- NativeInputStream.read(,,,) return nBytes, " + nBytes);
+            return nBytes;
+        }
+
+        private int readInner(byte[] aBytes, int aOffset, int aLength)
+        throws IOException
+        {
+            // Logger.LOG(Logger.ESOCKET,
+            // Logger.EInfo,"NativeInputStream.readInner() iOffset: "+ iOffset +
+            // "iLength: " + iLength);
+            if (iOffset == iLength)
+            {
+                getBytes();
+            }
+            if (iLength == 0)
+            {
+                return EOF;
+            }
+
+            int nBytesLeft = iLength - iOffset;
+            // Logger.LOG(Logger.ESOCKET,
+            // Logger.EInfo,"NativeInputStream.readInner() : nBytesLeft "+
+            // nBytesLeft);
+            int nBytesToCopy = aLength > nBytesLeft ? nBytesLeft : aLength;
+            System.arraycopy(iBuffer, iOffset, aBytes, aOffset, nBytesToCopy);
+            iOffset += nBytesToCopy;
+            return nBytesToCopy;
+        }
+
+        private void getBytes() throws IOException
+        {
+            int nBytes = iByteSource.getBytes(iBuffer, iBuffer.length);
+
+            if (nBytes == -1)
+            {
+                throw new IOException();
+            }
+            iLength = nBytes;
+            iOffset = 0;
+        }
+
+
+        /**
+         * Returns the number of bytes that can be read (or skipped over) from
+         * this input stream without blocking by the next caller of a method for
+         * this input stream. The next caller might be the same thread or
+         * another thread.
+         *
+         * @return the number of bytes that can be read from this input stream
+         *         without blocking.
+         * @exception IOException
+         *                if an I/O error occurs.
+         */
+        public int available() throws IOException
+        {
+            throwIfClosed();
+            return iByteSource.available();
+        }
+
+        private void throwIfClosed() throws IOException
+        {
+            if (iIsClosed)
+            {
+                throw new IOException("Connection is already closed");
+            }
+        }
+    }
+}