Repository: netty/netty
Issue: [3.5.5.Final] Error when uploading nothing
Labels: defect

I'm using Netty 3.5.5.Final and Firefox 14.0.1.

When uploading a form like this:

```
<form class="well span6" method="post" action="/apps/573d3849f224f9f1a160b6feb2759967/markers/7ab50bf507ac9bd3cf08f2141b4e2158" enctype="multipart/form-data">
  <input type="hidden" name="csrf-token" value="9d26ddeb-3b38-4a49-924e-9f3093f37cf7"/>
  <input type="hidden" name="_method" value="put"/>
  <label>Image</label>
  <input class="span6" type="file" name="image"/>
  <label>Description</label>
  <input class="span6" type="text" name="description" value="" />
  <label>Return data</label>
  <input class="span6" type="text" name="return_data" value="" />
</form>
```

I sometimes (not always, about once every 5 times) get this error when not selecting any image file to upload:

```
java.lang.IndexOutOfBoundsException: null
    at org.jboss.netty.buffer.AbstractChannelBuffer.readerIndex(AbstractChannelBuffer.java:44) ~[netty-3.5.5.Final.jar:na]
    at org.jboss.netty.handler.codec.http.multipart.HttpPostBodyUtil$SeekAheadOptimize.setReadPosition(HttpPostBodyUtil.java:163) ~[netty-3.5.5.Final.jar:na]
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.skipControlCharacters(HttpPostRequestDecoder.java:865) ~[netty-3.5.5.Final.jar:na]
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.findMultipartDelimiter(HttpPostRequestDecoder.java:895) ~[netty-3.5.5.Final.jar:na]
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.decodeMultipart(HttpPostRequestDecoder.java:766) ~[netty-3.5.5.Final.jar:na]
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.parseBodyMultipart(HttpPostRequestDecoder.java:727) ~[netty-3.5.5.Final.jar:na]
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.parseBody(HttpPostRequestDecoder.java:408) ~[netty-3.5.5.Final.jar:na]
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.<init>(HttpPostRequestDecoder.java:191) ~[netty-3.5.5.Final.jar:na]
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.<init>(HttpPostRequestDecoder.java:148) ~[netty-3.5.5.Final.jar:na]
    at xitrum.handler.up.BodyParser.messageReceived(BodyParser.scala:55) ~[xitrum_2.9.2-1.9.4.jar:1.9.4]
```

BodyParser.scala:55
https://github.com/ngocdaothanh/xitrum/blob/master/src/main/scala/xitrum/handler/up/BodyParser.scala#L55

Data sent to server:

```
POST /apps/573d3849f224f9f1a160b6feb2759967/markers/7ab50bf507ac9bd3cf08f2141b4e2158 HTTP/1.1

Host: localhost:2218

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:14.0) Gecko/20100101 Firefox/14.0.1

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-us,en;q=0.5

Accept-Encoding: gzip, deflate

Referer: http://localhost:2218/apps/573d3849f224f9f1a160b6feb2759967/markers/7ab50bf507ac9bd3cf08f2141b4e2158/edit

Cookie: _session=AAACQJAneGp2k3ib2OyAbItsVTmeqtcheBHiZx1aF2gXN4uoCBxlD8zMkKzlvEXKyQy8acAQGNBU6hfNt9XD5yywFmgZ72ZyFSHlYWVe7ha2xAhbNKI-5uxq7F0eLxVt-ks9_SJfPx1LAmMyY9SV42DOr9t8jl3UCbSJmKYFwi_JWMEvDNmruoUyaKBYMDwNbMnE2BZmsbWQ3IJt9AjrO3EVIwdFFF-LUpKP_0AWhtAOcgDek1Xm627eg9qHSNyWHRzz-mb3ZaR3EjfNXqq-dxfRt89bglfn4xHI8pK9TrFXHhbctQJLUeFdK3GVPYv1zqshGdYPGjv2S1d6D36sunYZujl5lHQ0tnSwdEcU2KzEs04jNkXPGPqjVsQLdOUlWvIXcrOVEXrIV3cqctpNW0ofZR24Z4mEhjo2X5O7zN4FeYGBF371BzkSuexa6Ix3mIrNu4pO5aUidMAufyAwqUl44hUr0rgMjfSEaj9THWavvVwctw6GiATeqQnE7xZNw8VIA8dbMLnJhZ2Czumwzh0YumQK_qTeVKEuFRHUdoDgU5jXuuj7vn9BAJ2eSDebUvZs7M_cI159TLb4c_hfclbMVXD8WVXlVkbGL_0NxYHvwtCYYPYZPtS90-s7oZQ4xSgH0ewK-EmUl7et25e7pphsr13PV_OyLcxccU9UNseiIy8HGL0OM5c8dWCjTW45eHeDv0ZcK_NtQLEHjUxmik6izC2rpelzSyUPl7IRocLTn1HoW79oz2H3fTdhee2MwWdgR9Z2BZhOgNA6erb-lPrNZruy97sS7QdiGZGs-7DUlmdX

Content-Type: multipart/form-data; boundary=---------------------------10903988221855041503514953775

Content-Length: 831



-----------------------------10903988221855041503514953775

Content-Disposition: form-data; name="csrf-token"



9d26ddeb-3b38-4a49-924e-9f3093f37cf7

-----------------------------10903988221855041503514953775

Content-Disposition: form-data; name="_method"



put

-----------------------------10903988221855041503514953775

Content-Disposition: form-data; name="image"; filename=""

Content-Type: application/octet-stream





-----------------------------10903988221855041503514953775

Content-Disposition: form-data; name="description"





-----------------------------10903988221855041503514953775

Content-Disposition: form-data; name="return_data"



http://169.254.47.225:2218/upload/file/f12a4df1da77a5c66ac8adaf42d3f28d/5ead9425a4571037e007c35186e452fb.mp4

-----------------------------10903988221855041503514953775--
```


Issue comments:
@ngocdaothanh does this also happen with older versions ?

1.

Yes, same problem with 3.5.3.Final.

2.

This problem can be reproduced with the last form (no file is selected) at:
org.jboss.netty.example.http.upload.HttpUploadServerHandler
of the latest Netty 3 branch.

I guess this problem may be related with cookie:

2.1. Using Chrome with some cookies

After posting the form about 3 times, this error will be printed in the console:

```
Aug 27, 2012 2:59:07 PM org.jboss.netty.example.http.upload.HttpUploadServerHandler
SEVERE: WELCOME TO THE WILD WILD WEB SERVER
===================================
VERSION: HTTP/1.1
REQUEST_URI: /formpostmultipart



HEADER: Host=localhost:8080
HEADER: Connection=keep-alive
HEADER: Content-Length=670
HEADER: Cache-Control=max-age=0
HEADER: Origin=http://localhost:8080
HEADER: User-Agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.82 Safari/537.1
HEADER: Content-Type=multipart/form-data; boundary=----WebKitFormBoundary3UbKLOfprY9gaKzo
HEADER: Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HEADER: Referer=http://localhost:8080/
HEADER: Accept-Encoding=gzip,deflate,sdch
HEADER: Accept-Language=en-US,en;q=0.8
HEADER: Accept-Charset=ISO-8859-1,utf-8;q=0.7,*;q=0.3
HEADER: Cookie=_session=AAACQJAneGp2k3ib2OyAbItsVTmeqtcheBHiZx1aF2gXN4uoCBxlD8zMkKzlvEXKyQy8acAQGNBU6hfNt9XD5yywFmgZ72ZyFSHlYWVe7ha2xAhbNKI-5uxq7F0eLxVt-ks9_SJfPx1LAmMyY9SV42DOr9t8jl3UCbSJmKYFwi_JWMEvDNmruoUyaKBYMDwNbMnE2BZmsbWQ3IJt9AjrO3EVIwcNtoRRWgnpUQJNuA_N7fuDwrtsCQc0x4r4StAfgjfEThz2tFxNPyLzmqet5DM2p8yRzUme8QaNSlCDV0v5EFHqUWMbwUc9pcd9I73REOYbT4L8JaXzCM6f6epYmYEHyMs5rCzOGnWs3inxkTDKpRnun1w8dsF0UR_YtR42K8d9uCNvBdpau0XUSQ_ZSCAubUArnr9SL-FJzVTkyHazQItM40ubIUzjLe3TRyHntsFYHGp0wvhLVjVQz3aFCZiyXEOM-SjiiIMQcvzLy4mZ4I7XY9Fmrg7Ee9WmmCuTjRNunEfHMAjFFvbmRt06zk-fs2NircRGDLR_3X1xbaQJnMhxHA99N1-Ow_3U_tJv_Xte929LY7vwHArwFHJrx-ecWNlQqnGKSS3sHDQttAFUXCx7yXwP4oTaMJLsqHor0tDHZkTTKGhQtR8nRAw0dmhF_M76LE9NQJs47kN_6MjcI6QtELiVjti_ZeST-dIVKLP09jTlKkbMebeXq6UQCg8G6rgSdNofVb32m5geoVTtjKxKr4d2kaRoFcQ8kaThPnwYrGymzx6bShQUaEbs9fo3SghNi3OkgDzIhJvN5CFcEdk1; firstVisitorCookie=1; s_cc=true; s_nr=1345733923539; s_ppv=24; s_sq=%5B%5BB%5D%5D; ttc11=1353505852841


COOKIE: _session=AAACQJAneGp2k3ib2OyAbItsVTmeqtcheBHiZx1aF2gXN4uoCBxlD8zMkKzlvEXKyQy8acAQGNBU6hfNt9XD5yywFmgZ72ZyFSHlYWVe7ha2xAhbNKI-5uxq7F0eLxVt-ks9_SJfPx1LAmMyY9SV42DOr9t8jl3UCbSJmKYFwi_JWMEvDNmruoUyaKBYMDwNbMnE2BZmsbWQ3IJt9AjrO3EVIwcNtoRRWgnpUQJNuA_N7fuDwrtsCQc0x4r4StAfgjfEThz2tFxNPyLzmqet5DM2p8yRzUme8QaNSlCDV0v5EFHqUWMbwUc9pcd9I73REOYbT4L8JaXzCM6f6epYmYEHyMs5rCzOGnWs3inxkTDKpRnun1w8dsF0UR_YtR42K8d9uCNvBdpau0XUSQ_ZSCAubUArnr9SL-FJzVTkyHazQItM40ubIUzjLe3TRyHntsFYHGp0wvhLVjVQz3aFCZiyXEOM-SjiiIMQcvzLy4mZ4I7XY9Fmrg7Ee9WmmCuTjRNunEfHMAjFFvbmRt06zk-fs2NircRGDLR_3X1xbaQJnMhxHA99N1-Ow_3U_tJv_Xte929LY7vwHArwFHJrx-ecWNlQqnGKSS3sHDQttAFUXCx7yXwP4oTaMJLsqHor0tDHZkTTKGhQtR8nRAw0dmhF_M76LE9NQJs47kN_6MjcI6QtELiVjti_ZeST-dIVKLP09jTlKkbMebeXq6UQCg8G6rgSdNofVb32m5geoVTtjKxKr4d2kaRoFcQ8kaThPnwYrGymzx6bShQUaEbs9fo3SghNi3OkgDzIhJvN5CFcEdk1
COOKIE: firstVisitorCookie=1
COOKIE: s_cc=true
COOKIE: s_nr=1345733923539
COOKIE: s_ppv=24
COOKIE: s_sq=%5B%5BB%5D%5D
COOKIE: ttc11=1353505852841





java.lang.IndexOutOfBoundsException
    at org.jboss.netty.buffer.AbstractChannelBuffer.readerIndex(AbstractChannelBuffer.java:44)
    at org.jboss.netty.handler.codec.http.multipart.HttpPostBodyUtil$SeekAheadOptimize.setReadPosition(HttpPostBodyUtil.java:163)
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.skipControlCharacters(HttpPostRequestDecoder.java:865)
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.findMultipartDelimiter(HttpPostRequestDecoder.java:895)
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.decodeMultipart(HttpPostRequestDecoder.java:766)
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.parseBodyMultipart(HttpPostRequestDecoder.java:727)
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.parseBody(HttpPostRequestDecoder.java:408)
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.<init>(HttpPostRequestDecoder.java:191)
    at org.jboss.netty.handler.codec.http.multipart.HttpPostRequestDecoder.<init>(HttpPostRequestDecoder.java:148)
    at org.jboss.netty.example.http.upload.HttpUploadServerHandler.messageReceived(HttpUploadServerHandler.java:161)
    at org.jboss.netty.handler.codec.http.HttpContentEncoder.messageReceived(HttpContentEncoder.java:82)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296)
    at org.jboss.netty.handler.codec.frame.FrameDecoder.unfoldAndFireMessageReceived(FrameDecoder.java:455)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.callDecode(ReplayingDecoder.java:538)
    at org.jboss.netty.handler.codec.replay.ReplayingDecoder.messageReceived(ReplayingDecoder.java:437)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268)
    at org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255)
    at org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:94)
    at org.jboss.netty.channel.socket.nio.AbstractNioWorker.processSelectedKeys(AbstractNioWorker.java:390)
    at org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:261)
    at org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:35)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)
```

2.2. Using Firefox without cookie

There's no error.

But there's another problem: when posting the form the second time with "1", "2", and "3" as values for the first 3 parameters of the last form, the server side does not output the values.

```
WELCOME TO THE WILD WILD WEB SERVER
===================================
VERSION: HTTP/1.1
REQUEST_URI: /formpostmultipart



HEADER: Host=localhost:8080
HEADER: User-Agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:14.0) Gecko/20100101 Firefox/14.0.1
HEADER: Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
HEADER: Accept-Language=en-us,en;q=0.5
HEADER: Accept-Encoding=gzip, deflate
HEADER: Connection=keep-alive
HEADER: Referer=http://localhost:8080/
HEADER: Content-Type=multipart/form-data; boundary=---------------------------8006742647998229461524106849
HEADER: Content-Length=789






Is Chunked: false
IsMultipart: true


END OF CONTENT AT FINAL END


END OF NOT CHUNKED CONTENT
```

@fredericBregier could you please have a look. I think the problem is that ChannelBuffer.readerIndex(-1) is called.

@normanmaurer I will try this evening to take a look at this issue...

Thanks!

Sent from my iPhone. Excuse any typos....

Am 27.08.2012 um 18:08 schrieb Frédéric Brégier notifications@github.com:

> @normanmaurer I will try this evening to take a look at this issue...
> 
> —
> Reply to this email directly or view it on GitHub.

Hi all,

OK, I don't find yet the reason but I can reproduce and not reproduce it. I explain myself:

1- If the file is specified with enough size (such that chunk is ON, so more than 8K), it is OK for all Browsers.

2- Now with only 1, 2, 3 as parameter and no file (or a small one such that the global size is less than 8K):
- IE 9 :  no problem, always ok
- Firefox 14.0.1 : 
  OK first time
  KO next times, 
  except if I do a CTRL-F5 (force reload) on the page (whatever the upload page or web menu page), 
  but not OK if only simple F5
- Chrome 23.0.1243.3
  OK first time
  KO next times, 

Moreover, if I wait a long time (such that the browser finish the connection that were pending due to persistent connection), the request is OK again (for all browsers).

So from those elements, I try to force "close" on each response (in writeResponse method, after computation of close boolean, I force close to be "true"), and then for all browser it is ok.

So I don't think right now it is related to the channelbuffer.readerIndex(-1).
But I do not know where the issue is...
I suppose that a value is kept behind since the connection is not closed and therefore bringing wrong result... but which one ? I try to seek the value in the handler and see nothing. All variables seem new for each request, as if the connection where new.

From some trace, what I've seen is that when not reloading, Firefox send the exact same "boundary".
OK, I stop here for tonight, but if you have an idea I could check tomorrow...

Hi all,

For your information, I've made another test by deactivating the SeekAheadOptimize optimization, and there is no more issue with Firefox. I didn't test yet with Chrome but will do...
If this is the root cause (and it seems so), then I will spend some time to understand why.

Edit: I confirm that with Chrome this is OK too by deactivating SeekAheadOptimize... I'm starting to look for the reason.

For all, I finally found the issue. This was introduced by a correction on arrayOffset usage in the optimization part, but not enough... So I test it under IE, Firefox and Chrome, it seems now correct whatever the size of the upload.

@normanmaurer Sorry Norman, but I don't have access to all my tools to check the "typo" stuff.
Could you check it before any merge ?

@fredericBregier
Thanks a lot!

@normanmaurer
Sorry because 3.5.5.Final was only released a few days ago, but should a new version be released soon?

@ngocdaothanh Ok I think we are good now as @fredericBregier fixed the bug (yay)... Please retest with latest code out of the 3 branch and give us feedback..

Thanks!

By the way, Thank you @normanmaurer to check my fix ;-) 
I was pretty sure of what I've done, but as I had only web access to publish, I was not 100% sure, and you did it well and quick ! 
Sorry to have push this on your side and Thank you again !

You are welcome... Thanks to find the time to investigate, I only did the dumb part

Sent from my iPhone. Excuse any typos....

Am 28.08.2012 um 18:49 schrieb Frédéric Brégier notifications@github.com:

> By the way, Thank you @normanmaurer to check my fix ;-) 
> I was pretty sure of what I've done, but as I had only web access to publish, I was not 100% sure, and you did it well and quick ! 
> Sorry to have push this on your side and Thank you again !
> 
> —
> Reply to this email directly or view it on GitHub.

> Please retest with latest code out of the 3 branch and give us feedback..

I confirm that there's no error now.
Thanks a lot.


Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostBodyUtil.java b/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostBodyUtil.java
index c0d923c83e4..12a4a0e5e1d 100644
--- a/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostBodyUtil.java
+++ b/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostBodyUtil.java
@@ -134,6 +134,8 @@ static class SeekAheadOptimize {
         int readerIndex;
 
         int pos;
+        
+        int origPos;
 
         int limit;
 
@@ -148,7 +150,8 @@ static class SeekAheadOptimize {
             }
             this.buffer = buffer;
             bytes = buffer.array();
-            pos = readerIndex = buffer.arrayOffset() + buffer.readerIndex();
+            readerIndex = buffer.readerIndex();
+            origPos = pos = buffer.arrayOffset() + readerIndex;
             limit = buffer.arrayOffset() + buffer.writerIndex();
         }
 
@@ -159,10 +162,19 @@ static class SeekAheadOptimize {
         */
         void setReadPosition(int minus) {
             pos -= minus;
-            readerIndex = pos;
+            readerIndex = getReadPosition(pos);
             buffer.readerIndex(readerIndex);
         }
 
+        /**
+        *
+        * @param index raw index of the array (pos in general)
+        * @return the value equivalent of raw index to be used in readerIndex(value)
+        */
+        int getReadPosition(int index) {
+            return index - origPos + readerIndex;
+        }
+
         void clear() {
             buffer = null;
             bytes = null;
diff --git a/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java b/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java
index 3b674280fed..0cf9837b23e 100644
--- a/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java
+++ b/src/main/java/org/jboss/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java
@@ -849,13 +849,18 @@ private InterfaceHttpData decodeMultipart(MultiPartStatus state)
 
     /**
      * Skip control Characters
+     * @throws NotEnoughDataDecoderException
      */
-    void skipControlCharacters() {
+    void skipControlCharacters() throws NotEnoughDataDecoderException {
         SeekAheadOptimize sao = null;
         try {
             sao = new SeekAheadOptimize(undecodedChunk);
         } catch (SeekAheadNoBackArrayException e) {
-            skipControlCharactersStandard(undecodedChunk);
+            try {
+                skipControlCharactersStandard(undecodedChunk);
+            } catch (IndexOutOfBoundsException e1) {
+                throw new NotEnoughDataDecoderException(e1);
+            }
             return;
         }
 
@@ -866,13 +871,13 @@ void skipControlCharacters() {
                 return;
             }
         }
-        sao.setReadPosition(0);
+        throw new NotEnoughDataDecoderException("Access out of bounds");
     }
-    static void skipControlCharactersStandard(ChannelBuffer buffer) {
+    void skipControlCharactersStandard() {
         for (;;) {
-            char c = (char) buffer.readUnsignedByte();
+            char c = (char) undecodedChunk.readUnsignedByte();
             if (!Character.isISOControl(c) && !Character.isWhitespace(c)) {
-                buffer.readerIndex(buffer.readerIndex() - 1);
+                undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
                 break;
             }
         }
@@ -892,7 +897,12 @@ private InterfaceHttpData findMultipartDelimiter(String delimiter,
             throws ErrorDataDecoderException {
         // --AaB03x or --AaB03x--
         int readerIndex = undecodedChunk.readerIndex();
-        skipControlCharacters();
+        try {
+            skipControlCharacters();
+        } catch (NotEnoughDataDecoderException e1) {
+            undecodedChunk.readerIndex(readerIndex);
+            return null;
+        }
         skipOneLine();
         String newline;
         try {
@@ -933,9 +943,9 @@ private InterfaceHttpData findMultipartDisposition()
         }
         // read many lines until empty line with newline found! Store all data
         while (!skipOneLine()) {
-            skipControlCharacters();
             String newline;
             try {
+                skipControlCharacters();
                 newline = readLine();
             } catch (NotEnoughDataDecoderException e) {
                 undecodedChunk.readerIndex(readerIndex);
@@ -1594,8 +1604,8 @@ private void readFileUploadByteMultipart(String delimiter)
         // found the decoder limit
         boolean newLine = true;
         int index = 0;
+        int lastrealpos = sao.pos;
         int lastPosition = undecodedChunk.readerIndex();
-        int setReadPosition = -1;
         boolean found = false;
 
         while (sao.pos < sao.limit) {
@@ -1606,7 +1616,6 @@ private void readFileUploadByteMultipart(String delimiter)
                     index ++;
                     if (delimiter.length() == index) {
                         found = true;
-                        sao.setReadPosition(0);
                         break;
                     }
                     continue;
@@ -1620,23 +1629,16 @@ private void readFileUploadByteMultipart(String delimiter)
                             if (nextByte == HttpConstants.LF) {
                                 newLine = true;
                                 index = 0;
-                                setReadPosition = sao.pos;
-                                lastPosition = sao.pos - 2;
+                                lastrealpos = sao.pos - 2;
                             }
-                        } else {
-                            // save last valid position
-                            setReadPosition = sao.pos;
-                            lastPosition = sao.pos;
                         }
                     } else if (nextByte == HttpConstants.LF) {
                         newLine = true;
                         index = 0;
-                        setReadPosition = sao.pos;
-                        lastPosition = sao.pos - 1;
+                        lastrealpos = sao.pos - 1;
                     } else {
                         // save last valid position
-                        setReadPosition = sao.pos;
-                        lastPosition = sao.pos;
+                        lastrealpos = sao.pos;
                     }
                 }
             } else {
@@ -1647,30 +1649,20 @@ private void readFileUploadByteMultipart(String delimiter)
                         if (nextByte == HttpConstants.LF) {
                             newLine = true;
                             index = 0;
-                            setReadPosition = sao.pos;
-                            lastPosition = sao.pos - 2;
+                            lastrealpos = sao.pos - 2;
                         }
-                    } else {
-                        // save last valid position
-                        setReadPosition = sao.pos;
-                        lastPosition = sao.pos;
                     }
                 } else if (nextByte == HttpConstants.LF) {
                     newLine = true;
                     index = 0;
-                    setReadPosition = sao.pos;
-                    lastPosition = sao.pos - 1;
+                    lastrealpos = sao.pos - 1;
                 } else {
                     // save last valid position
-                    setReadPosition = sao.pos;
-                    lastPosition = sao.pos;
+                    lastrealpos = sao.pos;
                 }
             }
         }
-        if (setReadPosition > 0) {
-            sao.pos = setReadPosition;
-            sao.setReadPosition(0);
-        }
+        lastPosition = sao.getReadPosition(lastrealpos);
         ChannelBuffer buffer = undecodedChunk.slice(readerIndex, lastPosition - readerIndex);
         if (found) {
             // found so lastPosition is correct and final
@@ -1809,7 +1801,7 @@ private void loadFieldMultipart(String delimiter)
             boolean newLine = true;
             int index = 0;
             int lastPosition = undecodedChunk.readerIndex();
-            int setReadPosition = -1;
+            int lastrealpos = sao.pos;
             boolean found = false;
 
             while (sao.pos < sao.limit) {
@@ -1820,7 +1812,6 @@ private void loadFieldMultipart(String delimiter)
                         index ++;
                         if (delimiter.length() == index) {
                             found = true;
-                            sao.setReadPosition(0);
                             break;
                         }
                         continue;
@@ -1834,21 +1825,15 @@ private void loadFieldMultipart(String delimiter)
                                 if (nextByte == HttpConstants.LF) {
                                     newLine = true;
                                     index = 0;
-                                    lastPosition = sao.pos - 2;
-                                    setReadPosition = sao.pos;
+                                    lastrealpos = sao.pos - 2;
                                 }
-                            } else {
-                                lastPosition = sao.pos;
-                                setReadPosition = sao.pos;
                             }
                         } else if (nextByte == HttpConstants.LF) {
                             newLine = true;
                             index = 0;
-                            lastPosition = sao.pos - 1;
-                            setReadPosition = sao.pos;
+                            lastrealpos = sao.pos - 1;
                         } else {
-                            lastPosition = sao.pos;
-                            setReadPosition = sao.pos;
+                            lastrealpos = sao.pos;
                         }
                     }
                 } else {
@@ -1859,28 +1844,19 @@ private void loadFieldMultipart(String delimiter)
                             if (nextByte == HttpConstants.LF) {
                                 newLine = true;
                                 index = 0;
-                                lastPosition = sao.pos - 2;
-                                setReadPosition = sao.pos;
+                                lastrealpos = sao.pos - 2;
                             }
-                        } else {
-                            lastPosition = sao.pos;
-                            setReadPosition = sao.pos;
                         }
                     } else if (nextByte == HttpConstants.LF) {
                         newLine = true;
                         index = 0;
-                        lastPosition = sao.pos - 1;
-                        setReadPosition = sao.pos;
+                        lastrealpos = sao.pos - 1;
                     } else {
-                        lastPosition = sao.pos;
-                        setReadPosition = sao.pos;
+                        lastrealpos = sao.pos;
                     }
                 }
             }
-            if (setReadPosition > 0) {
-                sao.pos = setReadPosition;
-                sao.setReadPosition(0);
-            }
+            lastPosition = sao.getReadPosition(lastrealpos);
             if (found) {
                 // found so lastPosition is correct
                 // but position is just after the delimiter (either close delimiter or simple one)


Test patch: