http server: change uri query handling
parent
1c6d454691
commit
0d940df4e2
|
@ -120,15 +120,20 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
|
||||
public NameValueCollection QueryString
|
||||
{
|
||||
get { return _queryString; }
|
||||
get { return _request.QueryString;}
|
||||
}
|
||||
private NameValueCollection _queryString;
|
||||
|
||||
public Dictionary<string,string> Query
|
||||
{
|
||||
get { return _query; }
|
||||
get
|
||||
{
|
||||
if (_queryKeyValues == null)
|
||||
BuildQueryDictionary();
|
||||
return _queryKeyValues;
|
||||
}
|
||||
}
|
||||
private Dictionary<string, string> _query;
|
||||
|
||||
private Dictionary<string, string> _queryKeyValues = null;
|
||||
|
||||
/// <value>
|
||||
/// POST request values, if applicable
|
||||
|
@ -190,7 +195,7 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
{
|
||||
_contentEncoding = Encoding.GetEncoding(_request.Headers["content-encoding"]);
|
||||
}
|
||||
catch (Exception)
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
@ -224,29 +229,6 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
}
|
||||
}
|
||||
|
||||
_queryString = new NameValueCollection();
|
||||
_query = new Dictionary<string, string>();
|
||||
try
|
||||
{
|
||||
foreach (HttpInputItem item in req.QueryString)
|
||||
{
|
||||
try
|
||||
{
|
||||
_queryString.Add(item.Name, item.Value);
|
||||
_query[item.Name] = item.Value;
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
_log.DebugFormat("[OSHttpRequest]: error parsing {0} query item, skipping it", item.Name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_log.ErrorFormat("[OSHttpRequest]: Error parsing querystring");
|
||||
}
|
||||
|
||||
// Form = new Hashtable();
|
||||
// foreach (HttpInputItem item in req.Form)
|
||||
// {
|
||||
|
@ -255,6 +237,22 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
// }
|
||||
}
|
||||
|
||||
private void BuildQueryDictionary()
|
||||
{
|
||||
NameValueCollection q = _request.QueryString;
|
||||
_queryKeyValues = new Dictionary<string, string>(); // only key value pairs
|
||||
for(int i = 0; i <q.Count; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
var name = q.GetKey(i);
|
||||
if(!string.IsNullOrEmpty(name))
|
||||
_queryKeyValues[name] = q[i];
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder me = new StringBuilder();
|
||||
|
|
|
@ -314,7 +314,13 @@ namespace OSHttpServer
|
|||
if (m_stream != null)
|
||||
{
|
||||
if (error == SocketError.Success)
|
||||
m_stream.Flush(); // we should be on a work task
|
||||
{
|
||||
try
|
||||
{
|
||||
m_stream.Flush(); // we should be on a work task so hold on it
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
m_stream.Close();
|
||||
m_stream = null;
|
||||
}
|
||||
|
@ -344,7 +350,7 @@ namespace OSHttpServer
|
|||
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
Disconnect(SocketError.ConnectionReset);
|
||||
Disconnect(SocketError.Success);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -394,7 +400,8 @@ namespace OSHttpServer
|
|||
{
|
||||
LogWriter.Write(this, LogPrio.Fatal, "Failed to reply to a bad request. " + err2);
|
||||
}
|
||||
Disconnect(SocketError.NoRecovery);
|
||||
//Disconnect(SocketError.NoRecovery);
|
||||
Disconnect(SocketError.Success); // try to flush
|
||||
}
|
||||
catch (IOException err)
|
||||
{
|
||||
|
@ -434,6 +441,21 @@ namespace OSHttpServer
|
|||
if(--m_maxRequests == 0)
|
||||
m_currentRequest.Connection = ConnectionType.Close;
|
||||
|
||||
if(m_currentRequest.Uri == null)
|
||||
{
|
||||
// should not happen
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(m_currentRequest.Secure ? "https://" : "http://" + m_currentRequest.UriPath);
|
||||
m_currentRequest.Uri = uri;
|
||||
m_currentRequest.UriPath = uri.AbsolutePath;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// load cookies if they exist
|
||||
if(m_currentRequest.Headers["cookie"] != null)
|
||||
m_currentRequest.SetCookies(new RequestCookies(m_currentRequest.Headers["cookie"]));
|
||||
|
|
|
@ -86,7 +86,6 @@ namespace OSHttpServer
|
|||
return;
|
||||
|
||||
m_activeContexts.TryRemove(imp.contextID, out HttpClientContext dummy);
|
||||
|
||||
imp.Close();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
using System;
|
||||
using System.Web;
|
||||
|
||||
namespace OSHttpServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic helper functions for HTTP
|
||||
/// </summary>
|
||||
public static class HttpHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty URI
|
||||
/// </summary>
|
||||
public static readonly Uri EmptyUri = new Uri("http://localhost/");
|
||||
|
||||
/// <summary>
|
||||
/// Parses a query string.
|
||||
/// </summary>
|
||||
/// <param name="queryString">Query string (URI encoded)</param>
|
||||
/// <returns>A <see cref="HttpInput"/> object if successful; otherwise <see cref="HttpInput.Empty"/></returns>
|
||||
/// <exception cref="ArgumentNullException"><c>queryString</c> is null.</exception>
|
||||
/// <exception cref="FormatException">If string cannot be parsed.</exception>
|
||||
public static HttpInput ParseQueryString(string queryString)
|
||||
{
|
||||
if (queryString == null)
|
||||
throw new ArgumentNullException("queryString");
|
||||
if (queryString == string.Empty)
|
||||
return HttpInput.Empty;
|
||||
|
||||
HttpInput input = new HttpInput("QueryString");
|
||||
|
||||
queryString = queryString.TrimStart('?', '&');
|
||||
|
||||
// a simple value.
|
||||
if (queryString.IndexOf("&") == -1 && !queryString.Contains("%3d") && !queryString.Contains("%3D") && !queryString.Contains("="))
|
||||
{
|
||||
input.Add(string.Empty, queryString);
|
||||
return input;
|
||||
}
|
||||
|
||||
int state = 0;
|
||||
int startpos = 0;
|
||||
string name = null;
|
||||
for (int i = 0; i < queryString.Length; ++i)
|
||||
{
|
||||
int newIndexPos;
|
||||
if (state == 0 && IsEqual(queryString, ref i, out newIndexPos))
|
||||
{
|
||||
name = queryString.Substring(startpos, i - startpos);
|
||||
i = newIndexPos;
|
||||
startpos = i + 1;
|
||||
++state;
|
||||
}
|
||||
else if (state == 1 && IsAmp(queryString, ref i, out newIndexPos))
|
||||
{
|
||||
Add(input, name, queryString.Substring(startpos, i - startpos));
|
||||
i = newIndexPos;
|
||||
startpos = i + 1;
|
||||
state = 0;
|
||||
name = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == 0 && !input.GetEnumerator().MoveNext())
|
||||
throw new FormatException("Not a valid query string: " + queryString);
|
||||
|
||||
if (startpos <= queryString.Length)
|
||||
{
|
||||
if (name != null)
|
||||
Add(input, name, queryString.Substring(startpos, queryString.Length - startpos));
|
||||
else
|
||||
Add(input, string.Empty, queryString.Substring(startpos, queryString.Length - startpos));
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
private static bool IsEqual(string queryStr, ref int index, out int outIndex)
|
||||
{
|
||||
outIndex = index;
|
||||
if (queryStr[index] == '=')
|
||||
return true;
|
||||
if (queryStr[index] == '%' && queryStr.Length > index + 2 && queryStr[index + 1] == '3'
|
||||
&& (queryStr[index + 2] == 'd' || queryStr[index + 2] == 'D'))
|
||||
{
|
||||
outIndex += 2;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsAmp(string queryStr, ref int index, out int outIndex)
|
||||
{
|
||||
outIndex = index;
|
||||
if (queryStr[index] == '%' && queryStr.Length > index + 2 && queryStr[index + 1] == '2' &&
|
||||
queryStr[index + 2] == '6')
|
||||
outIndex += 2;
|
||||
else if (queryStr[index] == '&')
|
||||
{
|
||||
if (queryStr.Length > index + 4
|
||||
&& (queryStr[index + 1] == 'a' || queryStr[index + 1] == 'A')
|
||||
&& (queryStr[index + 2] == 'm' || queryStr[index + 2] == 'M')
|
||||
&& (queryStr[index + 3] == 'p' || queryStr[index + 3] == 'P')
|
||||
&& queryStr[index + 4] == ';')
|
||||
outIndex += 4;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void Add(IHttpInput input, string name, string value)
|
||||
{
|
||||
input.Add(HttpUtility.UrlDecode(name), HttpUtility.UrlDecode(value));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using OSHttpServer.Exceptions;
|
||||
|
||||
|
||||
|
@ -26,8 +27,8 @@ namespace OSHttpServer
|
|||
private int m_contentLength;
|
||||
private string m_httpVersion = string.Empty;
|
||||
private string m_method = string.Empty;
|
||||
private HttpInput m_queryString = HttpInput.Empty;
|
||||
private Uri m_uri = HttpHelper.EmptyUri;
|
||||
private NameValueCollection m_queryString = null;
|
||||
private Uri m_uri = null;
|
||||
private string m_uriPath;
|
||||
public readonly IHttpClientContext m_context;
|
||||
|
||||
|
@ -42,7 +43,7 @@ namespace OSHttpServer
|
|||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="HttpRequest"/> is secure.
|
||||
/// </summary>
|
||||
public bool Secure { get; internal set; }
|
||||
public bool Secure { get { return m_context.IsSecured; } }
|
||||
|
||||
public IHttpClientContext Context { get { return m_context; } }
|
||||
/// <summary>
|
||||
|
@ -52,24 +53,7 @@ namespace OSHttpServer
|
|||
public string UriPath
|
||||
{
|
||||
get { return m_uriPath; }
|
||||
set
|
||||
{
|
||||
m_uriPath = value;
|
||||
int pos = m_uriPath.IndexOf('?');
|
||||
if (pos != -1)
|
||||
{
|
||||
m_queryString = HttpHelper.ParseQueryString(m_uriPath.Substring(pos + 1));
|
||||
m_param.SetQueryString(m_queryString);
|
||||
string path = m_uriPath.Substring(0, pos);
|
||||
m_uriPath = System.Web.HttpUtility.UrlDecode(path) + "?" + m_uriPath.Substring(pos + 1);
|
||||
UriParts = value.Substring(0, pos).Split(UriSplitters, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_uriPath = System.Web.HttpUtility.UrlDecode(m_uriPath);
|
||||
UriParts = value.Split(UriSplitters, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
}
|
||||
set { m_uriPath = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -85,14 +69,6 @@ namespace OSHttpServer
|
|||
|
||||
#region IHttpRequest Members
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the body is complete.
|
||||
/// </summary>
|
||||
public bool BodyIsComplete
|
||||
{
|
||||
get { return m_bodyBytesLeft == 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets kind of types accepted by the client.
|
||||
/// </summary>
|
||||
|
@ -167,40 +143,38 @@ namespace OSHttpServer
|
|||
/// <summary>
|
||||
/// Gets variables sent in the query string
|
||||
/// </summary>
|
||||
public HttpInput QueryString
|
||||
public NameValueCollection QueryString
|
||||
{
|
||||
get { return m_queryString; }
|
||||
get
|
||||
{
|
||||
if(m_queryString == null)
|
||||
{
|
||||
if(m_uri == null || m_uri.Query.Length == 0)
|
||||
m_queryString = new NameValueCollection();
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
m_queryString = HttpUtility.ParseQueryString(m_uri.Query);
|
||||
}
|
||||
catch { m_queryString = new NameValueCollection(); }
|
||||
}
|
||||
}
|
||||
|
||||
return m_queryString;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static readonly Uri EmptyUri = new Uri("http://localhost/");
|
||||
/// <summary>
|
||||
/// Gets or sets requested URI.
|
||||
/// </summary>
|
||||
public Uri Uri
|
||||
{
|
||||
get { return m_uri; }
|
||||
set
|
||||
{
|
||||
m_uri = value ?? HttpHelper.EmptyUri;
|
||||
UriParts = m_uri.AbsolutePath.Split(UriSplitters, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
set { m_uri = value ?? EmptyUri; } // not safe
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uri absolute path splitted into parts.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// // uri is: http://gauffin.com/code/tiny/
|
||||
/// Console.WriteLine(request.UriParts[0]); // result: code
|
||||
/// Console.WriteLine(request.UriParts[1]); // result: tiny
|
||||
/// </example>
|
||||
/// <remarks>
|
||||
/// If you're using controllers than the first part is controller name,
|
||||
/// the second part is method name and the third part is Id property.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Uri"/>
|
||||
public string[] UriParts { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets parameter from <see cref="QueryString"/> or <see cref="Form"/>.
|
||||
/// </summary>
|
||||
|
@ -341,7 +315,7 @@ namespace OSHttpServer
|
|||
try
|
||||
{
|
||||
m_uri = new Uri(Secure ? "https://" : "http://" + value + m_uriPath);
|
||||
UriParts = m_uri.AbsolutePath.Split(UriSplitters, StringSplitOptions.RemoveEmptyEntries);
|
||||
m_uriPath = m_uri.AbsolutePath;
|
||||
}
|
||||
catch (UriFormatException err)
|
||||
{
|
||||
|
@ -421,8 +395,8 @@ namespace OSHttpServer
|
|||
m_body = null;
|
||||
m_contentLength = 0;
|
||||
m_method = string.Empty;
|
||||
m_uri = HttpHelper.EmptyUri;
|
||||
m_queryString = HttpInput.Empty;
|
||||
m_uri = null;
|
||||
m_queryString = null;
|
||||
m_bodyBytesLeft = 0;
|
||||
m_headers.Clear();
|
||||
m_connection = ConnectionType.KeepAlive;
|
||||
|
|
|
@ -123,12 +123,15 @@ namespace OSHttpServer.Parser
|
|||
string path = value.Substring(oldPos, pos - oldPos);
|
||||
if (path.Length > 4196)
|
||||
throw new BadRequestException("Too long URI.");
|
||||
if (path == "*")
|
||||
throw new BadRequestException("Not supported URI.");
|
||||
|
||||
if (pos + 1 >= value.Length)
|
||||
{
|
||||
m_log.Write(this, LogPrio.Warning, "Invalid request line, missing HTTP-Version. Line: " + value);
|
||||
throw new BadRequestException("Invalid request line, missing HTTP-Version. Line: " + value);
|
||||
}
|
||||
|
||||
string version = value.Substring(pos + 1);
|
||||
if (version.Length < 4 || string.Compare(version.Substring(0, 4), "HTTP", true) != 0)
|
||||
{
|
||||
|
|
|
@ -21,11 +21,6 @@ namespace OSHttpServer
|
|||
/// </summary>
|
||||
Stream Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the body is complete.
|
||||
/// </summary>
|
||||
bool BodyIsComplete { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets kind of connection used for the session.
|
||||
/// </summary>
|
||||
|
@ -84,28 +79,13 @@ namespace OSHttpServer
|
|||
/// <summary>
|
||||
/// Gets variables sent in the query string
|
||||
/// </summary>
|
||||
HttpInput QueryString { get; }
|
||||
NameValueCollection QueryString { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets requested URI.
|
||||
/// </summary>
|
||||
Uri Uri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets URI absolute path divided into parts.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// // URI is: http://gauffin.com/code/tiny/
|
||||
/// Console.WriteLine(request.UriParts[0]); // result: code
|
||||
/// Console.WriteLine(request.UriParts[1]); // result: tiny
|
||||
/// </example>
|
||||
/// <remarks>
|
||||
/// If you're using controllers than the first part is controller name,
|
||||
/// the second part is method name and the third part is Id property.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Uri"/>
|
||||
string[] UriParts { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets path and query.
|
||||
/// </summary>
|
||||
|
|
Loading…
Reference in New Issue