From 45f19d02ff9a9e2c72e4d95e38c5d7f73960fe57 Mon Sep 17 00:00:00 2001
From: Trustin Lee
Date: Sat, 2 Jun 2012 01:38:10 -0700
Subject: [PATCH] Remove the codecs and handlers that can't make it on time for
4.0.0.A1
- Removed ones are: IP filer and HTTP multipart codec
- Needs closer code review and polishing
- Sorry. I'll add them back in the next alpha releases
- SSL handler and ChunkedWriteHandler also need more work, but
I really want to make them part of the first alpha because they
are used pretty often by users.
---
.../http/multipart/AbstractDiskHttpData.java | 349 ---
.../http/multipart/AbstractHttpData.java | 99 -
.../multipart/AbstractMemoryHttpData.java | 226 --
.../codec/http/multipart/Attribute.java | 34 -
.../multipart/DefaultHttpDataFactory.java | 190 --
.../codec/http/multipart/DiskAttribute.java | 148 --
.../codec/http/multipart/DiskFileUpload.java | 162 --
.../codec/http/multipart/FileUpload.java | 60 -
.../codec/http/multipart/HttpData.java | 153 --
.../codec/http/multipart/HttpDataFactory.java | 80 -
.../http/multipart/HttpPostBodyUtil.java | 233 --
.../multipart/HttpPostRequestDecoder.java | 1947 -----------------
.../multipart/HttpPostRequestEncoder.java | 986 ---------
.../http/multipart/InterfaceHttpData.java | 36 -
.../http/multipart/InternalAttribute.java | 105 -
.../codec/http/multipart/MemoryAttribute.java | 109 -
.../http/multipart/MemoryFileUpload.java | 128 --
.../codec/http/multipart/MixedAttribute.java | 202 --
.../codec/http/multipart/MixedFileUpload.java | 227 --
.../example/http/upload/HttpUploadClient.java | 981 ---------
.../http/upload/HttpUploadClientHandler.java | 78 -
.../HttpUploadClientPipelineFactory.java | 62 -
.../example/http/upload/HttpUploadServer.java | 54 -
.../http/upload/HttpUploadServerHandler.java | 487 -----
.../HttpUploadServerPipelineFactory.java | 47 -
.../java/io/netty/handler/ipfilter/CIDR.java | 234 --
.../java/io/netty/handler/ipfilter/CIDR4.java | 159 --
.../java/io/netty/handler/ipfilter/CIDR6.java | 164 --
.../handler/ipfilter/IpFilterListener.java | 67 -
.../netty/handler/ipfilter/IpFilterRule.java | 25 -
.../handler/ipfilter/IpFilterRuleHandler.java | 259 ---
.../handler/ipfilter/IpFilterRuleList.java | 95 -
.../handler/ipfilter/IpFilteringHandler.java | 35 -
.../ipfilter/IpFilteringHandlerImpl.java | 164 --
.../java/io/netty/handler/ipfilter/IpSet.java | 30 -
.../io/netty/handler/ipfilter/IpSubnet.java | 140 --
.../handler/ipfilter/IpSubnetFilterRule.java | 68 -
.../io/netty/handler/ipfilter/IpV4Subnet.java | 250 ---
.../ipfilter/IpV4SubnetFilterRule.java | 63 -
.../handler/ipfilter/OneIpFilterHandler.java | 73 -
.../netty/handler/ipfilter/PatternRule.java | 202 --
.../netty/handler/ipfilter/package-info.java | 91 -
.../handler/ipfilter/IpFilterRuleTest.java | 347 ---
43 files changed, 9649 deletions(-)
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractDiskHttpData.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractHttpData.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/AbstractMemoryHttpData.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/Attribute.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/DefaultHttpDataFactory.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskAttribute.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/DiskFileUpload.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/FileUpload.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpData.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpDataFactory.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostBodyUtil.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/InterfaceHttpData.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/InternalAttribute.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryAttribute.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/MemoryFileUpload.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedAttribute.java
delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/multipart/MixedFileUpload.java
delete mode 100644 example/src/main/java/io/netty/example/http/upload/HttpUploadClient.java
delete mode 100644 example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java
delete mode 100644 example/src/main/java/io/netty/example/http/upload/HttpUploadClientPipelineFactory.java
delete mode 100644 example/src/main/java/io/netty/example/http/upload/HttpUploadServer.java
delete mode 100644 example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java
delete mode 100644 example/src/main/java/io/netty/example/http/upload/HttpUploadServerPipelineFactory.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/CIDR.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/CIDR4.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/CIDR6.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/IpFilterListener.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/IpFilterRule.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleHandler.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/IpFilterRuleList.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/IpFilteringHandler.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/IpFilteringHandlerImpl.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/IpSet.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/IpSubnet.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/IpSubnetFilterRule.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/IpV4Subnet.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/IpV4SubnetFilterRule.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/OneIpFilterHandler.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/PatternRule.java
delete mode 100644 handler/src/main/java/io/netty/handler/ipfilter/package-info.java
delete mode 100644 handler/src/test/java/io/netty/handler/ipfilter/IpFilterRuleTest.java
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("Netty Test Form ");
- responseContent.append("Choose one FORM");
- responseContent.append(" ");
- responseContent.append(" ");
- responseContent.append("
\r\n");
-
- // GET
- responseContent
- .append("GET FORM ");
- responseContent.append("\r\n");
- responseContent
- .append(" ");
-
- // POST
- responseContent
- .append("POST FORM ");
- responseContent.append("");
- responseContent
- .append(" ");
- responseContent.append(" \r\n");
- responseContent
- .append(" ");
-
- // POST with enctype="multipart/form-data"
- responseContent
- .append("POST MULTIPART FORM ");
- responseContent
- .append("");
- responseContent
- .append(" ");
- 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));
-
- }
-
-}