javacommons/gcfprotocols/http/javasrc.s60/com/nokia/mj/impl/http/HttpConnectionNative.java
changeset 80 d6dafc5d983f
parent 49 35baca0e7a2e
child 87 1627c337e51e
--- a/javacommons/gcfprotocols/http/javasrc.s60/com/nokia/mj/impl/http/HttpConnectionNative.java	Mon Oct 04 11:29:25 2010 +0300
+++ b/javacommons/gcfprotocols/http/javasrc.s60/com/nokia/mj/impl/http/HttpConnectionNative.java	Fri Oct 15 12:29:39 2010 +0300
@@ -78,6 +78,7 @@
     protected final static int REQUEST_HEADERS_SENT = 3;
     protected final static int REQUEST_SENT = 4;
     protected final static int REPLY_RECEIVED = 5;
+    protected final static int PARTIAL_REQUEST_SENT = 6;
 
     // Character constants
     protected final static char HASH = '#';
@@ -131,6 +132,12 @@
     private boolean iTrustedSuite = true;
     private int iRespTimeOut = -1;
     private int iRetries = 0;
+    private int iRequestBodyLen = -1;
+    private int iTotalPostDataLen = 0;
+    private boolean iEndOfRequest = false;
+    private boolean iChunkedTransfer = false;
+
+    protected final BlockingOperation iPostDataBlock;
 
     static
     {
@@ -194,6 +201,7 @@
         iTransactionBlock = new BlockingOperation();
         iNativeDataReadyForRead = new BlockingOperation();
         iNativeDataReadyForRead.setResult(BlockingOperation.BLOCKED);
+        iPostDataBlock = new BlockingOperation();
         iFinalizer = registerForFinalization();
         Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- HttpConnectionNative new ");
 
@@ -814,6 +822,20 @@
             {
                 iRequestProperties.put(aKey, aValue);
             }
+            if (aKey.equals(TRANSFER_ENCODING))
+            {
+                String tmp = aValue.toLowerCase();
+                if (tmp.equals("chunked"))
+                {
+                    iChunkedTransfer = true;
+                    iRequestBodyLen = -1;    // as per RFC, precedence should be give to "transfer-encoding" when "content-length" is also set.
+                }
+            }
+            if (aKey.equals(CONTENT_LENGTH))
+            {
+                iRequestBodyLen = Integer.parseInt(aValue);
+
+            }
         }
         //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- setRequestProperty(String, String)" );
         return;
@@ -864,20 +886,114 @@
     }
 
 
-    protected synchronized void outputStreamFlushed() throws IOException
+    private void waitForPostDataCompletion()
     {
-        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ outputStreamFlushed()" );
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+waitForPostDataCompletion");
+        iPostDataBlock.waitForCompletion();
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "-waitForPostDataCompletion");
+
+    }
+
+    private synchronized void sendPartialRequest() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "HTTP sendPartialRequest() ");
         switch (iState)
         {
         case SETUP:
         case CONNECTED:
-        case REQUEST_HEADERS_SENT:
-            ensureResponse();
+
+            synchronized (iPostDataBlock.getLock())
+            {
+                synchronized (iPostDataBlock)
+                {
+                    sendRequest(true);
+                    iPostedDataStream.reset();
+                    waitForPostDataCompletion();
+                }
+            }
             break;
 
-        default:
-            // No-op
-            ;
+        case PARTIAL_REQUEST_SENT :
+
+            byte[] postData = iPostedDataStream.toByteArray();
+            iPostedDataStream.reset();
+            int len = -1;
+            if (postData != null)
+            {
+                len = postData.length;
+                iTotalPostDataLen = iTotalPostDataLen + len;
+            }
+            // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "HTTP content-len = "+iRequestBodyLen + " totalflushed = "+ iTotalPostDataLen +" os len = "+ postData.length);
+            if (iTotalPostDataLen == iRequestBodyLen)
+            {
+                iEndOfRequest = true;
+            }
+
+            synchronized (iPostDataBlock.getLock())
+            {
+                synchronized (iPostDataBlock)
+                {
+                    int err = _postData(iNativeTransactionHande, postData, len, iEndOfRequest);
+
+                    if (err != 0)
+                    {
+                        throwIOException("Unable to write data .Symbian os error code: "
+                                         + err);
+                    }
+                    if (iEndOfRequest == false)
+                    {
+                        // wait until http stack consumes the data
+                        waitForPostDataCompletion();
+                    }
+                    else
+                    {
+                        // request complete, wait until the first chunk of response header arrives
+                        synchronized (iTransactionBlock.getLock())
+                        {
+                            synchronized (iTransactionBlock)
+                            {
+                                waitForTransaction();
+                            }
+                        }
+
+                    }  // end else
+                } // end syn
+
+            } // end syn getlock
+
+            break;
+        }
+
+    }
+
+    protected synchronized void outputStreamFlushed() throws IOException
+    {
+        int postDataLength = iPostedDataStream.size();
+
+
+        if ((iRequestBodyLen > postDataLength) || (iChunkedTransfer == true))
+        {
+            // chunked http request will be sent in two cases
+            // 1) application sets "Content-Lenght" request header and writes to output stream multiple times using flush
+            //     like a sequence of os.write(); os.flush() operations
+            // 2) application sets "Transfer-Encoding" request header and writes to ouput stream multiple times using flush
+            sendPartialRequest();
+
+        }
+        else
+        {
+            switch (iState)
+            {
+            case SETUP:
+            case CONNECTED:
+            case REQUEST_HEADERS_SENT:
+                ensureResponse();
+                break;
+
+            default:
+                // No-op
+                ;
+            }
         }
         // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- outputStreamFlushed()" );
     }
@@ -938,15 +1054,55 @@
         return;
     }
 
+    private void waitForResponse() throws IOException
+    {
+        // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "HTTP flush case, waitForResponse: total len =  "+iRequestBodyLen + " totalflushed = "+ iTotalPostDataLen );
+        if (iEndOfRequest == false)
+        {
+            // chunked http request was sent, but end of request not sent
+            // only in case of "transfer-encoding" request header, control will come here
+            iEndOfRequest = true;
+            byte[] postData = iPostedDataStream.toByteArray();
+            iPostedDataStream.reset();
+            int len = -1;
+            if (postData != null)
+            {
+                len = postData.length;
+                // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "More data left in os, len = "+len);
+                iTotalPostDataLen = iTotalPostDataLen + len;
+            }
+            int err = _postData(iNativeTransactionHande, postData, len , iEndOfRequest);
+            if (err != 0)
+            {
+                throwIOException("Unable to write data .Symbian os error code: "
+                                 + err);
+            }
+            synchronized (iTransactionBlock.getLock())
+            {
+                synchronized (iTransactionBlock)
+                {
+                    waitForTransaction();
+                }
+            }
+        }
+        getResponse();
+        iState = REPLY_RECEIVED;
+
+    }
+
     protected void ensureResponse() throws IOException
     {
         // Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ ensureResponse()" );
         ensureConnected();
         switch (iState)
         {
+        case PARTIAL_REQUEST_SENT:
+            waitForResponse();
+            break;
+
         case CONNECTED:
 
-            sendRequest();
+            sendRequest(false);      // sendRequest() will block until the first chunk of http response arrives from the server.
             // Fall Through
         case REQUEST_HEADERS_SENT:
         case REQUEST_SENT:
@@ -1073,9 +1229,10 @@
         //Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- readHeaders(InputStream)" );
     }
 
-    protected synchronized void sendRequest() throws IOException
+    protected synchronized void sendRequest(boolean aPartialDataFlag) throws IOException
     {
         ensureConnected();
+        Logger.PLOG(Logger.ESOCKET, "HTTP sendRequest() , Flag : "  + aPartialDataFlag);
         final int count = iRequestProperties.size();
         int headerCount = count;
 
@@ -1107,6 +1264,7 @@
         {
             postData = iPostedDataStream.toByteArray();
             postDataLength = postData.length;
+
         }
 
         synchronized (iTransactionBlock.getLock())
@@ -1119,7 +1277,7 @@
                 Logger.LOG(Logger.ESOCKET, Logger.EInfo,
                            "before _submitTransaction ");
                 final int err = _submitTransaction(iNativeTransactionHande,
-                                                   headers, postData, postDataLength,iRespTimeOut);
+                                                   headers, postData, postDataLength,iRespTimeOut, aPartialDataFlag);
 
                 Logger.LOG(Logger.ESOCKET, Logger.EInfo,
                            "-_submitTransaction +err " + err);
@@ -1130,20 +1288,32 @@
                 if (err != 0)
                     throwIOException("Unable to connect to server.Symbian os error code: "
                                      + err);
-                iState = REQUEST_SENT;
-                waitForTransaction();
+                if (aPartialDataFlag == true)
+                {
+                    iState = PARTIAL_REQUEST_SENT;
+                    iTotalPostDataLen = iTotalPostDataLen + postDataLength;
+                }
+                else
+                {
+                    // block and wait for response headers only when it a complete request
+                    iState = REQUEST_SENT;
+                    waitForTransaction();
+                }
                 Logger.LOG(Logger.ESOCKET, Logger.EInfo,
                            "- sendRequest _submitTransaction");
             }
         }
-        if ((iTransactionBlock.getResult() == -18) &&(iRetries < 2))
+        if (aPartialDataFlag == false)
         {
-            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");
+            if ((iTransactionBlock.getResult() == -18) &&(iRetries < 2))
+            {
+                iRetries++;
+                Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                           "- sendRequest KErrNotReady erroi, calling recursive sendRequest()");
+                sendRequest(false);
+                Logger.LOG(Logger.ESOCKET, Logger.EInfo,
+                           "- sendRequest KErrNotReady erroi, calling recursive sendRequest() returned");
+            }
         }
     }
 
@@ -1291,6 +1461,13 @@
         Logger.LOG(Logger.ESOCKET, Logger.EInfo, "- dataReadyForReadCallBack");
     }
 
+    protected void postDataConsumedCallback()
+    {
+        Logger.LOG(Logger.ESOCKET, Logger.EInfo, "+ postDataconsumedCallback  ");
+        iPostDataBlock.notifyCompleted(0);
+
+    }
+
     /*
      * Native Calls
      */
@@ -1300,13 +1477,14 @@
             String aUri, String aRequestMethod);
     private native int _submitTransaction(int aNativeTransactionHande,
                                           String[] aHeaders, byte[] aPostData, int aPostLength,
-                                          int aRespTimeOut);
+                                          int aRespTimeOut, boolean aPartialData);
     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);
+    private native int _postData(int aNativeTransactionHandle, byte[] aPostData, int aLength, boolean iEndOfRequest);
 
     /*
      *