diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpData.java deleted file mode 100644 index 4322bbe974..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpData.java +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ChannelBuffer; -import io.netty.buffer.ChannelBuffers; -import io.netty.handler.codec.http.HttpConstants; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.charset.Charset; - -/** - * Abstract Disk HttpData implementation - */ -public abstract class AbstractDiskHttpData extends AbstractHttpData { - - protected File file; - private boolean isRenamed; - private FileChannel fileChannel; - - public AbstractDiskHttpData(String name, Charset charset, long size) { - super(name, charset, size); - } - - /** - * - * @return the real DiskFilename (basename) - */ - protected abstract String getDiskFilename(); - /** - * - * @return the default prefix - */ - protected abstract String getPrefix(); - /** - * - * @return the default base Directory - */ - protected abstract String getBaseDirectory(); - /** - * - * @return the default postfix - */ - protected abstract String getPostfix(); - /** - * - * @return True if the file should be deleted on Exit by default - */ - protected abstract boolean deleteOnExit(); - - /** - * - * @return a new Temp File from getDiskFilename(), default prefix, postfix and baseDirectory - * @throws IOException - */ - private File tempFile() throws IOException { - String newpostfix = null; - String diskFilename = getDiskFilename(); - if (diskFilename != null) { - newpostfix = "_" + diskFilename; - } else { - newpostfix = getPostfix(); - } - File tmpFile; - if (getBaseDirectory() == null) { - // create a temporary file - tmpFile = File.createTempFile(getPrefix(), newpostfix); - } else { - tmpFile = File.createTempFile(getPrefix(), newpostfix, new File( - getBaseDirectory())); - } - if (deleteOnExit()) { - tmpFile.deleteOnExit(); - } - return tmpFile; - } - - @Override - public void setContent(ChannelBuffer buffer) throws IOException { - if (buffer == null) { - throw new NullPointerException("buffer"); - } - size = buffer.readableBytes(); - if (definedSize > 0 && definedSize < size) { - throw new IOException("Out of size: " + size + " > " + definedSize); - } - if (file == null) { - file = tempFile(); - } - if (buffer.readableBytes() == 0) { - // empty file - file.createNewFile(); - return; - } - FileOutputStream outputStream = new FileOutputStream(file); - FileChannel localfileChannel = outputStream.getChannel(); - int written = 0; - while (written < size) { - written += buffer.readBytes( - localfileChannel, (int) Math.min(size - written, Integer.MAX_VALUE)); - } - localfileChannel.force(false); - localfileChannel.close(); - completed = true; - } - - @Override - public void addContent(ChannelBuffer buffer, boolean last) - throws IOException { - if (buffer != null) { - int localsize = buffer.readableBytes(); - if (definedSize > 0 && definedSize < size + localsize) { - throw new IOException("Out of size: " + (size + localsize) + - " > " + definedSize); - } - int written = 0; - if (file == null) { - file = tempFile(); - } - if (fileChannel == null) { - FileOutputStream outputStream = new FileOutputStream(file); - fileChannel = outputStream.getChannel(); - } - while (written < localsize) { - written += buffer.readBytes(fileChannel, localsize - written); - } - size += localsize; - buffer.readerIndex(buffer.readerIndex() + written); - } - if (last) { - if (file == null) { - file = tempFile(); - } - if (fileChannel == null) { - FileOutputStream outputStream = new FileOutputStream(file); - fileChannel = outputStream.getChannel(); - } - fileChannel.force(false); - fileChannel.close(); - fileChannel = null; - completed = true; - } else { - if (buffer == null) { - throw new NullPointerException("buffer"); - } - } - } - - @Override - public void setContent(File file) throws IOException { - if (this.file != null) { - delete(); - } - this.file = file; - size = file.length(); - isRenamed = true; - completed = true; - } - - @Override - public void setContent(InputStream inputStream) throws IOException { - if (inputStream == null) { - throw new NullPointerException("inputStream"); - } - if (file != null) { - delete(); - } - file = tempFile(); - FileOutputStream outputStream = new FileOutputStream(file); - FileChannel localfileChannel = outputStream.getChannel(); - byte[] bytes = new byte[4096 * 4]; - ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); - int read = inputStream.read(bytes); - int written = 0; - while (read > 0) { - byteBuffer.position(read).flip(); - written += localfileChannel.write(byteBuffer); - read = inputStream.read(bytes); - } - localfileChannel.force(false); - localfileChannel.close(); - size = written; - if (definedSize > 0 && definedSize < size) { - file.delete(); - file = null; - throw new IOException("Out of size: " + size + " > " + definedSize); - } - isRenamed = true; - completed = true; - } - - @Override - public void delete() { - if (! isRenamed) { - if (file != null) { - file.delete(); - } - } - } - - @Override - public byte[] get() throws IOException { - if (file == null) { - return new byte[0]; - } - return readFrom(file); - } - - @Override - public ChannelBuffer getChannelBuffer() throws IOException { - if (file == null) { - return ChannelBuffers.EMPTY_BUFFER; - } - byte[] array = readFrom(file); - return ChannelBuffers.wrappedBuffer(array); - } - - @Override - public ChannelBuffer getChunk(int length) throws IOException { - if (file == null || length == 0) { - return ChannelBuffers.EMPTY_BUFFER; - } - if (fileChannel == null) { - FileInputStream inputStream = new FileInputStream(file); - fileChannel = inputStream.getChannel(); - } - int read = 0; - ByteBuffer byteBuffer = ByteBuffer.allocate(length); - while (read < length) { - int readnow = fileChannel.read(byteBuffer); - if (readnow == -1) { - fileChannel.close(); - fileChannel = null; - break; - } else { - read += readnow; - } - } - if (read == 0) { - return ChannelBuffers.EMPTY_BUFFER; - } - byteBuffer.flip(); - ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(byteBuffer); - buffer.readerIndex(0); - buffer.writerIndex(read); - return buffer; - } - - @Override - public String getString() throws IOException { - return getString(HttpConstants.DEFAULT_CHARSET); - } - - @Override - public String getString(Charset encoding) throws IOException { - if (file == null) { - return ""; - } - if (encoding == null) { - byte[] array = readFrom(file); - return new String(array, HttpConstants.DEFAULT_CHARSET); - } - byte[] array = readFrom(file); - return new String(array, encoding); - } - - @Override - public boolean isInMemory() { - return false; - } - - @Override - public boolean renameTo(File dest) throws IOException { - if (dest == null) { - throw new NullPointerException("dest"); - } - if (!file.renameTo(dest)) { - // must copy - FileInputStream inputStream = new FileInputStream(file); - FileOutputStream outputStream = new FileOutputStream(dest); - 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; - isRenamed = true; - return true; - } else { - dest.delete(); - return false; - } - } - file = dest; - isRenamed = true; - return true; - } - - /** - * Utility function - * @param src - * @return the array of bytes - * @throws IOException - */ - private static byte[] readFrom(File src) throws IOException { - long srcsize = src.length(); - if (srcsize > Integer.MAX_VALUE) { - throw new IllegalArgumentException( - "File too big to be loaded in memory"); - } - FileInputStream inputStream = new FileInputStream(src); - FileChannel fileChannel = inputStream.getChannel(); - byte[] array = new byte[(int) srcsize]; - ByteBuffer byteBuffer = ByteBuffer.wrap(array); - int read = 0; - while (read < srcsize) { - read += fileChannel.read(byteBuffer); - } - fileChannel.close(); - return array; - } - - @Override - public File getFile() throws IOException { - return file; - } - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractHttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractHttpData.java deleted file mode 100644 index 092eb95121..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractHttpData.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.handler.codec.http.HttpConstants; - -import java.nio.charset.Charset; - -/** - * Abstract HttpData implementation - */ -public abstract class AbstractHttpData implements HttpData { - - protected final String name; - protected long definedSize; - protected long size; - protected Charset charset = HttpConstants.DEFAULT_CHARSET; - protected boolean completed; - - public AbstractHttpData(String name, Charset charset, long size) { - if (name == null) { - throw new NullPointerException("name"); - } - name = name.trim(); - if (name.length() == 0) { - throw new IllegalArgumentException("empty name"); - } - - for (int i = 0; i < name.length(); i ++) { - char c = name.charAt(i); - if (c > 127) { - throw new IllegalArgumentException( - "name contains non-ascii character: " + name); - } - - // Check prohibited characters. - switch (c) { - case '=': - case ',': - case ';': - case ' ': - case '\t': - case '\r': - case '\n': - case '\f': - case 0x0b: // Vertical tab - throw new IllegalArgumentException( - "name contains one of the following prohibited characters: " + - "=,; \\t\\r\\n\\v\\f: " + name); - } - } - this.name = name; - if (charset != null) { - setCharset(charset); - } - definedSize = size; - } - - @Override - public String getName() { - return name; - } - - @Override - public boolean isCompleted() { - return completed; - } - - @Override - public Charset getCharset() { - return charset; - } - - @Override - public void setCharset(Charset charset) { - if (charset == null) { - throw new NullPointerException("charset"); - } - this.charset = charset; - } - - @Override - public long length() { - return size; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpData.java deleted file mode 100644 index 75a49cdd24..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpData.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ChannelBuffer; -import io.netty.buffer.ChannelBuffers; -import io.netty.handler.codec.http.HttpConstants; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.charset.Charset; - -/** - * Abstract Memory HttpData implementation - */ -public abstract class AbstractMemoryHttpData extends AbstractHttpData { - - private ChannelBuffer channelBuffer; - private int chunkPosition; - protected boolean isRenamed; - - public AbstractMemoryHttpData(String name, Charset charset, long size) { - super(name, charset, size); - } - - @Override - public void setContent(ChannelBuffer buffer) throws IOException { - if (buffer == null) { - throw new NullPointerException("buffer"); - } - long localsize = buffer.readableBytes(); - if (definedSize > 0 && definedSize < localsize) { - throw new IOException("Out of size: " + localsize + " > " + - definedSize); - } - channelBuffer = buffer; - size = localsize; - completed = true; - } - - @Override - public void setContent(InputStream inputStream) throws IOException { - if (inputStream == null) { - throw new NullPointerException("inputStream"); - } - ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(); - byte[] bytes = new byte[4096 * 4]; - int read = inputStream.read(bytes); - int written = 0; - while (read > 0) { - buffer.writeBytes(bytes); - written += read; - read = inputStream.read(bytes); - } - size = written; - if (definedSize > 0 && definedSize < size) { - throw new IOException("Out of size: " + size + " > " + definedSize); - } - channelBuffer = buffer; - completed = true; - } - - @Override - public void addContent(ChannelBuffer buffer, boolean last) - throws IOException { - if (buffer != null) { - long localsize = buffer.readableBytes(); - if (definedSize > 0 && definedSize < size + localsize) { - throw new IOException("Out of size: " + (size + localsize) + - " > " + definedSize); - } - size += localsize; - if (channelBuffer == null) { - channelBuffer = buffer; - } else { - channelBuffer = ChannelBuffers.wrappedBuffer( - channelBuffer, buffer); - } - } - if (last) { - completed = true; - } else { - if (buffer == null) { - throw new NullPointerException("buffer"); - } - } - } - - @Override - public void setContent(File file) throws IOException { - if (file == null) { - throw new NullPointerException("file"); - } - long newsize = file.length(); - if (newsize > Integer.MAX_VALUE) { - throw new IllegalArgumentException( - "File too big to be loaded in memory"); - } - FileInputStream inputStream = new FileInputStream(file); - FileChannel fileChannel = inputStream.getChannel(); - byte[] array = new byte[(int) newsize]; - ByteBuffer byteBuffer = ByteBuffer.wrap(array); - int read = 0; - while (read < newsize) { - read += fileChannel.read(byteBuffer); - } - fileChannel.close(); - byteBuffer.flip(); - channelBuffer = ChannelBuffers.wrappedBuffer(byteBuffer); - size = newsize; - completed = true; - } - - @Override - public void delete() { - // nothing to do - } - - @Override - public byte[] get() { - if (channelBuffer == null) { - return new byte[0]; - } - byte[] array = new byte[channelBuffer.readableBytes()]; - channelBuffer.getBytes(channelBuffer.readerIndex(), array); - return array; - } - - @Override - public String getString() { - return getString(HttpConstants.DEFAULT_CHARSET); - } - - @Override - public String getString(Charset encoding) { - if (channelBuffer == null) { - return ""; - } - if (encoding == null) { - return getString(HttpConstants.DEFAULT_CHARSET); - } - return channelBuffer.toString(encoding); - } - - /** - * Utility to go from a In Memory FileUpload - * to a Disk (or another implementation) FileUpload - * @return the attached ChannelBuffer containing the actual bytes - */ - @Override - public ChannelBuffer getChannelBuffer() { - return channelBuffer; - } - - @Override - public ChannelBuffer getChunk(int length) throws IOException { - if (channelBuffer == null || length == 0 || channelBuffer.readableBytes() == 0) { - chunkPosition = 0; - return ChannelBuffers.EMPTY_BUFFER; - } - int sizeLeft = channelBuffer.readableBytes() - chunkPosition; - if (sizeLeft == 0) { - chunkPosition = 0; - return ChannelBuffers.EMPTY_BUFFER; - } - int sliceLength = length; - if (sizeLeft < length) { - sliceLength = sizeLeft; - } - ChannelBuffer chunk = channelBuffer.slice(chunkPosition, sliceLength); - chunkPosition += sliceLength; - return chunk; - } - - @Override - public boolean isInMemory() { - return true; - } - - @Override - public boolean renameTo(File dest) throws IOException { - if (dest == null) { - throw new NullPointerException("dest"); - } - if (channelBuffer == null) { - // empty file - dest.createNewFile(); - isRenamed = true; - return true; - } - int length = channelBuffer.readableBytes(); - FileOutputStream outputStream = new FileOutputStream(dest); - FileChannel fileChannel = outputStream.getChannel(); - int written = 0; - while (written < length) { - written += channelBuffer.readBytes(fileChannel, length - written); - } - fileChannel.force(false); - fileChannel.close(); - isRenamed = true; - return written == length; - } - - @Override - public File getFile() throws IOException { - throw new IOException("Not represented by a file"); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/Attribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/Attribute.java deleted file mode 100644 index b39d107ec3..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/Attribute.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import java.io.IOException; - -/** - * Attribute interface - */ -public interface Attribute extends HttpData { - /** - * Returns the value of this HttpData. - */ - String getValue() throws IOException; - - /** - * Sets the value of this HttpData. - * @param value - */ - void setValue(String value) throws IOException; -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DefaultHttpDataFactory.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DefaultHttpDataFactory.java deleted file mode 100644 index 368d980012..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DefaultHttpDataFactory.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.handler.codec.http.HttpRequest; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Default factory giving Attribute and FileUpload according to constructor - * - * Attribute and FileUpload could be :
- * - MemoryAttribute, DiskAttribute or MixedAttribute
- * - MemoryFileUpload, DiskFileUpload or MixedFileUpload
- * according to the constructor. - */ -public class DefaultHttpDataFactory implements HttpDataFactory { - /** - * Proposed default MINSIZE as 16 KB. - */ - public static long MINSIZE = 0x4000; - - private final boolean useDisk; - - private final boolean checkSize; - - private long minSize; - - /** - * Keep all HttpDatas until cleanAllHttpDatas() is called. - */ - private final ConcurrentHashMap> requestFileDeleteMap = - new ConcurrentHashMap>(); - /** - * HttpData will be in memory if less than default size (16KB). - * The type will be Mixed. - */ - public DefaultHttpDataFactory() { - useDisk = false; - checkSize = true; - minSize = MINSIZE; - } - - /** - * HttpData will be always on Disk if useDisk is True, else always in Memory if False - * @param useDisk - */ - public DefaultHttpDataFactory(boolean useDisk) { - this.useDisk = useDisk; - checkSize = false; - } - - /** - * HttpData will be on Disk if the size of the file is greater than minSize, else it - * will be in memory. The type will be Mixed. - * @param minSize - */ - public DefaultHttpDataFactory(long minSize) { - useDisk = false; - checkSize = true; - this.minSize = minSize; - } - - /** - * - * @param request - * @return the associated list of Files for the request - */ - private List getList(HttpRequest request) { - List list = requestFileDeleteMap.get(request); - if (list == null) { - list = new ArrayList(); - requestFileDeleteMap.put(request, list); - } - return list; - } - - @Override - public Attribute createAttribute(HttpRequest request, String name) { - if (useDisk) { - Attribute attribute = new DiskAttribute(name); - List fileToDelete = getList(request); - fileToDelete.add(attribute); - return attribute; - } else if (checkSize) { - Attribute attribute = new MixedAttribute(name, minSize); - List fileToDelete = getList(request); - fileToDelete.add(attribute); - return attribute; - } - return new MemoryAttribute(name); - } - - @Override - public Attribute createAttribute(HttpRequest request, String name, String value) { - if (useDisk) { - Attribute attribute; - try { - attribute = new DiskAttribute(name, value); - } catch (IOException e) { - // revert to Mixed mode - attribute = new MixedAttribute(name, value, minSize); - } - List fileToDelete = getList(request); - fileToDelete.add(attribute); - return attribute; - } else if (checkSize) { - Attribute attribute = new MixedAttribute(name, value, minSize); - List fileToDelete = getList(request); - fileToDelete.add(attribute); - return attribute; - } - try { - return new MemoryAttribute(name, value); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } - - @Override - public FileUpload createFileUpload(HttpRequest request, String name, String filename, - String contentType, String contentTransferEncoding, Charset charset, - long size) { - if (useDisk) { - FileUpload fileUpload = new DiskFileUpload(name, filename, contentType, - contentTransferEncoding, charset, size); - List fileToDelete = getList(request); - fileToDelete.add(fileUpload); - return fileUpload; - } else if (checkSize) { - FileUpload fileUpload = new MixedFileUpload(name, filename, contentType, - contentTransferEncoding, charset, size, minSize); - List fileToDelete = getList(request); - fileToDelete.add(fileUpload); - return fileUpload; - } - return new MemoryFileUpload(name, filename, contentType, - contentTransferEncoding, charset, size); - } - - @Override - public void removeHttpDataFromClean(HttpRequest request, InterfaceHttpData data) { - if (data instanceof HttpData) { - List fileToDelete = getList(request); - fileToDelete.remove(data); - } - } - - @Override - public void cleanRequestHttpDatas(HttpRequest request) { - List fileToDelete = requestFileDeleteMap.remove(request); - if (fileToDelete != null) { - for (HttpData data: fileToDelete) { - data.delete(); - } - fileToDelete.clear(); - } - } - - @Override - public void cleanAllHttpDatas() { - for (HttpRequest request : requestFileDeleteMap.keySet()) { - List fileToDelete = requestFileDeleteMap.get(request); - if (fileToDelete != null) { - for (HttpData data: fileToDelete) { - data.delete(); - } - fileToDelete.clear(); - } - requestFileDeleteMap.remove(request); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskAttribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskAttribute.java deleted file mode 100644 index df50437a78..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskAttribute.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ChannelBuffer; -import io.netty.buffer.ChannelBuffers; -import io.netty.handler.codec.http.HttpConstants; - -import java.io.IOException; - -/** - * Disk implementation of Attributes - */ -public class DiskAttribute extends AbstractDiskHttpData implements Attribute { - public static String baseDirectory; - - public static boolean deleteOnExitTemporaryFile = true; - - public static String prefix = "Attr_"; - - public static String postfix = ".att"; - - /** - * Constructor used for huge Attribute - * @param name - */ - public DiskAttribute(String name) { - super(name, HttpConstants.DEFAULT_CHARSET, 0); - } - /** - * - * @param name - * @param value - * @throws NullPointerException - * @throws IllegalArgumentException - * @throws IOException - */ - public DiskAttribute(String name, String value) throws IOException { - super(name, HttpConstants.DEFAULT_CHARSET, 0); // Attribute have no default size - setValue(value); - } - - @Override - public HttpDataType getHttpDataType() { - return HttpDataType.Attribute; - } - - @Override - public String getValue() throws IOException { - byte [] bytes = get(); - return new String(bytes, charset); - } - - @Override - public void setValue(String value) throws IOException { - if (value == null) { - throw new NullPointerException("value"); - } - byte [] bytes = value.getBytes(charset); - ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(bytes); - if (definedSize > 0) { - definedSize = buffer.readableBytes(); - } - setContent(buffer); - } - - @Override - public void addContent(ChannelBuffer buffer, boolean last) throws IOException { - int localsize = buffer.readableBytes(); - if (definedSize > 0 && definedSize < size + localsize) { - definedSize = size + localsize; - } - super.addContent(buffer, last); - } - @Override - public int hashCode() { - return getName().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Attribute)) { - return false; - } - Attribute attribute = (Attribute) o; - return getName().equalsIgnoreCase(attribute.getName()); - } - - @Override - public int compareTo(InterfaceHttpData arg0) { - if (!(arg0 instanceof Attribute)) { - throw new ClassCastException("Cannot compare " + getHttpDataType() + - " with " + arg0.getHttpDataType()); - } - return compareTo((Attribute) arg0); - } - - public int compareTo(Attribute o) { - return getName().compareToIgnoreCase(o.getName()); - } - - @Override - public String toString() { - try { - return getName() + "=" + getValue(); - } catch (IOException e) { - return getName() + "=IoException"; - } - } - - @Override - protected boolean deleteOnExit() { - return deleteOnExitTemporaryFile; - } - - @Override - protected String getBaseDirectory() { - return baseDirectory; - } - - @Override - protected String getDiskFilename() { - return getName() + postfix; - } - - @Override - protected String getPostfix() { - return postfix; - } - - @Override - protected String getPrefix() { - return prefix; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskFileUpload.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskFileUpload.java deleted file mode 100644 index dbbe1d6c16..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskFileUpload.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.handler.codec.http.HttpHeaders; - -import java.io.File; -import java.nio.charset.Charset; - -/** - * Disk FileUpload implementation that stores file into real files - */ -public class DiskFileUpload extends AbstractDiskHttpData implements FileUpload { - public static String baseDirectory; - - public static boolean deleteOnExitTemporaryFile = true; - - public static String prefix = "FUp_"; - - public static String postfix = ".tmp"; - - private String filename; - - private String contentType; - - private String contentTransferEncoding; - - public DiskFileUpload(String name, String filename, String contentType, - String contentTransferEncoding, Charset charset, long size) { - super(name, charset, size); - setFilename(filename); - setContentType(contentType); - setContentTransferEncoding(contentTransferEncoding); - } - - @Override - public HttpDataType getHttpDataType() { - return HttpDataType.FileUpload; - } - - @Override - public String getFilename() { - return filename; - } - - @Override - public void setFilename(String filename) { - if (filename == null) { - throw new NullPointerException("filename"); - } - this.filename = filename; - } - - @Override - public int hashCode() { - return getName().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Attribute)) { - return false; - } - Attribute attribute = (Attribute) o; - return getName().equalsIgnoreCase(attribute.getName()); - } - - @Override - public int compareTo(InterfaceHttpData arg0) { - if (!(arg0 instanceof FileUpload)) { - throw new ClassCastException("Cannot compare " + getHttpDataType() + - " with " + arg0.getHttpDataType()); - } - return compareTo((FileUpload) arg0); - } - - public int compareTo(FileUpload o) { - int v; - v = getName().compareToIgnoreCase(o.getName()); - if (v != 0) { - return v; - } - // TODO should we compare size ? - return v; - } - - @Override - public void setContentType(String contentType) { - if (contentType == null) { - throw new NullPointerException("contentType"); - } - this.contentType = contentType; - } - - @Override - public String getContentType() { - return contentType; - } - - @Override - public String getContentTransferEncoding() { - return contentTransferEncoding; - } - - @Override - public void setContentTransferEncoding(String contentTransferEncoding) { - this.contentTransferEncoding = contentTransferEncoding; - } - - @Override - public String toString() { - return HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + - HttpPostBodyUtil.FORM_DATA + "; " + HttpPostBodyUtil.NAME + "=\"" + getName() + - "\"; " + HttpPostBodyUtil.FILENAME + "=\"" + filename + "\"\r\n" + - HttpHeaders.Names.CONTENT_TYPE + ": " + contentType + - (charset != null? "; " + HttpHeaders.Values.CHARSET + "=" + charset + "\r\n" : "\r\n") + - HttpHeaders.Names.CONTENT_LENGTH + ": " + length() + "\r\n" + - "Completed: " + isCompleted() + - "\r\nIsInMemory: " + isInMemory() + "\r\nRealFile: " + - file.getAbsolutePath() + " DefaultDeleteAfter: " + - deleteOnExitTemporaryFile; - } - - @Override - protected boolean deleteOnExit() { - return deleteOnExitTemporaryFile; - } - - @Override - protected String getBaseDirectory() { - return baseDirectory; - } - - @Override - protected String getDiskFilename() { - File file = new File(filename); - return file.getName(); - } - - @Override - protected String getPostfix() { - return postfix; - } - - @Override - protected String getPrefix() { - return prefix; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/FileUpload.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/FileUpload.java deleted file mode 100644 index b15681dfe1..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/FileUpload.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -/** - * FileUpload interface that could be in memory, on temporary file or any other implementations. - * - * Most methods are inspired from java.io.File API. - */ -public interface FileUpload extends HttpData { - /** - * Returns the original filename in the client's filesystem, - * as provided by the browser (or other client software). - * @return the original filename - */ - String getFilename(); - - /** - * Set the original filename - * @param filename - */ - void setFilename(String filename); - - /** - * Set the Content Type passed by the browser if defined - * @param contentType Content Type to set - must be not null - */ - void setContentType(String contentType); - - /** - * Returns the content type passed by the browser or null if not defined. - * @return the content type passed by the browser or null if not defined. - */ - String getContentType(); - - /** - * Set the Content-Transfer-Encoding type from String as 7bit, 8bit or binary - * @param contentTransferEncoding - */ - void setContentTransferEncoding(String contentTransferEncoding); - - /** - * Returns the Content-Transfer-Encoding - * @return the Content-Transfer-Encoding - */ - String getContentTransferEncoding(); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpData.java deleted file mode 100644 index 8764da435a..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpData.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ChannelBuffer; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; - -/** - * Extended interface for InterfaceHttpData - */ -public interface HttpData extends InterfaceHttpData { - /** - * Set the content from the ChannelBuffer (erase any previous data) - * @param buffer must be not null - * @exception IOException - */ - void setContent(ChannelBuffer buffer) throws IOException; - - /** - * Add the content from the ChannelBuffer - * @param buffer must be not null except if last is set to False - * @param last True of the buffer is the last one - * @exception IOException - */ - void addContent(ChannelBuffer buffer, boolean last) - throws IOException; - - /** - * Set the content from the file (erase any previous data) - * @param file must be not null - * @exception IOException - */ - void setContent(File file) throws IOException; - - /** - * Set the content from the inputStream (erase any previous data) - * @param inputStream must be not null - * @exception IOException - */ - void setContent(InputStream inputStream) throws IOException; - - /** - * - * @return True if the InterfaceHttpData is completed (all data are stored) - */ - boolean isCompleted(); - - /** - * Returns the size in byte of the InterfaceHttpData - * @return the size of the InterfaceHttpData - */ - long length(); - - /** - * Deletes the underlying storage for a file item, - * including deleting any associated temporary disk file. - */ - void delete(); - - /** - * Returns the contents of the file item as an array of bytes. - * @return the contents of the file item as an array of bytes. - * @exception IOException - */ - byte[] get() throws IOException; - - /** - * Returns the content of the file item as a ChannelBuffer - * @return the content of the file item as a ChannelBuffer - * @throws IOException - */ - ChannelBuffer getChannelBuffer() throws IOException; - - /** - * Returns a ChannelBuffer for the content from the current position with at most length read - * bytes, increasing the current position of the Bytes read. Once it arrives at the end, - * it returns an EMPTY_BUFFER and it resets the current position to 0. - * @param length - * @return a ChannelBuffer for the content from the current position or - * an EMPTY_BUFFER if there is no more data to return - * @throws IOException - */ - ChannelBuffer getChunk(int length) throws IOException; - - /** - * Returns the contents of the file item as a String, using the default character encoding. - * @return the contents of the file item as a String, using the default character encoding. - * @exception IOException - */ - String getString() throws IOException; - - /** - * Returns the contents of the file item as a String, using the specified charset. - * @param encoding the charset to use - * @return the contents of the file item as a String, using the specified charset. - * @exception IOException - */ - String getString(Charset encoding) throws IOException; - - /** - * Set the Charset passed by the browser if defined - * @param charset Charset to set - must be not null - */ - void setCharset(Charset charset); - - /** - * Returns the Charset passed by the browser or null if not defined. - * @return the Charset passed by the browser or null if not defined. - */ - Charset getCharset(); - - /** - * A convenience method to write an uploaded item to disk. - * If a previous one exists, it will be deleted. - * Once this method is called, if successful, the new file will be out of the cleaner - * of the factory that creates the original InterfaceHttpData object. - * @param dest destination file - must be not null - * @return True if the write is successful - * @exception IOException - */ - boolean renameTo(File dest) throws IOException; - - /** - * Provides a hint as to whether or not the file contents will be read from memory. - * @return True if the file contents is in memory. - */ - boolean isInMemory(); - - /** - * - * @return the associated File if this data is represented in a file - * @exception IOException if this data is not represented by a file - */ - File getFile() throws IOException; - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpDataFactory.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpDataFactory.java deleted file mode 100644 index 2e554d993e..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpDataFactory.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.handler.codec.http.HttpRequest; - -import java.nio.charset.Charset; - -/** - * Interface to enable creation of InterfaceHttpData objects - */ -public interface HttpDataFactory { - /** - * - * @param request associated request - * @param name - * @return a new Attribute with no value - * @throws NullPointerException - * @throws IllegalArgumentException - */ - Attribute createAttribute(HttpRequest request, String name); - - /** - * - * @param request associated request - * @param name - * @param value - * @return a new Attribute - * @throws NullPointerException - * @throws IllegalArgumentException - */ - Attribute createAttribute(HttpRequest request, String name, String value); - - /** - * - * @param request associated request - * @param name - * @param filename - * @param contentType - * @param charset - * @param size the size of the Uploaded file - * @return a new FileUpload - */ - FileUpload createFileUpload(HttpRequest request, String name, String filename, - String contentType, String contentTransferEncoding, Charset charset, - long size); - - /** - * Remove the given InterfaceHttpData from clean list (will not delete the file, except if the file - * is still a temporary one as setup at construction) - * @param request associated request - * @param data - */ - void removeHttpDataFromClean(HttpRequest request, InterfaceHttpData data); - - /** - * Remove all InterfaceHttpData from virtual File storage from clean list for the request - * - * @param request associated request - */ - void cleanRequestHttpDatas(HttpRequest request); - - /** - * Remove all InterfaceHttpData from virtual File storage from clean list for all requests - */ - void cleanAllHttpDatas(); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostBodyUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostBodyUtil.java deleted file mode 100644 index b583937b81..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostBodyUtil.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ChannelBuffer; -import io.netty.util.CharsetUtil; - -import java.nio.charset.Charset; - -/** - * Shared Static object between HttpMessageDecoder, HttpPostRequestDecoder and HttpPostRequestEncoder - */ -final class HttpPostBodyUtil { - - public static int chunkSize = 8096; - /** - * HTTP content disposition header name. - */ - public static final String CONTENT_DISPOSITION = "Content-Disposition"; - - public static final String NAME = "name"; - - public static final String FILENAME = "filename"; - - /** - * Content-disposition value for form data. - */ - public static final String FORM_DATA = "form-data"; - - /** - * Content-disposition value for file attachment. - */ - public static final String ATTACHMENT = "attachment"; - - /** - * Content-disposition value for file attachment. - */ - public static final String FILE = "file"; - - /** - * HTTP content type body attribute for multiple uploads. - */ - public static final String MULTIPART_MIXED = "multipart/mixed"; - - /** - * Charset for 8BIT - */ - public static final Charset ISO_8859_1 = CharsetUtil.ISO_8859_1; - - /** - * Charset for 7BIT - */ - public static final Charset US_ASCII = CharsetUtil.US_ASCII; - - /** - * Default Content-Type in binary form - */ - public static final String DEFAULT_BINARY_CONTENT_TYPE = "application/octet-stream"; - - /** - * Default Content-Type in Text form - */ - public static final String DEFAULT_TEXT_CONTENT_TYPE = "text/plain"; - - /** - * Allowed mechanism for multipart - * mechanism := "7bit" - / "8bit" - / "binary" - Not allowed: "quoted-printable" - / "base64" - */ - public enum TransferEncodingMechanism { - /** - * Default encoding - */ - BIT7("7bit"), - /** - * Short lines but not in ASCII - no encoding - */ - BIT8("8bit"), - /** - * Could be long text not in ASCII - no encoding - */ - BINARY("binary"); - - public String value; - - TransferEncodingMechanism(String value) { - this.value = value; - } - - TransferEncodingMechanism() { - value = name(); - } - - @Override - public String toString() { - return value; - } - } - - private HttpPostBodyUtil() { - } - - /** - * Exception when NO Backend Array is found - */ - static class SeekAheadNoBackArrayException extends Exception { - - static final SeekAheadNoBackArrayException INSTANCE = new SeekAheadNoBackArrayException(); - - private static final long serialVersionUID = -630418804938699495L; - - private SeekAheadNoBackArrayException() { - // Hide - } - - @Override - public Throwable fillInStackTrace() { - return this; - } - } - - /** - * This class intends to decrease the CPU in seeking ahead some bytes in - * HttpPostRequestDecoder - */ - static class SeekAheadOptimize { - byte[] bytes; - - int readerIndex; - - int pos; - - int limit; - - ChannelBuffer buffer; - - /** - * @param buffer - */ - SeekAheadOptimize(ChannelBuffer buffer) - throws SeekAheadNoBackArrayException { - if (! buffer.hasArray()) { - throw SeekAheadNoBackArrayException.INSTANCE; - } - this.buffer = buffer; - bytes = buffer.array(); - pos = readerIndex = buffer.readerIndex(); - 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() { - buffer = null; - bytes = null; - limit = 0; - pos = 0; - readerIndex = 0; - } - } - - /** - * Find the first non whitespace - * @param sb - * @param offset - * @return the rank of the first non whitespace - */ - static int findNonWhitespace(String sb, int offset) { - int result; - for (result = offset; result < sb.length(); result ++) { - if (!Character.isWhitespace(sb.charAt(result))) { - break; - } - } - return result; - } - - /** - * Find the first whitespace - * @param sb - * @param offset - * @return the rank of the first whitespace - */ - static int findWhitespace(String sb, int offset) { - int result; - for (result = offset; result < sb.length(); result ++) { - if (Character.isWhitespace(sb.charAt(result))) { - break; - } - } - return result; - } - - /** - * Find the end of String - * @param sb - * @return the rank of the end of string - */ - static int findEndOfString(String sb) { - int result; - for (result = sb.length(); result > 0; result --) { - if (!Character.isWhitespace(sb.charAt(result - 1))) { - break; - } - } - return result; - } - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java deleted file mode 100644 index 793589e271..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java +++ /dev/null @@ -1,1947 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ChannelBuffer; -import io.netty.buffer.ChannelBuffers; -import io.netty.handler.codec.http.HttpChunk; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadNoBackArrayException; -import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize; -import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.TransferEncodingMechanism; -import io.netty.util.internal.CaseIgnoringComparator; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -/** - * This decoder will decode Body and can handle POST BODY. - */ -public class HttpPostRequestDecoder { - /** - * Factory used to create InterfaceHttpData - */ - private final HttpDataFactory factory; - - /** - * Request to decode - */ - private final HttpRequest request; - - /** - * Default charset to use - */ - private final Charset charset; - - /** - * Does request have a body to decode - */ - private boolean bodyToDecode; - - /** - * Does the last chunk already received - */ - private boolean isLastChunk; - - /** - * HttpDatas from Body - */ - private final List bodyListHttpData = new ArrayList(); - - /** - * HttpDatas as Map from Body - */ - private final Map> bodyMapHttpData = new TreeMap>( - CaseIgnoringComparator.INSTANCE); - - /** - * The current channelBuffer - */ - private ChannelBuffer undecodedChunk; - - /** - * Does this request is a Multipart request - */ - private boolean isMultipart; - - /** - * Body HttpDatas current position - */ - private int bodyListHttpDataRank; - - /** - * If multipart, this is the boundary for the flobal multipart - */ - private String multipartDataBoundary; - - /** - * If multipart, there could be internal multiparts (mixed) to the global multipart. - * Only one level is allowed. - */ - private String multipartMixedBoundary; - - /** - * Current status - */ - private MultiPartStatus currentStatus = MultiPartStatus.NOTSTARTED; - - /** - * Used in Multipart - */ - private Map currentFieldAttributes; - - /** - * The current FileUpload that is currently in decode process - */ - private FileUpload currentFileUpload; - - /** - * The current Attribute that is currently in decode process - */ - private Attribute currentAttribute; - - /** - * - * @param request the request to decode - * @throws NullPointerException for request - * @throws IncompatibleDataDecoderException if the request has no body to decode - * @throws ErrorDataDecoderException if the default charset was wrong when decoding or other errors - */ - public HttpPostRequestDecoder(HttpRequest request) - throws ErrorDataDecoderException, IncompatibleDataDecoderException { - this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), - request, HttpConstants.DEFAULT_CHARSET); - } - - /** - * - * @param factory the factory used to create InterfaceHttpData - * @param request the request to decode - * @throws NullPointerException for request or factory - * @throws IncompatibleDataDecoderException if the request has no body to decode - * @throws ErrorDataDecoderException if the default charset was wrong when decoding or other errors - */ - public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request) - throws ErrorDataDecoderException, IncompatibleDataDecoderException { - this(factory, request, HttpConstants.DEFAULT_CHARSET); - } - - /** - * - * @param factory the factory used to create InterfaceHttpData - * @param request the request to decode - * @param charset the charset to use as default - * @throws NullPointerException for request or charset or factory - * @throws IncompatibleDataDecoderException if the request has no body to decode - * @throws ErrorDataDecoderException if the default charset was wrong when decoding or other errors - */ - public HttpPostRequestDecoder(HttpDataFactory factory, HttpRequest request, - Charset charset) throws ErrorDataDecoderException, - IncompatibleDataDecoderException { - if (factory == null) { - throw new NullPointerException("factory"); - } - if (request == null) { - throw new NullPointerException("request"); - } - if (charset == null) { - throw new NullPointerException("charset"); - } - this.request = request; - HttpMethod method = request.getMethod(); - if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PATCH)) { - bodyToDecode = true; - } - this.charset = charset; - this.factory = factory; - // Fill default values - if (this.request.containsHeader(HttpHeaders.Names.CONTENT_TYPE)) { - checkMultipart(this.request.getHeader(HttpHeaders.Names.CONTENT_TYPE)); - } else { - isMultipart = false; - } - if (!bodyToDecode) { - throw new IncompatibleDataDecoderException("No Body to decode"); - } - if (!this.request.isChunked()) { - undecodedChunk = this.request.getContent(); - isLastChunk = true; - parseBody(); - } - } - - /** - * states follow - * NOTSTARTED PREAMBLE ( - * (HEADERDELIMITER DISPOSITION (FIELD | FILEUPLOAD))* - * (HEADERDELIMITER DISPOSITION MIXEDPREAMBLE - * (MIXEDDELIMITER MIXEDDISPOSITION MIXEDFILEUPLOAD)+ - * MIXEDCLOSEDELIMITER)* - * CLOSEDELIMITER)+ EPILOGUE - * - * First status is: NOSTARTED - - Content-type: multipart/form-data, boundary=AaB03x => PREAMBLE in Header - - --AaB03x => HEADERDELIMITER - content-disposition: form-data; name="field1" => DISPOSITION - - Joe Blow => FIELD - --AaB03x => HEADERDELIMITER - content-disposition: form-data; name="pics" => DISPOSITION - Content-type: multipart/mixed, boundary=BbC04y - - --BbC04y => MIXEDDELIMITER - Content-disposition: attachment; filename="file1.txt" => MIXEDDISPOSITION - Content-Type: text/plain - - ... contents of file1.txt ... => MIXEDFILEUPLOAD - --BbC04y => MIXEDDELIMITER - Content-disposition: file; filename="file2.gif" => MIXEDDISPOSITION - Content-type: image/gif - Content-Transfer-Encoding: binary - - ...contents of file2.gif... => MIXEDFILEUPLOAD - --BbC04y-- => MIXEDCLOSEDELIMITER - --AaB03x-- => CLOSEDELIMITER - - Once CLOSEDELIMITER is found, last status is EPILOGUE - */ - private enum MultiPartStatus { - NOTSTARTED, - PREAMBLE, - HEADERDELIMITER, - DISPOSITION, - FIELD, - FILEUPLOAD, - MIXEDPREAMBLE, - MIXEDDELIMITER, - MIXEDDISPOSITION, - MIXEDFILEUPLOAD, - MIXEDCLOSEDELIMITER, - CLOSEDELIMITER, - PREEPILOGUE, - EPILOGUE - } - - /** - * Check from the request ContentType if this request is a Multipart request. - * @param contentType - * @throws ErrorDataDecoderException - */ - private void checkMultipart(String contentType) - throws ErrorDataDecoderException { - // Check if Post using "multipart/form-data; boundary=--89421926422648" - String[] headerContentType = splitHeaderContentType(contentType); - if (headerContentType[0].toLowerCase().startsWith( - HttpHeaders.Values.MULTIPART_FORM_DATA) && - headerContentType[1].toLowerCase().startsWith( - HttpHeaders.Values.BOUNDARY)) { - String[] boundary = headerContentType[1].split("="); - if (boundary.length != 2) { - throw new ErrorDataDecoderException("Needs a boundary value"); - } - multipartDataBoundary = "--" + boundary[1]; - isMultipart = true; - currentStatus = MultiPartStatus.HEADERDELIMITER; - } else { - isMultipart = false; - } - } - - /** - * True if this request is a Multipart request - * @return True if this request is a Multipart request - */ - public boolean isMultipart() { - return isMultipart; - } - - /** - * This method returns a List of all HttpDatas from body.
- * - * If chunked, all chunks must have been offered using offer() method. - * If not, NotEnoughDataDecoderException will be raised. - * - * @return the list of HttpDatas from Body part for POST method - * @throws NotEnoughDataDecoderException Need more chunks - */ - public List getBodyHttpDatas() - throws NotEnoughDataDecoderException { - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - return bodyListHttpData; - } - - /** - * This method returns a List of all HttpDatas with the given name from body.
- * - * If chunked, all chunks must have been offered using offer() method. - * If not, NotEnoughDataDecoderException will be raised. - - * @param name - * @return All Body HttpDatas with the given name (ignore case) - * @throws NotEnoughDataDecoderException need more chunks - */ - public List getBodyHttpDatas(String name) - throws NotEnoughDataDecoderException { - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - return bodyMapHttpData.get(name); - } - - /** - * This method returns the first InterfaceHttpData with the given name from body.
- * - * If chunked, all chunks must have been offered using offer() method. - * If not, NotEnoughDataDecoderException will be raised. - * - * @param name - * @return The first Body InterfaceHttpData with the given name (ignore case) - * @throws NotEnoughDataDecoderException need more chunks - */ - public InterfaceHttpData getBodyHttpData(String name) - throws NotEnoughDataDecoderException { - if (!isLastChunk) { - throw new NotEnoughDataDecoderException(); - } - List list = bodyMapHttpData.get(name); - if (list != null) { - return list.get(0); - } - return null; - } - - /** - * Initialized the internals from a new chunk - * @param chunk the new received chunk - * @throws ErrorDataDecoderException if there is a problem with the charset decoding or - * other errors - */ - public void offer(HttpChunk chunk) throws ErrorDataDecoderException { - ChannelBuffer chunked = chunk.getContent(); - if (undecodedChunk == null) { - undecodedChunk = chunked; - } else { - //undecodedChunk = ChannelBuffers.wrappedBuffer(undecodedChunk, chunk.getContent()); - // less memory usage - undecodedChunk = ChannelBuffers.wrappedBuffer( - undecodedChunk, chunked); - } - if (chunk.isLast()) { - isLastChunk = true; - } - parseBody(); - } - - /** - * True if at current status, there is an available decoded InterfaceHttpData from the Body. - * - * This method works for chunked and not chunked request. - * - * @return True if at current status, there is a decoded InterfaceHttpData - * @throws EndOfDataDecoderException No more data will be available - */ - public boolean hasNext() throws EndOfDataDecoderException { - if (currentStatus == MultiPartStatus.EPILOGUE) { - // OK except if end of list - if (bodyListHttpDataRank >= bodyListHttpData.size()) { - throw new EndOfDataDecoderException(); - } - } - return bodyListHttpData.size() > 0 && - bodyListHttpDataRank < bodyListHttpData.size(); - } - - /** - * Returns the next available InterfaceHttpData or null if, at the time it is called, there is no more - * available InterfaceHttpData. A subsequent call to offer(httpChunk) could enable more data. - * - * @return the next available InterfaceHttpData or null if none - * @throws EndOfDataDecoderException No more data will be available - */ - public InterfaceHttpData next() throws EndOfDataDecoderException { - if (hasNext()) { - return bodyListHttpData.get(bodyListHttpDataRank++); - } - return null; - } - - /** - * This method will parse as much as possible data and fill the list and map - * @throws ErrorDataDecoderException if there is a problem with the charset decoding or - * other errors - */ - private void parseBody() throws ErrorDataDecoderException { - if (currentStatus == MultiPartStatus.PREEPILOGUE || - currentStatus == MultiPartStatus.EPILOGUE) { - if (isLastChunk) { - currentStatus = MultiPartStatus.EPILOGUE; - } - return; - } - if (isMultipart) { - parseBodyMultipart(); - } else { - parseBodyAttributes(); - } - } - - /** - * Utility function to add a new decoded data - * @param data - */ - private void addHttpData(InterfaceHttpData data) { - if (data == null) { - return; - } - List datas = bodyMapHttpData.get(data.getName()); - if (datas == null) { - datas = new ArrayList(1); - bodyMapHttpData.put(data.getName(), datas); - } - datas.add(data); - bodyListHttpData.add(data); - } - - /** - * 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 parseBodyAttributesStandard() throws ErrorDataDecoderException { - int firstpos = undecodedChunk.readerIndex(); - int currentpos = firstpos; - int equalpos = firstpos; - int ampersandpos = firstpos; - if (currentStatus == MultiPartStatus.NOTSTARTED) { - currentStatus = MultiPartStatus.DISPOSITION; - } - boolean contRead = true; - try { - while (undecodedChunk.readable() && contRead) { - char read = (char) undecodedChunk.readUnsignedByte(); - 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 == HttpConstants.CR) { - if (undecodedChunk.readable()) { - read = (char) undecodedChunk.readUnsignedByte(); - currentpos++; - if (read == HttpConstants.LF) { - currentStatus = MultiPartStatus.PREEPILOGUE; - ampersandpos = currentpos - 2; - setFinalBuffer( - undecodedChunk.slice(firstpos, ampersandpos - firstpos)); - firstpos = currentpos; - contRead = false; - } else { - // Error - contRead = false; - throw new ErrorDataDecoderException("Bad end of line"); - } - } else { - currentpos--; - } - } else if (read == HttpConstants.LF) { - currentStatus = MultiPartStatus.PREEPILOGUE; - ampersandpos = currentpos - 1; - setFinalBuffer( - undecodedChunk.slice(firstpos, ampersandpos - firstpos)); - firstpos = currentpos; - contRead = false; - } - break; - default: - // just stop - contRead = false; - } - } - 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); - } - } - - /** - * 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 (SeekAheadNoBackArrayException 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 == HttpConstants.CR) { - if (sao.pos < sao.limit) { - read = (char) (sao.bytes[sao.pos ++] & 0xFF); - currentpos++; - if (read == HttpConstants.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 == HttpConstants.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( - currentAttribute.getChannelBuffer().toString(charset), - charset); - currentAttribute.setValue(value); - addHttpData(currentAttribute); - currentAttribute = null; - } - - /** - * Decode component - * @param s - * @param charset - * @return the decoded component - * @throws ErrorDataDecoderException - */ - private static String decodeAttribute(String s, Charset charset) - throws ErrorDataDecoderException { - if (s == null) { - return ""; - } - try { - return URLDecoder.decode(s, charset.name()); - } catch (UnsupportedEncodingException e) { - throw new ErrorDataDecoderException(charset.toString(), e); - } - } - - /** - * Parse the Body for multipart - * - * @throws ErrorDataDecoderException if there is a problem with the charset decoding or other errors - */ - private void parseBodyMultipart() throws ErrorDataDecoderException { - if (undecodedChunk == null || undecodedChunk.readableBytes() == 0) { - // nothing to decode - return; - } - InterfaceHttpData data = decodeMultipart(currentStatus); - while (data != null) { - addHttpData(data); - if (currentStatus == MultiPartStatus.PREEPILOGUE || - currentStatus == MultiPartStatus.EPILOGUE) { - break; - } - data = decodeMultipart(currentStatus); - } - } - - /** - * Decode a multipart request by pieces
- *
- * NOTSTARTED PREAMBLE (
- * (HEADERDELIMITER DISPOSITION (FIELD | FILEUPLOAD))*
- * (HEADERDELIMITER DISPOSITION MIXEDPREAMBLE
- * (MIXEDDELIMITER MIXEDDISPOSITION MIXEDFILEUPLOAD)+
- * MIXEDCLOSEDELIMITER)*
- * CLOSEDELIMITER)+ EPILOGUE
- * - * Inspired from HttpMessageDecoder - * - * @param state - * @return the next decoded InterfaceHttpData or null if none until now. - * @throws ErrorDataDecoderException if an error occurs - */ - private InterfaceHttpData decodeMultipart(MultiPartStatus state) - throws ErrorDataDecoderException { - switch (state) { - case NOTSTARTED: - throw new ErrorDataDecoderException( - "Should not be called with the current status"); - case PREAMBLE: - // Content-type: multipart/form-data, boundary=AaB03x - throw new ErrorDataDecoderException( - "Should not be called with the current status"); - case HEADERDELIMITER: { - // --AaB03x or --AaB03x-- - return findMultipartDelimiter(multipartDataBoundary, - MultiPartStatus.DISPOSITION, MultiPartStatus.PREEPILOGUE); - } - case DISPOSITION: { - // content-disposition: form-data; name="field1" - // content-disposition: form-data; name="pics"; filename="file1.txt" - // and other immediate values like - // Content-type: image/gif - // Content-Type: text/plain - // Content-Type: text/plain; charset=ISO-8859-1 - // Content-Transfer-Encoding: binary - // The following line implies a change of mode (mixed mode) - // Content-type: multipart/mixed, boundary=BbC04y - return findMultipartDisposition(); - } - case FIELD: { - // Now get value according to Content-Type and Charset - Charset localCharset = null; - Attribute charsetAttribute = currentFieldAttributes - .get(HttpHeaders.Values.CHARSET); - if (charsetAttribute != null) { - try { - localCharset = Charset.forName(charsetAttribute.getValue()); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - } - Attribute nameAttribute = currentFieldAttributes - .get(HttpPostBodyUtil.NAME); - if (currentAttribute == null) { - try { - currentAttribute = factory.createAttribute(request, nameAttribute - .getValue()); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - if (localCharset != null) { - currentAttribute.setCharset(localCharset); - } - } - // load data - try { - loadFieldMultipart(multipartDataBoundary); - } catch (NotEnoughDataDecoderException e) { - return null; - } - Attribute finalAttribute = currentAttribute; - currentAttribute = null; - currentFieldAttributes = null; - // ready to load the next one - currentStatus = MultiPartStatus.HEADERDELIMITER; - return finalAttribute; - } - case FILEUPLOAD: { - // eventually restart from existing FileUpload - return getFileUpload(multipartDataBoundary); - } - case MIXEDDELIMITER: { - // --AaB03x or --AaB03x-- - // Note that currentFieldAttributes exists - return findMultipartDelimiter(multipartMixedBoundary, - MultiPartStatus.MIXEDDISPOSITION, - MultiPartStatus.HEADERDELIMITER); - } - case MIXEDDISPOSITION: { - return findMultipartDisposition(); - } - case MIXEDFILEUPLOAD: { - // eventually restart from existing FileUpload - return getFileUpload(multipartMixedBoundary); - } - case PREEPILOGUE: - return null; - case EPILOGUE: - return null; - default: - throw new ErrorDataDecoderException("Shouldn't reach here."); - } - } - - - /** - * Skip control Characters - */ - void skipControlCharacters() { - SeekAheadOptimize sao = null; - try { - sao = new SeekAheadOptimize(undecodedChunk); - } catch (SeekAheadNoBackArrayException e) { - skipControlCharactersStandard(undecodedChunk); - return; - } - - while (sao.pos < sao.limit) { - char c = (char) (sao.bytes[sao.pos ++] & 0xFF); - 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 - * @param dispositionStatus the next status if the delimiter is a start - * @param closeDelimiterStatus the next status if the delimiter is a close delimiter - * @return the next InterfaceHttpData if any - * @throws ErrorDataDecoderException - */ - private InterfaceHttpData findMultipartDelimiter(String delimiter, - MultiPartStatus dispositionStatus, - MultiPartStatus closeDelimiterStatus) - throws ErrorDataDecoderException { - // --AaB03x or --AaB03x-- - int readerIndex = undecodedChunk.readerIndex(); - skipControlCharacters(); - skipOneLine(); - String newline; - try { - newline = readLine(); - } catch (NotEnoughDataDecoderException e) { - undecodedChunk.readerIndex(readerIndex); - return null; - } - if (newline.equals(delimiter)) { - currentStatus = dispositionStatus; - return decodeMultipart(dispositionStatus); - } else if (newline.equals(delimiter + "--")) { - // CLOSEDELIMITER or MIXED CLOSEDELIMITER found - currentStatus = closeDelimiterStatus; - if (currentStatus == MultiPartStatus.HEADERDELIMITER) { - // MIXEDCLOSEDELIMITER - // end of the Mixed part - currentFieldAttributes = null; - return decodeMultipart(MultiPartStatus.HEADERDELIMITER); - } - return null; - } - undecodedChunk.readerIndex(readerIndex); - throw new ErrorDataDecoderException("No Multipart delimiter found"); - } - - /** - * Find the next Disposition - * @return the next InterfaceHttpData if any - * @throws ErrorDataDecoderException - */ - private InterfaceHttpData findMultipartDisposition() - throws ErrorDataDecoderException { - int readerIndex = undecodedChunk.readerIndex(); - if (currentStatus == MultiPartStatus.DISPOSITION) { - currentFieldAttributes = new TreeMap( - CaseIgnoringComparator.INSTANCE); - } - // read many lines until empty line with newline found! Store all data - while (!skipOneLine()) { - skipControlCharacters(); - String newline; - try { - newline = readLine(); - } catch (NotEnoughDataDecoderException e) { - undecodedChunk.readerIndex(readerIndex); - return null; - } - String[] contents = splitMultipartHeader(newline); - if (contents[0].equalsIgnoreCase(HttpPostBodyUtil.CONTENT_DISPOSITION)) { - boolean checkSecondArg = false; - if (currentStatus == MultiPartStatus.DISPOSITION) { - checkSecondArg = contents[1] - .equalsIgnoreCase(HttpPostBodyUtil.FORM_DATA); - } else { - checkSecondArg = contents[1] - .equalsIgnoreCase(HttpPostBodyUtil.ATTACHMENT) || - contents[1] - .equalsIgnoreCase(HttpPostBodyUtil.FILE); - } - if (checkSecondArg) { - // read next values and store them in the map as Attribute - for (int i = 2; i < contents.length; i ++) { - String[] values = contents[i].split("="); - Attribute attribute; - try { - attribute = factory.createAttribute(request, values[0].trim(), - decodeAttribute(cleanString(values[1]), charset)); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put(attribute.getName(), - attribute); - } - } - } else if (contents[0] - .equalsIgnoreCase(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING)) { - Attribute attribute; - try { - attribute = factory.createAttribute(request, - HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, - cleanString(contents[1])); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put( - HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, attribute); - } else if (contents[0] - .equalsIgnoreCase(HttpHeaders.Names.CONTENT_LENGTH)) { - Attribute attribute; - try { - attribute = factory.createAttribute(request, - HttpHeaders.Names.CONTENT_LENGTH, - cleanString(contents[1])); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put(HttpHeaders.Names.CONTENT_LENGTH, - attribute); - } else if (contents[0].equalsIgnoreCase(HttpHeaders.Names.CONTENT_TYPE)) { - // Take care of possible "multipart/mixed" - if (contents[1].equalsIgnoreCase(HttpPostBodyUtil.MULTIPART_MIXED)) { - if (currentStatus == MultiPartStatus.DISPOSITION) { - String[] values = contents[2].split("="); - multipartMixedBoundary = "--" + values[1]; - currentStatus = MultiPartStatus.MIXEDDELIMITER; - return decodeMultipart(MultiPartStatus.MIXEDDELIMITER); - } else { - throw new ErrorDataDecoderException( - "Mixed Multipart found in a previous Mixed Multipart"); - } - } else { - for (int i = 1; i < contents.length; i ++) { - if (contents[i].toLowerCase().startsWith( - HttpHeaders.Values.CHARSET)) { - String[] values = contents[i].split("="); - Attribute attribute; - try { - attribute = factory.createAttribute(request, - HttpHeaders.Values.CHARSET, - cleanString(values[1])); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put(HttpHeaders.Values.CHARSET, - attribute); - } else { - Attribute attribute; - try { - attribute = factory.createAttribute(request, - contents[0].trim(), - decodeAttribute(cleanString(contents[i]), charset)); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } - currentFieldAttributes.put(attribute.getName(), - attribute); - } - } - } - } else { - throw new ErrorDataDecoderException("Unknown Params: " + - newline); - } - } - // Is it a FileUpload - Attribute filenameAttribute = currentFieldAttributes - .get(HttpPostBodyUtil.FILENAME); - if (currentStatus == MultiPartStatus.DISPOSITION) { - if (filenameAttribute != null) { - // FileUpload - currentStatus = MultiPartStatus.FILEUPLOAD; - // do not change the buffer position - return decodeMultipart(MultiPartStatus.FILEUPLOAD); - } else { - // Field - currentStatus = MultiPartStatus.FIELD; - // do not change the buffer position - return decodeMultipart(MultiPartStatus.FIELD); - } - } else { - if (filenameAttribute != null) { - // FileUpload - currentStatus = MultiPartStatus.MIXEDFILEUPLOAD; - // do not change the buffer position - return decodeMultipart(MultiPartStatus.MIXEDFILEUPLOAD); - } else { - // Field is not supported in MIXED mode - throw new ErrorDataDecoderException("Filename not found"); - } - } - } - - /** - * Get the FileUpload (new one or current one) - * @param delimiter the delimiter to use - * @return the InterfaceHttpData if any - * @throws ErrorDataDecoderException - */ - private InterfaceHttpData getFileUpload(String delimiter) - throws ErrorDataDecoderException { - // eventually restart from existing FileUpload - // Now get value according to Content-Type and Charset - Attribute encoding = currentFieldAttributes - .get(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING); - Charset localCharset = charset; - // Default - TransferEncodingMechanism mechanism = TransferEncodingMechanism.BIT7; - if (encoding != null) { - String code; - try { - code = encoding.getValue().toLowerCase(); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - if (code.equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT7.value)) { - localCharset = HttpPostBodyUtil.US_ASCII; - } else if (code.equals(HttpPostBodyUtil.TransferEncodingMechanism.BIT8.value)) { - localCharset = HttpPostBodyUtil.ISO_8859_1; - mechanism = TransferEncodingMechanism.BIT8; - } else if (code - .equals(HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value)) { - // no real charset, so let the default - mechanism = TransferEncodingMechanism.BINARY; - } else { - throw new ErrorDataDecoderException( - "TransferEncoding Unknown: " + code); - } - } - Attribute charsetAttribute = currentFieldAttributes - .get(HttpHeaders.Values.CHARSET); - if (charsetAttribute != null) { - try { - localCharset = Charset.forName(charsetAttribute.getValue()); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - } - if (currentFileUpload == null) { - Attribute filenameAttribute = currentFieldAttributes - .get(HttpPostBodyUtil.FILENAME); - Attribute nameAttribute = currentFieldAttributes - .get(HttpPostBodyUtil.NAME); - Attribute contentTypeAttribute = currentFieldAttributes - .get(HttpHeaders.Names.CONTENT_TYPE); - if (contentTypeAttribute == null) { - throw new ErrorDataDecoderException( - "Content-Type is absent but required"); - } - Attribute lengthAttribute = currentFieldAttributes - .get(HttpHeaders.Names.CONTENT_LENGTH); - long size; - try { - size = lengthAttribute != null? Long.parseLong(lengthAttribute - .getValue()) : 0L; - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } catch (NumberFormatException e) { - size = 0; - } - try { - currentFileUpload = factory.createFileUpload( - request, - nameAttribute.getValue(), filenameAttribute.getValue(), - contentTypeAttribute.getValue(), mechanism.value, - localCharset, size); - } catch (NullPointerException e) { - throw new ErrorDataDecoderException(e); - } catch (IllegalArgumentException e) { - throw new ErrorDataDecoderException(e); - } catch (IOException e) { - throw new ErrorDataDecoderException(e); - } - } - // load data as much as possible - try { - readFileUploadByteMultipart(delimiter); - } catch (NotEnoughDataDecoderException e) { - // do not change the buffer position - // since some can be already saved into FileUpload - // So do not change the currentStatus - return null; - } - if (currentFileUpload.isCompleted()) { - // ready to load the next one - if (currentStatus == MultiPartStatus.FILEUPLOAD) { - currentStatus = MultiPartStatus.HEADERDELIMITER; - currentFieldAttributes = null; - } else { - currentStatus = MultiPartStatus.MIXEDDELIMITER; - cleanMixedAttributes(); - } - FileUpload fileUpload = currentFileUpload; - currentFileUpload = null; - return fileUpload; - } - // do not change the buffer position - // since some can be already saved into FileUpload - // So do not change the currentStatus - return null; - } - - /** - * Clean all HttpDatas (on Disk) for the current request. - */ - public void cleanFiles() { - factory.cleanRequestHttpDatas(request); - } - - /** - * Remove the given FileUpload from the list of FileUploads to clean - */ - public void removeHttpDataFromClean(InterfaceHttpData data) { - factory.removeHttpDataFromClean(request, data); - } - - /** - * Remove all Attributes that should be cleaned between two FileUpload in Mixed mode - */ - private void cleanMixedAttributes() { - currentFieldAttributes.remove(HttpHeaders.Values.CHARSET); - currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_LENGTH); - currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING); - currentFieldAttributes.remove(HttpHeaders.Names.CONTENT_TYPE); - currentFieldAttributes.remove(HttpPostBodyUtil.FILENAME); - } - - /** - * 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 readLineStandard() throws NotEnoughDataDecoderException { - int readerIndex = undecodedChunk.readerIndex(); - try { - StringBuilder sb = new StringBuilder(64); - while (undecodedChunk.readable()) { - byte nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.CR) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.LF) { - return sb.toString(); - } - } else if (nextByte == HttpConstants.LF) { - 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 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 (SeekAheadNoBackArrayException 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 == HttpConstants.CR) { - if (sao.pos < sao.limit) { - nextByte = sao.bytes[sao.pos ++]; - if (nextByte == HttpConstants.LF) { - sao.setReadPosition(0); - return sb.toString(); - } - } else { - sb.append((char) nextByte); - } - } else if (nextByte == HttpConstants.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. - * @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 readFileUploadByteMultipartStandard(String delimiter) - throws NotEnoughDataDecoderException, ErrorDataDecoderException { - int readerIndex = undecodedChunk.readerIndex(); - // found the decoder limit - boolean newLine = true; - int index = 0; - int lastPosition = undecodedChunk.readerIndex(); - boolean found = false; - while (undecodedChunk.readable()) { - byte nextByte = undecodedChunk.readByte(); - if (newLine) { - // Check the delimiter - if (nextByte == delimiter.codePointAt(index)) { - index ++; - if (delimiter.length() == index) { - found = true; - break; - } - continue; - } else { - newLine = false; - index = 0; - // continue until end of line - if (nextByte == HttpConstants.CR) { - if (undecodedChunk.readable()) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 2; - } - } - } else if (nextByte == HttpConstants.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 1; - } else { - // save last valid position - lastPosition = undecodedChunk.readerIndex(); - } - } - } else { - // continue until end of line - if (nextByte == HttpConstants.CR) { - if (undecodedChunk.readable()) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 2; - } - } - } else if (nextByte == HttpConstants.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 1; - } else { - // save last valid position - 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); - } - } - } - - /** - * 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 (SeekAheadNoBackArrayException 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 == HttpConstants.CR) { - if (sao.pos < sao.limit) { - nextByte = sao.bytes[sao.pos ++]; - if (nextByte == HttpConstants.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 == HttpConstants.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 == HttpConstants.CR) { - if (sao.pos < sao.limit) { - nextByte = sao.bytes[sao.pos ++]; - if (nextByte == HttpConstants.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 == HttpConstants.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 loadFieldMultipartStandard(String delimiter) - throws NotEnoughDataDecoderException, ErrorDataDecoderException { - int readerIndex = undecodedChunk.readerIndex(); - try { - // found the decoder limit - boolean newLine = true; - int index = 0; - int lastPosition = undecodedChunk.readerIndex(); - boolean found = false; - while (undecodedChunk.readable()) { - byte nextByte = undecodedChunk.readByte(); - if (newLine) { - // Check the delimiter - if (nextByte == delimiter.codePointAt(index)) { - index ++; - if (delimiter.length() == index) { - found = true; - break; - } - continue; - } else { - newLine = false; - index = 0; - // continue until end of line - if (nextByte == HttpConstants.CR) { - if (undecodedChunk.readable()) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 2; - } - } - } else if (nextByte == HttpConstants.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 1; - } else { - lastPosition = undecodedChunk.readerIndex(); - } - } - } else { - // continue until end of line - if (nextByte == HttpConstants.CR) { - if (undecodedChunk.readable()) { - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 2; - } - } - } else if (nextByte == HttpConstants.LF) { - newLine = true; - index = 0; - lastPosition = undecodedChunk.readerIndex() - 1; - } else { - 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); - } - } - - /** - * 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 (SeekAheadNoBackArrayException 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 == HttpConstants.CR) { - if (sao.pos < sao.limit) { - nextByte = sao.bytes[sao.pos ++]; - if (nextByte == HttpConstants.LF) { - newLine = true; - index = 0; - sao.setReadPosition(0); - lastPosition = undecodedChunk.readerIndex() - 2; - } - } else { - sao.setReadPosition(0); - lastPosition = undecodedChunk.readerIndex(); - } - } else if (nextByte == HttpConstants.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 == HttpConstants.CR) { - if (sao.pos < sao.limit) { - nextByte = sao.bytes[sao.pos ++]; - if (nextByte == HttpConstants.LF) { - newLine = true; - index = 0; - sao.setReadPosition(0); - lastPosition = undecodedChunk.readerIndex() - 2; - } - } else { - sao.setReadPosition(0); - lastPosition = undecodedChunk.readerIndex(); - } - } else if (nextByte == HttpConstants.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 - */ - private static String cleanString(String field) { - StringBuilder sb = new StringBuilder(field.length()); - int i = 0; - for (i = 0; i < field.length(); i ++) { - char nextChar = field.charAt(i); - if (nextChar == HttpConstants.COLON) { - sb.append(HttpConstants.SP); - } else if (nextChar == HttpConstants.COMMA) { - sb.append(HttpConstants.SP); - } else if (nextChar == HttpConstants.EQUALS) { - sb.append(HttpConstants.SP); - } else if (nextChar == HttpConstants.SEMICOLON) { - sb.append(HttpConstants.SP); - } else if (nextChar == HttpConstants.HT) { - sb.append(HttpConstants.SP); - } else if (nextChar == HttpConstants.DOUBLE_QUOTE) { - // nothing added, just removes it - } else { - sb.append(nextChar); - } - } - return sb.toString().trim(); - } - - /** - * Skip one empty line - * @return True if one empty line was skipped - */ - private boolean skipOneLine() { - if (!undecodedChunk.readable()) { - return false; - } - byte nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.CR) { - if (!undecodedChunk.readable()) { - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); - return false; - } - nextByte = undecodedChunk.readByte(); - if (nextByte == HttpConstants.LF) { - return true; - } - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 2); - return false; - } else if (nextByte == HttpConstants.LF) { - return true; - } - undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1); - return false; - } - - /** - * Split the very first line (Content-Type value) in 2 Strings - * @param sb - * @return the array of 2 Strings - */ - private static String[] splitHeaderContentType(String sb) { - int size = sb.length(); - int aStart; - int aEnd; - int bStart; - int bEnd; - aStart = HttpPostBodyUtil.findNonWhitespace(sb, 0); - aEnd = HttpPostBodyUtil.findWhitespace(sb, aStart); - if (aEnd >= size) { - return new String[] { sb, "" }; - } - if (sb.charAt(aEnd) == ';') { - aEnd --; - } - bStart = HttpPostBodyUtil.findNonWhitespace(sb, aEnd); - bEnd = HttpPostBodyUtil.findEndOfString(sb); - return new String[] { sb.substring(aStart, aEnd), - sb.substring(bStart, bEnd) }; - } - - /** - * Split one header in Multipart - * @param sb - * @return an array of String where rank 0 is the name of the header, follows by several - * values that were separated by ';' or ',' - */ - private static String[] splitMultipartHeader(String sb) { - ArrayList headers = new ArrayList(1); - int nameStart; - int nameEnd; - int colonEnd; - int valueStart; - int valueEnd; - nameStart = HttpPostBodyUtil.findNonWhitespace(sb, 0); - for (nameEnd = nameStart; nameEnd < sb.length(); nameEnd ++) { - char ch = sb.charAt(nameEnd); - if (ch == ':' || Character.isWhitespace(ch)) { - break; - } - } - for (colonEnd = nameEnd; colonEnd < sb.length(); colonEnd ++) { - if (sb.charAt(colonEnd) == ':') { - colonEnd ++; - break; - } - } - valueStart = HttpPostBodyUtil.findNonWhitespace(sb, colonEnd); - valueEnd = HttpPostBodyUtil.findEndOfString(sb); - headers.add(sb.substring(nameStart, nameEnd)); - String svalue = sb.substring(valueStart, valueEnd); - String[] values = null; - if (svalue.indexOf(";") >= 0) { - values = svalue.split(";"); - } else { - values = svalue.split(","); - } - for (String value: values) { - headers.add(value.trim()); - } - String[] array = new String[headers.size()]; - for (int i = 0; i < headers.size(); i ++) { - array[i] = headers.get(i); - } - return array; - } - - /** - * Exception when try reading data from request in chunked format, and not enough - * data are available (need more chunks) - */ - public static class NotEnoughDataDecoderException extends Exception { - /** - */ - private static final long serialVersionUID = -7846841864603865638L; - - /** - */ - public NotEnoughDataDecoderException() { - } - - /** - * @param arg0 - */ - public NotEnoughDataDecoderException(String arg0) { - super(arg0); - } - - /** - * @param arg0 - */ - public NotEnoughDataDecoderException(Throwable arg0) { - super(arg0); - } - - /** - * @param arg0 - * @param arg1 - */ - public NotEnoughDataDecoderException(String arg0, Throwable arg1) { - super(arg0, arg1); - } - } - - /** - * Exception when the body is fully decoded, even if there is still data - */ - public static class EndOfDataDecoderException extends Exception { - /** - */ - private static final long serialVersionUID = 1336267941020800769L; - - } - - /** - * Exception when an error occurs while decoding - */ - public static class ErrorDataDecoderException extends Exception { - /** - */ - private static final long serialVersionUID = 5020247425493164465L; - - /** - */ - public ErrorDataDecoderException() { - } - - /** - * @param arg0 - */ - public ErrorDataDecoderException(String arg0) { - super(arg0); - } - - /** - * @param arg0 - */ - public ErrorDataDecoderException(Throwable arg0) { - super(arg0); - } - - /** - * @param arg0 - * @param arg1 - */ - public ErrorDataDecoderException(String arg0, Throwable arg1) { - super(arg0, arg1); - } - } - - /** - * Exception when an unappropriated method was called on a request - */ - public static class IncompatibleDataDecoderException extends Exception { - /** - */ - private static final long serialVersionUID = -953268047926250267L; - - /** - */ - public IncompatibleDataDecoderException() { - } - - /** - * @param arg0 - */ - public IncompatibleDataDecoderException(String arg0) { - super(arg0); - } - - /** - * @param arg0 - */ - public IncompatibleDataDecoderException(Throwable arg0) { - super(arg0); - } - - /** - * @param arg0 - * @param arg1 - */ - public IncompatibleDataDecoderException(String arg0, Throwable arg1) { - super(arg0, arg1); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java deleted file mode 100644 index a63f9e939c..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java +++ /dev/null @@ -1,986 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ChannelBuffer; -import io.netty.buffer.ChannelBuffers; -import io.netty.handler.codec.http.DefaultHttpChunk; -import io.netty.handler.codec.http.HttpChunk; -import io.netty.handler.codec.http.HttpConstants; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.stream.ChunkedInput; - -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.ListIterator; -import java.util.Random; - -/** - * This encoder will help to encode Request for a FORM as POST. - */ -public class HttpPostRequestEncoder implements ChunkedInput { - /** - * Factory used to create InterfaceHttpData - */ - private final HttpDataFactory factory; - - /** - * Request to encode - */ - private final HttpRequest request; - - /** - * Default charset to use - */ - private final Charset charset; - - /** - * Chunked false by default - */ - private boolean isChunked; - - /** - * InterfaceHttpData for Body (without encoding) - */ - private final List bodyListDatas; - /** - * The final Multipart List of InterfaceHttpData including encoding - */ - private final List multipartHttpDatas; - - /** - * Does this request is a Multipart request - */ - private final boolean isMultipart; - - /** - * If multipart, this is the boundary for the flobal multipart - */ - private String multipartDataBoundary; - - /** - * If multipart, there could be internal multiparts (mixed) to the global multipart. - * Only one level is allowed. - */ - private String multipartMixedBoundary; - /** - * To check if the header has been finalized - */ - private boolean headerFinalized; - - /** - * - * @param request the request to encode - * @param multipart True if the FORM is a ENCTYPE="multipart/form-data" - * @throws NullPointerException for request - * @throws ErrorDataEncoderException if the request is not a POST - */ - public HttpPostRequestEncoder(HttpRequest request, boolean multipart) - throws ErrorDataEncoderException { - this(new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE), - request, multipart, HttpConstants.DEFAULT_CHARSET); - } - - /** - * - * @param factory the factory used to create InterfaceHttpData - * @param request the request to encode - * @param multipart True if the FORM is a ENCTYPE="multipart/form-data" - * @throws NullPointerException for request and factory - * @throws ErrorDataEncoderException if the request is not a POST - */ - public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, boolean multipart) - throws ErrorDataEncoderException { - this(factory, request, multipart, HttpConstants.DEFAULT_CHARSET); - } - - /** - * - * @param factory the factory used to create InterfaceHttpData - * @param request the request to encode - * @param multipart True if the FORM is a ENCTYPE="multipart/form-data" - * @param charset the charset to use as default - * @throws NullPointerException for request or charset or factory - * @throws ErrorDataEncoderException if the request is not a POST - */ - public HttpPostRequestEncoder(HttpDataFactory factory, HttpRequest request, - boolean multipart, Charset charset) throws ErrorDataEncoderException { - if (factory == null) { - throw new NullPointerException("factory"); - } - if (request == null) { - throw new NullPointerException("request"); - } - if (charset == null) { - throw new NullPointerException("charset"); - } - if (request.getMethod() != HttpMethod.POST) { - throw new ErrorDataEncoderException("Cannot create a Encoder if not a POST"); - } - this.request = request; - this.charset = charset; - this.factory = factory; - // Fill default values - bodyListDatas = new ArrayList(); - // default mode - isLastChunk = false; - isLastChunkSent = false; - isMultipart = multipart; - multipartHttpDatas = new ArrayList(); - if (isMultipart) { - initDataMultipart(); - } - } - - /** - * Clean all HttpDatas (on Disk) for the current request. - */ - public void cleanFiles() { - factory.cleanRequestHttpDatas(request); - } - - /** - * Does the last non empty chunk already encoded so that next chunk will be empty (last chunk) - */ - private boolean isLastChunk; - /** - * Last chunk already sent - */ - private boolean isLastChunkSent; - /** - * The current FileUpload that is currently in encode process - */ - private FileUpload currentFileUpload; - /** - * While adding a FileUpload, is the multipart currently in Mixed Mode - */ - private boolean duringMixedMode; - - /** - * Global Body size - */ - private long globalBodySize; - - /** - * True if this request is a Multipart request - * @return True if this request is a Multipart request - */ - public boolean isMultipart() { - return isMultipart; - } - - /** - * Init the delimiter for Global Part (Data). - */ - private void initDataMultipart() { - multipartDataBoundary = getNewMultipartDelimiter(); - } - - /** - * Init the delimiter for Mixed Part (Mixed). - */ - private void initMixedMultipart() { - multipartMixedBoundary = getNewMultipartDelimiter(); - } - - /** - * - * @return a newly generated Delimiter (either for DATA or MIXED) - */ - private static String getNewMultipartDelimiter() { - // construct a generated delimiter - Random random = new Random(); - return Long.toHexString(random.nextLong()).toLowerCase(); - } - - /** - * This method returns a List of all InterfaceHttpData from body part.
- - * @return the list of InterfaceHttpData from Body part - */ - public List getBodyListAttributes() { - return bodyListDatas; - } - - /** - * Set the Body HttpDatas list - * @param datas - * @throws NullPointerException for datas - * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done - */ - public void setBodyHttpDatas(List datas) - throws ErrorDataEncoderException { - if (datas == null) { - throw new NullPointerException("datas"); - } - globalBodySize = 0; - bodyListDatas.clear(); - currentFileUpload = null; - duringMixedMode = false; - multipartHttpDatas.clear(); - for (InterfaceHttpData data: datas) { - addBodyHttpData(data); - } - } - - /** - * Add a simple attribute in the body as Name=Value - * @param name name of the parameter - * @param value the value of the parameter - * @throws NullPointerException for name - * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done - */ - public void addBodyAttribute(String name, String value) - throws ErrorDataEncoderException { - if (name == null) { - throw new NullPointerException("name"); - } - String svalue = value; - if (value == null) { - svalue = ""; - } - Attribute data = factory.createAttribute(request, name, svalue); - addBodyHttpData(data); - } - - /** - * Add a file as a FileUpload - * @param name the name of the parameter - * @param file the file to be uploaded (if not Multipart mode, only the filename will be included) - * @param contentType the associated contentType for the File - * @param isText True if this file should be transmitted in Text format (else binary) - * @throws NullPointerException for name and file - * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done - */ - public void addBodyFileUpload(String name, File file, String contentType, boolean isText) - throws ErrorDataEncoderException { - if (name == null) { - throw new NullPointerException("name"); - } - if (file == null) { - throw new NullPointerException("file"); - } - String scontentType = contentType; - String contentTransferEncoding = null; - if (contentType == null) { - if (isText) { - scontentType = HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE; - } else { - scontentType = HttpPostBodyUtil.DEFAULT_BINARY_CONTENT_TYPE; - } - } - if (!isText) { - contentTransferEncoding = HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value; - } - FileUpload fileUpload = factory.createFileUpload(request, name, file.getName(), - scontentType, contentTransferEncoding, null, file.length()); - try { - fileUpload.setContent(file); - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - addBodyHttpData(fileUpload); - } - - /** - * Add a series of Files associated with one File parameter (implied Mixed mode in Multipart) - * @param name the name of the parameter - * @param file the array of files - * @param contentType the array of content Types associated with each file - * @param isText the array of isText attribute (False meaning binary mode) for each file - * @throws NullPointerException also throws if array have different sizes - * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done - */ - public void addBodyFileUploads(String name, File[] file, String[] contentType, boolean[] isText) - throws ErrorDataEncoderException { - if (file.length != contentType.length && file.length != isText.length) { - throw new NullPointerException("Different array length"); - } - for (int i = 0; i < file.length; i++) { - addBodyFileUpload(name, file[i], contentType[i], isText[i]); - } - } - - /** - * Add the InterfaceHttpData to the Body list - * @param data - * @throws NullPointerException for data - * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done - */ - public void addBodyHttpData(InterfaceHttpData data) - throws ErrorDataEncoderException { - if (headerFinalized) { - throw new ErrorDataEncoderException("Cannot add value once finalized"); - } - if (data == null) { - throw new NullPointerException("data"); - } - bodyListDatas.add(data); - if (! isMultipart) { - if (data instanceof Attribute) { - Attribute attribute = (Attribute) data; - try { - // name=value& with encoded name and attribute - String key = encodeAttribute(attribute.getName(), charset); - String value = encodeAttribute(attribute.getValue(), charset); - Attribute newattribute = factory.createAttribute(request, key, value); - multipartHttpDatas.add(newattribute); - globalBodySize += newattribute.getName().length() + 1 + - newattribute.length() + 1; - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - } else if (data instanceof FileUpload) { - // since not Multipart, only name=filename => Attribute - FileUpload fileUpload = (FileUpload) data; - // name=filename& with encoded name and filename - String key = encodeAttribute(fileUpload.getName(), charset); - String value = encodeAttribute(fileUpload.getFilename(), charset); - Attribute newattribute = factory.createAttribute(request, key, value); - multipartHttpDatas.add(newattribute); - globalBodySize += newattribute.getName().length() + 1 + - newattribute.length() + 1; - } - return; - } - /* - * Logic: - * if not Attribute: - * add Data to body list - * if (duringMixedMode) - * add endmixedmultipart delimiter - * currentFileUpload = null - * duringMixedMode = false; - * add multipart delimiter, multipart body header and Data to multipart list - * reset currentFileUpload, duringMixedMode - * if FileUpload: take care of multiple file for one field => mixed mode - * if (duringMixeMode) - * if (currentFileUpload.name == data.name) - * add mixedmultipart delimiter, mixedmultipart body header and Data to multipart list - * else - * add endmixedmultipart delimiter, multipart body header and Data to multipart list - * currentFileUpload = data - * duringMixedMode = false; - * else - * if (currentFileUpload.name == data.name) - * change multipart body header of previous file into multipart list to - * mixedmultipart start, mixedmultipart body header - * add mixedmultipart delimiter, mixedmultipart body header and Data to multipart list - * duringMixedMode = true - * else - * add multipart delimiter, multipart body header and Data to multipart list - * currentFileUpload = data - * duringMixedMode = false; - * Do not add last delimiter! Could be: - * if duringmixedmode: endmixedmultipart + endmultipart - * else only endmultipart - */ - if (data instanceof Attribute) { - if (duringMixedMode) { - InternalAttribute internal = new InternalAttribute(); - internal.addValue("\r\n--" + multipartMixedBoundary + "--"); - multipartHttpDatas.add(internal); - multipartMixedBoundary = null; - currentFileUpload = null; - duringMixedMode = false; - } - InternalAttribute internal = new InternalAttribute(); - if (multipartHttpDatas.size() > 0) { - // previously a data field so CRLF - internal.addValue("\r\n"); - } - internal.addValue("--" + multipartDataBoundary + "\r\n"); - // content-disposition: form-data; name="field1" - Attribute attribute = (Attribute) data; - internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + - HttpPostBodyUtil.FORM_DATA + "; " + - HttpPostBodyUtil.NAME + "=\"" + - encodeAttribute(attribute.getName(), charset) + "\"\r\n"); - Charset localcharset = attribute.getCharset(); - if (localcharset != null) { - // Content-Type: charset=charset - internal.addValue(HttpHeaders.Names.CONTENT_TYPE + ": " + - HttpHeaders.Values.CHARSET + "=" + localcharset + "\r\n"); - } - // CRLF between body header and data - internal.addValue("\r\n"); - multipartHttpDatas.add(internal); - multipartHttpDatas.add(data); - globalBodySize += attribute.length() + internal.size(); - } else if (data instanceof FileUpload) { - FileUpload fileUpload = (FileUpload) data; - InternalAttribute internal = new InternalAttribute(); - if (multipartHttpDatas.size() > 0) { - // previously a data field so CRLF - internal.addValue("\r\n"); - } - boolean localMixed = false; - if (duringMixedMode) { - if (currentFileUpload != null && - currentFileUpload.getName().equals(fileUpload.getName())) { - // continue a mixed mode - - localMixed = true; - } else { - // end a mixed mode - - // add endmixedmultipart delimiter, multipart body header and - // Data to multipart list - internal.addValue("--" + multipartMixedBoundary + "--"); - multipartHttpDatas.add(internal); - multipartMixedBoundary = null; - // start a new one (could be replaced if mixed start again from here - internal = new InternalAttribute(); - internal.addValue("\r\n"); - localMixed = false; - // new currentFileUpload and no more in Mixed mode - currentFileUpload = fileUpload; - duringMixedMode = false; - } - } else { - if (currentFileUpload != null && - currentFileUpload.getName().equals(fileUpload.getName())) { - // create a new mixed mode (from previous file) - - // change multipart body header of previous file into multipart list to - // mixedmultipart start, mixedmultipart body header - - // change Internal (size()-2 position in multipartHttpDatas) - // from (line starting with *) - // --AaB03x - // * Content-Disposition: form-data; name="files"; filename="file1.txt" - // Content-Type: text/plain - // to (lines starting with *) - // --AaB03x - // * Content-Disposition: form-data; name="files" - // * Content-Type: multipart/mixed; boundary=BbC04y - // * - // * --BbC04y - // * Content-Disposition: file; filename="file1.txt" - // Content-Type: text/plain - initMixedMultipart(); - InternalAttribute pastAttribute = - (InternalAttribute) multipartHttpDatas.get(multipartHttpDatas.size() - 2); - // remove past size - globalBodySize -= pastAttribute.size(); - String replacement = HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + - HttpPostBodyUtil.FORM_DATA + "; " + HttpPostBodyUtil.NAME + "=\"" + - encodeAttribute(fileUpload.getName(), charset) + "\"\r\n"; - replacement += HttpHeaders.Names.CONTENT_TYPE + ": " + - HttpPostBodyUtil.MULTIPART_MIXED + "; " + HttpHeaders.Values.BOUNDARY + - "=" + multipartMixedBoundary + "\r\n\r\n"; - replacement += "--" + multipartMixedBoundary + "\r\n"; - replacement += HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + - HttpPostBodyUtil.FILE + "; " + HttpPostBodyUtil.FILENAME + "=\"" + - encodeAttribute(fileUpload.getFilename(), charset) + - "\"\r\n"; - pastAttribute.setValue(replacement, 1); - // update past size - globalBodySize += pastAttribute.size(); - - // now continue - // add mixedmultipart delimiter, mixedmultipart body header and - // Data to multipart list - localMixed = true; - duringMixedMode = true; - } else { - // a simple new multipart - //add multipart delimiter, multipart body header and Data to multipart list - localMixed = false; - currentFileUpload = fileUpload; - duringMixedMode = false; - } - } - - if (localMixed) { - // add mixedmultipart delimiter, mixedmultipart body header and - // Data to multipart list - internal.addValue("--" + multipartMixedBoundary + "\r\n"); - // Content-Disposition: file; filename="file1.txt" - internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + - HttpPostBodyUtil.FILE + "; " + HttpPostBodyUtil.FILENAME + "=\"" + - encodeAttribute(fileUpload.getFilename(), charset) + - "\"\r\n"); - - } else { - internal.addValue("--" + multipartDataBoundary + "\r\n"); - // Content-Disposition: form-data; name="files"; filename="file1.txt" - internal.addValue(HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + - HttpPostBodyUtil.FORM_DATA + "; " + HttpPostBodyUtil.NAME + "=\"" + - encodeAttribute(fileUpload.getName(), charset) + "\"; " + - HttpPostBodyUtil.FILENAME + "=\"" + - encodeAttribute(fileUpload.getFilename(), charset) + - "\"\r\n"); - } - // Content-Type: image/gif - // Content-Type: text/plain; charset=ISO-8859-1 - // Content-Transfer-Encoding: binary - internal.addValue(HttpHeaders.Names.CONTENT_TYPE + ": " + - fileUpload.getContentType()); - String contentTransferEncoding = fileUpload.getContentTransferEncoding(); - if (contentTransferEncoding != null && - contentTransferEncoding.equals( - HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value)) { - internal.addValue("\r\n" + HttpHeaders.Names.CONTENT_TRANSFER_ENCODING + - ": " + HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value + - "\r\n\r\n"); - } else if (fileUpload.getCharset() != null) { - internal.addValue("; " + HttpHeaders.Values.CHARSET + "=" + - fileUpload.getCharset() + "\r\n\r\n"); - } else { - internal.addValue("\r\n\r\n"); - } - multipartHttpDatas.add(internal); - multipartHttpDatas.add(data); - globalBodySize += fileUpload.length() + internal.size(); - } - } - - /** - * Iterator to be used when encoding will be called chunk after chunk - */ - private ListIterator iterator; - - /** - * Finalize the request by preparing the Header in the request and - * returns the request ready to be sent.
- * Once finalized, no data must be added.
- * If the request does not need chunk (isChunked() == false), - * this request is the only object to send to - * the remote server. - * - * @return the request object (chunked or not according to size of body) - * @throws ErrorDataEncoderException if the encoding is in error or if the finalize were already done - */ - public HttpRequest finalizeRequest() throws ErrorDataEncoderException { - // Finalize the multipartHttpDatas - if (! headerFinalized) { - if (isMultipart) { - InternalAttribute internal = new InternalAttribute(); - if (duringMixedMode) { - internal.addValue("\r\n--" + multipartMixedBoundary + "--"); - } - internal.addValue("\r\n--" + multipartDataBoundary + "--\r\n"); - multipartHttpDatas.add(internal); - multipartMixedBoundary = null; - currentFileUpload = null; - duringMixedMode = false; - globalBodySize += internal.size(); - } - headerFinalized = true; - } else { - throw new ErrorDataEncoderException("Header already encoded"); - } - List contentTypes = request.getHeaders(HttpHeaders.Names.CONTENT_TYPE); - List transferEncoding = - request.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING); - if (contentTypes != null) { - request.removeHeader(HttpHeaders.Names.CONTENT_TYPE); - for (String contentType: contentTypes) { - // "multipart/form-data; boundary=--89421926422648" - if (contentType.toLowerCase().startsWith( - HttpHeaders.Values.MULTIPART_FORM_DATA)) { - // ignore - } else if (contentType.toLowerCase().startsWith( - HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED)) { - // ignore - } else { - request.addHeader(HttpHeaders.Names.CONTENT_TYPE, contentType); - } - } - } - if (isMultipart) { - String value = HttpHeaders.Values.MULTIPART_FORM_DATA + "; " + - HttpHeaders.Values.BOUNDARY + "=" + multipartDataBoundary; - request.addHeader(HttpHeaders.Names.CONTENT_TYPE, value); - } else { - // Not multipart - request.addHeader(HttpHeaders.Names.CONTENT_TYPE, - HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED); - } - // Now consider size for chunk or not - long realSize = globalBodySize; - if (isMultipart) { - iterator = multipartHttpDatas.listIterator(); - } else { - realSize -= 1; // last '&' removed - iterator = multipartHttpDatas.listIterator(); - } - request.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String - .valueOf(realSize)); - if (realSize > HttpPostBodyUtil.chunkSize) { - isChunked = true; - if (transferEncoding != null) { - request.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING); - for (String v: transferEncoding) { - if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) { - // ignore - } else { - request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, v); - } - } - } - request.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, - HttpHeaders.Values.CHUNKED); - request.setContent(ChannelBuffers.EMPTY_BUFFER); - } else { - // get the only one body and set it to the request - HttpChunk chunk = nextChunk(); - request.setContent(chunk.getContent()); - } - return request; - } - - /** - * @return True if the request is by Chunk - */ - public boolean isChunked() { - return isChunked; - } - - /** - * Encode one attribute - * @param s - * @param charset - * @return the encoded attribute - * @throws ErrorDataEncoderException if the encoding is in error - */ - private static String encodeAttribute(String s, Charset charset) - throws ErrorDataEncoderException { - if (s == null) { - return ""; - } - try { - return URLEncoder.encode(s, charset.name()); - } catch (UnsupportedEncodingException e) { - throw new ErrorDataEncoderException(charset.name(), e); - } - } - - /** - * The ChannelBuffer currently used by the encoder - */ - private ChannelBuffer currentBuffer; - /** - * The current InterfaceHttpData to encode (used if more chunks are available) - */ - private InterfaceHttpData currentData; - /** - * If not multipart, does the currentBuffer stands for the Key or for the Value - */ - private boolean isKey = true; - - /** - * - * @return the next ChannelBuffer to send as a HttpChunk and modifying currentBuffer - * accordingly - */ - private ChannelBuffer fillChannelBuffer() { - int length = currentBuffer.readableBytes(); - if (length > HttpPostBodyUtil.chunkSize) { - ChannelBuffer slice = - currentBuffer.slice(currentBuffer.readerIndex(), HttpPostBodyUtil.chunkSize); - currentBuffer.skipBytes(HttpPostBodyUtil.chunkSize); - return slice; - } else { - // to continue - ChannelBuffer slice = currentBuffer; - currentBuffer = null; - return slice; - } - } - - /** - * From the current context (currentBuffer and currentData), returns the next HttpChunk - * (if possible) trying to get sizeleft bytes more into the currentBuffer. - * This is the Multipart version. - * - * @param sizeleft the number of bytes to try to get from currentData - * @return the next HttpChunk or null if not enough bytes were found - * @throws ErrorDataEncoderException if the encoding is in error - */ - private HttpChunk encodeNextChunkMultipart(int sizeleft) throws ErrorDataEncoderException { - if (currentData == null) { - return null; - } - ChannelBuffer buffer; - if (currentData instanceof InternalAttribute) { - String internal = ((InternalAttribute) currentData).toString(); - byte[] bytes; - try { - bytes = internal.getBytes("ASCII"); - } catch (UnsupportedEncodingException e) { - throw new ErrorDataEncoderException(e); - } - buffer = ChannelBuffers.wrappedBuffer(bytes); - currentData = null; - } else { - if (currentData instanceof Attribute) { - try { - buffer = ((Attribute) currentData).getChunk(sizeleft); - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - } else { - try { - buffer = ((FileUpload) currentData).getChunk(sizeleft); - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - } - if (buffer.capacity() == 0) { - // end for current InterfaceHttpData, need more data - currentData = null; - return null; - } - } - if (currentBuffer == null) { - currentBuffer = buffer; - } else { - currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, - buffer); - } - if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) { - currentData = null; - return null; - } - buffer = fillChannelBuffer(); - return new DefaultHttpChunk(buffer); - } - - /** - * From the current context (currentBuffer and currentData), returns the next HttpChunk - * (if possible) trying to get sizeleft bytes more into the currentBuffer. - * This is the UrlEncoded version. - * - * @param sizeleft the number of bytes to try to get from currentData - * @return the next HttpChunk or null if not enough bytes were found - * @throws ErrorDataEncoderException if the encoding is in error - */ - private HttpChunk encodeNextChunkUrlEncoded(int sizeleft) throws ErrorDataEncoderException { - if (currentData == null) { - return null; - } - int size = sizeleft; - ChannelBuffer buffer; - if (isKey) { - // get name - String key = currentData.getName(); - buffer = ChannelBuffers.wrappedBuffer(key.getBytes()); - isKey = false; - if (currentBuffer == null) { - currentBuffer = ChannelBuffers.wrappedBuffer( - buffer, ChannelBuffers.wrappedBuffer("=".getBytes())); - //continue - size -= buffer.readableBytes() + 1; - } else { - currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, - buffer, ChannelBuffers.wrappedBuffer("=".getBytes())); - //continue - size -= buffer.readableBytes() + 1; - } - if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) { - buffer = fillChannelBuffer(); - return new DefaultHttpChunk(buffer); - } - } - try { - buffer = ((Attribute) currentData).getChunk(size); - } catch (IOException e) { - throw new ErrorDataEncoderException(e); - } - ChannelBuffer delimiter = null; - if (buffer.readableBytes() < size) { - // delimiter - isKey = true; - delimiter = iterator.hasNext() ? - ChannelBuffers.wrappedBuffer("&".getBytes()) : - null; - } - if (buffer.capacity() == 0) { - // end for current InterfaceHttpData, need potentially more data - currentData = null; - if (currentBuffer == null) { - currentBuffer = delimiter; - } else { - if (delimiter != null) { - currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, - delimiter); - } - } - if (currentBuffer.readableBytes() >= HttpPostBodyUtil.chunkSize) { - buffer = fillChannelBuffer(); - return new DefaultHttpChunk(buffer); - } - return null; - } - if (currentBuffer == null) { - if (delimiter != null) { - currentBuffer = ChannelBuffers.wrappedBuffer(buffer, - delimiter); - } else { - currentBuffer = buffer; - } - } else { - if (delimiter != null) { - currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, - buffer, delimiter); - } else { - currentBuffer = ChannelBuffers.wrappedBuffer(currentBuffer, - buffer); - } - } - if (currentBuffer.readableBytes() < HttpPostBodyUtil.chunkSize) { - // end for current InterfaceHttpData, need more data - currentData = null; - isKey = true; - return null; - } - buffer = fillChannelBuffer(); - // size = 0 - return new DefaultHttpChunk(buffer); - } - - @Override - public void close() throws Exception { - //NO since the user can want to reuse (broadcast for instance) cleanFiles(); - } - - /** - * Returns the next available HttpChunk. The caller is responsible to test if this chunk is the - * last one (isLast()), in order to stop calling this method. - * - * @return the next available HttpChunk - * @throws ErrorDataEncoderException if the encoding is in error - */ - @Override - public HttpChunk nextChunk() throws ErrorDataEncoderException { - if (isLastChunk) { - isLastChunkSent = true; - return new DefaultHttpChunk(ChannelBuffers.EMPTY_BUFFER); - } - ChannelBuffer buffer = null; - int size = HttpPostBodyUtil.chunkSize; - // first test if previous buffer is not empty - if (currentBuffer != null) { - size -= currentBuffer.readableBytes(); - } - if (size <= 0) { - //NextChunk from buffer - buffer = fillChannelBuffer(); - return new DefaultHttpChunk(buffer); - } - // size > 0 - if (currentData != null) { - // continue to read data - if (isMultipart) { - HttpChunk chunk = encodeNextChunkMultipart(size); - if (chunk != null) { - return chunk; - } - } else { - HttpChunk chunk = encodeNextChunkUrlEncoded(size); - if (chunk != null) { - //NextChunk Url from currentData - return chunk; - } - } - size = HttpPostBodyUtil.chunkSize - currentBuffer.readableBytes(); - } - if (! iterator.hasNext()) { - isLastChunk = true; - //NextChunk as last non empty from buffer - buffer = currentBuffer; - currentBuffer = null; - return new DefaultHttpChunk(buffer); - } - while (size > 0 && iterator.hasNext()) { - currentData = iterator.next(); - HttpChunk chunk; - if (isMultipart) { - chunk = encodeNextChunkMultipart(size); - } else { - chunk = encodeNextChunkUrlEncoded(size); - } - if (chunk == null) { - // not enough - size = HttpPostBodyUtil.chunkSize - currentBuffer.readableBytes(); - continue; - } - //NextChunk from data - return chunk; - } - // end since no more data - isLastChunk = true; - if (currentBuffer == null) { - isLastChunkSent = true; - //LastChunk with no more data - return new DefaultHttpChunk(ChannelBuffers.EMPTY_BUFFER); - } - //Previous LastChunk with no more data - buffer = currentBuffer; - currentBuffer = null; - return new DefaultHttpChunk(buffer); - } - - @Override - public boolean isEndOfInput() throws Exception { - return isLastChunkSent; - } - - /** - * Exception when an error occurs while encoding - */ - public static class ErrorDataEncoderException extends Exception { - /** - */ - private static final long serialVersionUID = 5020247425493164465L; - - /** - */ - public ErrorDataEncoderException() { - } - - /** - * @param arg0 - */ - public ErrorDataEncoderException(String arg0) { - super(arg0); - } - - /** - * @param arg0 - */ - public ErrorDataEncoderException(Throwable arg0) { - super(arg0); - } - - /** - * @param arg0 - * @param arg1 - */ - public ErrorDataEncoderException(String arg0, Throwable arg1) { - super(arg0, arg1); - } - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpData.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpData.java deleted file mode 100644 index 39377ab565..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpData.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -/** - * Interface for all Objects that could be encoded/decoded using HttpPostRequestEncoder/Decoder - */ -public interface InterfaceHttpData extends Comparable { - enum HttpDataType { - Attribute, FileUpload, InternalAttribute - } - - /** - * Returns the name of this InterfaceHttpData. - */ - String getName(); - - /** - * - * @return The HttpDataType - */ - HttpDataType getHttpDataType(); -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InternalAttribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InternalAttribute.java deleted file mode 100644 index 4f65f25aa5..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/InternalAttribute.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import java.util.ArrayList; -import java.util.List; - -/** - * This Attribute is only for Encoder use to insert special command between object if needed - * (like Multipart Mixed mode) - */ -public class InternalAttribute implements InterfaceHttpData { - protected List value = new ArrayList(); - - @Override - public HttpDataType getHttpDataType() { - return HttpDataType.InternalAttribute; - } - - public List getValue() { - return value; - } - - public void addValue(String value) { - if (value == null) { - throw new NullPointerException("value"); - } - this.value.add(value); - } - - public void addValue(String value, int rank) { - if (value == null) { - throw new NullPointerException("value"); - } - this.value.add(rank, value); - } - - public void setValue(String value, int rank) { - if (value == null) { - throw new NullPointerException("value"); - } - this.value.set(rank, value); - } - - @Override - public int hashCode() { - return getName().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Attribute)) { - return false; - } - Attribute attribute = (Attribute) o; - return getName().equalsIgnoreCase(attribute.getName()); - } - - @Override - public int compareTo(InterfaceHttpData arg0) { - if (!(arg0 instanceof InternalAttribute)) { - throw new ClassCastException("Cannot compare " + getHttpDataType() + - " with " + arg0.getHttpDataType()); - } - return compareTo((InternalAttribute) arg0); - } - - public int compareTo(InternalAttribute o) { - return getName().compareToIgnoreCase(o.getName()); - } - - public int size() { - int size = 0; - for (String elt : value) { - size += elt.length(); - } - return size; - } - @Override - public String toString() { - StringBuilder result = new StringBuilder(); - for (String elt : value) { - result.append(elt); - } - return result.toString(); - } - - @Override - public String getName() { - return "InternalAttribute"; - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryAttribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryAttribute.java deleted file mode 100644 index 0370d359be..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryAttribute.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ChannelBuffer; -import io.netty.buffer.ChannelBuffers; -import io.netty.handler.codec.http.HttpConstants; - -import java.io.IOException; - -/** - * Memory implementation of Attributes - */ -public class MemoryAttribute extends AbstractMemoryHttpData implements Attribute { - - public MemoryAttribute(String name) { - super(name, HttpConstants.DEFAULT_CHARSET, 0); - } - /** - * - * @param name - * @param value - * @throws NullPointerException - * @throws IllegalArgumentException - * @throws IOException - */ - public MemoryAttribute(String name, String value) throws IOException { - super(name, HttpConstants.DEFAULT_CHARSET, 0); // Attribute have no default size - setValue(value); - } - - @Override - public HttpDataType getHttpDataType() { - return HttpDataType.Attribute; - } - - @Override - public String getValue() { - return getChannelBuffer().toString(charset); - } - - @Override - public void setValue(String value) throws IOException { - if (value == null) { - throw new NullPointerException("value"); - } - byte [] bytes = value.getBytes(charset); - ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(bytes); - if (definedSize > 0) { - definedSize = buffer.readableBytes(); - } - setContent(buffer); - } - - @Override - public void addContent(ChannelBuffer buffer, boolean last) throws IOException { - int localsize = buffer.readableBytes(); - if (definedSize > 0 && definedSize < size + localsize) { - definedSize = size + localsize; - } - super.addContent(buffer, last); - } - - @Override - public int hashCode() { - return getName().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Attribute)) { - return false; - } - Attribute attribute = (Attribute) o; - return getName().equalsIgnoreCase(attribute.getName()); - } - - @Override - public int compareTo(InterfaceHttpData arg0) { - if (!(arg0 instanceof Attribute)) { - throw new ClassCastException("Cannot compare " + getHttpDataType() + - " with " + arg0.getHttpDataType()); - } - return compareTo((Attribute) arg0); - } - - public int compareTo(Attribute o) { - return getName().compareToIgnoreCase(o.getName()); - } - - @Override - public String toString() { - return getName() + "=" + getValue(); - } - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryFileUpload.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryFileUpload.java deleted file mode 100644 index 6ea04a6e58..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryFileUpload.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.handler.codec.http.HttpHeaders; - -import java.nio.charset.Charset; - -/** - * Default FileUpload implementation that stores file into memory.

- * - * Warning: be aware of the memory limitation. - */ -public class MemoryFileUpload extends AbstractMemoryHttpData implements FileUpload { - - private String filename; - - private String contentType; - - private String contentTransferEncoding; - - public MemoryFileUpload(String name, String filename, String contentType, - String contentTransferEncoding, Charset charset, long size) { - super(name, charset, size); - setFilename(filename); - setContentType(contentType); - setContentTransferEncoding(contentTransferEncoding); - } - - @Override - public HttpDataType getHttpDataType() { - return HttpDataType.FileUpload; - } - - @Override - public String getFilename() { - return filename; - } - - @Override - public void setFilename(String filename) { - if (filename == null) { - throw new NullPointerException("filename"); - } - this.filename = filename; - } - - @Override - public int hashCode() { - return getName().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Attribute)) { - return false; - } - Attribute attribute = (Attribute) o; - return getName().equalsIgnoreCase(attribute.getName()); - } - - @Override - public int compareTo(InterfaceHttpData arg0) { - if (!(arg0 instanceof FileUpload)) { - throw new ClassCastException("Cannot compare " + getHttpDataType() + - " with " + arg0.getHttpDataType()); - } - return compareTo((FileUpload) arg0); - } - - public int compareTo(FileUpload o) { - int v; - v = getName().compareToIgnoreCase(o.getName()); - if (v != 0) { - return v; - } - // TODO should we compare size for instance ? - return v; - } - - @Override - public void setContentType(String contentType) { - if (contentType == null) { - throw new NullPointerException("contentType"); - } - this.contentType = contentType; - } - - @Override - public String getContentType() { - return contentType; - } - - @Override - public String getContentTransferEncoding() { - return contentTransferEncoding; - } - - @Override - public void setContentTransferEncoding(String contentTransferEncoding) { - this.contentTransferEncoding = contentTransferEncoding; - } - - @Override - public String toString() { - return HttpPostBodyUtil.CONTENT_DISPOSITION + ": " + - HttpPostBodyUtil.FORM_DATA + "; " + HttpPostBodyUtil.NAME + "=\"" + getName() + - "\"; " + HttpPostBodyUtil.FILENAME + "=\"" + filename + "\"\r\n" + - HttpHeaders.Names.CONTENT_TYPE + ": " + contentType + - (charset != null? "; " + HttpHeaders.Values.CHARSET + "=" + charset + "\r\n" : "\r\n") + - HttpHeaders.Names.CONTENT_LENGTH + ": " + length() + "\r\n" + - "Completed: " + isCompleted() + - "\r\nIsInMemory: " + isInMemory(); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedAttribute.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedAttribute.java deleted file mode 100644 index e44e450778..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedAttribute.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ChannelBuffer; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; - -/** - * Mixed implementation using both in Memory and in File with a limit of size - */ -public class MixedAttribute implements Attribute { - private Attribute attribute; - - private final long limitSize; - - public MixedAttribute(String name, long limitSize) { - this.limitSize = limitSize; - attribute = new MemoryAttribute(name); - } - - public MixedAttribute(String name, String value, long limitSize) { - this.limitSize = limitSize; - if (value.length() > this.limitSize) { - try { - attribute = new DiskAttribute(name, value); - } catch (IOException e) { - // revert to Memory mode - try { - attribute = new MemoryAttribute(name, value); - } catch (IOException e1) { - throw new IllegalArgumentException(e); - } - } - } else { - try { - attribute = new MemoryAttribute(name, value); - } catch (IOException e) { - throw new IllegalArgumentException(e); - } - } - } - - @Override - public void addContent(ChannelBuffer buffer, boolean last) throws IOException { - if (attribute instanceof MemoryAttribute) { - if (attribute.length() + buffer.readableBytes() > limitSize) { - DiskAttribute diskAttribute = new DiskAttribute(attribute - .getName()); - if (((MemoryAttribute) attribute).getChannelBuffer() != null) { - diskAttribute.addContent(((MemoryAttribute) attribute) - .getChannelBuffer(), false); - } - attribute = diskAttribute; - } - } - attribute.addContent(buffer, last); - } - - @Override - public void delete() { - attribute.delete(); - } - - @Override - public byte[] get() throws IOException { - return attribute.get(); - } - - @Override - public ChannelBuffer getChannelBuffer() throws IOException { - return attribute.getChannelBuffer(); - } - - @Override - public Charset getCharset() { - return attribute.getCharset(); - } - - @Override - public String getString() throws IOException { - return attribute.getString(); - } - - @Override - public String getString(Charset encoding) throws IOException { - return attribute.getString(encoding); - } - - @Override - public boolean isCompleted() { - return attribute.isCompleted(); - } - - @Override - public boolean isInMemory() { - return attribute.isInMemory(); - } - - @Override - public long length() { - return attribute.length(); - } - - @Override - public boolean renameTo(File dest) throws IOException { - return attribute.renameTo(dest); - } - - @Override - public void setCharset(Charset charset) { - attribute.setCharset(charset); - } - - @Override - public void setContent(ChannelBuffer buffer) throws IOException { - if (buffer.readableBytes() > limitSize) { - if (attribute instanceof MemoryAttribute) { - // change to Disk - attribute = new DiskAttribute(attribute.getName()); - } - } - attribute.setContent(buffer); - } - - @Override - public void setContent(File file) throws IOException { - if (file.length() > limitSize) { - if (attribute instanceof MemoryAttribute) { - // change to Disk - attribute = new DiskAttribute(attribute.getName()); - } - } - attribute.setContent(file); - } - - @Override - public void setContent(InputStream inputStream) throws IOException { - if (attribute instanceof MemoryAttribute) { - // change to Disk even if we don't know the size - attribute = new DiskAttribute(attribute.getName()); - } - attribute.setContent(inputStream); - } - - @Override - public HttpDataType getHttpDataType() { - return attribute.getHttpDataType(); - } - - @Override - public String getName() { - return attribute.getName(); - } - - @Override - public int compareTo(InterfaceHttpData o) { - return attribute.compareTo(o); - } - - @Override - public String toString() { - return "Mixed: " + attribute.toString(); - } - - @Override - public String getValue() throws IOException { - return attribute.getValue(); - } - - @Override - public void setValue(String value) throws IOException { - attribute.setValue(value); - } - - @Override - public ChannelBuffer getChunk(int length) throws IOException { - return attribute.getChunk(length); - } - - @Override - public File getFile() throws IOException { - return attribute.getFile(); - } - -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedFileUpload.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedFileUpload.java deleted file mode 100644 index b933f04070..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedFileUpload.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http.multipart; - -import io.netty.buffer.ChannelBuffer; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; - -/** - * Mixed implementation using both in Memory and in File with a limit of size - */ -public class MixedFileUpload implements FileUpload { - private FileUpload fileUpload; - - private final long limitSize; - - private final long definedSize; - - public MixedFileUpload(String name, String filename, String contentType, - String contentTransferEncoding, Charset charset, long size, - long limitSize) { - this.limitSize = limitSize; - if (size > this.limitSize) { - fileUpload = new DiskFileUpload(name, filename, contentType, - contentTransferEncoding, charset, size); - } else { - fileUpload = new MemoryFileUpload(name, filename, contentType, - contentTransferEncoding, charset, size); - } - definedSize = size; - } - - @Override - public void addContent(ChannelBuffer buffer, boolean last) - throws IOException { - if (fileUpload instanceof MemoryFileUpload) { - if (fileUpload.length() + buffer.readableBytes() > limitSize) { - DiskFileUpload diskFileUpload = new DiskFileUpload(fileUpload - .getName(), fileUpload.getFilename(), fileUpload - .getContentType(), fileUpload - .getContentTransferEncoding(), fileUpload.getCharset(), - definedSize); - if (((MemoryFileUpload) fileUpload).getChannelBuffer() != null) { - diskFileUpload.addContent(((MemoryFileUpload) fileUpload) - .getChannelBuffer(), false); - } - fileUpload = diskFileUpload; - } - } - fileUpload.addContent(buffer, last); - } - - @Override - public void delete() { - fileUpload.delete(); - } - - @Override - public byte[] get() throws IOException { - return fileUpload.get(); - } - - @Override - public ChannelBuffer getChannelBuffer() throws IOException { - return fileUpload.getChannelBuffer(); - } - - @Override - public Charset getCharset() { - return fileUpload.getCharset(); - } - - @Override - public String getContentType() { - return fileUpload.getContentType(); - } - - @Override - public String getContentTransferEncoding() { - return fileUpload.getContentTransferEncoding(); - } - - @Override - public String getFilename() { - return fileUpload.getFilename(); - } - - @Override - public String getString() throws IOException { - return fileUpload.getString(); - } - - @Override - public String getString(Charset encoding) throws IOException { - return fileUpload.getString(encoding); - } - - @Override - public boolean isCompleted() { - return fileUpload.isCompleted(); - } - - @Override - public boolean isInMemory() { - return fileUpload.isInMemory(); - } - - @Override - public long length() { - return fileUpload.length(); - } - - @Override - public boolean renameTo(File dest) throws IOException { - return fileUpload.renameTo(dest); - } - - @Override - public void setCharset(Charset charset) { - fileUpload.setCharset(charset); - } - - @Override - public void setContent(ChannelBuffer buffer) throws IOException { - if (buffer.readableBytes() > limitSize) { - if (fileUpload instanceof MemoryFileUpload) { - // change to Disk - fileUpload = new DiskFileUpload(fileUpload - .getName(), fileUpload.getFilename(), fileUpload - .getContentType(), fileUpload - .getContentTransferEncoding(), fileUpload.getCharset(), - definedSize); - } - } - fileUpload.setContent(buffer); - } - - @Override - public void setContent(File file) throws IOException { - if (file.length() > limitSize) { - if (fileUpload instanceof MemoryFileUpload) { - // change to Disk - fileUpload = new DiskFileUpload(fileUpload - .getName(), fileUpload.getFilename(), fileUpload - .getContentType(), fileUpload - .getContentTransferEncoding(), fileUpload.getCharset(), - definedSize); - } - } - fileUpload.setContent(file); - } - - @Override - public void setContent(InputStream inputStream) throws IOException { - if (fileUpload instanceof MemoryFileUpload) { - // change to Disk - fileUpload = new DiskFileUpload(fileUpload - .getName(), fileUpload.getFilename(), fileUpload - .getContentType(), fileUpload - .getContentTransferEncoding(), fileUpload.getCharset(), - definedSize); - } - fileUpload.setContent(inputStream); - } - - @Override - public void setContentType(String contentType) { - fileUpload.setContentType(contentType); - } - - @Override - public void setContentTransferEncoding(String contentTransferEncoding) { - fileUpload.setContentTransferEncoding(contentTransferEncoding); - } - - @Override - public void setFilename(String filename) { - fileUpload.setFilename(filename); - } - - @Override - public HttpDataType getHttpDataType() { - return fileUpload.getHttpDataType(); - } - - @Override - public String getName() { - return fileUpload.getName(); - } - - @Override - public int compareTo(InterfaceHttpData o) { - return fileUpload.compareTo(o); - } - - @Override - public String toString() { - return "Mixed: " + fileUpload.toString(); - } - - @Override - public ChannelBuffer getChunk(int length) throws IOException { - return fileUpload.getChunk(length); - } - - @Override - public File getFile() throws IOException { - return fileUpload.getFile(); - } - -} diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java deleted file mode 100644 index e1ce1e9d05..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java +++ /dev/null @@ -1,981 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import java.io.File; -import java.net.InetSocketAddress; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Map.Entry; -import java.util.concurrent.Executors; - -import io.netty.bootstrap.ClientBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.socket.nio.NioClientSocketChannelFactory; -import io.netty.handler.codec.http.CookieEncoder; -import io.netty.handler.codec.http.DefaultHttpRequest; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpMethod; -import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; -import io.netty.handler.codec.http.multipart.DiskAttribute; -import io.netty.handler.codec.http.multipart.DiskFileUpload; -import io.netty.handler.codec.http.multipart.HttpDataFactory; -import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder; -import io.netty.handler.codec.http.multipart.InterfaceHttpData; -import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder.ErrorDataEncoderException; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.QueryStringEncoder; - -/** - */ -public class HttpUploadClient { - - private final String baseUri; - private final String filePath; - - public HttpUploadClient(String baseUri, String filePath) { - this.baseUri = baseUri; - this.filePath = filePath; - } - - public void run() { - String postSimple, postFile, get; - if (baseUri.endsWith("/")) { - postSimple = baseUri + "formpost"; - postFile = baseUri + "formpostmultipart"; - get = baseUri + "formget"; - } else { - postSimple = baseUri + "/formpost"; - postFile = baseUri + "/formpostmultipart"; - get = baseUri + "/formget"; - } - URI uriSimple; - try { - uriSimple = new URI(postSimple); - } catch (URISyntaxException e) { - System.err.println("Error: " + e.getMessage()); - return; - } - String scheme = uriSimple.getScheme() == null? "http" : uriSimple.getScheme(); - String host = uriSimple.getHost() == null? "localhost" : uriSimple.getHost(); - int port = uriSimple.getPort(); - if (port == -1) { - if (scheme.equalsIgnoreCase("http")) { - port = 80; - } else if (scheme.equalsIgnoreCase("https")) { - port = 443; - } - } - - if (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https")) { - System.err.println("Only HTTP(S) is supported."); - return; - } - - boolean ssl = scheme.equalsIgnoreCase("https"); - - URI uriFile; - try { - uriFile = new URI(postFile); - } catch (URISyntaxException e) { - System.err.println("Error: " + e.getMessage()); - return; - } - File file = new File(filePath); - if (! file.canRead()) { - System.err.println("A correct path is needed"); - return; - } - - // Configure the client. - ClientBootstrap bootstrap = new ClientBootstrap( - new NioClientSocketChannelFactory( - Executors.newCachedThreadPool())); - - // Set up the event pipeline factory. - bootstrap.setPipelineFactory(new HttpUploadClientPipelineFactory(ssl)); - - // setup the factory: here using a mixed memory/disk based on size threshold - HttpDataFactory factory = new DefaultHttpDataFactory( - DefaultHttpDataFactory.MINSIZE); // Disk if size exceed MINSIZE - DiskFileUpload.deleteOnExitTemporaryFile = true; // should delete file on exit (in normal exit) - DiskFileUpload.baseDirectory = null; // system temp directory - DiskAttribute.deleteOnExitTemporaryFile = true; // should delete file on exit (in normal exit) - DiskAttribute.baseDirectory = null; // system temp directory - - // Simple Get form: no factory used (not usable) - List> headers = - formget(bootstrap, host, port, get, uriSimple); - if (headers == null) { - factory.cleanAllHttpDatas(); - return; - } - // Simple Post form: factory used for big attributes - List bodylist = - formpost(bootstrap, host, port, uriSimple, file, factory, headers); - if (bodylist == null) { - factory.cleanAllHttpDatas(); - return; - } - // Multipart Post form: factory used - formpostmultipart(bootstrap, host, port, uriFile, factory, headers, bodylist); - - // Shut down executor threads to exit. - bootstrap.releaseExternalResources(); - // Really clean all temporary files if they still exist - factory.cleanAllHttpDatas(); - } - - /** - * Standard usage of HTTP API in Netty without file Upload (get is not able to achieve File upload - * due to limitation on request size). - * @return the list of headers that will be used in every example after - **/ - private static List> formget(ClientBootstrap bootstrap, String host, int port, String get, - URI uriSimple) { - // XXX /formget - // No use of HttpPostRequestEncoder since not a POST - // Start the connection attempt. - ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); - // Wait until the connection attempt succeeds or fails. - Channel channel = future.awaitUninterruptibly().channel(); - if (!future.isSuccess()) { - future.cause().printStackTrace(); - bootstrap.releaseExternalResources(); - return null; - } - - // Prepare the HTTP request. - QueryStringEncoder encoder = new QueryStringEncoder(get); - // add Form attribute - encoder.addParam("getform", "GET"); - encoder.addParam("info", "first value"); - encoder.addParam("secondinfo", "secondvalue ���&"); - // not the big one since it is not compatible with GET size - // encoder.addParam("thirdinfo", textArea); - encoder.addParam("thirdinfo", "third value\r\ntest second line\r\n\r\nnew line\r\n"); - encoder.addParam("Send", "Send"); - - URI uriGet; - try { - uriGet = new URI(encoder.toString()); - } catch (URISyntaxException e) { - System.err.println("Error: " + e.getMessage()); - bootstrap.releaseExternalResources(); - return null; - } - - HttpRequest request = new DefaultHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString()); - request.setHeader(HttpHeaders.Names.HOST, host); - request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); - request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP + "," + - HttpHeaders.Values.DEFLATE); - - request.setHeader(HttpHeaders.Names.ACCEPT_CHARSET, "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); - request.setHeader(HttpHeaders.Names.ACCEPT_LANGUAGE, "fr"); - request.setHeader(HttpHeaders.Names.REFERER, uriSimple.toString()); - request.setHeader(HttpHeaders.Names.USER_AGENT, "Netty Simple Http Client side"); - request.setHeader(HttpHeaders.Names.ACCEPT, - "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - //connection will not close but needed - // request.setHeader("Connection","keep-alive"); - // request.setHeader("Keep-Alive","300"); - - CookieEncoder httpCookieEncoder = new CookieEncoder(false); - httpCookieEncoder.addCookie("my-cookie", "foo"); - httpCookieEncoder.addCookie("another-cookie", "bar"); - request.setHeader(HttpHeaders.Names.COOKIE, httpCookieEncoder.encode()); - - List> headers = request.getHeaders(); - // send request - channel.write(request); - - // Wait for the server to close the connection. - channel.getCloseFuture().awaitUninterruptibly(); - - return headers; - } - - /** - * Standard post without multipart but already support on Factory (memory management) - * - * @return the list of HttpData object (attribute and file) to be reused on next post - */ - private static List formpost(ClientBootstrap bootstrap, - String host, int port, - URI uriSimple, File file, HttpDataFactory factory, - List> headers) { - // XXX /formpost - // Start the connection attempt. - ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); - // Wait until the connection attempt succeeds or fails. - Channel channel = future.awaitUninterruptibly().channel(); - if (!future.isSuccess()) { - future.cause().printStackTrace(); - bootstrap.releaseExternalResources(); - return null; - } - - // Prepare the HTTP request. - HttpRequest request = new DefaultHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString()); - - // Use the PostBody encoder - HttpPostRequestEncoder bodyRequestEncoder = null; - try { - bodyRequestEncoder = new HttpPostRequestEncoder(factory, - request, false); // false => not multipart - } catch (NullPointerException e) { - // should not be since args are not null - e.printStackTrace(); - } catch (ErrorDataEncoderException e) { - // test if method is a POST method - e.printStackTrace(); - } - - // it is legal to add directly header or cookie into the request until finalize - for (Entry entry : headers) { - request.setHeader(entry.getKey(), entry.getValue()); - } - - // add Form attribute - try { - bodyRequestEncoder.addBodyAttribute("getform", "POST"); - bodyRequestEncoder.addBodyAttribute("info", "first value"); - bodyRequestEncoder.addBodyAttribute("secondinfo", "secondvalue ���&"); - bodyRequestEncoder.addBodyAttribute("thirdinfo", textArea); - bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", false); - bodyRequestEncoder.addBodyAttribute("Send", "Send"); - } catch (NullPointerException e) { - // should not be since not null args - e.printStackTrace(); - } catch (ErrorDataEncoderException e) { - // if an encoding error occurs - e.printStackTrace(); - } - - // finalize request - try { - request = bodyRequestEncoder.finalizeRequest(); - } catch (ErrorDataEncoderException e) { - // if an encoding error occurs - e.printStackTrace(); - } - // Create the bodylist to be reused on the last version with Multipart support - List bodylist = bodyRequestEncoder.getBodyListAttributes(); - - // send request - channel.write(request); - - // test if request was chunked and if so, finish the write - if (bodyRequestEncoder.isChunked()) { // could do either request.isChunked() - // either do it through ChunkedWriteHandler - channel.write(bodyRequestEncoder).awaitUninterruptibly(); - } - - // Do not clear here since we will reuse the InterfaceHttpData on the next request - // for the example (limit action on client side). Take this as a broadcast of the same - // request on both Post actions. - // - // On standard program, it is clearly recommended to clean all files after each request - // bodyRequestEncoder.cleanFiles(); - - // Wait for the server to close the connection. - channel.getCloseFuture().awaitUninterruptibly(); - return bodylist; - } - - /** - * Multipart example - */ - private static void formpostmultipart(ClientBootstrap bootstrap, String host, int port, - URI uriFile, HttpDataFactory factory, - List> headers, List bodylist) { - // XXX /formpostmultipart - // Start the connection attempt. - ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); - // Wait until the connection attempt succeeds or fails. - Channel channel = future.awaitUninterruptibly().channel(); - if (!future.isSuccess()) { - future.cause().printStackTrace(); - bootstrap.releaseExternalResources(); - return; - } - - // Prepare the HTTP request. - HttpRequest request = new DefaultHttpRequest( - HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString()); - - // Use the PostBody encoder - HttpPostRequestEncoder bodyRequestEncoder = null; - try { - bodyRequestEncoder = new HttpPostRequestEncoder(factory, - request, true); // true => multipart - } catch (NullPointerException e) { - // should not be since no null args - e.printStackTrace(); - } catch (ErrorDataEncoderException e) { - // test if method is a POST method - e.printStackTrace(); - } - - // it is legal to add directly header or cookie into the request until finalize - for (Entry entry : headers) { - request.setHeader(entry.getKey(), entry.getValue()); - } - - // add Form attribute from previous request in formpost() - try { - bodyRequestEncoder.setBodyHttpDatas(bodylist); - } catch (NullPointerException e1) { - // should not be since previously created - e1.printStackTrace(); - } catch (ErrorDataEncoderException e1) { - // again should not be since previously encoded (except if an error occurs previously) - e1.printStackTrace(); - } - - // finalize request - try { - bodyRequestEncoder.finalizeRequest(); - } catch (ErrorDataEncoderException e) { - // if an encoding error occurs - e.printStackTrace(); - } - - // send request - channel.write(request); - - // test if request was chunked and if so, finish the write - if (bodyRequestEncoder.isChunked()) { - channel.write(bodyRequestEncoder).awaitUninterruptibly(); - } - - // Now no more use of file representation (and list of HttpData) - bodyRequestEncoder.cleanFiles(); - - // Wait for the server to close the connection. - channel.getCloseFuture().awaitUninterruptibly(); - } - - public static void main(String[] args) { - if (args.length != 2) { - System.err.println( - "Usage: " + HttpUploadClient.class.getSimpleName() + - " baseURI filePath"); - return; - } - - String baseUri = args[0]; - String filePath = args[1]; - - new HttpUploadClient(baseUri, filePath).run(); - } - - // use to simulate a big TEXTAREA field in a form - private static final String textArea = - "lkjlkjlKJLKJLKJLKJLJlkj lklkj\r\n\r\nLKJJJJJJJJKKKKKKKKKKKKKKK ����&\r\n\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n" + - "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\r\n"; - -} diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java deleted file mode 100644 index b6b157f420..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import io.netty.buffer.ChannelBuffer; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ExceptionEvent; -import io.netty.channel.MessageEvent; -import io.netty.channel.SimpleChannelUpstreamHandler; -import io.netty.handler.codec.http.HttpChunk; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.util.CharsetUtil; - -public class HttpUploadClientHandler extends SimpleChannelUpstreamHandler { - - private volatile boolean readingChunks; - - @Override - public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { - if (!readingChunks) { - HttpResponse response = (HttpResponse) e.getMessage(); - - System.out.println("STATUS: " + response.getStatus()); - System.out.println("VERSION: " + response.getProtocolVersion()); - System.out.println(); - - if (!response.getHeaderNames().isEmpty()) { - for (String name: response.getHeaderNames()) { - for (String value: response.getHeaders(name)) { - System.out.println("HEADER: " + name + " = " + value); - } - } - System.out.println(); - } - - if (response.getStatus().getCode() == 200 && response.isChunked()) { - readingChunks = true; - System.out.println("CHUNKED CONTENT {"); - } else { - ChannelBuffer content = response.getContent(); - if (content.readable()) { - System.out.println("CONTENT {"); - System.out.println(content.toString(CharsetUtil.UTF_8)); - System.out.println("} END OF CONTENT"); - } - } - } else { - HttpChunk chunk = (HttpChunk) e.getMessage(); - if (chunk.isLast()) { - readingChunks = false; - System.out.println("} END OF CHUNKED CONTENT"); - } else { - System.out.print(chunk.getContent().toString(CharsetUtil.UTF_8)); - System.out.flush(); - } - } - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) - throws Exception { - e.cause().printStackTrace(); - e.channel().close(); - } -} diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientPipelineFactory.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadClientPipelineFactory.java deleted file mode 100644 index 149d8c622e..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientPipelineFactory.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import static io.netty.channel.Channels.*; - -import javax.net.ssl.SSLEngine; - -import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelPipelineFactory; -import io.netty.example.securechat.SecureChatSslContextFactory; -import io.netty.handler.codec.http.HttpClientCodec; -import io.netty.handler.codec.http.HttpContentDecompressor; -import io.netty.handler.ssl.SslHandler; -import io.netty.handler.stream.ChunkedWriteHandler; - -public class HttpUploadClientPipelineFactory implements ChannelPipelineFactory { - private final boolean ssl; - - public HttpUploadClientPipelineFactory(boolean ssl) { - this.ssl = ssl; - } - - @Override - public ChannelPipeline getPipeline() throws Exception { - // Create a default pipeline implementation. - ChannelPipeline pipeline = pipeline(); - - // Enable HTTPS if necessary. - if (ssl) { - SSLEngine engine = - SecureChatSslContextFactory.getClientContext().createSSLEngine(); - engine.setUseClientMode(true); - - pipeline.addLast("ssl", new SslHandler(engine)); - } - - pipeline.addLast("codec", new HttpClientCodec()); - - // Remove the following line if you don't want automatic content decompression. - pipeline.addLast("inflater", new HttpContentDecompressor()); - - // to be used since huge file transfer - pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); - - pipeline.addLast("handler", new HttpUploadClientHandler()); - return pipeline; - } -} diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadServer.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadServer.java deleted file mode 100644 index 8a45511588..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadServer.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import java.net.InetSocketAddress; -import java.util.concurrent.Executors; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.socket.nio.NioServerSocketChannelFactory; - -public class HttpUploadServer { - - private final int port; - - public HttpUploadServer(int port) { - this.port = port; - } - - public void run() { - // Configure the server. - ServerBootstrap bootstrap = new ServerBootstrap( - new NioServerSocketChannelFactory( - Executors.newCachedThreadPool())); - - // Set up the event pipeline factory. - bootstrap.setPipelineFactory(new HttpUploadServerPipelineFactory()); - - // Bind and start to accept incoming connections. - bootstrap.bind(new InetSocketAddress(port)); - } - - public static void main(String[] args) { - int port; - if (args.length > 0) { - port = Integer.parseInt(args[0]); - } else { - port = 8080; - } - new HttpUploadServer(port).run(); - } -} diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java deleted file mode 100644 index f29f00fd34..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import java.io.IOException; -import java.net.URI; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import io.netty.buffer.ChannelBuffer; -import io.netty.buffer.ChannelBuffers; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelStateEvent; -import io.netty.channel.Channels; -import io.netty.channel.ExceptionEvent; -import io.netty.channel.MessageEvent; -import io.netty.channel.SimpleChannelUpstreamHandler; -import io.netty.handler.codec.http.Cookie; -import io.netty.handler.codec.http.CookieDecoder; -import io.netty.handler.codec.http.CookieEncoder; -import io.netty.handler.codec.http.DefaultHttpResponse; -import io.netty.handler.codec.http.HttpChunk; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.multipart.Attribute; -import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; -import io.netty.handler.codec.http.multipart.DiskAttribute; -import io.netty.handler.codec.http.multipart.DiskFileUpload; -import io.netty.handler.codec.http.multipart.FileUpload; -import io.netty.handler.codec.http.multipart.HttpDataFactory; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; -import io.netty.handler.codec.http.multipart.InterfaceHttpData; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.IncompatibleDataDecoderException; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException; -import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.util.CharsetUtil; - -public class HttpUploadServerHandler extends SimpleChannelUpstreamHandler { - - private volatile HttpRequest request; - - private volatile boolean readingChunks; - - private final StringBuilder responseContent = new StringBuilder(); - - private static final HttpDataFactory factory = new DefaultHttpDataFactory( - DefaultHttpDataFactory.MINSIZE); // Disk if size exceed MINSIZE - - private HttpPostRequestDecoder decoder; - static { - DiskFileUpload.deleteOnExitTemporaryFile = true; // should delete file - // on exit (in normal - // exit) - DiskFileUpload.baseDirectory = null; // system temp directory - DiskAttribute.deleteOnExitTemporaryFile = true; // should delete file on - // exit (in normal exit) - DiskAttribute.baseDirectory = null; // system temp directory - } - - @Override - public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) - throws Exception { - if (decoder != null) { - decoder.cleanFiles(); - } - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { - if (!readingChunks) { - // clean previous FileUpload if Any - if (decoder != null) { - decoder.cleanFiles(); - decoder = null; - } - - HttpRequest request = this.request = (HttpRequest) e.getMessage(); - URI uri = new URI(request.getUri()); - if (!uri.getPath().startsWith("/form")) { - // Write Menu - writeMenu(e); - return; - } - responseContent.setLength(0); - responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n"); - responseContent.append("===================================\r\n"); - - responseContent.append("VERSION: " + - request.getProtocolVersion().getText() + "\r\n"); - - responseContent.append("REQUEST_URI: " + request.getUri() + - "\r\n\r\n"); - responseContent.append("\r\n\r\n"); - - // new method - List> headers = request.getHeaders(); - for (Entry entry: headers) { - responseContent.append("HEADER: " + entry.getKey() + "=" + - entry.getValue() + "\r\n"); - } - responseContent.append("\r\n\r\n"); - - // new method - Set cookies; - String value = request.getHeader(HttpHeaders.Names.COOKIE); - if (value == null) { - cookies = Collections.emptySet(); - } else { - CookieDecoder decoder = new CookieDecoder(); - cookies = decoder.decode(value); - } - for (Cookie cookie: cookies) { - responseContent.append("COOKIE: " + cookie.toString() + "\r\n"); - } - responseContent.append("\r\n\r\n"); - - QueryStringDecoder decoderQuery = new QueryStringDecoder(request - .getUri()); - Map> uriAttributes = decoderQuery - .getParameters(); - for (String key: uriAttributes.keySet()) { - for (String valuen: uriAttributes.get(key)) { - responseContent.append("URI: " + key + "=" + valuen + - "\r\n"); - } - } - responseContent.append("\r\n\r\n"); - - // if GET Method: should not try to create a HttpPostRequestDecoder - try { - decoder = new HttpPostRequestDecoder(factory, request); - } catch (ErrorDataDecoderException e1) { - e1.printStackTrace(); - responseContent.append(e1.getMessage()); - writeResponse(e.channel()); - Channels.close(e.channel()); - return; - } catch (IncompatibleDataDecoderException e1) { - // GET Method: should not try to create a HttpPostRequestDecoder - // So OK but stop here - responseContent.append(e1.getMessage()); - responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n"); - writeResponse(e.channel()); - return; - } - - responseContent.append("Is Chunked: " + request.isChunked() + - "\r\n"); - responseContent.append("IsMultipart: " + decoder.isMultipart() + - "\r\n"); - if (request.isChunked()) { - // Chunk version - responseContent.append("Chunks: "); - readingChunks = true; - } else { - // Not chunk version - readHttpDataAllReceive(e.channel()); - responseContent - .append("\r\n\r\nEND OF NOT CHUNKED CONTENT\r\n"); - writeResponse(e.channel()); - } - } else { - // New chunk is received - HttpChunk chunk = (HttpChunk) e.getMessage(); - try { - decoder.offer(chunk); - } catch (ErrorDataDecoderException e1) { - e1.printStackTrace(); - responseContent.append(e1.getMessage()); - writeResponse(e.channel()); - Channels.close(e.channel()); - return; - } - responseContent.append("o"); - // example of reading chunk by chunk (minimize memory usage due to Factory) - readHttpDataChunkByChunk(e.channel()); - // example of reading only if at the end - if (chunk.isLast()) { - readHttpDataAllReceive(e.channel()); - writeResponse(e.channel()); - readingChunks = false; - } - } - } - - /** - * Example of reading all InterfaceHttpData from finished transfer - * - * @param channel - */ - private void readHttpDataAllReceive(Channel channel) { - List datas = null; - try { - datas = decoder.getBodyHttpDatas(); - } catch (NotEnoughDataDecoderException e1) { - // Should not be! - e1.printStackTrace(); - responseContent.append(e1.getMessage()); - writeResponse(channel); - Channels.close(channel); - return; - } - for (InterfaceHttpData data: datas) { - writeHttpData(data); - } - responseContent.append("\r\n\r\nEND OF CONTENT AT FINAL END\r\n"); - } - - /** - * Example of reading request by chunk and getting values from chunk to - * chunk - * - * @param channel - */ - private void readHttpDataChunkByChunk(Channel channel) { - try { - while (decoder.hasNext()) { - InterfaceHttpData data = decoder.next(); - if (data != null) { - // new value - writeHttpData(data); - } - } - } catch (EndOfDataDecoderException e1) { - // end - responseContent - .append("\r\n\r\nEND OF CONTENT CHUNK BY CHUNK\r\n\r\n"); - } - } - - private void writeHttpData(InterfaceHttpData data) { - if (data.getHttpDataType() == HttpDataType.Attribute) { - Attribute attribute = (Attribute) data; - String value; - try { - value = attribute.getValue(); - } catch (IOException e1) { - // Error while reading data from File, only print name and error - e1.printStackTrace(); - responseContent.append("\r\nBODY Attribute: " + - attribute.getHttpDataType().name() + ": " + - attribute.getName() + " Error while reading value: " + - e1.getMessage() + "\r\n"); - return; - } - if (value.length() > 100) { - responseContent.append("\r\nBODY Attribute: " + - attribute.getHttpDataType().name() + ": " + - attribute.getName() + " data too long\r\n"); - } else { - responseContent.append("\r\nBODY Attribute: " + - attribute.getHttpDataType().name() + ": " + - attribute.toString() + "\r\n"); - } - } else { - responseContent.append("\r\nBODY FileUpload: " + - data.getHttpDataType().name() + ": " + data.toString() + - "\r\n"); - if (data.getHttpDataType() == HttpDataType.FileUpload) { - FileUpload fileUpload = (FileUpload) data; - if (fileUpload.isCompleted()) { - if (fileUpload.length() < 10000) { - responseContent.append("\tContent of file\r\n"); - try { - responseContent - .append(((FileUpload) data) - .getString(((FileUpload) data) - .getCharset())); - } catch (IOException e1) { - // do nothing for the example - e1.printStackTrace(); - } - responseContent.append("\r\n"); - } else { - responseContent - .append("\tFile too long to be printed out:" + - fileUpload.length() + "\r\n"); - } - // fileUpload.isInMemory();// tells if the file is in Memory - // or on File - // fileUpload.renameTo(dest); // enable to move into another - // File dest - // decoder.removeFileUploadFromClean(fileUpload); //remove - // the File of to delete file - } else { - responseContent - .append("\tFile to be continued but should not!\r\n"); - } - } - } - } - - private void writeResponse(Channel channel) { - // Convert the response content to a ChannelBuffer. - ChannelBuffer buf = ChannelBuffers.copiedBuffer(responseContent - .toString(), CharsetUtil.UTF_8); - responseContent.setLength(0); - - // Decide whether to close the connection or not. - boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request - .getHeader(HttpHeaders.Names.CONNECTION)) || - request.getProtocolVersion().equals(HttpVersion.HTTP_1_0) && - !HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request - .getHeader(HttpHeaders.Names.CONNECTION)); - - // Build the response object. - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, - HttpResponseStatus.OK); - response.setContent(buf); - response.setHeader(HttpHeaders.Names.CONTENT_TYPE, - "text/plain; charset=UTF-8"); - - if (!close) { - // There's no need to add 'Content-Length' header - // if this is the last response. - response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String - .valueOf(buf.readableBytes())); - } - - Set cookies; - String value = request.getHeader(HttpHeaders.Names.COOKIE); - if (value == null) { - cookies = Collections.emptySet(); - } else { - CookieDecoder decoder = new CookieDecoder(); - cookies = decoder.decode(value); - } - if (!cookies.isEmpty()) { - // Reset the cookies if necessary. - CookieEncoder cookieEncoder = new CookieEncoder(true); - for (Cookie cookie: cookies) { - cookieEncoder.addCookie(cookie); - } - response.addHeader(HttpHeaders.Names.SET_COOKIE, cookieEncoder - .encode()); - } - // Write the response. - ChannelFuture future = channel.write(response); - // Close the connection after the write operation is done if necessary. - if (close) { - future.addListener(ChannelFutureListener.CLOSE); - } - } - - private void writeMenu(MessageEvent e) { - // print several HTML forms - // Convert the response content to a ChannelBuffer. - responseContent.setLength(0); - - // create Pseudo Menu - responseContent.append(""); - responseContent.append(""); - responseContent.append("Netty Test Form\r\n"); - responseContent.append("\r\n"); - responseContent - .append(""); - - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append(""); - responseContent.append("
"); - responseContent.append("

Netty Test Form

"); - responseContent.append("Choose one FORM"); - responseContent.append("
\r\n"); - - // GET - responseContent - .append("
GET FORM
"); - responseContent.append("
"); - responseContent - .append(""); - responseContent.append(""); - responseContent - .append(""); - responseContent - .append(""); - responseContent - .append(""); - responseContent - .append(""); - responseContent.append("
Fill with value:
Fill with value:
"); - responseContent - .append("
Fill with value:
"); - responseContent.append("
\r\n"); - responseContent - .append("

"); - - // POST - responseContent - .append("
POST FORM
"); - responseContent.append("
"); - responseContent - .append(""); - responseContent.append(""); - responseContent - .append(""); - responseContent - .append(""); - responseContent - .append(""); - responseContent - .append(""); - responseContent.append("
Fill with value:
Fill with value:
"); - responseContent - .append("
Fill with value:
"); - responseContent - .append("
Fill with file (only file name will be transmitted):
"); - responseContent.append("
\r\n"); - responseContent - .append("

"); - - // POST with enctype="multipart/form-data" - responseContent - .append("
POST MULTIPART FORM
"); - responseContent - .append("
"); - responseContent - .append(""); - responseContent.append(""); - responseContent - .append(""); - responseContent - .append(""); - responseContent - .append(""); - responseContent - .append(""); - responseContent.append("
Fill with value:
Fill with value:
"); - responseContent - .append("
Fill with value:
"); - responseContent - .append("
Fill with file:
"); - responseContent.append("
\r\n"); - responseContent - .append("

"); - - responseContent.append(""); - responseContent.append(""); - - ChannelBuffer buf = ChannelBuffers.copiedBuffer(responseContent - .toString(), CharsetUtil.UTF_8); - // Build the response object. - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, - HttpResponseStatus.OK); - response.setContent(buf); - response.setHeader(HttpHeaders.Names.CONTENT_TYPE, - "text/html; charset=UTF-8"); - response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf - .readableBytes())); - // Write the response. - e.channel().write(response); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) - throws Exception { - e.cause().printStackTrace(); - System.err.println(responseContent.toString()); - e.channel().close(); - } -} diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerPipelineFactory.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadServerPipelineFactory.java deleted file mode 100644 index edb1a5a802..0000000000 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerPipelineFactory.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.example.http.upload; - -import static io.netty.channel.Channels.pipeline; - -import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelPipelineFactory; -import io.netty.handler.codec.http.HttpContentCompressor; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; - -public class HttpUploadServerPipelineFactory implements ChannelPipelineFactory { - @Override - public ChannelPipeline getPipeline() throws Exception { - // Create a default pipeline implementation. - ChannelPipeline pipeline = pipeline(); - - // Uncomment the following line if you want HTTPS - //SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine(); - //engine.setUseClientMode(false); - //pipeline.addLast("ssl", new SslHandler(engine)); - - pipeline.addLast("decoder", new HttpRequestDecoder()); - - pipeline.addLast("encoder", new HttpResponseEncoder()); - - // Remove the following line if you don't want automatic content compression. - pipeline.addLast("deflater", new HttpContentCompressor()); - - pipeline.addLast("handler", new HttpUploadServerHandler()); - return pipeline; - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/CIDR.java b/handler/src/main/java/io/netty/handler/ipfilter/CIDR.java deleted file mode 100644 index b0a4857daa..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/CIDR.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.StringTokenizer; - -/** - */ -public abstract class CIDR implements Comparable { - /** The base address of the CIDR notation */ - protected InetAddress baseAddress; - - /** The mask used in the CIDR notation */ - protected int cidrMask; - - /** - * Create CIDR using the CIDR Notation - * - * @return the generated CIDR - */ - public static CIDR newCIDR(InetAddress baseAddress, int cidrMask) throws UnknownHostException { - if (cidrMask < 0) { - throw new UnknownHostException("Invalid mask length used: " + cidrMask); - } - if (baseAddress instanceof Inet4Address) { - if (cidrMask > 32) { - throw new UnknownHostException("Invalid mask length used: " + cidrMask); - } - return new CIDR4((Inet4Address) baseAddress, cidrMask); - } - // IPv6. - if (cidrMask > 128) { - throw new UnknownHostException("Invalid mask length used: " + cidrMask); - } - return new CIDR6((Inet6Address) baseAddress, cidrMask); - } - - /** - * Create CIDR using the normal Notation - * - * @return the generated CIDR - */ - public static CIDR newCIDR(InetAddress baseAddress, String scidrMask) throws UnknownHostException { - int cidrMask = getNetMask(scidrMask); - if (cidrMask < 0) { - throw new UnknownHostException("Invalid mask length used: " + cidrMask); - } - if (baseAddress instanceof Inet4Address) { - if (cidrMask > 32) { - throw new UnknownHostException("Invalid mask length used: " + cidrMask); - } - return new CIDR4((Inet4Address) baseAddress, cidrMask); - } - cidrMask += 96; - // IPv6. - if (cidrMask > 128) { - throw new UnknownHostException("Invalid mask length used: " + cidrMask); - } - return new CIDR6((Inet6Address) baseAddress, cidrMask); - } - - /** - * Create CIDR using the CIDR or normal Notation
- * i.e.: - * CIDR subnet = newCIDR ("10.10.10.0/24"); or - * CIDR subnet = newCIDR ("1fff:0:0a88:85a3:0:0:ac1f:8001/24"); or - * CIDR subnet = newCIDR ("10.10.10.0/255.255.255.0"); - * - * @return the generated CIDR - */ - public static CIDR newCIDR(String cidr) throws UnknownHostException { - int p = cidr.indexOf("/"); - if (p < 0) { - throw new UnknownHostException("Invalid CIDR notation used: " + cidr); - } - String addrString = cidr.substring(0, p); - String maskString = cidr.substring(p + 1); - InetAddress addr = addressStringToInet(addrString); - int mask = 0; - if (maskString.indexOf(".") < 0) { - mask = parseInt(maskString, -1); - } else { - mask = getNetMask(maskString); - if (addr instanceof Inet6Address) { - mask += 96; - } - } - if (mask < 0) { - throw new UnknownHostException("Invalid mask length used: " + maskString); - } - return newCIDR(addr, mask); - } - - /** @return the baseAddress of the CIDR block. */ - public InetAddress getBaseAddress() { - return baseAddress; - } - - /** @return the Mask length. */ - public int getMask() { - return cidrMask; - } - - /** @return the textual CIDR notation. */ - @Override - public String toString() { - return baseAddress.getHostAddress() + "/" + cidrMask; - } - - /** @return the end address of this block. */ - public abstract InetAddress getEndAddress(); - - /** - * Compares the given InetAddress against the CIDR and returns true if - * the ip is in the subnet-ip-range and false if not. - * - * @return returns true if the given IP address is inside the currently - * set network. - */ - public abstract boolean contains(InetAddress inetAddress); - - @Override - public boolean equals(Object arg0) { - if (!(arg0 instanceof CIDR)) { - return false; - } - return this.compareTo((CIDR) arg0) == 0; - } - - @Override - public int hashCode() { - return baseAddress.hashCode(); - } - - /** - * Convert an IPv4 or IPv6 textual representation into an - * InetAddress. - * - * @return the created InetAddress - */ - private static InetAddress addressStringToInet(String addr) throws UnknownHostException { - return InetAddress.getByName(addr); - } - - /** - * Get the Subnet's Netmask in Decimal format.
- * i.e.: getNetMask("255.255.255.0") returns the integer CIDR mask - * - * @param netMask a network mask - * @return the integer CIDR mask - */ - private static int getNetMask(String netMask) { - StringTokenizer nm = new StringTokenizer(netMask, "."); - int i = 0; - int[] netmask = new int[4]; - while (nm.hasMoreTokens()) { - netmask[i] = Integer.parseInt(nm.nextToken()); - i++; - } - int mask1 = 0; - for (i = 0; i < 4; i++) { - mask1 += Integer.bitCount(netmask[i]); - } - return mask1; - } - - /** - * @param intstr a string containing an integer. - * @param def the default if the string does not contain a valid - * integer. - * @return the inetAddress from the integer - */ - private static int parseInt(String intstr, int def) { - Integer res; - if (intstr == null) { - return def; - } - try { - res = Integer.decode(intstr); - } catch (Exception e) { - res = new Integer(def); - } - return res.intValue(); - } - - /** - * Compute a byte representation of IpV4 from a IpV6 - * - * @return the byte representation - * @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4 - */ - public static byte[] getIpV4FromIpV6(Inet6Address address) { - byte[] baddr = address.getAddress(); - for (int i = 0; i < 9; i++) { - if (baddr[i] != 0) { - throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context"); - } - } - if (baddr[10] != 0 && baddr[10] != 0xFF || baddr[11] != 0 && baddr[11] != 0xFF) { - throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context"); - } - return new byte[] - {baddr[12], baddr[13], baddr[14], baddr[15]}; - } - - /** - * Compute a byte representation of IpV6 from a IpV4 - * - * @return the byte representation - * @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4 - */ - public static byte[] getIpV6FromIpV4(Inet4Address address) { - byte[] baddr = address.getAddress(); - return new byte[] - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, baddr[0], baddr[1], baddr[2], baddr[3]}; - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/CIDR4.java b/handler/src/main/java/io/netty/handler/ipfilter/CIDR4.java deleted file mode 100644 index 1a4bc458b5..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/CIDR4.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - */ -public class CIDR4 extends CIDR { - /** The integer for the base address */ - private int addressInt; - - /** The integer for the end address */ - private final int addressEndInt; - - /** - * @param newaddr - * @param mask - */ - protected CIDR4(Inet4Address newaddr, int mask) { - cidrMask = mask; - addressInt = ipv4AddressToInt(newaddr); - int newmask = ipv4PrefixLengthToMask(mask); - addressInt &= newmask; - try { - baseAddress = intToIPv4Address(addressInt); - } catch (UnknownHostException e) { - // this should never happen - } - addressEndInt = addressInt + ipv4PrefixLengthToLength(cidrMask) - 1; - } - - @Override - public InetAddress getEndAddress() { - try { - return intToIPv4Address(addressEndInt); - } catch (UnknownHostException e) { - // this should never happen - return null; - } - } - - @Override - public int compareTo(CIDR arg) { - if (arg instanceof CIDR6) { - byte[] address = getIpV4FromIpV6((Inet6Address) arg.baseAddress); - int net = ipv4AddressToInt(address); - if (net == addressInt && arg.cidrMask == cidrMask) { - return 0; - } - if (net < addressInt) { - return 1; - } else if (net > addressInt) { - return -1; - } else if (arg.cidrMask < cidrMask) { - return -1; - } - return 1; - } - CIDR4 o = (CIDR4) arg; - if (o.addressInt == addressInt && o.cidrMask == cidrMask) { - return 0; - } - if (o.addressInt < addressInt) { - return 1; - } else if (o.addressInt > addressInt) { - return -1; - } else if (o.cidrMask < cidrMask) { - // greater Mask means less IpAddresses so -1 - return -1; - } - return 1; - } - - @Override - public boolean contains(InetAddress inetAddress) { - int search = ipv4AddressToInt(inetAddress); - return search >= addressInt && search <= addressEndInt; - } - - /** - * Given an IPv4 baseAddress length, return the block length. I.e., a - * baseAddress length of 24 will return 256. - */ - private static int ipv4PrefixLengthToLength(int prefix_length) { - return 1 << 32 - prefix_length; - } - - /** - * Given a baseAddress length, return a netmask. I.e, a baseAddress length - * of 24 will return 0xFFFFFF00. - */ - private static int ipv4PrefixLengthToMask(int prefix_length) { - return ~((1 << 32 - prefix_length) - 1); - } - - /** - * Convert an integer into an (IPv4) InetAddress. - * - * @return the created InetAddress - */ - private static InetAddress intToIPv4Address(int addr) throws UnknownHostException { - byte[] a = new byte[4]; - a[0] = (byte) (addr >> 24 & 0xFF); - a[1] = (byte) (addr >> 16 & 0xFF); - a[2] = (byte) (addr >> 8 & 0xFF); - a[3] = (byte) (addr & 0xFF); - return InetAddress.getByAddress(a); - } - - /** - * Given an IPv4 address, convert it into an integer. - * - * @return the integer representation of the InetAddress - * @throws IllegalArgumentException if the address is really an - * IPv6 address. - */ - private static int ipv4AddressToInt(InetAddress addr) { - byte[] address = null; - if (addr instanceof Inet6Address) { - address = getIpV4FromIpV6((Inet6Address) addr); - } else { - address = addr.getAddress(); - } - return ipv4AddressToInt(address); - } - - /** - * Given an IPv4 address as array of bytes, convert it into an integer. - * - * @return the integer representation of the InetAddress - * @throws IllegalArgumentException if the address is really an - * IPv6 address. - */ - private static int ipv4AddressToInt(byte[] address) { - int net = 0; - for (byte addres : address) { - net <<= 8; - net |= addres & 0xFF; - } - return net; - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/CIDR6.java b/handler/src/main/java/io/netty/handler/ipfilter/CIDR6.java deleted file mode 100644 index fe2e81ea3f..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/CIDR6.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.math.BigInteger; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; - -import io.netty.logging.InternalLogger; -import io.netty.logging.InternalLoggerFactory; - -/** - */ -public class CIDR6 extends CIDR { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(CIDR6.class); - - /** The big integer for the base address */ - private BigInteger addressBigInt; - - /** The big integer for the end address */ - private final BigInteger addressEndBigInt; - - /** - * @param newaddress - * @param newmask - */ - protected CIDR6(Inet6Address newaddress, int newmask) { - cidrMask = newmask; - addressBigInt = ipv6AddressToBigInteger(newaddress); - BigInteger mask = ipv6CidrMaskToMask(newmask); - try { - addressBigInt = addressBigInt.and(mask); - baseAddress = bigIntToIPv6Address(addressBigInt); - } catch (UnknownHostException e) { - // this should never happen. - } - addressEndBigInt = addressBigInt.add(ipv6CidrMaskToBaseAddress(cidrMask)).subtract(BigInteger.ONE); - } - - @Override - public InetAddress getEndAddress() { - try { - return bigIntToIPv6Address(addressEndBigInt); - } catch (UnknownHostException e) { - if (logger.isErrorEnabled()) { - logger.error("invalid ip address calculated as an end address"); - } - return null; - } - } - - @Override - public int compareTo(CIDR arg) { - if (arg instanceof CIDR4) { - BigInteger net = ipv6AddressToBigInteger(arg.baseAddress); - int res = net.compareTo(addressBigInt); - if (res == 0) { - if (arg.cidrMask == cidrMask) { - return 0; - } else if (arg.cidrMask < cidrMask) { - return -1; - } - return 1; - } - return res; - } - CIDR6 o = (CIDR6) arg; - if (o.addressBigInt.equals(addressBigInt) && o.cidrMask == cidrMask) { - return 0; - } - int res = o.addressBigInt.compareTo(addressBigInt); - if (res == 0) { - if (o.cidrMask < cidrMask) { - // greater Mask means less IpAddresses so -1 - return -1; - } - return 1; - } - return res; - } - - @Override - public boolean contains(InetAddress inetAddress) { - BigInteger search = ipv6AddressToBigInteger(inetAddress); - return search.compareTo(addressBigInt) >= 0 && search.compareTo(addressEndBigInt) <= 0; - } - - /** - * Given an IPv6 baseAddress length, return the block length. I.e., a - * baseAddress length of 96 will return 2**32. - */ - private static BigInteger ipv6CidrMaskToBaseAddress(int cidrMask) { - return BigInteger.ONE.shiftLeft(128 - cidrMask); - } - - private static BigInteger ipv6CidrMaskToMask(int cidrMask) { - return BigInteger.ONE.shiftLeft(128 - cidrMask).subtract(BigInteger.ONE).not(); - } - - /** - * Given an IPv6 address, convert it into a BigInteger. - * - * @return the integer representation of the InetAddress - * @throws IllegalArgumentException if the address is not an IPv6 - * address. - */ - private static BigInteger ipv6AddressToBigInteger(InetAddress addr) { - byte[] ipv6; - if (addr instanceof Inet4Address) { - ipv6 = getIpV6FromIpV4((Inet4Address) addr); - } else { - ipv6 = addr.getAddress(); - } - if (ipv6[0] == -1) { - return new BigInteger(1, ipv6); - } - return new BigInteger(ipv6); - } - - /** - * Convert a big integer into an IPv6 address. - * - * @return the inetAddress from the integer - * @throws UnknownHostException if the big integer is too large, - * and thus an invalid IPv6 address. - */ - private static InetAddress bigIntToIPv6Address(BigInteger addr) throws UnknownHostException { - byte[] a = new byte[16]; - byte[] b = addr.toByteArray(); - if (b.length > 16 && !(b.length == 17 && b[0] == 0)) { - throw new UnknownHostException("invalid IPv6 address (too big)"); - } - if (b.length == 16) { - return InetAddress.getByAddress(b); - } - // handle the case where the IPv6 address starts with "FF". - if (b.length == 17) { - System.arraycopy(b, 1, a, 0, 16); - } else { - // copy the address into a 16 byte array, zero-filled. - int p = 16 - b.length; - for (int i = 0; i < b.length; i++) { - a[p + i] = b[i]; - } - } - return InetAddress.getByAddress(a); - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterListener.java b/handler/src/main/java/io/netty/handler/ipfilter/IpFilterListener.java deleted file mode 100644 index 3156fc6293..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterListener.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetSocketAddress; - -import io.netty.channel.ChannelEvent; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; - -/** - * The listener interface for receiving ipFilter events. - * - * @see IpFilteringHandler - */ -public interface IpFilterListener { - - /** - * Called when the channel has the CONNECTED status and the channel was allowed by a previous call to accept(). - * This method enables your implementation to send a message back to the client before closing - * or whatever you need. This method returns a ChannelFuture on which the implementation - * can wait uninterruptibly before continuing.
- * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned. - * - * @param inetSocketAddress the remote {@link InetSocketAddress} from client - * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed. - */ - ChannelFuture allowed(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress); - - /** - * Called when the channel has the CONNECTED status and the channel was refused by a previous call to accept(). - * This method enables your implementation to send a message back to the client before closing - * or whatever you need. This method returns a ChannelFuture on which the implementation - * will wait uninterruptibly before closing the channel.
- * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned. - * - * @param inetSocketAddress the remote {@link InetSocketAddress} from client - * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed. - */ - ChannelFuture refused(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress); - - /** - * Called in handleUpstream, if this channel was previously blocked, - * to check if whatever the event, it should be passed to the next entry in the pipeline.
- * If one wants to not block events, just overridden this method by returning always true.

- * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since - * those events come out before the CONNECTED event and so the possibility to filter the connection. - * - * @return True if the event should continue, False if the event should not continue - * since this channel was blocked by this filter - */ - boolean continues(ChannelHandlerContext ctx, ChannelEvent e); - -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRule.java b/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRule.java deleted file mode 100644 index 31e540c7a3..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRule.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -/** This Interface defines an Ip Filter Rule. */ -public interface IpFilterRule extends IpSet { - /** @return True if this Rule is an ALLOW rule */ - boolean isAllowRule(); - - /** @return True if this Rule is a DENY rule */ - boolean isDenyRule(); -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleHandler.java b/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleHandler.java deleted file mode 100644 index edb6cc9206..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleHandler.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import io.netty.channel.ChannelEvent; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; - -/** - * Implementation of Filter of IP based on ALLOW and DENY rules.
- *

- * This implementation could be changed by implementing a new {@link IpFilterRule} than default - * {@link IpV4SubnetFilterRule} (IPV4 support only), {@link IpSubnetFilterRule} (IPV4 and IPV6 support) or {@link IpFilterRule} (IP and host name string pattern support) .
- *
- * The check is done by going from step to step in the underlying array of IpFilterRule.
- * Each {@link IpFilterRule} answers to the method accept if the {@link InetAddress} is accepted or not, - * according to its implementation. If an InetAddress arrives at the end of the list, as in Firewall - * usual rules, the InetAddress is therefore accepted by default.
- *
    - *
  • If it was constructed with True as first argument, - * the IpFilterRule is an ALLOW rule (every InetAddress that fits in the rule will be accepted).
  • - *
  • If it was constructed with False as first argument, - * the IpFilterRule is a DENY rule (every InetAddress that fits in the rule will be refused).
  • - *

- *
- * An empty list means allow all (no limitation).

- * For efficiency reason, you should not add/remove too frequently IpFilterRules to/from this handler. - * You should prefer to replace an entry (set method) with an ALLOW/DENY ALL IpFilterRule - * if possible.


- * This handler should be created only once and reused on every pipeline since it handles - * a global status of what is allowed or blocked.

- *

- * Note that {@link IpSubnetFilterRule} which supports IPV4 and IPV6 should be used with as much as - * possible no mixed IP protocol. Both IPV4 and IPV6 are supported but a mix (IpFilter in IPV6 notation - * and the address from the channel in IPV4, or the reverse) can lead to wrong result. - */ -@Sharable -public class IpFilterRuleHandler extends IpFilteringHandlerImpl { - /** List of {@link IpFilterRule} */ - private final CopyOnWriteArrayList ipFilterRuleList = new CopyOnWriteArrayList(); - - /** Constructor from a new list of IpFilterRule */ - public IpFilterRuleHandler(List newList) { - if (newList != null) { - ipFilterRuleList.addAll(newList); - } - } - - /** - * Empty constructor (no IpFilterRule in the List at construction). In such a situation, - * empty list implies allow all. - */ - public IpFilterRuleHandler() { - } - - // Below are methods directly inspired from CopyOnWriteArrayList methods - - /** Add an ipFilterRule in the list at the end */ - public void add(IpFilterRule ipFilterRule) { - if (ipFilterRule == null) { - throw new NullPointerException("IpFilterRule can not be null"); - } - ipFilterRuleList.add(ipFilterRule); - } - - /** Add an ipFilterRule in the list at the specified position (shifting to the right other elements) */ - public void add(int index, IpFilterRule ipFilterRule) { - if (ipFilterRule == null) { - throw new NullPointerException("IpFilterRule can not be null"); - } - ipFilterRuleList.add(index, ipFilterRule); - } - - /** - * Appends all of the elements in the specified collection to the end of this list, - * in the order that they are returned by the specified collection's iterator. - */ - public void addAll(Collection c) { - if (c == null) { - throw new NullPointerException("Collection can not be null"); - } - ipFilterRuleList.addAll(c); - } - - /** Inserts all of the elements in the specified collection into this list, starting at the specified position. */ - public void addAll(int index, Collection c) { - if (c == null) { - throw new NullPointerException("Collection can not be null"); - } - ipFilterRuleList.addAll(index, c); - } - - /** - * Append the element if not present. - * - * @return the number of elements added - */ - public int addAllAbsent(Collection c) { - if (c == null) { - throw new NullPointerException("Collection can not be null"); - } - return ipFilterRuleList.addAllAbsent(c); - } - - /** - * Append the element if not present. - * - * @return true if the element was added - */ - public boolean addIfAbsent(IpFilterRule ipFilterRule) { - if (ipFilterRule == null) { - throw new NullPointerException("IpFilterRule can not be null"); - } - return ipFilterRuleList.addIfAbsent(ipFilterRule); - } - - /** Clear the list */ - public void clear() { - ipFilterRuleList.clear(); - } - - /** - * Returns true if this list contains the specified element - * - * @return true if this list contains the specified element - */ - public boolean contains(IpFilterRule ipFilterRule) { - if (ipFilterRule == null) { - throw new NullPointerException("IpFilterRule can not be null"); - } - return ipFilterRuleList.contains(ipFilterRule); - } - - /** - * Returns true if this list contains all of the elements of the specified collection - * - * @return true if this list contains all of the elements of the specified collection - */ - public boolean containsAll(Collection c) { - if (c == null) { - throw new NullPointerException("Collection can not be null"); - } - return ipFilterRuleList.containsAll(c); - } - - /** - * Returns the element at the specified position in this list - * - * @return the element at the specified position in this list - */ - public IpFilterRule get(int index) { - return ipFilterRuleList.get(index); - } - - /** - * Returns true if this list contains no elements - * - * @return true if this list contains no elements - */ - public boolean isEmpty() { - return ipFilterRuleList.isEmpty(); - } - - /** Remove the ipFilterRule from the list */ - public void remove(IpFilterRule ipFilterRule) { - if (ipFilterRule == null) { - throw new NullPointerException("IpFilterRule can not be null"); - } - ipFilterRuleList.remove(ipFilterRule); - } - - /** - * Removes the element at the specified position in this list - * - * @return the element previously at the specified position - */ - public IpFilterRule remove(int index) { - return ipFilterRuleList.remove(index); - } - - /** Removes from this list all of its elements that are contained in the specified collection */ - public void removeAll(Collection c) { - if (c == null) { - throw new NullPointerException("Collection can not be null"); - } - ipFilterRuleList.removeAll(c); - } - - /** Retains only the elements in this list that are contained in the specified collection */ - public void retainAll(Collection c) { - if (c == null) { - throw new NullPointerException("Collection can not be null"); - } - ipFilterRuleList.retainAll(c); - } - - /** - * Replaces the element at the specified position in this list with the specified element - * - * @return the element previously at the specified position - */ - public IpFilterRule set(int index, IpFilterRule ipFilterRule) { - if (ipFilterRule == null) { - throw new NullPointerException("IpFilterRule can not be null"); - } - return ipFilterRuleList.set(index, ipFilterRule); - } - - /** - * Returns the number of elements in this list. - * - * @return the number of elements in this list. - */ - public int size() { - return ipFilterRuleList.size(); - } - - @Override - protected boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress) - throws Exception { - if (ipFilterRuleList.isEmpty()) { - // No limitation neither in deny or allow, so accept - return true; - } - InetAddress inetAddress = inetSocketAddress.getAddress(); - Iterator iterator = ipFilterRuleList.iterator(); - IpFilterRule ipFilterRule = null; - while (iterator.hasNext()) { - ipFilterRule = iterator.next(); - if (ipFilterRule.contains(inetAddress)) { - // Match founds, is it a ALLOW or DENY rule - return ipFilterRule.isAllowRule(); - } - } - // No limitation founds and no allow either, but as it is like Firewall rules, it is therefore accepted - return true; - } - -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleList.java b/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleList.java deleted file mode 100644 index cb3ca84e54..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleList.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.UnknownHostException; -import java.util.ArrayList; - -import io.netty.logging.InternalLogger; -import io.netty.logging.InternalLoggerFactory; - -/** - * The Class IpFilterRuleList is a helper class to generate a List of Rules from a string. - * In case of parse errors no exceptions are thrown. The error is logged. - *
- * Rule List Syntax: - *
- *

- * RuleList ::= Rule[,Rule]*
- * Rule ::= AllowRule | BlockRule
- * AllowRule ::= +Filter
- * BlockRule ::= -Filter
- * Filter ::= PatternFilter | CIDRFilter
- * PatternFilter ::= @see PatternRule
- * CIDRFilter ::= c:CIDRFilter
- * CIDRFilter ::= @see CIDR.newCIDR(String)
- * 
- *
- * Example: allow only localhost: - *
- * new IPFilterRuleHandler().addAll(new IpFilterRuleList("+n:localhost, -n:*")); - *
- */ -public class IpFilterRuleList extends ArrayList { - private static final long serialVersionUID = -6164162941749588780L; - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(IpFilterRuleList.class); - - /** - * Instantiates a new ip filter rule list. - * - * @param rules the rules - */ - public IpFilterRuleList(String rules) { - parseRules(rules); - } - - private void parseRules(String rules) { - String[] ruless = rules.split(","); - for (String rule : ruless) { - parseRule(rule.trim()); - } - } - - private void parseRule(String rule) { - if (rule == null || rule.length() == 0) { - return; - } - if (!(rule.startsWith("+") || rule.startsWith("-"))) { - if (logger.isErrorEnabled()) { - logger.error("syntax error in ip filter rule:" + rule); - } - return; - } - - boolean allow = rule.startsWith("+"); - if (rule.charAt(1) == 'n' || rule.charAt(1) == 'i') { - this.add(new PatternRule(allow, rule.substring(1))); - } else if (rule.charAt(1) == 'c') { - try { - this.add(new IpSubnetFilterRule(allow, rule.substring(3))); - } catch (UnknownHostException e) { - if (logger.isErrorEnabled()) { - logger.error("error parsing ip filter " + rule, e); - } - } - } else { - if (logger.isErrorEnabled()) { - logger.error("syntax error in ip filter rule:" + rule); - } - } - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpFilteringHandler.java b/handler/src/main/java/io/netty/handler/ipfilter/IpFilteringHandler.java deleted file mode 100644 index 0d04ef1ce3..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpFilteringHandler.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -/** - * The Interface IpFilteringHandler. - * A Filter of IP. - *
- * Users can add an {@link IpFilterListener} to add specific actions in case a connection is allowed or refused. - */ -public interface IpFilteringHandler { - - /** - * Sets the filter listener. - * - * @param listener the new ip filter listener - */ - void setIpFilterListener(IpFilterListener listener); - - /** Remove the filter listener. */ - void removeIpFilterListener(); -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpFilteringHandlerImpl.java b/handler/src/main/java/io/netty/handler/ipfilter/IpFilteringHandlerImpl.java deleted file mode 100644 index d2d2bfff4e..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpFilteringHandlerImpl.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetSocketAddress; - -import io.netty.channel.ChannelEvent; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelStateEvent; -import io.netty.channel.ChannelUpstreamHandler; -import io.netty.channel.Channels; - -// TODO: Auto-generated Javadoc - -/** General class that handle Ip Filtering. */ -public abstract class IpFilteringHandlerImpl implements ChannelUpstreamHandler, IpFilteringHandler { - - private IpFilterListener listener; - - /** - * Called when the channel is connected. It returns True if the corresponding connection - * is to be allowed. Else it returns False. - * - * @param inetSocketAddress the remote {@link InetSocketAddress} from client - * @return True if the corresponding connection is allowed, else False. - */ - protected abstract boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress) - throws Exception; - - /** - * Called when the channel has the CONNECTED status and the channel was refused by a previous call to accept(). - * This method enables your implementation to send a message back to the client before closing - * or whatever you need. This method returns a ChannelFuture on which the implementation - * will wait uninterruptibly before closing the channel.
- * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned. - * - * @param inetSocketAddress the remote {@link InetSocketAddress} from client - * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed. - */ - protected ChannelFuture handleRefusedChannel(ChannelHandlerContext ctx, ChannelEvent e, - InetSocketAddress inetSocketAddress) throws Exception { - if (listener == null) { - return null; - } - return listener.refused(ctx, e, inetSocketAddress); - } - - protected ChannelFuture handleAllowedChannel(ChannelHandlerContext ctx, ChannelEvent e, - InetSocketAddress inetSocketAddress) throws Exception { - if (listener == null) { - return null; - } - return listener.allowed(ctx, e, inetSocketAddress); - } - - /** - * Internal method to test if the current channel is blocked. Should not be overridden. - * - * @return True if the current channel is blocked, else False - */ - protected boolean isBlocked(ChannelHandlerContext ctx) { - return ctx.getAttachment() != null; - } - - /** - * Called in handleUpstream, if this channel was previously blocked, - * to check if whatever the event, it should be passed to the next entry in the pipeline.
- * If one wants to not block events, just overridden this method by returning always true.

- * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since - * those events come out before the CONNECTED event and so the possibility to filter the connection. - * - * @return True if the event should continue, False if the event should not continue - * since this channel was blocked by this filter - */ - protected boolean continues(ChannelHandlerContext ctx, ChannelEvent e) throws Exception { - if (listener != null) { - return listener.continues(ctx, e); - } else { - return false; - } - } - - @Override - public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception { - if (e instanceof ChannelStateEvent) { - ChannelStateEvent evt = (ChannelStateEvent) e; - switch (evt.getState()) { - case OPEN: - case BOUND: - // Special case: OPEND and BOUND events are before CONNECTED, - // but CLOSED and UNBOUND events are after DISCONNECTED: should those events be blocked too? - if (isBlocked(ctx) && !continues(ctx, evt)) { - // don't pass to next level since channel was blocked early - return; - } else { - ctx.sendUpstream(e); - return; - } - case CONNECTED: - if (evt.getValue() != null) { - // CONNECTED - InetSocketAddress inetSocketAddress = (InetSocketAddress) e.getChannel().getRemoteAddress(); - if (!accept(ctx, e, inetSocketAddress)) { - ctx.setAttachment(Boolean.TRUE); - ChannelFuture future = handleRefusedChannel(ctx, e, inetSocketAddress); - if (future != null) { - future.addListener(ChannelFutureListener.CLOSE); - } else { - Channels.close(e.getChannel()); - } - if (isBlocked(ctx) && !continues(ctx, evt)) { - // don't pass to next level since channel was blocked early - return; - } - } else { - handleAllowedChannel(ctx, e, inetSocketAddress); - } - // This channel is not blocked - ctx.setAttachment(null); - } else { - // DISCONNECTED - if (isBlocked(ctx) && !continues(ctx, evt)) { - // don't pass to next level since channel was blocked early - return; - } - } - break; - } - } - if (isBlocked(ctx) && !continues(ctx, e)) { - // don't pass to next level since channel was blocked early - return; - } - // Whatever it is, if not blocked, goes to the next level - ctx.sendUpstream(e); - } - - @Override - public void setIpFilterListener(IpFilterListener listener) { - this.listener = listener; - } - - @Override - public void removeIpFilterListener() { - this.listener = null; - - } - -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpSet.java b/handler/src/main/java/io/netty/handler/ipfilter/IpSet.java deleted file mode 100644 index 6689480c06..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpSet.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetAddress; - -/** This Interface defines an IpSet object. */ -public interface IpSet { - /** - * Compares the given InetAddress against the IpSet and returns true if - * the InetAddress is contained in this Rule and false if not. - * - * @return returns true if the given IP address is contained in the current - * IpSet. - */ - boolean contains(InetAddress inetAddress1); -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpSubnet.java b/handler/src/main/java/io/netty/handler/ipfilter/IpSubnet.java deleted file mode 100644 index c8dceba7c7..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpSubnet.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * This class allows to check if an IP V4 or V6 Address is contained in a subnet.
- *

- * Supported IP V4 Formats for the Subnets are: 1.1.1.1/255.255.255.255 or 1.1.1.1/32 (CIDR-Notation) - * and (InetAddress,Mask) where Mask is a integer for CIDR-notation or a String for Standard Mask notation.
- *

Example1:
- * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/24");
- * System.out.println("Result: "+ ips.contains("192.168.1.123"));
- * System.out.println("Result: "+ ips.contains(inetAddress2));
- *
Example1 bis:
- * IpV4Subnet ips = new IpV4Subnet(inetAddress, 24);
- * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
- *

Example2:
- * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/255.255.255.0");
- * System.out.println("Result: "+ ips.contains("192.168.1.123"));
- * System.out.println("Result: "+ ips.contains(inetAddress2));
- *
Example2 bis:
- * IpV4Subnet ips = new IpV4Subnet(inetAddress, "255.255.255.0");
- * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
- *
- * Supported IP V6 Formats for the Subnets are: a:b:c:d:e:f:g:h/NN (CIDR-Notation) - * or any IPV6 notations (like a:b:c:d::/NN, a:b:c:d:e:f:w.x.y.z/NN) - * and (InetAddress,Mask) where Mask is a integer for CIDR-notation - * and (InetAddress,subnet).
- *

Example1:
- * IpSubnet ips = new IpSubnet("1fff:0:0a88:85a3:0:0:0:0/24");
- * IpSubnet ips = new IpSubnet("1fff:0:0a88:85a3::/24");
- * System.out.println("Result: "+ ips.contains("1fff:0:0a88:85a3:0:0:ac1f:8001"));
- * System.out.println("Result: "+ ips.contains(inetAddress2));
- *
Example1 bis:
- * IpSubnet ips = new IpSubnet(inetAddress, 24);
- * where inetAddress2 is 1fff:0:0a88:85a3:0:0:ac1f:8001
- */ -public class IpSubnet implements IpSet, Comparable { - /** Internal representation */ - private final CIDR cidr; - - /** Create IpSubnet for ALL (used for ALLOW or DENY ALL) */ - public IpSubnet() { - // ALLOW or DENY ALL - cidr = null; - } - - /** - * Create IpSubnet using the CIDR or normal Notation
- * i.e.:
- * IpSubnet subnet = new IpSubnet("10.10.10.0/24"); or
- * IpSubnet subnet = new IpSubnet("10.10.10.0/255.255.255.0"); or
- * IpSubnet subnet = new IpSubnet("1fff:0:0a88:85a3:0:0:0:0/24"); - * - * @param netAddress a network address as string. - */ - public IpSubnet(String netAddress) throws UnknownHostException { - cidr = CIDR.newCIDR(netAddress); - } - - /** Create IpSubnet using the CIDR Notation */ - public IpSubnet(InetAddress inetAddress, int cidrNetMask) throws UnknownHostException { - cidr = CIDR.newCIDR(inetAddress, cidrNetMask); - } - - /** Create IpSubnet using the normal Notation */ - public IpSubnet(InetAddress inetAddress, String netMask) throws UnknownHostException { - cidr = CIDR.newCIDR(inetAddress, netMask); - } - - /** - * Compares the given IP-Address against the Subnet and returns true if - * the ip is in the subnet-ip-range and false if not. - * - * @param ipAddr an ipaddress - * @return returns true if the given IP address is inside the currently - * set network. - */ - public boolean contains(String ipAddr) throws UnknownHostException { - InetAddress inetAddress1 = InetAddress.getByName(ipAddr); - return this.contains(inetAddress1); - } - - /** - * Compares the given InetAddress against the Subnet and returns true if - * the ip is in the subnet-ip-range and false if not. - * - * @return returns true if the given IP address is inside the currently - * set network. - */ - @Override - public boolean contains(InetAddress inetAddress) { - if (cidr == null) { - // ANY - return true; - } - return cidr.contains(inetAddress); - } - - @Override - public String toString() { - return cidr.toString(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof IpSubnet)) { - return false; - } - IpSubnet ipSubnet = (IpSubnet) o; - return ipSubnet.cidr.equals(cidr); - } - - @Override - public int hashCode() { - return cidr.hashCode(); - } - - /** Compare two IpSubnet */ - @Override - public int compareTo(IpSubnet o) { - return cidr.toString().compareTo(o.cidr.toString()); - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilterRule.java b/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilterRule.java deleted file mode 100644 index 162a74f409..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilterRule.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * Ip V4 and Ip V6 filter rule.
- *
- * Note that mix of IPV4 and IPV6 is allowed but it is not recommended. So it is preferable to not - * mix IPV4 addresses with IPV6 rules, even if it should work. - */ -public class IpSubnetFilterRule extends IpSubnet implements IpFilterRule { - /** Is this IpV4Subnet an ALLOW or DENY rule */ - private boolean isAllowRule = true; - - /** - * Constructor for a ALLOW or DENY ALL - * - * @param allow True for ALLOW, False for DENY - */ - public IpSubnetFilterRule(boolean allow) { - isAllowRule = allow; - } - - /** @param allow True for ALLOW, False for DENY */ - public IpSubnetFilterRule(boolean allow, InetAddress inetAddress, int cidrNetMask) throws UnknownHostException { - super(inetAddress, cidrNetMask); - isAllowRule = allow; - } - - /** @param allow True for ALLOW, False for DENY */ - public IpSubnetFilterRule(boolean allow, InetAddress inetAddress, String netMask) throws UnknownHostException { - super(inetAddress, netMask); - isAllowRule = allow; - } - - /** @param allow True for ALLOW, False for DENY */ - public IpSubnetFilterRule(boolean allow, String netAddress) throws UnknownHostException { - super(netAddress); - isAllowRule = allow; - } - - @Override - public boolean isAllowRule() { - return isAllowRule; - } - - @Override - public boolean isDenyRule() { - return !isAllowRule; - } - -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpV4Subnet.java b/handler/src/main/java/io/netty/handler/ipfilter/IpV4Subnet.java deleted file mode 100644 index 7b0a5ef935..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpV4Subnet.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.StringTokenizer; -import java.util.Vector; - -/** - * This class allows to check if an IP-V4-Address is contained in a subnet.
- * Supported Formats for the Subnets are: 1.1.1.1/255.255.255.255 or 1.1.1.1/32 (CIDR-Notation) - * and (InetAddress,Mask) where Mask is a integer for CIDR-notation or a String for Standard Mask notation.
- *

Example1:
- * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/24");
- * System.out.println("Result: "+ ips.contains("192.168.1.123"));
- * System.out.println("Result: "+ ips.contains(inetAddress2));
- *
Example1 bis:
- * IpV4Subnet ips = new IpV4Subnet(inetAddress, 24);
- * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
- *

Example2:
- * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/255.255.255.0");
- * System.out.println("Result: "+ ips.contains("192.168.1.123"));
- * System.out.println("Result: "+ ips.contains(inetAddress2));
- *
Example2 bis:
- * IpV4Subnet ips = new IpV4Subnet(inetAddress, "255.255.255.0");
- * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
- */ -public class IpV4Subnet implements IpSet, Comparable { - private static final int SUBNET_MASK = 0x80000000; - - private static final int BYTE_ADDRESS_MASK = 0xFF; - - private InetAddress inetAddress; - - private int subnet; - - private int mask; - - private int cidrMask; - - /** Create IpV4Subnet for ALL (used for ALLOW or DENY ALL) */ - public IpV4Subnet() { - // ALLOW or DENY ALL - mask = -1; - // other will be ignored - inetAddress = null; - subnet = 0; - cidrMask = 0; - } - - /** - * Create IpV4Subnet using the CIDR or normal Notation
- * i.e.: - * IpV4Subnet subnet = new IpV4Subnet("10.10.10.0/24"); or - * IpV4Subnet subnet = new IpV4Subnet("10.10.10.0/255.255.255.0"); - * - * @param netAddress a network address as string. - */ - public IpV4Subnet(String netAddress) throws UnknownHostException { - setNetAddress(netAddress); - } - - /** Create IpV4Subnet using the CIDR Notation */ - public IpV4Subnet(InetAddress inetAddress, int cidrNetMask) { - setNetAddress(inetAddress, cidrNetMask); - } - - /** Create IpV4Subnet using the normal Notation */ - public IpV4Subnet(InetAddress inetAddress, String netMask) { - setNetAddress(inetAddress, netMask); - } - - /** - * Sets the Network Address in either CIDR or Decimal Notation.
- * i.e.: setNetAddress("1.1.1.1/24"); or
- * setNetAddress("1.1.1.1/255.255.255.0");
- * - * @param netAddress a network address as string. - */ - private void setNetAddress(String netAddress) throws UnknownHostException { - Vector vec = new Vector(); - StringTokenizer st = new StringTokenizer(netAddress, "/"); - while (st.hasMoreTokens()) { - vec.add(st.nextElement()); - } - - if (vec.get(1).toString().length() < 3) { - setNetId(vec.get(0).toString()); - setCidrNetMask(Integer.parseInt(vec.get(1).toString())); - } else { - setNetId(vec.get(0).toString()); - setNetMask(vec.get(1).toString()); - } - } - - /** Sets the Network Address in CIDR Notation. */ - private void setNetAddress(InetAddress inetAddress, int cidrNetMask) { - setNetId(inetAddress); - setCidrNetMask(cidrNetMask); - } - - /** Sets the Network Address in Decimal Notation. */ - private void setNetAddress(InetAddress inetAddress, String netMask) { - setNetId(inetAddress); - setNetMask(netMask); - } - - /** - * Sets the BaseAdress of the Subnet.
- * i.e.: setNetId("192.168.1.0"); - * - * @param netId a network ID - */ - private void setNetId(String netId) throws UnknownHostException { - InetAddress inetAddress1 = InetAddress.getByName(netId); - this.setNetId(inetAddress1); - } - - /** - * Compute integer representation of InetAddress - * - * @return the integer representation - */ - private int toInt(InetAddress inetAddress1) { - byte[] address = inetAddress1.getAddress(); - int net = 0; - for (byte addres : address) { - net <<= 8; - net |= addres & BYTE_ADDRESS_MASK; - } - return net; - } - - /** Sets the BaseAdress of the Subnet. */ - private void setNetId(InetAddress inetAddress) { - this.inetAddress = inetAddress; - subnet = toInt(inetAddress); - } - - /** - * Sets the Subnet's Netmask in Decimal format.
- * i.e.: setNetMask("255.255.255.0"); - * - * @param netMask a network mask - */ - private void setNetMask(String netMask) { - StringTokenizer nm = new StringTokenizer(netMask, "."); - int i = 0; - int[] netmask = new int[4]; - while (nm.hasMoreTokens()) { - netmask[i] = Integer.parseInt(nm.nextToken()); - i++; - } - int mask1 = 0; - for (i = 0; i < 4; i++) { - mask1 += Integer.bitCount(netmask[i]); - } - setCidrNetMask(mask1); - } - - /** - * Sets the CIDR Netmask
- * i.e.: setCidrNetMask(24); - * - * @param cidrNetMask a netmask in CIDR notation - */ - private void setCidrNetMask(int cidrNetMask) { - cidrMask = cidrNetMask; - mask = SUBNET_MASK >> cidrMask - 1; - } - - /** - * Compares the given IP-Address against the Subnet and returns true if - * the ip is in the subnet-ip-range and false if not. - * - * @param ipAddr an ipaddress - * @return returns true if the given IP address is inside the currently - * set network. - */ - public boolean contains(String ipAddr) throws UnknownHostException { - InetAddress inetAddress1 = InetAddress.getByName(ipAddr); - return this.contains(inetAddress1); - } - - /** - * Compares the given InetAddress against the Subnet and returns true if - * the ip is in the subnet-ip-range and false if not. - * - * @return returns true if the given IP address is inside the currently - * set network. - */ - @Override - public boolean contains(InetAddress inetAddress1) { - if (mask == -1) { - // ANY - return true; - } - return (toInt(inetAddress1) & mask) == subnet; - } - - @Override - public String toString() { - return inetAddress.getHostAddress() + "/" + cidrMask; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof IpV4Subnet)) { - return false; - } - IpV4Subnet ipV4Subnet = (IpV4Subnet) o; - return ipV4Subnet.subnet == subnet && ipV4Subnet.cidrMask == cidrMask; - } - - @Override - public int hashCode() { - return subnet; - } - - /** Compare two IpV4Subnet */ - @Override - public int compareTo(IpV4Subnet o) { - if (o.subnet == subnet && o.cidrMask == cidrMask) { - return 0; - } - if (o.subnet < subnet) { - return 1; - } else if (o.subnet > subnet) { - return -1; - } else if (o.cidrMask < cidrMask) { - // greater Mask means less IpAddresses so -1 - return -1; - } - return 1; - } -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/IpV4SubnetFilterRule.java b/handler/src/main/java/io/netty/handler/ipfilter/IpV4SubnetFilterRule.java deleted file mode 100644 index 7d5511ff24..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/IpV4SubnetFilterRule.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** IpV4 only Filter Rule */ -public class IpV4SubnetFilterRule extends IpV4Subnet implements IpFilterRule { - /** Is this IpV4Subnet an ALLOW or DENY rule */ - private boolean isAllowRule = true; - - /** - * Constructor for a ALLOW or DENY ALL - * - * @param allow True for ALLOW, False for DENY - */ - public IpV4SubnetFilterRule(boolean allow) { - isAllowRule = allow; - } - - /** @param allow True for ALLOW, False for DENY */ - public IpV4SubnetFilterRule(boolean allow, InetAddress inetAddress, int cidrNetMask) { - super(inetAddress, cidrNetMask); - isAllowRule = allow; - } - - /** @param allow True for ALLOW, False for DENY */ - public IpV4SubnetFilterRule(boolean allow, InetAddress inetAddress, String netMask) { - super(inetAddress, netMask); - isAllowRule = allow; - } - - /** @param allow True for ALLOW, False for DENY */ - public IpV4SubnetFilterRule(boolean allow, String netAddress) throws UnknownHostException { - super(netAddress); - isAllowRule = allow; - } - - @Override - public boolean isAllowRule() { - return isAllowRule; - } - - @Override - public boolean isDenyRule() { - return !isAllowRule; - } - -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/OneIpFilterHandler.java b/handler/src/main/java/io/netty/handler/ipfilter/OneIpFilterHandler.java deleted file mode 100644 index 0f55e08dcb..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/OneIpFilterHandler.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import io.netty.channel.ChannelEvent; -import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelState; -import io.netty.channel.ChannelStateEvent; - -/** - * Handler that block any new connection if there are already a currently active - * channel connected with the same InetAddress (IP).
- *
- *

- * Take care to not change isBlocked method except if you know what you are doing - * since it is used to test if the current closed connection is to be removed - * or not from the map of currently connected channel. - */ -@Sharable -public class OneIpFilterHandler extends IpFilteringHandlerImpl { - /** HashMap of current remote connected InetAddress */ - private final ConcurrentMap connectedSet = new ConcurrentHashMap(); - - @Override - protected boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress) - throws Exception { - InetAddress inetAddress = inetSocketAddress.getAddress(); - if (connectedSet.containsKey(inetAddress)) { - return false; - } - connectedSet.put(inetAddress, Boolean.TRUE); - return true; - } - - @Override - public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception { - super.handleUpstream(ctx, e); - // Try to remove entry from Map if already exists - if (e instanceof ChannelStateEvent) { - ChannelStateEvent evt = (ChannelStateEvent) e; - if (evt.getState() == ChannelState.CONNECTED) { - if (evt.getValue() == null) { - // DISCONNECTED but was this channel blocked or not - if (isBlocked(ctx)) { - // remove inetsocketaddress from set since this channel was not blocked before - InetSocketAddress inetSocketAddress = (InetSocketAddress) e.getChannel().getRemoteAddress(); - connectedSet.remove(inetSocketAddress.getAddress()); - } - } - } - } - } - -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/PatternRule.java b/handler/src/main/java/io/netty/handler/ipfilter/PatternRule.java deleted file mode 100644 index d321283f44..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/PatternRule.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.handler.ipfilter; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.regex.Pattern; - -import io.netty.logging.InternalLogger; -import io.netty.logging.InternalLoggerFactory; - -/** - * The Class PatternRule represents an IP filter rule using string patterns. - *
- * Rule Syntax: - *
- *

- * Rule ::= [n|i]:address          n stands for computer name, i for ip address
- * address ::= <regex> | localhost
- * regex is a regular expression with '*' as multi character and '?' as single character wild card
- * 
- *
- * Example: allow localhost: - *
- * new PatternRule(true, "n:localhost") - *
- * Example: allow local lan: - *
- * new PatternRule(true, "i:192.168.0.*") - *
- * Example: block all - *
- * new PatternRule(false, "n:*") - *
- */ -public class PatternRule implements IpFilterRule, Comparable { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(PatternRule.class); - - private Pattern ipPattern; - - private Pattern namePattern; - - private boolean isAllowRule = true; - - private boolean localhost; - - private String pattern; - - /** - * Instantiates a new pattern rule. - * - * @param allow indicates if this is an allow or block rule - * @param pattern the filter pattern - */ - public PatternRule(boolean allow, String pattern) { - this.isAllowRule = allow; - this.pattern = pattern; - parse(pattern); - } - - /** - * returns the pattern. - * - * @return the pattern - */ - public String getPattern() { - return this.pattern; - } - - @Override - public boolean isAllowRule() { - return isAllowRule; - } - - @Override - public boolean isDenyRule() { - return !isAllowRule; - } - - @Override - public boolean contains(InetAddress inetAddress) { - if (localhost) { - if (isLocalhost(inetAddress)) { - return true; - } - } - if (ipPattern != null) { - if (ipPattern.matcher(inetAddress.getHostAddress()).matches()) { - return true; - } - } - if (namePattern != null) { - if (namePattern.matcher(inetAddress.getHostName()).matches()) { - return true; - } - } - return false; - } - - private void parse(String pattern) { - if (pattern == null) { - return; - } - - String[] acls = pattern.split(","); - - String ip = ""; - String name = ""; - for (String c : acls) { - c = c.trim(); - if (c.equals("n:localhost")) { - this.localhost = true; - } else if (c.startsWith("n:")) { - name = addRule(name, c.substring(2)); - } else if (c.startsWith("i:")) { - ip = addRule(ip, c.substring(2)); - } - } - if (ip.length() != 0) { - ipPattern = Pattern.compile(ip); - } - if (name.length() != 0) { - namePattern = Pattern.compile(name); - } - } - - private String addRule(String pattern, String rule) { - if (rule == null || rule.length() == 0) { - return pattern; - } - if (pattern.length() != 0) { - pattern += "|"; - } - rule = rule.replaceAll("\\.", "\\\\."); - rule = rule.replaceAll("\\*", ".*"); - rule = rule.replaceAll("\\?", "."); - pattern += "(" + rule + ")"; - return pattern; - } - - private boolean isLocalhost(InetAddress address) { - try { - if (address.equals(InetAddress.getLocalHost())) { - return true; - } - } catch (UnknownHostException e) { - if (logger.isInfoEnabled()) { - logger.info("error getting ip of localhost", e); - } - } - try { - InetAddress[] addrs = InetAddress.getAllByName("127.0.0.1"); - for (InetAddress addr : addrs) { - if (addr.equals(address)) { - return true; - } - } - } catch (UnknownHostException e) { - if (logger.isInfoEnabled()) { - logger.info("error getting ip of localhost", e); - } - } - return false; - - } - - @Override - public int compareTo(Object o) { - if (o == null) { - return -1; - } - if (!(o instanceof PatternRule)) { - return -1; - } - PatternRule p = (PatternRule) o; - if (p.isAllowRule() && !this.isAllowRule) { - return -1; - } - if (this.pattern == null && p.pattern == null) { - return 0; - } - if (this.pattern != null) { - return this.pattern.compareTo(p.getPattern()); - } - return -1; - } - -} diff --git a/handler/src/main/java/io/netty/handler/ipfilter/package-info.java b/handler/src/main/java/io/netty/handler/ipfilter/package-info.java deleted file mode 100644 index 7d95e91ecc..0000000000 --- a/handler/src/main/java/io/netty/handler/ipfilter/package-info.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * Implementation of a Ip based Filter handlers.
- *

- *

The main goal of this package is to allow to filter connections based on IP rules. - * The main interface is {@link io.netty.handler.ipfilter.IpFilteringHandler} which all filters will extend.

- * - *

Two IP filtering are proposed:
- *

    - *
  • {@link io.netty.handler.ipfilter.OneIpFilterHandler}: This filter proposes to allow only one connection by client's IP Address. - * I.E. this filter will prevent two connections from the same client based on its IP address.


  • - * - *
  • {@link io.netty.handler.ipfilter.IpFilterRuleHandler}: This filter proposes to allow or block IP range (based on standard notation - * or on CIDR notation) when the connection is running. It relies on another class like - * IpV4SubnetFilterRule (IPV4 support only), IpSubnetFilterRule (IPV4 and IPV6 support) or PatternRule (string pattern support) - * which implements those Ip ranges.


  • - * - *

- * - *

Standard use could be as follow: The accept method must be overridden (of course you can - * override others).

- * - *

    - *
  • accept method allows to specify your way of choosing if a new connection is - * to be allowed or not.

  • - * In OneIpFilterHandler and IpFilterRuleHandler, - * this method is already implemented.
    - *
    - * - *
  • handleRefusedChannel method is executed when the accept method filters (blocks, so returning false) - * the new connection. This method allows you to implement specific actions to be taken before the channel is - * closed. After this method is called, the channel is immediately closed.

  • - * So if you want to send back a message to the client, don't forget to return a respectful ChannelFuture, - * otherwise the message could be missed since the channel will be closed immediately after this - * call and the waiting on this channelFuture (at least with respect of asynchronous operations).

    - * Per default implementation this method invokes an {@link io.netty.handler.ipfilter.IpFilterListener} or returns null if no listener has been set. - *

    - * - *
  • continues is called when any event appears after CONNECTED event and only for - * blocked channels.

  • - * It should return True if this new event has to go to next handlers - * in the pipeline if any, and False (default) if no events has to be passed to the next - * handlers when a channel is blocked. This is intend to prevent any unnecessary action since the connection is refused.
    - * However, you could change its behavior for instance because you don't want that any event - * will be blocked by this filter by returning always true or according to some events.
    - * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since - * those events come out before the CONNECTED event, so there is no possibility to filter those two events - * before the CONNECTED event shows up. Therefore, you might want to let CLOSED and UNBOUND be passed - * to the next entry in the pipeline.

    - * Per default implementation this method invokes an {@link io.netty.handler.ipfilter.IpFilterListener} or returns false if no listener has been set. - *

    - * - *
  • Finally handleUpstream traps the CONNECTED and DISCONNECTED events.

  • - * If in the CONNECTED events the channel is blocked (accept refused the channel), - * then any new events on this channel will be blocked.
    - * However, you could change its behavior for instance because you don't want that all events - * will be blocked by this filter by testing the result of isBlocked, and if so, - * calling ctx.sendUpstream(e); after calling the super method or by changing the continues method.

    - - *



- * - * A typical setup for ip filter for TCP/IP socket would be: - * - *
- * {@link io.netty.channel.ChannelPipeline} pipeline = ...;
- *
- * IpFilterRuleHandler firewall = new IpFilterRuleHandler();
- * firewall.addAll(new IpFilterRuleList("+n:localhost, +c:192.168.0.0/27, -n:*"));
- * pipeline.addFirst("firewall", firewall);
- * 
- * - * @apiviz.exclude ^java\.lang\. - */ -package io.netty.handler.ipfilter; - - diff --git a/handler/src/test/java/io/netty/handler/ipfilter/IpFilterRuleTest.java b/handler/src/test/java/io/netty/handler/ipfilter/IpFilterRuleTest.java deleted file mode 100644 index cb2793ebae..0000000000 --- a/handler/src/test/java/io/netty/handler/ipfilter/IpFilterRuleTest.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright 2011 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.ipfilter; - -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; - -import junit.framework.TestCase; - -import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; -import io.netty.channel.ChannelEvent; -import io.netty.channel.ChannelFactory; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.UpstreamMessageEvent; -import org.junit.Test; - -public class IpFilterRuleTest extends TestCase -{ - public static boolean accept(IpFilterRuleHandler h, InetSocketAddress addr) throws Exception - { - return h.accept(new ChannelHandlerContext() - { - - @Override - public boolean canHandleDownstream() - { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean canHandleUpstream() - { - // TODO Auto-generated method stub - return false; - } - - @Override - public Object getAttachment() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public Channel getChannel() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelHandler getHandler() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public String getName() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelPipeline getPipeline() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public void sendDownstream(ChannelEvent e) - { - // TODO Auto-generated method stub - - } - - @Override - public void sendUpstream(ChannelEvent e) - { - // TODO Auto-generated method stub - - } - - @Override - public void setAttachment(Object attachment) - { - // TODO Auto-generated method stub - - } - - }, - new UpstreamMessageEvent(new Channel() - { - - @Override - public ChannelFuture bind(SocketAddress localAddress) - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelFuture close() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelFuture connect(SocketAddress remoteAddress) - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelFuture disconnect() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelFuture getCloseFuture() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelConfig getConfig() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelFactory getFactory() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public Integer getId() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public int getInterestOps() - { - // TODO Auto-generated method stub - return 0; - } - - @Override - public SocketAddress getLocalAddress() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public Channel getParent() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelPipeline getPipeline() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public SocketAddress getRemoteAddress() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public boolean isBound() - { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean isConnected() - { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean isOpen() - { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean isReadable() - { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean isWritable() - { - // TODO Auto-generated method stub - return false; - } - - @Override - public ChannelFuture setInterestOps(int interestOps) - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelFuture setReadable(boolean readable) - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelFuture unbind() - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelFuture write(Object message) - { - // TODO Auto-generated method stub - return null; - } - - @Override - public ChannelFuture write(Object message, SocketAddress remoteAddress) - { - // TODO Auto-generated method stub - return null; - } - - @Override - public int compareTo(Channel o) - { - // TODO Auto-generated method stub - return 0; - } - - @Override - public Object getAttachment() { - return null; - } - - @Override - public void setAttachment(Object attachment) { - - } - - }, h, addr), - addr); - } - - @Test - public void testIpFilterRule() throws Exception - { - IpFilterRuleHandler h = new IpFilterRuleHandler(); - h.addAll(new IpFilterRuleList("+n:localhost, -n:*")); - InetSocketAddress addr = new InetSocketAddress(InetAddress.getLocalHost(), 8080); - assertTrue(accept(h, addr)); - addr = new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 8080); - assertFalse(accept(h, addr)); - addr = new InetSocketAddress(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), 8080); - assertTrue(accept(h, addr)); - - h.clear(); - h.addAll(new IpFilterRuleList("+n:*"+InetAddress.getLocalHost().getHostName().substring(1)+", -n:*")); - addr = new InetSocketAddress(InetAddress.getLocalHost(), 8080); - assertTrue(accept(h, addr)); - addr = new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 8080); - assertFalse(accept(h, addr)); - addr = new InetSocketAddress(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), 8080); - assertTrue(accept(h, addr)); - - h.clear(); - h.addAll(new IpFilterRuleList("+c:"+InetAddress.getLocalHost().getHostAddress()+"/32, -n:*")); - addr = new InetSocketAddress(InetAddress.getLocalHost(), 8080); - assertTrue(accept(h, addr)); - addr = new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 8080); - assertFalse(accept(h, addr)); - addr = new InetSocketAddress(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), 8080); - assertTrue(accept(h, addr)); - - h.clear(); - h.addAll(new IpFilterRuleList("")); - addr = new InetSocketAddress(InetAddress.getLocalHost(), 8080); - assertTrue(accept(h, addr)); - addr = new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 8080); - assertTrue(accept(h, addr)); - addr = new InetSocketAddress(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), 8080); - assertTrue(accept(h, addr)); - - h.clear(); - addr = new InetSocketAddress(InetAddress.getLocalHost(), 8080); - assertTrue(accept(h, addr)); - addr = new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 8080); - assertTrue(accept(h, addr)); - addr = new InetSocketAddress(InetAddress.getByName(InetAddress.getLocalHost().getHostName()), 8080); - assertTrue(accept(h, addr)); - - } - -}