make additional classes portable

This commit is contained in:
Luke Pulverenti
2016-11-10 20:58:20 -05:00
parent 01fc207b62
commit 836e1fdc30
5 changed files with 48 additions and 112 deletions

View File

@@ -79,8 +79,10 @@
<Compile Include="HttpServer\Security\AuthorizationContext.cs" />
<Compile Include="HttpServer\Security\AuthService.cs" />
<Compile Include="HttpServer\Security\SessionContext.cs" />
<Compile Include="HttpServer\SocketSharp\RequestMono.cs" />
<Compile Include="HttpServer\SocketSharp\SharpWebSocket.cs" />
<Compile Include="HttpServer\SocketSharp\WebSocketSharpListener.cs" />
<Compile Include="HttpServer\SocketSharp\WebSocketSharpRequest.cs" />
<Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" />
<Compile Include="HttpServer\StreamWriter.cs" />
<Compile Include="HttpServer\SwaggerService.cs" />

View File

@@ -0,0 +1,846 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using MediaBrowser.Model.Services;
namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
public partial class WebSocketSharpRequest : IHttpRequest
{
static internal string GetParameter(string header, string attr)
{
int ap = header.IndexOf(attr);
if (ap == -1)
return null;
ap += attr.Length;
if (ap >= header.Length)
return null;
char ending = header[ap];
if (ending != '"')
ending = ' ';
int end = header.IndexOf(ending, ap + 1);
if (end == -1)
return ending == '"' ? null : header.Substring(ap);
return header.Substring(ap + 1, end - ap - 1);
}
async Task LoadMultiPart()
{
string boundary = GetParameter(ContentType, "; boundary=");
if (boundary == null)
return;
using (var requestStream = GetSubStream(InputStream, _memoryStreamProvider))
{
//DB: 30/01/11 - Hack to get around non-seekable stream and received HTTP request
//Not ending with \r\n?
var ms = _memoryStreamProvider.CreateNew(32 * 1024);
await requestStream.CopyToAsync(ms).ConfigureAwait(false);
var input = ms;
ms.WriteByte((byte)'\r');
ms.WriteByte((byte)'\n');
input.Position = 0;
//Uncomment to debug
//var content = new StreamReader(ms).ReadToEnd();
//Console.WriteLine(boundary + "::" + content);
//input.Position = 0;
var multi_part = new HttpMultipart(input, boundary, ContentEncoding);
HttpMultipart.Element e;
while ((e = multi_part.ReadNextElement()) != null)
{
if (e.Filename == null)
{
byte[] copy = new byte[e.Length];
input.Position = e.Start;
input.Read(copy, 0, (int)e.Length);
form.Add(e.Name, (e.Encoding ?? ContentEncoding).GetString(copy, 0, copy.Length));
}
else
{
//
// We use a substream, as in 2.x we will support large uploads streamed to disk,
//
HttpPostedFile sub = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length);
files[e.Name] = sub;
}
}
}
}
public QueryParamCollection Form
{
get
{
if (form == null)
{
form = new WebROCollection();
files = new Dictionary<string, HttpPostedFile>();
if (IsContentType("multipart/form-data", true))
{
var task = LoadMultiPart();
Task.WaitAll(task);
}
else if (IsContentType("application/x-www-form-urlencoded", true))
{
var task = LoadWwwForm();
Task.WaitAll(task);
}
form.Protect();
}
#if NET_4_0
if (validateRequestNewMode && !checked_form) {
// Setting this before calling the validator prevents
// possible endless recursion
checked_form = true;
ValidateNameValueCollection ("Form", query_string_nvc, RequestValidationSource.Form);
} else
#endif
if (validate_form && !checked_form)
{
checked_form = true;
ValidateNameValueCollection("Form", form);
}
return form;
}
}
public string Accept
{
get
{
return string.IsNullOrEmpty(request.Headers["Accept"]) ? null : request.Headers["Accept"];
}
}
public string Authorization
{
get
{
return string.IsNullOrEmpty(request.Headers["Authorization"]) ? null : request.Headers["Authorization"];
}
}
protected bool validate_cookies, validate_query_string, validate_form;
protected bool checked_cookies, checked_query_string, checked_form;
static void ThrowValidationException(string name, string key, string value)
{
string v = "\"" + value + "\"";
if (v.Length > 20)
v = v.Substring(0, 16) + "...\"";
string msg = String.Format("A potentially dangerous Request.{0} value was " +
"detected from the client ({1}={2}).", name, key, v);
throw new Exception(msg);
}
static void ValidateNameValueCollection(string name, QueryParamCollection coll)
{
if (coll == null)
return;
foreach (var pair in coll)
{
var key = pair.Name;
var val = pair.Value;
if (val != null && val.Length > 0 && IsInvalidString(val))
ThrowValidationException(name, key, val);
}
}
internal static bool IsInvalidString(string val)
{
int validationFailureIndex;
return IsInvalidString(val, out validationFailureIndex);
}
internal static bool IsInvalidString(string val, out int validationFailureIndex)
{
validationFailureIndex = 0;
int len = val.Length;
if (len < 2)
return false;
char current = val[0];
for (int idx = 1; idx < len; idx++)
{
char next = val[idx];
// See http://secunia.com/advisories/14325
if (current == '<' || current == '\xff1c')
{
if (next == '!' || next < ' '
|| (next >= 'a' && next <= 'z')
|| (next >= 'A' && next <= 'Z'))
{
validationFailureIndex = idx - 1;
return true;
}
}
else if (current == '&' && next == '#')
{
validationFailureIndex = idx - 1;
return true;
}
current = next;
}
return false;
}
public void ValidateInput()
{
validate_cookies = true;
validate_query_string = true;
validate_form = true;
}
bool IsContentType(string ct, bool starts_with)
{
if (ct == null || ContentType == null) return false;
if (starts_with)
return StrUtils.StartsWith(ContentType, ct, true);
return string.Equals(ContentType, ct, StringComparison.OrdinalIgnoreCase);
}
async Task LoadWwwForm()
{
using (Stream input = GetSubStream(InputStream, _memoryStreamProvider))
{
using (var ms = _memoryStreamProvider.CreateNew())
{
await input.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
using (StreamReader s = new StreamReader(ms, ContentEncoding))
{
StringBuilder key = new StringBuilder();
StringBuilder value = new StringBuilder();
int c;
while ((c = s.Read()) != -1)
{
if (c == '=')
{
value.Length = 0;
while ((c = s.Read()) != -1)
{
if (c == '&')
{
AddRawKeyValue(key, value);
break;
}
else
value.Append((char)c);
}
if (c == -1)
{
AddRawKeyValue(key, value);
return;
}
}
else if (c == '&')
AddRawKeyValue(key, value);
else
key.Append((char)c);
}
if (c == -1)
AddRawKeyValue(key, value);
}
}
}
}
void AddRawKeyValue(StringBuilder key, StringBuilder value)
{
string decodedKey = WebUtility.UrlDecode(key.ToString());
form.Add(decodedKey,
WebUtility.UrlDecode(value.ToString()));
key.Length = 0;
value.Length = 0;
}
WebROCollection form;
Dictionary<string, HttpPostedFile> files;
class WebROCollection : QueryParamCollection
{
bool got_id;
int id;
public bool GotID
{
get { return got_id; }
}
public int ID
{
get { return id; }
set
{
got_id = true;
id = value;
}
}
public void Protect()
{
//IsReadOnly = true;
}
public void Unprotect()
{
//IsReadOnly = false;
}
public override string ToString()
{
StringBuilder result = new StringBuilder();
foreach (var pair in this)
{
if (result.Length > 0)
result.Append('&');
var key = pair.Name;
if (key != null && key.Length > 0)
{
result.Append(key);
result.Append('=');
}
result.Append(pair.Value);
}
return result.ToString();
}
}
public sealed class HttpPostedFile
{
string name;
string content_type;
Stream stream;
class ReadSubStream : Stream
{
Stream s;
long offset;
long end;
long position;
public ReadSubStream(Stream s, long offset, long length)
{
this.s = s;
this.offset = offset;
this.end = offset + length;
position = offset;
}
public override void Flush()
{
}
public override int Read(byte[] buffer, int dest_offset, int count)
{
if (buffer == null)
throw new ArgumentNullException("buffer");
if (dest_offset < 0)
throw new ArgumentOutOfRangeException("dest_offset", "< 0");
if (count < 0)
throw new ArgumentOutOfRangeException("count", "< 0");
int len = buffer.Length;
if (dest_offset > len)
throw new ArgumentException("destination offset is beyond array size");
// reordered to avoid possible integer overflow
if (dest_offset > len - count)
throw new ArgumentException("Reading would overrun buffer");
if (count > end - position)
count = (int)(end - position);
if (count <= 0)
return 0;
s.Position = position;
int result = s.Read(buffer, dest_offset, count);
if (result > 0)
position += result;
else
position = end;
return result;
}
public override int ReadByte()
{
if (position >= end)
return -1;
s.Position = position;
int result = s.ReadByte();
if (result < 0)
position = end;
else
position++;
return result;
}
public override long Seek(long d, SeekOrigin origin)
{
long real;
switch (origin)
{
case SeekOrigin.Begin:
real = offset + d;
break;
case SeekOrigin.End:
real = end + d;
break;
case SeekOrigin.Current:
real = position + d;
break;
default:
throw new ArgumentException();
}
long virt = real - offset;
if (virt < 0 || virt > Length)
throw new ArgumentException();
position = s.Seek(real, SeekOrigin.Begin);
return position;
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { return end - offset; }
}
public override long Position
{
get
{
return position - offset;
}
set
{
if (value > Length)
throw new ArgumentOutOfRangeException();
position = Seek(value, SeekOrigin.Begin);
}
}
}
internal HttpPostedFile(string name, string content_type, Stream base_stream, long offset, long length)
{
this.name = name;
this.content_type = content_type;
this.stream = new ReadSubStream(base_stream, offset, length);
}
public string ContentType
{
get
{
return content_type;
}
}
public int ContentLength
{
get
{
return (int)stream.Length;
}
}
public string FileName
{
get
{
return name;
}
}
public Stream InputStream
{
get
{
return stream;
}
}
}
class Helpers
{
public static readonly CultureInfo InvariantCulture = CultureInfo.InvariantCulture;
}
internal sealed class StrUtils
{
public static bool StartsWith(string str1, string str2, bool ignore_case)
{
if (string.IsNullOrWhiteSpace(str1))
{
return false;
}
var comparison = ignore_case ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
return str1.IndexOf(str2, comparison) == 0;
}
public static bool EndsWith(string str1, string str2, bool ignore_case)
{
int l2 = str2.Length;
if (l2 == 0)
return true;
int l1 = str1.Length;
if (l2 > l1)
return false;
var comparison = ignore_case ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
return str1.IndexOf(str2, comparison) == str1.Length - str2.Length - 1;
}
}
class HttpMultipart
{
public class Element
{
public string ContentType;
public string Name;
public string Filename;
public Encoding Encoding;
public long Start;
public long Length;
public override string ToString()
{
return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
Start.ToString() + ", Length " + Length.ToString();
}
}
Stream data;
string boundary;
byte[] boundary_bytes;
byte[] buffer;
bool at_eof;
Encoding encoding;
StringBuilder sb;
const byte HYPHEN = (byte)'-', LF = (byte)'\n', CR = (byte)'\r';
// See RFC 2046
// In the case of multipart entities, in which one or more different
// sets of data are combined in a single body, a "multipart" media type
// field must appear in the entity's header. The body must then contain
// one or more body parts, each preceded by a boundary delimiter line,
// and the last one followed by a closing boundary delimiter line.
// After its boundary delimiter line, each body part then consists of a
// header area, a blank line, and a body area. Thus a body part is
// similar to an RFC 822 message in syntax, but different in meaning.
public HttpMultipart(Stream data, string b, Encoding encoding)
{
this.data = data;
//DB: 30/01/11: cannot set or read the Position in HttpListener in Win.NET
//var ms = new MemoryStream(32 * 1024);
//data.CopyTo(ms);
//this.data = ms;
boundary = b;
boundary_bytes = encoding.GetBytes(b);
buffer = new byte[boundary_bytes.Length + 2]; // CRLF or '--'
this.encoding = encoding;
sb = new StringBuilder();
}
string ReadLine()
{
// CRLF or LF are ok as line endings.
bool got_cr = false;
int b = 0;
sb.Length = 0;
while (true)
{
b = data.ReadByte();
if (b == -1)
{
return null;
}
if (b == LF)
{
break;
}
got_cr = b == CR;
sb.Append((char)b);
}
if (got_cr)
sb.Length--;
return sb.ToString();
}
static string GetContentDispositionAttribute(string l, string name)
{
int idx = l.IndexOf(name + "=\"");
if (idx < 0)
return null;
int begin = idx + name.Length + "=\"".Length;
int end = l.IndexOf('"', begin);
if (end < 0)
return null;
if (begin == end)
return "";
return l.Substring(begin, end - begin);
}
string GetContentDispositionAttributeWithEncoding(string l, string name)
{
int idx = l.IndexOf(name + "=\"");
if (idx < 0)
return null;
int begin = idx + name.Length + "=\"".Length;
int end = l.IndexOf('"', begin);
if (end < 0)
return null;
if (begin == end)
return "";
string temp = l.Substring(begin, end - begin);
byte[] source = new byte[temp.Length];
for (int i = temp.Length - 1; i >= 0; i--)
source[i] = (byte)temp[i];
return encoding.GetString(source, 0, source.Length);
}
bool ReadBoundary()
{
try
{
string line = ReadLine();
while (line == "")
line = ReadLine();
if (line[0] != '-' || line[1] != '-')
return false;
if (!StrUtils.EndsWith(line, boundary, false))
return true;
}
catch
{
}
return false;
}
string ReadHeaders()
{
string s = ReadLine();
if (s == "")
return null;
return s;
}
bool CompareBytes(byte[] orig, byte[] other)
{
for (int i = orig.Length - 1; i >= 0; i--)
if (orig[i] != other[i])
return false;
return true;
}
long MoveToNextBoundary()
{
long retval = 0;
bool got_cr = false;
int state = 0;
int c = data.ReadByte();
while (true)
{
if (c == -1)
return -1;
if (state == 0 && c == LF)
{
retval = data.Position - 1;
if (got_cr)
retval--;
state = 1;
c = data.ReadByte();
}
else if (state == 0)
{
got_cr = c == CR;
c = data.ReadByte();
}
else if (state == 1 && c == '-')
{
c = data.ReadByte();
if (c == -1)
return -1;
if (c != '-')
{
state = 0;
got_cr = false;
continue; // no ReadByte() here
}
int nread = data.Read(buffer, 0, buffer.Length);
int bl = buffer.Length;
if (nread != bl)
return -1;
if (!CompareBytes(boundary_bytes, buffer))
{
state = 0;
data.Position = retval + 2;
if (got_cr)
{
data.Position++;
got_cr = false;
}
c = data.ReadByte();
continue;
}
if (buffer[bl - 2] == '-' && buffer[bl - 1] == '-')
{
at_eof = true;
}
else if (buffer[bl - 2] != CR || buffer[bl - 1] != LF)
{
state = 0;
data.Position = retval + 2;
if (got_cr)
{
data.Position++;
got_cr = false;
}
c = data.ReadByte();
continue;
}
data.Position = retval + 2;
if (got_cr)
data.Position++;
break;
}
else
{
// state == 1
state = 0; // no ReadByte() here
}
}
return retval;
}
public Element ReadNextElement()
{
if (at_eof || ReadBoundary())
return null;
Element elem = new Element();
string header;
while ((header = ReadHeaders()) != null)
{
if (StrUtils.StartsWith(header, "Content-Disposition:", true))
{
elem.Name = GetContentDispositionAttribute(header, "name");
elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename"));
}
else if (StrUtils.StartsWith(header, "Content-Type:", true))
{
elem.ContentType = header.Substring("Content-Type:".Length).Trim();
elem.Encoding = GetEncoding(elem.ContentType);
}
}
long start = 0;
start = data.Position;
elem.Start = start;
long pos = MoveToNextBoundary();
if (pos == -1)
return null;
elem.Length = pos - start;
return elem;
}
static string StripPath(string path)
{
if (path == null || path.Length == 0)
return path;
if (path.IndexOf(":\\") != 1 && !path.StartsWith("\\\\"))
return path;
return path.Substring(path.LastIndexOf('\\') + 1);
}
}
}
}

View File

@@ -0,0 +1,620 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.HttpServer.SocketSharp;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Services;
using SocketHttpListener.Net;
using IHttpFile = MediaBrowser.Model.Services.IHttpFile;
using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest;
using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
using IResponse = MediaBrowser.Model.Services.IResponse;
namespace Emby.Server.Implementations.HttpServer.SocketSharp
{
public partial class WebSocketSharpRequest : IHttpRequest
{
private readonly HttpListenerRequest request;
private readonly IHttpResponse response;
private readonly IMemoryStreamFactory _memoryStreamProvider;
public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, ILogger logger, IMemoryStreamFactory memoryStreamProvider)
{
this.OperationName = operationName;
_memoryStreamProvider = memoryStreamProvider;
this.request = httpContext.Request;
this.response = new WebSocketSharpResponse(logger, httpContext.Response, this);
}
public HttpListenerRequest HttpRequest
{
get { return request; }
}
public object OriginalRequest
{
get { return request; }
}
public IResponse Response
{
get { return response; }
}
public IHttpResponse HttpResponse
{
get { return response; }
}
public string OperationName { get; set; }
public object Dto { get; set; }
public string RawUrl
{
get { return request.RawUrl; }
}
public string AbsoluteUri
{
get { return request.Url.AbsoluteUri.TrimEnd('/'); }
}
public string UserHostAddress
{
get { return request.UserHostAddress; }
}
public string XForwardedFor
{
get
{
return String.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"];
}
}
public int? XForwardedPort
{
get
{
return string.IsNullOrEmpty(request.Headers["X-Forwarded-Port"]) ? (int?)null : int.Parse(request.Headers["X-Forwarded-Port"]);
}
}
public string XForwardedProtocol
{
get
{
return string.IsNullOrEmpty(request.Headers["X-Forwarded-Proto"]) ? null : request.Headers["X-Forwarded-Proto"];
}
}
public string XRealIp
{
get
{
return String.IsNullOrEmpty(request.Headers["X-Real-IP"]) ? null : request.Headers["X-Real-IP"];
}
}
private string remoteIp;
public string RemoteIp
{
get
{
return remoteIp ??
(remoteIp = (CheckBadChars(XForwardedFor)) ??
(NormalizeIp(CheckBadChars(XRealIp)) ??
(request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.IpAddress.ToString()) : null)));
}
}
private static readonly char[] HttpTrimCharacters = new char[] { (char)0x09, (char)0xA, (char)0xB, (char)0xC, (char)0xD, (char)0x20 };
//
// CheckBadChars - throws on invalid chars to be not found in header name/value
//
internal static string CheckBadChars(string name)
{
if (name == null || name.Length == 0)
{
return name;
}
// VALUE check
//Trim spaces from both ends
name = name.Trim(HttpTrimCharacters);
//First, check for correctly formed multi-line value
//Second, check for absenece of CTL characters
int crlf = 0;
for (int i = 0; i < name.Length; ++i)
{
char c = (char)(0x000000ff & (uint)name[i]);
switch (crlf)
{
case 0:
if (c == '\r')
{
crlf = 1;
}
else if (c == '\n')
{
// Technically this is bad HTTP. But it would be a breaking change to throw here.
// Is there an exploit?
crlf = 2;
}
else if (c == 127 || (c < ' ' && c != '\t'))
{
throw new ArgumentException("net_WebHeaderInvalidControlChars");
}
break;
case 1:
if (c == '\n')
{
crlf = 2;
break;
}
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
case 2:
if (c == ' ' || c == '\t')
{
crlf = 0;
break;
}
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
}
}
if (crlf != 0)
{
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
}
return name;
}
internal static bool ContainsNonAsciiChars(string token)
{
for (int i = 0; i < token.Length; ++i)
{
if ((token[i] < 0x20) || (token[i] > 0x7e))
{
return true;
}
}
return false;
}
private string NormalizeIp(string ip)
{
if (!string.IsNullOrWhiteSpace(ip))
{
// Handle ipv4 mapped to ipv6
const string srch = "::ffff:";
var index = ip.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
if (index == 0)
{
ip = ip.Substring(srch.Length);
}
}
return ip;
}
public bool IsSecureConnection
{
get { return request.IsSecureConnection || XForwardedProtocol == "https"; }
}
public string[] AcceptTypes
{
get { return request.AcceptTypes; }
}
private Dictionary<string, object> items;
public Dictionary<string, object> Items
{
get { return items ?? (items = new Dictionary<string, object>()); }
}
private string responseContentType;
public string ResponseContentType
{
get
{
return responseContentType
?? (responseContentType = GetResponseContentType(this));
}
set
{
this.responseContentType = value;
HasExplicitResponseContentType = true;
}
}
public const string FormUrlEncoded = "application/x-www-form-urlencoded";
public const string MultiPartFormData = "multipart/form-data";
private static string GetResponseContentType(IRequest httpReq)
{
var specifiedContentType = GetQueryStringContentType(httpReq);
if (!string.IsNullOrEmpty(specifiedContentType)) return specifiedContentType;
var serverDefaultContentType = "application/json";
var acceptContentTypes = httpReq.AcceptTypes;
var defaultContentType = httpReq.ContentType;
if (HasAnyOfContentTypes(httpReq, FormUrlEncoded, MultiPartFormData))
{
defaultContentType = serverDefaultContentType;
}
var preferredContentTypes = new string[] {};
var acceptsAnything = false;
var hasDefaultContentType = !string.IsNullOrEmpty(defaultContentType);
if (acceptContentTypes != null)
{
var hasPreferredContentTypes = new bool[preferredContentTypes.Length];
foreach (var acceptsType in acceptContentTypes)
{
var contentType = HttpResultFactory.GetRealContentType(acceptsType);
acceptsAnything = acceptsAnything || contentType == "*/*";
for (var i = 0; i < preferredContentTypes.Length; i++)
{
if (hasPreferredContentTypes[i]) continue;
var preferredContentType = preferredContentTypes[i];
hasPreferredContentTypes[i] = contentType.StartsWith(preferredContentType);
//Prefer Request.ContentType if it is also a preferredContentType
if (hasPreferredContentTypes[i] && preferredContentType == defaultContentType)
return preferredContentType;
}
}
for (var i = 0; i < preferredContentTypes.Length; i++)
{
if (hasPreferredContentTypes[i]) return preferredContentTypes[i];
}
if (acceptsAnything)
{
if (hasDefaultContentType)
return defaultContentType;
if (serverDefaultContentType != null)
return serverDefaultContentType;
}
}
if (acceptContentTypes == null && httpReq.ContentType == Soap11)
{
return Soap11;
}
//We could also send a '406 Not Acceptable', but this is allowed also
return serverDefaultContentType;
}
public const string Soap11 = "text/xml; charset=utf-8";
public static bool HasAnyOfContentTypes(IRequest request, params string[] contentTypes)
{
if (contentTypes == null || request.ContentType == null) return false;
foreach (var contentType in contentTypes)
{
if (IsContentType(request, contentType)) return true;
}
return false;
}
public static bool IsContentType(IRequest request, string contentType)
{
return request.ContentType.StartsWith(contentType, StringComparison.OrdinalIgnoreCase);
}
public const string Xml = "application/xml";
private static string GetQueryStringContentType(IRequest httpReq)
{
var format = httpReq.QueryString["format"];
if (format == null)
{
const int formatMaxLength = 4;
var pi = httpReq.PathInfo;
if (pi == null || pi.Length <= formatMaxLength) return null;
if (pi[0] == '/') pi = pi.Substring(1);
format = LeftPart(pi, '/');
if (format.Length > formatMaxLength) return null;
}
format = LeftPart(format, '.').ToLower();
if (format.Contains("json")) return "application/json";
if (format.Contains("xml")) return Xml;
return null;
}
public static string LeftPart(string strVal, char needle)
{
if (strVal == null) return null;
var pos = strVal.IndexOf(needle);
return pos == -1
? strVal
: strVal.Substring(0, pos);
}
public bool HasExplicitResponseContentType { get; private set; }
public static string HandlerFactoryPath;
private string pathInfo;
public string PathInfo
{
get
{
if (this.pathInfo == null)
{
var mode = HandlerFactoryPath;
var pos = request.RawUrl.IndexOf("?");
if (pos != -1)
{
var path = request.RawUrl.Substring(0, pos);
this.pathInfo = GetPathInfo(
path,
mode,
mode ?? "");
}
else
{
this.pathInfo = request.RawUrl;
}
this.pathInfo = WebUtility.UrlDecode(pathInfo);
this.pathInfo = NormalizePathInfo(pathInfo, mode);
}
return this.pathInfo;
}
}
private static string GetPathInfo(string fullPath, string mode, string appPath)
{
var pathInfo = ResolvePathInfoFromMappedPath(fullPath, mode);
if (!string.IsNullOrEmpty(pathInfo)) return pathInfo;
//Wildcard mode relies on this to work out the handlerPath
pathInfo = ResolvePathInfoFromMappedPath(fullPath, appPath);
if (!string.IsNullOrEmpty(pathInfo)) return pathInfo;
return fullPath;
}
private static string ResolvePathInfoFromMappedPath(string fullPath, string mappedPathRoot)
{
if (mappedPathRoot == null) return null;
var sbPathInfo = new StringBuilder();
var fullPathParts = fullPath.Split('/');
var mappedPathRootParts = mappedPathRoot.Split('/');
var fullPathIndexOffset = mappedPathRootParts.Length - 1;
var pathRootFound = false;
for (var fullPathIndex = 0; fullPathIndex < fullPathParts.Length; fullPathIndex++)
{
if (pathRootFound)
{
sbPathInfo.Append("/" + fullPathParts[fullPathIndex]);
}
else if (fullPathIndex - fullPathIndexOffset >= 0)
{
pathRootFound = true;
for (var mappedPathRootIndex = 0; mappedPathRootIndex < mappedPathRootParts.Length; mappedPathRootIndex++)
{
if (!string.Equals(fullPathParts[fullPathIndex - fullPathIndexOffset + mappedPathRootIndex], mappedPathRootParts[mappedPathRootIndex], StringComparison.OrdinalIgnoreCase))
{
pathRootFound = false;
break;
}
}
}
}
if (!pathRootFound) return null;
var path = sbPathInfo.ToString();
return path.Length > 1 ? path.TrimEnd('/') : "/";
}
private Dictionary<string, System.Net.Cookie> cookies;
public IDictionary<string, System.Net.Cookie> Cookies
{
get
{
if (cookies == null)
{
cookies = new Dictionary<string, System.Net.Cookie>();
foreach (var cookie in this.request.Cookies)
{
var httpCookie = (Cookie) cookie;
cookies[httpCookie.Name] = new System.Net.Cookie(httpCookie.Name, httpCookie.Value, httpCookie.Path, httpCookie.Domain);
}
}
return cookies;
}
}
public string UserAgent
{
get { return request.UserAgent; }
}
public QueryParamCollection Headers
{
get { return request.Headers; }
}
private QueryParamCollection queryString;
public QueryParamCollection QueryString
{
get { return queryString ?? (queryString = MyHttpUtility.ParseQueryString(request.Url.Query)); }
}
private QueryParamCollection formData;
public QueryParamCollection FormData
{
get { return formData ?? (formData = this.Form); }
}
public bool IsLocal
{
get { return request.IsLocal; }
}
private string httpMethod;
public string HttpMethod
{
get
{
return httpMethod
?? (httpMethod = request.HttpMethod);
}
}
public string Verb
{
get { return HttpMethod; }
}
public string Param(string name)
{
return Headers[name]
?? QueryString[name]
?? FormData[name];
}
public string ContentType
{
get { return request.ContentType; }
}
public Encoding contentEncoding;
public Encoding ContentEncoding
{
get { return contentEncoding ?? request.ContentEncoding; }
set { contentEncoding = value; }
}
public Uri UrlReferrer
{
get { return request.UrlReferrer; }
}
public static Encoding GetEncoding(string contentTypeHeader)
{
var param = GetParameter(contentTypeHeader, "charset=");
if (param == null) return null;
try
{
return Encoding.GetEncoding(param);
}
catch (ArgumentException)
{
return null;
}
}
public Stream InputStream
{
get { return request.InputStream; }
}
public long ContentLength
{
get { return request.ContentLength64; }
}
private IHttpFile[] httpFiles;
public IHttpFile[] Files
{
get
{
if (httpFiles == null)
{
if (files == null)
return httpFiles = new IHttpFile[0];
httpFiles = new IHttpFile[files.Count];
var i = 0;
foreach (var pair in files)
{
var reqFile = pair.Value;
httpFiles[i] = new HttpFile
{
ContentType = reqFile.ContentType,
ContentLength = reqFile.ContentLength,
FileName = reqFile.FileName,
InputStream = reqFile.InputStream,
};
i++;
}
}
return httpFiles;
}
}
static Stream GetSubStream(Stream stream, IMemoryStreamFactory streamProvider)
{
if (stream is MemoryStream)
{
var other = (MemoryStream)stream;
byte[] buffer;
if (streamProvider.TryGetBuffer(other, out buffer))
{
return streamProvider.CreateNew(buffer);
}
return streamProvider.CreateNew(other.ToArray());
}
return stream;
}
public static string GetHandlerPathIfAny(string listenerUrl)
{
if (listenerUrl == null) return null;
var pos = listenerUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase);
if (pos == -1) return null;
var startHostUrl = listenerUrl.Substring(pos + "://".Length);
var endPos = startHostUrl.IndexOf('/');
if (endPos == -1) return null;
var endHostUrl = startHostUrl.Substring(endPos + 1);
return String.IsNullOrEmpty(endHostUrl) ? null : endHostUrl.TrimEnd('/');
}
public static string NormalizePathInfo(string pathInfo, string handlerPath)
{
if (handlerPath != null && pathInfo.TrimStart('/').StartsWith(
handlerPath, StringComparison.OrdinalIgnoreCase))
{
return pathInfo.TrimStart('/').Substring(handlerPath.Length);
}
return pathInfo;
}
}
public class HttpFile : IHttpFile
{
public string Name { get; set; }
public string FileName { get; set; }
public long ContentLength { get; set; }
public string ContentType { get; set; }
public Stream InputStream { get; set; }
}
}