http server: change uri query handling

master
UbitUmarov 2020-04-11 15:18:49 +01:00
parent 1c6d454691
commit 0d940df4e2
7 changed files with 84 additions and 226 deletions

View File

@ -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();

View File

@ -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"]));

View File

@ -86,7 +86,6 @@ namespace OSHttpServer
return;
m_activeContexts.TryRemove(imp.contextID, out HttpClientContext dummy);
imp.Close();
}

View File

@ -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));
}
}
}

View File

@ -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;

View File

@ -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)
{

View File

@ -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>