diff --git a/.gitignore b/.gitignore
index 07f1aa9..e0ddd7b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@ obj
*.suo
*.user
_ReSharper.*
+*.ReSharper
*.DS_Store
*.userprefs
*.pidb
diff --git a/src/Main/Gate.Middleware/ShowExceptions.View.cs b/src/Main/Gate.Middleware/ShowExceptions.View.cs
index c5580b9..dd6c3c2 100644
--- a/src/Main/Gate.Middleware/ShowExceptions.View.cs
+++ b/src/Main/Gate.Middleware/ShowExceptions.View.cs
@@ -353,7 +353,7 @@ unknown location
");
var form = request.ReadForm();
- if (form.Any())
+ if (form.Fields.Any())
{
write(@"
@@ -365,7 +365,7 @@ unknown location
");
- foreach (var kv in form.OrderBy(kv => kv.Key))
+ foreach (var kv in form.Fields.OrderBy(kv => kv.Key))
{
write(@"
diff --git a/src/Main/Gate/Form/Form.cs b/src/Main/Gate/Form/Form.cs
new file mode 100644
index 0000000..f94e836
--- /dev/null
+++ b/src/Main/Gate/Form/Form.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Gate.Utils;
+
+namespace Gate.Form
+{
+ internal class Form : IForm
+ {
+ readonly Encoding _encoding = new ASCIIEncoding();
+
+ public Form()
+ {
+ Fields = ParamDictionary.Parse("");
+ Files = new Dictionary();
+ }
+
+ public Form(string text)
+ {
+ Fields = ParamDictionary.Parse(text);
+ Files = new Dictionary();
+ }
+
+ public Form(string boundary, Stream stream)
+ {
+ Fields = new Dictionary();
+ Files = new Dictionary();
+ if (stream == null)
+ return;
+ if (stream.CanSeek)
+ stream.Seek(0, SeekOrigin.Begin);
+ ReadData(boundary, stream);
+ }
+
+ private static int IndexOf(byte[] searchIn, byte[] searchBytes, int start = 0)
+ {
+ var found = -1;
+ if (searchIn.Length > 0 && searchBytes.Length > 0 && start <= (searchIn.Length - searchBytes.Length) && searchIn.Length >= searchBytes.Length)
+ {
+ for (var i = start; i <= searchIn.Length - searchBytes.Length; i++)
+ {
+ if (searchIn[i] != searchBytes[0]) continue;
+ if (searchIn.Length > 1)
+ {
+ var matched = true;
+ for (var y = 1; y <= searchBytes.Length - 1; y++)
+ {
+ if (searchIn[i + y] == searchBytes[y]) continue;
+ matched = false;
+ break;
+ }
+ if (matched)
+ {
+ found = i;
+ break;
+ }
+ }
+ else
+ {
+ found = i;
+ break;
+ }
+ }
+ }
+ return found;
+ }
+
+ private static byte[] ReadFully(Stream input)
+ {
+ var buffer = new byte[16 * 1024];
+ using (var ms = new MemoryStream())
+ {
+ int read;
+ while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ ms.Write(buffer, 0, read);
+ }
+ return ms.ToArray();
+ }
+ }
+
+ private static byte[] SubArray(byte[] data, int index, int length)
+ {
+ var result = new byte[length];
+ Array.Copy(data, index, result, 0, length);
+ return result;
+ }
+
+ private void ReadData(string boundary, Stream input)
+ {
+ var data = ReadFully(input);
+
+ var b = _encoding.GetBytes(boundary);
+ var ret1 = _encoding.GetBytes("\r\n--"+boundary);
+ var ret2 = _encoding.GetBytes("\r\n\r\n");
+
+ var offset = 0;
+ int pos;
+ while ((pos = IndexOf(data, b, offset)) != -1)
+ {
+ // pos - boundary starting point
+ if (pos + b.Length + 2 - data.Length > -5)
+ return;
+ // pos2 - boundary header endpoint/boundary body start point
+ var pos2 = IndexOf(data, ret2, pos + 1);
+ if (pos2 == -1)
+ return;
+ pos2 += ret2.Length;
+ // pos3 - boundary endpoint
+ var pos3 = IndexOf(data, ret1, pos2);
+ if (pos3 == -1)
+ return;
+ var file = new FormFile(SubArray(data, pos, pos2 - pos), SubArray(data, pos2, pos3 - pos2), _encoding);
+ if (file.IsFile)
+ Files[file.Name] = file;
+ else
+ Fields[file.Name] = file.FileName;
+ offset = pos3;
+ }
+ }
+
+ public IDictionary Fields { get; private set; }
+
+ public IDictionary Files { get; private set; }
+ }
+}
diff --git a/src/Main/Gate/Form/FormFile.cs b/src/Main/Gate/Form/FormFile.cs
new file mode 100644
index 0000000..4ee08c6
--- /dev/null
+++ b/src/Main/Gate/Form/FormFile.cs
@@ -0,0 +1,60 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace Gate.Form
+{
+ internal class FormFile : IFormFile
+ {
+ readonly Stream _stream;
+
+ public FormFile(byte[] infob, byte[] content, Encoding encoding)
+ {
+ var info = encoding.GetString(infob);
+ ContentType = null;
+ Size = -1;
+ var parts = info.Split(new[] {";", "\r\n"}, StringSplitOptions.RemoveEmptyEntries);
+ foreach (var part in parts.Select(x => x.Trim()))
+ {
+ if (part.StartsWith("name=", StringComparison.OrdinalIgnoreCase))
+ {
+ Name = UnEscape(part.Substring(5));
+ }
+ else if (part.StartsWith("filename=", StringComparison.OrdinalIgnoreCase))
+ {
+ FileName = UnEscape(part.Substring(9));
+ Size = content.Length;
+ }
+ else if (part.StartsWith("Content-Type: ", StringComparison.OrdinalIgnoreCase))
+ {
+ ContentType = UnEscape(part.Substring(14));
+ }
+ }
+
+ _stream = ContentType != null ? new MemoryStream(content) : null;
+ if (ContentType == null)
+ FileName = encoding.GetString(content);
+ }
+
+ private static string UnEscape(string v)
+ {
+ return v.Trim(' ', '"');
+ }
+
+ public bool IsFile { get { return Stream != null; } }
+
+ public string Name { get; private set; }
+
+ public string FileName { get; private set; }
+
+ public string ContentType { get; private set; }
+
+ public long Size { get; private set; }
+
+ public Stream Stream
+ {
+ get { return _stream; }
+ }
+ }
+}
diff --git a/src/Main/Gate/Gate.csproj b/src/Main/Gate/Gate.csproj
index cb9c6d6..05d9596 100644
--- a/src/Main/Gate/Gate.csproj
+++ b/src/Main/Gate/Gate.csproj
@@ -48,7 +48,11 @@
+
+
+
+
diff --git a/src/Main/Gate/IForm.cs b/src/Main/Gate/IForm.cs
new file mode 100644
index 0000000..0b42693
--- /dev/null
+++ b/src/Main/Gate/IForm.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace Gate
+{
+ public interface IForm
+ {
+ IDictionary Fields { get; }
+ IDictionary Files { get; }
+ }
+}
diff --git a/src/Main/Gate/IFormFile.cs b/src/Main/Gate/IFormFile.cs
new file mode 100644
index 0000000..8aea206
--- /dev/null
+++ b/src/Main/Gate/IFormFile.cs
@@ -0,0 +1,13 @@
+using System.IO;
+
+namespace Gate
+{
+ public interface IFormFile
+ {
+ string Name { get; }
+ string FileName { get; }
+ string ContentType { get; }
+ long Size { get; }
+ Stream Stream { get; }
+ }
+}
diff --git a/src/Main/Gate/Request.cs b/src/Main/Gate/Request.cs
index 044d2e1..407ff1c 100644
--- a/src/Main/Gate/Request.cs
+++ b/src/Main/Gate/Request.cs
@@ -2,11 +2,9 @@
using System.Collections.Generic;
using System.IO;
using System.Net;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Gate.Utils;
-using Owin;
namespace Gate
{
@@ -189,6 +187,14 @@ public bool HasParseableData
}
}
+ public bool HasBoundary
+ {
+ get
+ {
+ var ct = ContentType;
+ return ct != null && ContentType.IndexOf("boundary=", System.StringComparison.Ordinal) != -1;
+ }
+ }
public string ContentType
{
@@ -210,6 +216,22 @@ public string MediaType
}
}
+ public string Boundary
+ {
+ get
+ {
+ var contentType = ContentType;
+ if (contentType == null)
+ return null;
+ var idx = contentType.IndexOf("boundary=", System.StringComparison.Ordinal);
+ if (idx == -1)
+ return null;
+ var boundary = contentType.Substring(idx + "boundary=".Length);
+ var delimiterPos = boundary.IndexOfAny(CommaSemicolon);
+ return delimiterPos < 0 ? boundary : boundary.Substring(0, delimiterPos);
+ }
+ }
+
public Task CopyToStreamAsync(Stream stream)
{
if (Body == null)
@@ -291,14 +313,14 @@ public string ReadText()
return text;
}
- public Task> ReadFormAsync()
+ public Task ReadFormAsync()
{
- if (!HasFormData && !HasParseableData)
+ if (!HasFormData && !HasParseableData && !HasBoundary)
{
- return TaskHelpers.FromResult(ParamDictionary.Parse(""));
+ return TaskHelpers.FromResult((IForm) new Form.Form());
}
- var form = Environment.Get>("Gate.Request.Form");
+ var form = Environment.Get("Gate.Request.Form");
var thisInput = Body;
var lastInput = Environment.Get