Skip to content
Browse files

Initial commit

  • Loading branch information...
1 parent 4a164dc commit 5a6a3d06f892c48bca8381ce713327a340bf7323 @veebs veebs committed Mar 31, 2012
Showing with 6,783 additions and 0 deletions.
  1. +7 −0 .gitignore
  2. +19 −0 LICENSE
  3. +35 −0 build.sbt
  4. +20 −0 licenses/LICENSE.akka.txt
  5. +202 −0 licenses/LICENSE.netty.txt
  6. +44 −0 licenses/README.md
  7. +3 −0 project/plugins.sbt
  8. +347 −0 src/main/java/org/mashupbots/socko/postdecoder/AbstractDiskHttpData.java
  9. +97 −0 src/main/java/org/mashupbots/socko/postdecoder/AbstractHttpData.java
  10. +226 −0 src/main/java/org/mashupbots/socko/postdecoder/AbstractMemoryHttpData.java
  11. +34 −0 src/main/java/org/mashupbots/socko/postdecoder/Attribute.java
  12. +38 −0 src/main/java/org/mashupbots/socko/postdecoder/CaseIgnoringComparator.java
  13. +190 −0 src/main/java/org/mashupbots/socko/postdecoder/DefaultHttpDataFactory.java
  14. +147 −0 src/main/java/org/mashupbots/socko/postdecoder/DiskAttribute.java
  15. +162 −0 src/main/java/org/mashupbots/socko/postdecoder/DiskFileUpload.java
  16. +60 −0 src/main/java/org/mashupbots/socko/postdecoder/FileUpload.java
  17. +172 −0 src/main/java/org/mashupbots/socko/postdecoder/HttpCodeUtil.java
  18. +181 −0 src/main/java/org/mashupbots/socko/postdecoder/HttpData.java
  19. +80 −0 src/main/java/org/mashupbots/socko/postdecoder/HttpDataFactory.java
  20. +181 −0 src/main/java/org/mashupbots/socko/postdecoder/HttpPostBodyUtil.java
  21. +1,570 −0 src/main/java/org/mashupbots/socko/postdecoder/HttpPostRequestDecoder.java
  22. +36 −0 src/main/java/org/mashupbots/socko/postdecoder/InterfaceHttpData.java
  23. +108 −0 src/main/java/org/mashupbots/socko/postdecoder/MemoryAttribute.java
  24. +128 −0 src/main/java/org/mashupbots/socko/postdecoder/MemoryFileUpload.java
  25. +202 −0 src/main/java/org/mashupbots/socko/postdecoder/MixedAttribute.java
  26. +227 −0 src/main/java/org/mashupbots/socko/postdecoder/MixedFileUpload.java
  27. +25 −0 src/main/java/org/mashupbots/socko/postdecoder/package-info.java
  28. +34 −0 src/main/scala/org/mashupbots/socko/Logger.scala
  29. +60 −0 src/main/scala/org/mashupbots/socko/PipelineFactory.scala
  30. +86 −0 src/main/scala/org/mashupbots/socko/SslManager.scala
  31. +101 −0 src/main/scala/org/mashupbots/socko/WebServer.scala
  32. +116 −0 src/main/scala/org/mashupbots/socko/WebServerConfig.scala
  33. +65 −0 src/main/scala/org/mashupbots/socko/context/EndPoint.scala
  34. +80 −0 src/main/scala/org/mashupbots/socko/context/HttpChunkProcessingContext.scala
  35. +221 −0 src/main/scala/org/mashupbots/socko/context/HttpProcessingContext.scala
  36. +163 −0 src/main/scala/org/mashupbots/socko/context/HttpRequestProcessingContext.scala
  37. +62 −0 src/main/scala/org/mashupbots/socko/context/ProcessingContext.scala
  38. +120 −0 src/main/scala/org/mashupbots/socko/context/WsHandshakeProcessingContext.scala
  39. +90 −0 src/main/scala/org/mashupbots/socko/context/WsProcessingContext.scala
  40. +289 −0 src/main/scala/org/mashupbots/socko/handler/RequestHandler.scala
  41. +205 −0 src/main/scala/org/mashupbots/socko/handler/Routes.scala
  42. +179 −0 src/main/scala/org/mashupbots/socko/processors/SnoopProcessor.scala
  43. +371 −0 src/main/scala/org/mashupbots/socko/processors/StaticFileProcessor.scala
View
7 .gitignore
@@ -0,0 +1,7 @@
+/.project
+/.classpath
+/.settings
+/target
+/project/project
+/project/target
+/.cache
View
19 LICENSE
@@ -0,0 +1,19 @@
+This software is licensed under the Apache 2 license, quoted below.
+
+Copyright 2012 Vibul Imtarnasan and David Bolton
+
+Licensed 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.
+
+---------------
+
+Licenses for dependency projects are listed in the project licenses directory.
View
35 build.sbt
@@ -0,0 +1,35 @@
+
+//
+// Socko Web Server build file
+//
+
+
+organization := "org.mashupbots"
+
+name := "Socko"
+
+version := "0.1"
+
+scalaVersion := "2.9.1"
+
+resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
+
+libraryDependencies += "com.typesafe.akka" % "akka-actor" % "2.0"
+
+libraryDependencies += "com.typesafe.akka" % "akka-remote" % "2.0"
+
+libraryDependencies += "com.typesafe.akka" % "akka-slf4j" % "2.0"
+
+libraryDependencies += "com.typesafe.akka" % "akka-testkit" % "2.0"
+
+libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.0.0" % "runtime"
+
+libraryDependencies += "junit" % "junit" % "4.9" % "test"
+
+libraryDependencies += "org.scalatest" %% "scalatest" % "1.7.1" % "test"
+
+EclipseKeys.withSource := true
+
+EclipseKeys.createSrc := EclipseCreateSrc.Default + EclipseCreateSrc.Resource
+
+
View
20 licenses/LICENSE.akka.txt
@@ -0,0 +1,20 @@
+This software is licensed under the Apache 2 license, quoted below.
+
+Copyright 2009-2012 Typesafe Inc. [http://www.typesafe.com]
+
+Licensed 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.
+
+---------------
+
+Licenses for dependency projects can be found here:
+[http://akka.io/docs/akka/snapshot/project/licenses.html]
View
202 licenses/LICENSE.netty.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
View
44 licenses/README.md
@@ -0,0 +1,44 @@
+MashupBots Licenses
+===================
+This software is licensed under the Apache 2 license, quoted below.
+
+Copyright 2012 Vibul Imtarnasan and David Bolton
+
+Licensed 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.
+
+
+
+Licenses for dependency projects
+================================
+
+AKKA
+----
+This product uses the Akka project:
+
+ * LICENSE FILE:
+ * LICENSE.akka.txt (Apache 2)
+ * HOMEPAGE:
+ * [http://akka.io/]
+
+
+Netty
+-----
+This product uses the Netty project:
+
+ * LICENSE FILE:
+ * LICENSE.netty.txt (Apache 2)
+ * HOMEPAGE:
+ * [http://netty.io/]
+
+
+
View
3 project/plugins.sbt
@@ -0,0 +1,3 @@
+
+addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0")
+
View
347 src/main/java/org/mashupbots/socko/postdecoder/AbstractDiskHttpData.java
@@ -0,0 +1,347 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+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;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+
+/**
+ * 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();
+ ByteBuffer byteBuffer = buffer.toByteBuffer();
+ int written = 0;
+ while (written < size) {
+ written += localfileChannel.write(byteBuffer);
+ localfileChannel.force(false);
+ }
+ buffer.readerIndex(buffer.readerIndex() + written);
+ 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);
+ }
+ ByteBuffer byteBuffer = buffer.toByteBuffer();
+ int written = 0;
+ if (file == null) {
+ file = tempFile();
+ }
+ if (fileChannel == null) {
+ FileOutputStream outputStream = new FileOutputStream(file);
+ fileChannel = outputStream.getChannel();
+ }
+ while (written < localsize) {
+ written += fileChannel.write(byteBuffer);
+ fileChannel.force(false);
+ }
+ 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.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);
+ localfileChannel.force(false);
+ read = inputStream.read(bytes);
+ }
+ 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(HttpCodecUtil.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, HttpCodecUtil.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);
+ 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 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;
+ }
+
+}
View
97 src/main/java/org/mashupbots/socko/postdecoder/AbstractHttpData.java
@@ -0,0 +1,97 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+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 = HttpCodecUtil.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;
+ }
+}
View
226 src/main/java/org/mashupbots/socko/postdecoder/AbstractMemoryHttpData.java
@@ -0,0 +1,226 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+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;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+
+/**
+ * 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(HttpCodecUtil.DEFAULT_CHARSET);
+ }
+
+ @Override
+ public String getString(Charset encoding) {
+ if (channelBuffer == null) {
+ return "";
+ }
+ if (encoding == null) {
+ return getString(HttpCodecUtil.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();
+ ByteBuffer byteBuffer = channelBuffer.toByteBuffer();
+ int written = 0;
+ while (written < length) {
+ written += fileChannel.write(byteBuffer);
+ fileChannel.force(false);
+ }
+ fileChannel.close();
+ isRenamed = true;
+ return written == length;
+ }
+
+ @Override
+ public File getFile() throws IOException {
+ throw new IOException("Not represented by a file");
+ }
+}
View
34 src/main/java/org/mashupbots/socko/postdecoder/Attribute.java
@@ -0,0 +1,34 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+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;
+}
View
38 src/main/java/org/mashupbots/socko/postdecoder/CaseIgnoringComparator.java
@@ -0,0 +1,38 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+final class CaseIgnoringComparator implements Comparator<String>, Serializable {
+
+ private static final long serialVersionUID = 4582133183775373862L;
+
+ static final CaseIgnoringComparator INSTANCE = new CaseIgnoringComparator();
+
+ private CaseIgnoringComparator() {
+ }
+
+ @Override
+ public int compare(String o1, String o2) {
+ return o1.compareToIgnoreCase(o2);
+ }
+
+ private Object readResolve() {
+ return INSTANCE;
+ }
+}
View
190 src/main/java/org/mashupbots/socko/postdecoder/DefaultHttpDataFactory.java
@@ -0,0 +1,190 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jboss.netty.handler.codec.http.HttpRequest;
+
+/**
+ * Default factory giving Attribute and FileUpload according to constructor
+ *
+ * Attribute and FileUpload could be :<br>
+ * - MemoryAttribute, DiskAttribute or MixedAttribute<br>
+ * - MemoryFileUpload, DiskFileUpload or MixedFileUpload<br>
+ * according to the constructor.
+ */
+public class DefaultHttpDataFactory implements HttpDataFactory {
+ /**
+ * Proposed default MINSIZE as 16 KB.
+ */
+ public static long MINSIZE = 0x4000;
+
+ private boolean useDisk;
+
+ private boolean checkSize;
+
+ private long minSize;
+
+ /**
+ * Keep all HttpDatas until cleanAllHttpDatas() is called.
+ */
+ private final ConcurrentHashMap<HttpRequest, List<HttpData>> requestFileDeleteMap =
+ new ConcurrentHashMap<HttpRequest, List<HttpData>>();
+ /**
+ * HttpData will be in memory if less than default size (16KB).
+ * The type will be Mixed.
+ */
+ public DefaultHttpDataFactory() {
+ useDisk = false;
+ checkSize = true;
+ this.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<HttpData> getList(HttpRequest request) {
+ List<HttpData> list = requestFileDeleteMap.get(request);
+ if (list == null) {
+ list = new ArrayList<HttpData>();
+ requestFileDeleteMap.put(request, list);
+ }
+ return list;
+ }
+
+ @Override
+ public Attribute createAttribute(HttpRequest request, String name) {
+ if (useDisk) {
+ Attribute attribute = new DiskAttribute(name);
+ List<HttpData> fileToDelete = getList(request);
+ fileToDelete.add(attribute);
+ return attribute;
+ } else if (checkSize) {
+ Attribute attribute = new MixedAttribute(name, minSize);
+ List<HttpData> 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<HttpData> fileToDelete = getList(request);
+ fileToDelete.add(attribute);
+ return attribute;
+ } else if (checkSize) {
+ Attribute attribute = new MixedAttribute(name, value, minSize);
+ List<HttpData> 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<HttpData> fileToDelete = getList(request);
+ fileToDelete.add(fileUpload);
+ return fileUpload;
+ } else if (checkSize) {
+ FileUpload fileUpload = new MixedFileUpload(name, filename, contentType,
+ contentTransferEncoding, charset, size, minSize);
+ List<HttpData> 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<HttpData> fileToDelete = getList(request);
+ fileToDelete.remove(data);
+ }
+ }
+
+ @Override
+ public void cleanRequestHttpDatas(HttpRequest request) {
+ List<HttpData> 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<HttpData> fileToDelete = requestFileDeleteMap.get(request);
+ if (fileToDelete != null) {
+ for (HttpData data: fileToDelete) {
+ data.delete();
+ }
+ fileToDelete.clear();
+ }
+ requestFileDeleteMap.remove(request);
+ }
+ }
+}
View
147 src/main/java/org/mashupbots/socko/postdecoder/DiskAttribute.java
@@ -0,0 +1,147 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+import java.io.IOException;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+
+/**
+ * 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, HttpCodecUtil.DEFAULT_CHARSET, 0);
+ }
+ /**
+ *
+ * @param name
+ * @param value
+ * @throws NullPointerException
+ * @throws IllegalArgumentException
+ * @throws IOException
+ */
+ public DiskAttribute(String name, String value) throws IOException {
+ super(name, HttpCodecUtil.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;
+ }
+}
View
162 src/main/java/org/mashupbots/socko/postdecoder/DiskFileUpload.java
@@ -0,0 +1,162 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+import java.io.File;
+import java.nio.charset.Charset;
+
+import org.jboss.netty.handler.codec.http.HttpHeaders;
+
+/**
+ * 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;
+ }
+}
View
60 src/main/java/org/mashupbots/socko/postdecoder/FileUpload.java
@@ -0,0 +1,60 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+/**
+ * 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();
+}
View
172 src/main/java/org/mashupbots/socko/postdecoder/HttpCodeUtil.java
@@ -0,0 +1,172 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+import java.nio.charset.Charset;
+import java.util.List;
+
+import org.jboss.netty.handler.codec.http.HttpHeaders;
+import org.jboss.netty.handler.codec.http.HttpMessage;
+import org.jboss.netty.util.CharsetUtil;
+
+final class HttpCodecUtil {
+ //space ' '
+ static final byte SP = 32;
+
+ //tab ' '
+ static final byte HT = 9;
+
+ /**
+ * Carriage return
+ */
+ static final byte CR = 13;
+
+ /**
+ * Equals '='
+ */
+ static final byte EQUALS = 61;
+
+ /**
+ * Line feed character
+ */
+ static final byte LF = 10;
+
+ /**
+ * carriage return line feed
+ */
+ static final byte[] CRLF = { CR, LF };
+
+ /**
+ * Colon ':'
+ */
+ static final byte COLON = 58;
+
+ /**
+ * Semicolon ';'
+ */
+ static final byte SEMICOLON = 59;
+
+ /**
+ * comma ','
+ */
+ static final byte COMMA = 44;
+
+ static final byte DOUBLE_QUOTE = '"';
+
+ static final Charset DEFAULT_CHARSET = CharsetUtil.UTF_8;
+
+ private HttpCodecUtil() {
+ }
+
+ static void validateHeaderName(String name) {
+ if (name == null) {
+ throw new NullPointerException("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 '\t': case '\n': case 0x0b: case '\f': case '\r':
+ case ' ': case ',': case ':': case ';': case '=':
+ throw new IllegalArgumentException(
+ "name contains one of the following prohibited characters: " +
+ "=,;: \\t\\r\\n\\v\\f: " + name);
+ }
+ }
+ }
+
+ static void validateHeaderValue(String value) {
+ if (value == null) {
+ throw new NullPointerException("value");
+ }
+
+ // 0 - the previous character was neither CR nor LF
+ // 1 - the previous character was CR
+ // 2 - the previous character was LF
+ int state = 0;
+
+ for (int i = 0; i < value.length(); i ++) {
+ char c = value.charAt(i);
+
+ // Check the absolutely prohibited characters.
+ switch (c) {
+ case 0x0b: // Vertical tab
+ throw new IllegalArgumentException(
+ "value contains a prohibited character '\\v': " + value);
+ case '\f':
+ throw new IllegalArgumentException(
+ "value contains a prohibited character '\\f': " + value);
+ }
+
+ // Check the CRLF (HT | SP) pattern
+ switch (state) {
+ case 0:
+ switch (c) {
+ case '\r':
+ state = 1;
+ break;
+ case '\n':
+ state = 2;
+ break;
+ }
+ break;
+ case 1:
+ switch (c) {
+ case '\n':
+ state = 2;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Only '\\n' is allowed after '\\r': " + value);
+ }
+ break;
+ case 2:
+ switch (c) {
+ case '\t': case ' ':
+ state = 0;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Only ' ' and '\\t' are allowed after '\\n': " + value);
+ }
+ }
+ }
+
+ if (state != 0) {
+ throw new IllegalArgumentException(
+ "value must not end with '\\r' or '\\n':" + value);
+ }
+ }
+
+ static boolean isTransferEncodingChunked(HttpMessage m) {
+ List<String> chunked = m.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING);
+ if (chunked.isEmpty()) {
+ return false;
+ }
+
+ for (String v: chunked) {
+ if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
View
181 src/main/java/org/mashupbots/socko/postdecoder/HttpData.java
@@ -0,0 +1,181 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+/**
+ * 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;
+
+}
View
80 src/main/java/org/mashupbots/socko/postdecoder/HttpDataFactory.java
@@ -0,0 +1,80 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+import java.nio.charset.Charset;
+
+import org.jboss.netty.handler.codec.http.HttpRequest;
+
+/**
+ * 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();
+}
View
181 src/main/java/org/mashupbots/socko/postdecoder/HttpPostBodyUtil.java
@@ -0,0 +1,181 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+import java.nio.charset.Charset;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.util.CharsetUtil;
+
+/**
+ * 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() {
+ }
+
+ //Some commons methods between HttpPostRequestDecoder and HttpMessageDecoder
+ /**
+ * Skip control Characters
+ * @param buffer
+ */
+ static void skipControlCharacters(ChannelBuffer buffer) {
+ for (;;) {
+ char c = (char) buffer.readUnsignedByte();
+ if (!Character.isISOControl(c) && !Character.isWhitespace(c)) {
+ buffer.readerIndex(buffer.readerIndex() - 1);
+ break;
+ }
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+}
View
1,570 src/main/java/org/mashupbots/socko/postdecoder/HttpPostRequestDecoder.java
@@ -0,0 +1,1570 @@
+/*
+ * 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 org.mashupbots.socko.postdecoder;
+
+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;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.handler.codec.http.HttpChunk;
+import org.jboss.netty.handler.codec.http.HttpHeaders;
+import org.jboss.netty.handler.codec.http.HttpMethod;
+import org.jboss.netty.handler.codec.http.HttpRequest;
+import org.mashupbots.socko.postdecoder.HttpPostBodyUtil.TransferEncodingMechanism;
+
+
+/**
+ * This decoder will decode Body and can handle POST BODY.
+ */
+public class HttpPostRequestDecoder {
+
+ static final String MULTIPART_FORM_DATA = "multipart/form-data";
+ static final String BOUNDARY = "boundary";
+
+ /**
+ * 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<InterfaceHttpData> bodyListHttpData = new ArrayList<InterfaceHttpData>();
+
+ /**
+ * HttpDatas as Map from Body
+ */
+ private final Map<String, List<InterfaceHttpData>> bodyMapHttpData = new TreeMap<String, List<InterfaceHttpData>>(