From 65876fa7fbe2f63a73a590b5f2583daecf63984f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Br=C3=A9gier?= Date: Mon, 30 Apr 2012 12:29:05 +0300 Subject: [PATCH 1/9] Close channel when needed and optimize force() position after multiple writes not at each step --- .../netty/handler/codec/http/AbstractDiskHttpData.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/AbstractDiskHttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/AbstractDiskHttpData.java index ebb4290b5d..04b3147447 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/AbstractDiskHttpData.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/AbstractDiskHttpData.java @@ -116,9 +116,9 @@ public abstract class AbstractDiskHttpData extends AbstractHttpData { int written = 0; while (written < size) { written += localfileChannel.write(byteBuffer); - localfileChannel.force(false); } buffer.readerIndex(buffer.readerIndex() + written); + localfileChannel.force(false); localfileChannel.close(); completed = true; } @@ -143,7 +143,6 @@ public abstract class AbstractDiskHttpData extends AbstractHttpData { } while (written < localsize) { written += fileChannel.write(byteBuffer); - fileChannel.force(false); } size += localsize; buffer.readerIndex(buffer.readerIndex() + written); @@ -156,6 +155,7 @@ public abstract class AbstractDiskHttpData extends AbstractHttpData { FileOutputStream outputStream = new FileOutputStream(file); fileChannel = outputStream.getChannel(); } + fileChannel.force(false); fileChannel.close(); fileChannel = null; completed = true; @@ -195,9 +195,10 @@ public abstract class AbstractDiskHttpData extends AbstractHttpData { while (read > 0) { byteBuffer.position(read).flip(); written += localfileChannel.write(byteBuffer); - localfileChannel.force(false); read = inputStream.read(bytes); } + localfileChannel.force(false); + localfileChannel.close(); size = written; if (definedSize > 0 && definedSize < size) { file.delete(); @@ -300,6 +301,8 @@ public abstract class AbstractDiskHttpData extends AbstractHttpData { FileChannel in = inputStream.getChannel(); FileChannel out = outputStream.getChannel(); long destsize = in.transferTo(0, size, out); + in.close(); + out.close(); if (destsize == size) { file.delete(); file = dest; From 877383de3a518dd0dac56eba7337932dd55f59ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Br=C3=A9gier?= Date: Mon, 30 Apr 2012 12:30:08 +0300 Subject: [PATCH 2/9] Move force() after multiple writes, not at every steps --- .../io/netty/handler/codec/http/AbstractMemoryHttpData.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/AbstractMemoryHttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/AbstractMemoryHttpData.java index bdd24455f3..2e37976f32 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/AbstractMemoryHttpData.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/AbstractMemoryHttpData.java @@ -212,8 +212,8 @@ public abstract class AbstractMemoryHttpData extends AbstractHttpData { int written = 0; while (written < length) { written += fileChannel.write(byteBuffer); - fileChannel.force(false); } + fileChannel.force(false); fileChannel.close(); isRenamed = true; return written == length; From a410cb243b875890d96f64db5976a90e9e8295fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Br=C3=A9gier?= Date: Mon, 30 Apr 2012 12:32:24 +0300 Subject: [PATCH 3/9] Fix in addContent when switching from MemoryAttribute if it is done when last buffer added, in order to not close immediately the underlying file before adding the last buffer. --- .../main/java/io/netty/handler/codec/http/MixedAttribute.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/MixedAttribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/MixedAttribute.java index 5aa301037c..2e3ab69491 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/MixedAttribute.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/MixedAttribute.java @@ -65,7 +65,7 @@ public class MixedAttribute implements Attribute { .getName()); if (((MemoryAttribute) attribute).getChannelBuffer() != null) { diskAttribute.addContent(((MemoryAttribute) attribute) - .getChannelBuffer(), last); + .getChannelBuffer(), false); } attribute = diskAttribute; } From d9085e9e37669624f1e716023cd91e6aeadd3c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Br=C3=A9gier?= Date: Mon, 30 Apr 2012 12:33:10 +0300 Subject: [PATCH 4/9] Fix in addContent when switching from MemoryAttribute if it is done when last buffer added, in order to not close immediately the underlying file before adding the last buffer. --- .../main/java/io/netty/handler/codec/http/MixedFileUpload.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/MixedFileUpload.java b/codec-http/src/main/java/io/netty/handler/codec/http/MixedFileUpload.java index 6f0146cb37..b1a1350616 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/MixedFileUpload.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/MixedFileUpload.java @@ -58,7 +58,7 @@ public class MixedFileUpload implements FileUpload { definedSize); if (((MemoryFileUpload) fileUpload).getChannelBuffer() != null) { diskFileUpload.addContent(((MemoryFileUpload) fileUpload) - .getChannelBuffer(), last); + .getChannelBuffer(), false); } fileUpload = diskFileUpload; } From c83323f7484b094e8bee993154325d73dd19e11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Br=C3=A9gier?= Date: Mon, 30 Apr 2012 12:38:07 +0300 Subject: [PATCH 5/9] Add the SeekAheadOptimize class to enable faster seek of bytes values in HttpPostRequestDecoder --- .../handler/codec/http/HttpPostBodyUtil.java | 58 ++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostBodyUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostBodyUtil.java index e74e1ecdc0..b1ceca7bf0 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostBodyUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostBodyUtil.java @@ -116,18 +116,58 @@ final class HttpPostBodyUtil { private HttpPostBodyUtil() { } - //Some commons methods between HttpPostRequestDecoder and HttpMessageDecoder /** - * Skip control Characters - * @param buffer + * Exception when NO Backend Array is found + */ + static class SeekAheadNoBackArray extends Exception { + private static final long serialVersionUID = -630418804938699495L; + } + + /** + * This class intends to decrease the CPU in seeking ahead some bytes in + * HttpPostRequestDecoder */ - static void skipControlCharacters(ChannelBuffer buffer) { - for (;;) { - char c = (char) buffer.readUnsignedByte(); - if (!Character.isISOControl(c) && !Character.isWhitespace(c)) { - buffer.readerIndex(buffer.readerIndex() - 1); - break; + static class SeekAheadOptimize { + byte[] bytes; + + int readerIndex; + + int pos; + + int limit; + + ChannelBuffer buffer; + + /** + * @param buffer + */ + SeekAheadOptimize(ChannelBuffer buffer) + throws SeekAheadNoBackArray { + if (! buffer.hasArray()) { + throw new SeekAheadNoBackArray(); } + this.buffer = buffer; + this.bytes = buffer.array(); + this.pos = this.readerIndex = buffer.readerIndex(); + this.limit = buffer.writerIndex(); + } + /** + * + * @param minus this value will be used as (currentPos - minus) to set + * the current readerIndex in the buffer. + */ + void setReadPosition(int minus) { + pos -= minus; + readerIndex = pos; + buffer.readerIndex(readerIndex); + } + + void clear() { + this.buffer = null; + this.bytes = null; + this.limit = 0; + this.pos = 0; + this.readerIndex = 0; } } From a52439647562f842ec2dd8a70427bd8af6e30e87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Br=C3=A9gier?= Date: Mon, 30 Apr 2012 13:02:46 +0300 Subject: [PATCH 6/9] Optimize Buffer access while decoding by going through backend array when possible (divide by almost 2 the time spent in decoding) --- .../codec/http/HttpPostRequestDecoder.java | 454 +++++++++++++++++- 1 file changed, 448 insertions(+), 6 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java index b066479554..50adf9a206 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java @@ -26,6 +26,8 @@ import java.util.TreeMap; import io.netty.buffer.ChannelBuffer; import io.netty.buffer.ChannelBuffers; +import org.jboss.netty.handler.codec.http2.HttpPostBodyUtil.SeekAheadNoBackArray; +import org.jboss.netty.handler.codec.http2.HttpPostBodyUtil.SeekAheadOptimize; import io.netty.handler.codec.http.HttpPostBodyUtil.TransferEncodingMechanism; /** @@ -427,7 +429,7 @@ public class HttpPostRequestDecoder { * @throws ErrorDataDecoderException if there is a problem with the charset decoding or * other errors */ - private void parseBodyAttributes() throws ErrorDataDecoderException { + private void parseBodyAttributesStandard() throws ErrorDataDecoderException { int firstpos = undecodedChunk.readerIndex(); int currentpos = firstpos; int equalpos = firstpos; @@ -538,6 +540,141 @@ public class HttpPostRequestDecoder { } } + /** + * This method fill the map and list with as much Attribute as possible from Body in + * not Multipart mode. + * + * @throws ErrorDataDecoderException if there is a problem with the charset decoding or + * other errors + */ + private void parseBodyAttributes() throws ErrorDataDecoderException { + SeekAheadOptimize sao = null; + try { + sao = new SeekAheadOptimize(undecodedChunk); + } catch (SeekAheadNoBackArray e1) { + parseBodyAttributesStandard(); + return; + } + int firstpos = undecodedChunk.readerIndex(); + int currentpos = firstpos; + int equalpos = firstpos; + int ampersandpos = firstpos; + if (currentStatus == MultiPartStatus.NOTSTARTED) { + currentStatus = MultiPartStatus.DISPOSITION; + } + boolean contRead = true; + try { + loop: + while (sao.pos < sao.limit) { + char read = (char) (sao.bytes[sao.pos ++] & 0xFF); + currentpos ++; + switch (currentStatus) { + case DISPOSITION:// search '=' + if (read == '=') { + currentStatus = MultiPartStatus.FIELD; + equalpos = currentpos - 1; + String key = decodeAttribute( + undecodedChunk.toString(firstpos, equalpos - firstpos, charset), + charset); + currentAttribute = factory.createAttribute(request, key); + firstpos = currentpos; + } else if (read == '&') { // special empty FIELD + currentStatus = MultiPartStatus.DISPOSITION; + ampersandpos = currentpos - 1; + String key = decodeAttribute(undecodedChunk.toString(firstpos, ampersandpos - firstpos, charset), charset); + currentAttribute = factory.createAttribute(request, key); + currentAttribute.setValue(""); // empty + addHttpData(currentAttribute); + currentAttribute = null; + firstpos = currentpos; + contRead = true; + } + break; + case FIELD:// search '&' or end of line + if (read == '&') { + currentStatus = MultiPartStatus.DISPOSITION; + ampersandpos = currentpos - 1; + setFinalBuffer(undecodedChunk.slice(firstpos, ampersandpos - firstpos)); + firstpos = currentpos; + contRead = true; + } else if (read == HttpCodecUtil.CR) { + if (sao.pos < sao.limit) { + read = (char) (sao.bytes[sao.pos ++] & 0xFF); + currentpos++; + if (read == HttpCodecUtil.LF) { + currentStatus = MultiPartStatus.PREEPILOGUE; + ampersandpos = currentpos - 2; + sao.setReadPosition(0); + setFinalBuffer( + undecodedChunk.slice(firstpos, ampersandpos - firstpos)); + firstpos = currentpos; + contRead = false; + break loop; + } else { + // Error + sao.setReadPosition(0); + contRead = false; + throw new ErrorDataDecoderException("Bad end of line"); + } + } else { + if (sao.limit > 0) { + currentpos --; + } + } + } else if (read == HttpCodecUtil.LF) { + currentStatus = MultiPartStatus.PREEPILOGUE; + ampersandpos = currentpos - 1; + sao.setReadPosition(0); + setFinalBuffer( + undecodedChunk.slice(firstpos, ampersandpos - firstpos)); + firstpos = currentpos; + contRead = false; + break loop; + } + break; + default: + // just stop + sao.setReadPosition(0); + contRead = false; + break loop; + } + } + if (isLastChunk && currentAttribute != null) { + // special case + ampersandpos = currentpos; + if (ampersandpos > firstpos) { + setFinalBuffer( + undecodedChunk.slice(firstpos, ampersandpos - firstpos)); + } else if (! currentAttribute.isCompleted()) { + setFinalBuffer(ChannelBuffers.EMPTY_BUFFER); + } + firstpos = currentpos; + currentStatus = MultiPartStatus.EPILOGUE; + return; + } + if (contRead && currentAttribute != null) { + // reset index except if to continue in case of FIELD status + if (currentStatus == MultiPartStatus.FIELD) { + currentAttribute.addContent( + undecodedChunk.slice(firstpos, currentpos - firstpos), + false); + firstpos = currentpos; + } + undecodedChunk.readerIndex(firstpos); + } else { + // end of line so keep index + } + } catch (ErrorDataDecoderException e) { + // error while decoding + undecodedChunk.readerIndex(firstpos); + throw e; + } catch (IOException e) { + // error while decoding + undecodedChunk.readerIndex(firstpos); + throw new ErrorDataDecoderException(e); + } + } + private void setFinalBuffer(ChannelBuffer buffer) throws ErrorDataDecoderException, IOException { currentAttribute.addContent(buffer, true); String value = decodeAttribute( @@ -700,6 +837,38 @@ public class HttpPostRequestDecoder { } } + + /** + * Skip control Characters + */ + void skipControlCharacters() { + SeekAheadOptimize sao = null; + try { + sao = new SeekAheadOptimize(undecodedChunk); + } catch (SeekAheadNoBackArray e) { + skipControlCharactersStandard(undecodedChunk); + return; + } + + while (sao.pos < sao.limit) { + char c = (char) sao.bytes[sao.pos ++]; + if (!Character.isISOControl(c) && !Character.isWhitespace(c)) { + sao.setReadPosition(1); + return; + } + } + sao.setReadPosition(0); + } + static void skipControlCharactersStandard(ChannelBuffer buffer) { + for (;;) { + char c = (char) buffer.readUnsignedByte(); + if (!Character.isISOControl(c) && !Character.isWhitespace(c)) { + buffer.readerIndex(buffer.readerIndex() - 1); + break; + } + } + } + /** * Find the next Multipart Delimiter * @param delimiter delimiter to find @@ -714,7 +883,7 @@ public class HttpPostRequestDecoder { throws ErrorDataDecoderException { // --AaB03x or --AaB03x-- int readerIndex = undecodedChunk.readerIndex(); - HttpPostBodyUtil.skipControlCharacters(undecodedChunk); + skipControlCharacters(undecodedChunk); skipOneLine(); String newline; try { @@ -755,7 +924,7 @@ public class HttpPostRequestDecoder { } // read many lines until empty line with newline found! Store all data while (!skipOneLine()) { - HttpPostBodyUtil.skipControlCharacters(undecodedChunk); + skipControlCharacters(undecodedChunk); String newline; try { newline = readLine(); @@ -1038,7 +1207,7 @@ public class HttpPostRequestDecoder { * @throws NotEnoughDataDecoderException Need more chunks and * reset the readerInder to the previous value */ - private String readLine() throws NotEnoughDataDecoderException { + private String readLineStandard() throws NotEnoughDataDecoderException { int readerIndex = undecodedChunk.readerIndex(); try { StringBuilder sb = new StringBuilder(64); @@ -1063,6 +1232,49 @@ public class HttpPostRequestDecoder { throw new NotEnoughDataDecoderException(); } + /** + * Read one line up to the CRLF or LF + * @return the String from one line + * @throws NotEnoughDataDecoderException Need more chunks and + * reset the readerInder to the previous value + */ + private String readLine() throws NotEnoughDataDecoderException { + SeekAheadOptimize sao = null; + try { + sao = new SeekAheadOptimize(undecodedChunk); + } catch (SeekAheadNoBackArray e1) { + return readLineStandard(); + } + int readerIndex = undecodedChunk.readerIndex(); + try { + StringBuilder sb = new StringBuilder(64); + while (sao.pos < sao.limit) { + byte nextByte = sao.bytes[sao.pos ++]; + if (nextByte == HttpCodecUtil.CR) { + if (sao.pos < sao.limit) { + nextByte = sao.bytes[sao.pos ++]; + if (nextByte == HttpCodecUtil.LF) { + sao.setReadPosition(0); + return sb.toString(); + } + } else { + sb.append((char) nextByte); + } + } else if (nextByte == HttpCodecUtil.LF) { + sao.setReadPosition(0); + return sb.toString(); + } else { + sb.append((char) nextByte); + } + } + } catch (IndexOutOfBoundsException e) { + undecodedChunk.readerIndex(readerIndex); + throw new NotEnoughDataDecoderException(e); + } + undecodedChunk.readerIndex(readerIndex); + throw new NotEnoughDataDecoderException(); + } + /** * Read a FileUpload data as Byte (Binary) and add the bytes directly to the * FileUpload. If the delimiter is found, the FileUpload is completed. @@ -1071,7 +1283,7 @@ public class HttpPostRequestDecoder { * do not reset the readerInder since some values will be already added to the FileOutput * @throws ErrorDataDecoderException write IO error occurs with the FileUpload */ - private void readFileUploadByteMultipart(String delimiter) + private void readFileUploadByteMultipartStandard(String delimiter) throws NotEnoughDataDecoderException, ErrorDataDecoderException { int readerIndex = undecodedChunk.readerIndex(); // found the decoder limit @@ -1157,12 +1369,128 @@ public class HttpPostRequestDecoder { } } + /** + * Read a FileUpload data as Byte (Binary) and add the bytes directly to the + * FileUpload. If the delimiter is found, the FileUpload is completed. + * @param delimiter + * @throws NotEnoughDataDecoderException Need more chunks but + * do not reset the readerInder since some values will be already added to the FileOutput + * @throws ErrorDataDecoderException write IO error occurs with the FileUpload + */ + private void readFileUploadByteMultipart(String delimiter) + throws NotEnoughDataDecoderException, ErrorDataDecoderException { + SeekAheadOptimize sao = null; + try { + sao = new SeekAheadOptimize(undecodedChunk); + } catch (SeekAheadNoBackArray e1) { + readFileUploadByteMultipartStandard(delimiter); + return; + } + int readerIndex = undecodedChunk.readerIndex(); + // found the decoder limit + boolean newLine = true; + int index = 0; + int lastPosition = undecodedChunk.readerIndex(); + boolean found = false; + + while (sao.pos < sao.limit) { + byte nextByte = sao.bytes[sao.pos ++]; + if (newLine) { + // Check the delimiter + if (nextByte == delimiter.codePointAt(index)) { + index ++; + if (delimiter.length() == index) { + found = true; + sao.setReadPosition(0); + break; + } + continue; + } else { + newLine = false; + index = 0; + // continue until end of line + if (nextByte == HttpCodecUtil.CR) { + if (sao.pos < sao.limit) { + nextByte = sao.bytes[sao.pos ++]; + if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex() - 2; + } + } else { + // save last valid position + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex(); + } + } else if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex() - 1; + } else { + // save last valid position + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex(); + } + } + } else { + // continue until end of line + if (nextByte == HttpCodecUtil.CR) { + if (sao.pos < sao.limit) { + nextByte = sao.bytes[sao.pos ++]; + if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex() - 2; + } + } else { + // save last valid position + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex(); + } + } else if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex() - 1; + } else { + // save last valid position + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex(); + } + } + } + ChannelBuffer buffer = undecodedChunk.slice(readerIndex, lastPosition - readerIndex); + if (found) { + // found so lastPosition is correct and final + try { + currentFileUpload.addContent(buffer, true); + // just before the CRLF and delimiter + undecodedChunk.readerIndex(lastPosition); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + } else { + // possibly the delimiter is partially found but still the last position is OK + try { + currentFileUpload.addContent(buffer, false); + // last valid char (not CR, not LF, not beginning of delimiter) + undecodedChunk.readerIndex(lastPosition); + throw new NotEnoughDataDecoderException(); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + } + } + /** * Load the field value from a Multipart request * @throws NotEnoughDataDecoderException Need more chunks * @throws ErrorDataDecoderException */ - private void loadFieldMultipart(String delimiter) + private void loadFieldMultipartStandard(String delimiter) throws NotEnoughDataDecoderException, ErrorDataDecoderException { int readerIndex = undecodedChunk.readerIndex(); try { @@ -1252,6 +1580,120 @@ public class HttpPostRequestDecoder { } } + /** + * Load the field value from a Multipart request + * @throws NotEnoughDataDecoderException Need more chunks + * @throws ErrorDataDecoderException + */ + private void loadFieldMultipart(String delimiter) + throws NotEnoughDataDecoderException, ErrorDataDecoderException { + SeekAheadOptimize sao = null; + try { + sao = new SeekAheadOptimize(undecodedChunk); + } catch (SeekAheadNoBackArray e1) { + loadFieldMultipartStandard(delimiter); + return; + } + int readerIndex = undecodedChunk.readerIndex(); + try { + // found the decoder limit + boolean newLine = true; + int index = 0; + int lastPosition = undecodedChunk.readerIndex(); + boolean found = false; + + while (sao.pos < sao.limit) { + byte nextByte = sao.bytes[sao.pos ++]; + if (newLine) { + // Check the delimiter + if (nextByte == delimiter.codePointAt(index)) { + index ++; + if (delimiter.length() == index) { + found = true; + sao.setReadPosition(0); + break; + } + continue; + } else { + newLine = false; + index = 0; + // continue until end of line + if (nextByte == HttpCodecUtil.CR) { + if (sao.pos < sao.limit) { + nextByte = sao.bytes[sao.pos ++]; + if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex() - 2; + } + } else { + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex(); + } + } else if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex() - 1; + } else { + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex(); + } + } + } else { + // continue until end of line + if (nextByte == HttpCodecUtil.CR) { + if (sao.pos < sao.limit) { + nextByte = sao.bytes[sao.pos ++]; + if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex() - 2; + } + } else { + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex(); + } + } else if (nextByte == HttpCodecUtil.LF) { + newLine = true; + index = 0; + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex() - 1; + } else { + sao.setReadPosition(0); + lastPosition = undecodedChunk.readerIndex(); + } + } + } + if (found) { + // found so lastPosition is correct + // but position is just after the delimiter (either close delimiter or simple one) + // so go back of delimiter size + try { + currentAttribute.addContent( + undecodedChunk.slice(readerIndex, lastPosition - readerIndex), true); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + undecodedChunk.readerIndex(lastPosition); + } else { + try { + currentAttribute.addContent( + undecodedChunk.slice(readerIndex, lastPosition - readerIndex), false); + } catch (IOException e) { + throw new ErrorDataDecoderException(e); + } + undecodedChunk.readerIndex(lastPosition); + throw new NotEnoughDataDecoderException(); + } + } catch (IndexOutOfBoundsException e) { + undecodedChunk.readerIndex(readerIndex); + throw new NotEnoughDataDecoderException(e); + } + } + /** * Clean the String from any unallowed character * @return the cleaned String From 675bccc9eabbf10f665ae9f078ba6e15f711af87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Br=C3=A9gier?= Date: Mon, 30 Apr 2012 13:06:53 +0300 Subject: [PATCH 7/9] Update codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java --- .../netty/handler/codec/http/HttpPostRequestDecoder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java index 50adf9a206..ce57925395 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java @@ -26,8 +26,8 @@ import java.util.TreeMap; import io.netty.buffer.ChannelBuffer; import io.netty.buffer.ChannelBuffers; -import org.jboss.netty.handler.codec.http2.HttpPostBodyUtil.SeekAheadNoBackArray; -import org.jboss.netty.handler.codec.http2.HttpPostBodyUtil.SeekAheadOptimize; +import io.netty.handler.codec.http.HttpPostBodyUtil.SeekAheadNoBackArray; +import io.netty.handler.codec.http.HttpPostBodyUtil.SeekAheadOptimize; import io.netty.handler.codec.http.HttpPostBodyUtil.TransferEncodingMechanism; /** @@ -883,7 +883,7 @@ public class HttpPostRequestDecoder { throws ErrorDataDecoderException { // --AaB03x or --AaB03x-- int readerIndex = undecodedChunk.readerIndex(); - skipControlCharacters(undecodedChunk); + skipControlCharacters(); skipOneLine(); String newline; try { @@ -924,7 +924,7 @@ public class HttpPostRequestDecoder { } // read many lines until empty line with newline found! Store all data while (!skipOneLine()) { - skipControlCharacters(undecodedChunk); + skipControlCharacters(); String newline; try { newline = readLine(); From 1bf17c7c87769c803e0af21d37bc6b93f6d96ecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Br=C3=A9gier?= Date: Tue, 1 May 2012 00:14:42 +0300 Subject: [PATCH 8/9] Add Exception to name of the Exception ;-) --- .../java/io/netty/handler/codec/http/HttpPostBodyUtil.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostBodyUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostBodyUtil.java index b1ceca7bf0..02fb319e34 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostBodyUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostBodyUtil.java @@ -119,7 +119,7 @@ final class HttpPostBodyUtil { /** * Exception when NO Backend Array is found */ - static class SeekAheadNoBackArray extends Exception { + static class SeekAheadNoBackArrayException extends Exception { private static final long serialVersionUID = -630418804938699495L; } @@ -142,9 +142,9 @@ final class HttpPostBodyUtil { * @param buffer */ SeekAheadOptimize(ChannelBuffer buffer) - throws SeekAheadNoBackArray { + throws SeekAheadNoBackArrayException { if (! buffer.hasArray()) { - throw new SeekAheadNoBackArray(); + throw new SeekAheadNoBackArrayException(); } this.buffer = buffer; this.bytes = buffer.array(); From 502e469c45fc7dff596c3f0441db712a771f5a11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Br=C3=A9gier?= Date: Tue, 1 May 2012 00:18:42 +0300 Subject: [PATCH 9/9] Add Exception to the exception class name --- .../handler/codec/http/HttpPostRequestDecoder.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java index ce57925395..1347c1be89 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpPostRequestDecoder.java @@ -26,7 +26,7 @@ import java.util.TreeMap; import io.netty.buffer.ChannelBuffer; import io.netty.buffer.ChannelBuffers; -import io.netty.handler.codec.http.HttpPostBodyUtil.SeekAheadNoBackArray; +import io.netty.handler.codec.http.HttpPostBodyUtil.SeekAheadNoBackArrayException; import io.netty.handler.codec.http.HttpPostBodyUtil.SeekAheadOptimize; import io.netty.handler.codec.http.HttpPostBodyUtil.TransferEncodingMechanism; @@ -551,7 +551,7 @@ public class HttpPostRequestDecoder { SeekAheadOptimize sao = null; try { sao = new SeekAheadOptimize(undecodedChunk); - } catch (SeekAheadNoBackArray e1) { + } catch (SeekAheadNoBackArrayException e1) { parseBodyAttributesStandard(); return; } @@ -845,7 +845,7 @@ public class HttpPostRequestDecoder { SeekAheadOptimize sao = null; try { sao = new SeekAheadOptimize(undecodedChunk); - } catch (SeekAheadNoBackArray e) { + } catch (SeekAheadNoBackArrayException e) { skipControlCharactersStandard(undecodedChunk); return; } @@ -1242,7 +1242,7 @@ public class HttpPostRequestDecoder { SeekAheadOptimize sao = null; try { sao = new SeekAheadOptimize(undecodedChunk); - } catch (SeekAheadNoBackArray e1) { + } catch (SeekAheadNoBackArrayException e1) { return readLineStandard(); } int readerIndex = undecodedChunk.readerIndex(); @@ -1382,7 +1382,7 @@ public class HttpPostRequestDecoder { SeekAheadOptimize sao = null; try { sao = new SeekAheadOptimize(undecodedChunk); - } catch (SeekAheadNoBackArray e1) { + } catch (SeekAheadNoBackArrayException e1) { readFileUploadByteMultipartStandard(delimiter); return; } @@ -1590,7 +1590,7 @@ public class HttpPostRequestDecoder { SeekAheadOptimize sao = null; try { sao = new SeekAheadOptimize(undecodedChunk); - } catch (SeekAheadNoBackArray e1) { + } catch (SeekAheadNoBackArrayException e1) { loadFieldMultipartStandard(delimiter); return; }