several changes to llhttprequest like use HttpClient

This commit is contained in:
UbitUmarov
2023-05-06 18:06:40 +01:00
parent 9fa29c382c
commit 83491db916
3 changed files with 307 additions and 334 deletions

View File

@@ -40,6 +40,8 @@ using OpenSim.Framework.Monitoring;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using Mono.Addins;
using System.Net.Http;
using System.Security.Authentication;
/*****************************************************
*
@@ -64,14 +66,16 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly object m_mainLock = new object();
private static HttpClient VeriFyCertClient = null;
private static HttpClient VeriFyNoCertClient = null;
private static readonly object m_mainLock = new();
private static int m_numberScenes;
private static int m_httpTimeout = 30000;
private static readonly string m_name = "HttpScriptRequests";
private static OutboundUrlFilter m_outboundUrlFilter;
private static string m_proxyurl = "";
private static string m_proxyexcepts = "";
private static int m_HttpBodyMaxLenMAX = 16384;
private static float m_primPerSec = 1.0f;
private static float m_primBurst = 3.0f;
@@ -82,10 +86,9 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
private static Dictionary<UUID, HttpRequestClass> m_pendingRequests;
//this are per region/module
private readonly ConcurrentQueue<HttpRequestClass> m_CompletedRequests = new ConcurrentQueue<HttpRequestClass>();
private readonly ConcurrentDictionary<uint, ThrottleData> m_RequestsThrottle = new ConcurrentDictionary<uint, ThrottleData>();
private readonly ConcurrentDictionary<UUID, ThrottleData> m_OwnerRequestsThrottle = new ConcurrentDictionary<UUID, ThrottleData>();
private readonly ConcurrentQueue<HttpRequestClass> m_CompletedRequests = new();
private readonly ConcurrentDictionary<uint, ThrottleData> m_RequestsThrottle = new();
private readonly ConcurrentDictionary<UUID, ThrottleData> m_OwnerRequestsThrottle = new();
public HttpRequestModule()
{
@@ -98,29 +101,115 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
lock (m_mainLock)
{
// shared items
if (m_jobEngine == null)
if (m_jobEngine is null)
{
m_proxyurl = config.Configs["Startup"].GetString("HttpProxy");
m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions");
WebProxy proxy = null;
string proxyurl = config.Configs["Startup"].GetString("HttpProxy");
if (!string.IsNullOrEmpty(proxyurl))
{
string[] proxyexceptsArray = null;
string proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions");
if (!string.IsNullOrEmpty(proxyexcepts))
{
proxyexceptsArray = proxyexcepts.Split(';');
if(proxyexceptsArray.Length == 0)
proxyexceptsArray = null;
}
proxy = proxyexceptsArray is null ?
new WebProxy(proxyurl, true) :
new WebProxy(proxyurl, true, proxyexceptsArray);
}
HttpRequestClass.HttpBodyMaxLenMAX = config.Configs["Network"].GetInt("HttpBodyMaxLenMAX", 16384);
m_HttpBodyMaxLenMAX = config.Configs["Network"].GetInt("HttpBodyMaxLenMAX", m_HttpBodyMaxLenMAX);
m_outboundUrlFilter = new OutboundUrlFilter("Script HTTP request module", config);
int maxThreads = 8;
IConfig httpConfig = config.Configs["ScriptsHttpRequestModule"];
if (httpConfig != null)
int httpTimeout = 30000;
if (httpConfig is not null)
{
maxThreads = httpConfig.GetInt("MaxPoolThreads", maxThreads);
m_primBurst = httpConfig.GetFloat("PrimRequestsBurst", m_primBurst);
m_primPerSec = httpConfig.GetFloat("PrimRequestsPerSec", m_primPerSec);
m_primOwnerBurst = httpConfig.GetFloat("PrimOwnerRequestsBurst", m_primOwnerBurst);
m_primOwnerPerSec = httpConfig.GetFloat("PrimOwnerRequestsPerSec", m_primOwnerPerSec);
m_httpTimeout = httpConfig.GetInt("RequestsTimeOut", m_httpTimeout);
if (m_httpTimeout > 60000)
m_httpTimeout = 60000;
else if (m_httpTimeout < 200)
m_httpTimeout = 200;
httpTimeout = httpConfig.GetInt("RequestsTimeOut", httpTimeout);
if (httpTimeout > 60000)
httpTimeout = 60000;
else if (httpTimeout < 200)
httpTimeout = 200;
}
if (VeriFyNoCertClient is null)
{
SocketsHttpHandler shhnc = new()
{
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.None,
ConnectTimeout = TimeSpan.FromMilliseconds(httpTimeout),
PreAuthenticate = false,
UseCookies = false,
MaxConnectionsPerServer = maxThreads < 10 ? maxThreads : 10,
PooledConnectionLifetime = TimeSpan.FromMinutes(3)
};
//shhnc.SslOptions.ClientCertificates = null,
shhnc.SslOptions.EnabledSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13;
shhnc.SslOptions.CertificateRevocationCheckMode = X509RevocationMode.NoCheck;
shhnc.SslOptions.RemoteCertificateValidationCallback = (message, cert, chain, errors) =>
{
errors &= ~(SslPolicyErrors.RemoteCertificateChainErrors | SslPolicyErrors.RemoteCertificateNameMismatch);
return errors == SslPolicyErrors.None;
};
if (proxy is null)
shhnc.UseProxy = false;
else
{
shhnc.Proxy = proxy;
shhnc.UseProxy = true;
}
VeriFyNoCertClient = new HttpClient(shhnc)
{
Timeout = TimeSpan.FromMilliseconds(httpTimeout),
};
VeriFyNoCertClient.DefaultRequestHeaders.ExpectContinue = false;
VeriFyNoCertClient.DefaultRequestHeaders.ConnectionClose = true;
}
if (VeriFyCertClient is null)
{
SocketsHttpHandler shh = new()
{
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.None,
ConnectTimeout = TimeSpan.FromMilliseconds((double)httpTimeout),
PreAuthenticate = false,
UseCookies = false,
MaxConnectionsPerServer = maxThreads < 10 ? maxThreads : 10,
PooledConnectionLifetime = TimeSpan.FromMinutes(3)
};
//shhnc.SslOptions.ClientCertificates = null,
shh.SslOptions.EnabledSslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13;
shh.SslOptions.CertificateRevocationCheckMode = X509RevocationMode.NoCheck;
shh.SslOptions.RemoteCertificateValidationCallback = (message, cert, chain, errors) =>
{
errors &= ~SslPolicyErrors.RemoteCertificateChainErrors;
return errors == SslPolicyErrors.None;
};
if (proxy is null)
shh.UseProxy = false;
else
{
shh.Proxy = proxy;
shh.UseProxy = true;
}
VeriFyCertClient = new HttpClient(shh)
{
Timeout = TimeSpan.FromMilliseconds(httpTimeout)
};
VeriFyCertClient.DefaultRequestHeaders.ExpectContinue = false;
VeriFyCertClient.DefaultRequestHeaders.ConnectionClose = true;
}
m_pendingRequests = new Dictionary<UUID, HttpRequestClass>();
@@ -157,11 +246,15 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
{
lock(m_mainLock)
{
if (m_jobEngine != null)
if (m_jobEngine is not null)
{
m_jobEngine.Stop();
m_jobEngine = null;
}
VeriFyCertClient?.Dispose();
VeriFyCertClient = null;
VeriFyNoCertClient?.Dispose();
VeriFyNoCertClient = null;
}
}
}
@@ -185,6 +278,11 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
return UUID.Zero;
}
public HttpClient GetHttpClient(bool verify)
{
return verify ? VeriFyCertClient : VeriFyNoCertClient;
}
public bool CheckThrottle(uint localID, UUID ownerID)
{
double now = Util.GetTimeStamp();
@@ -252,47 +350,35 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
return ret;
}
public UUID StartHttpRequest(
uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body,
out HttpInitialRequestStatus status)
public UUID StartHttpRequest(uint localID, UUID itemID, string url,
List<string> parameters, Dictionary<string, string> headers, string body)
{
if (!CheckAllowed(new Uri(url)))
{
status = HttpInitialRequestStatus.DISALLOWED_BY_FILTER;
return UUID.Zero;
}
UUID reqID = UUID.Random();
HttpRequestClass htc = new HttpRequestClass();
HttpRequestClass htc = new();
// Partial implementation: support for parameter flags needed
// see http://wiki.secondlife.com/wiki/LlHTTPRequest
//
// Parameters are expected in {key, value, ... , key, value}
if (parameters != null)
if (parameters is not null)
{
string[] parms = parameters.ToArray();
for (int i = 0; i < parms.Length; i += 2)
for (int i = 0; i < parameters.Count; i += 2)
{
switch (Int32.Parse(parms[i]))
switch (Int32.Parse(parameters[i]))
{
case (int)HttpRequestConstants.HTTP_METHOD:
htc.HttpMethod = parms[i + 1];
htc.HttpMethod = parameters[i + 1];
break;
case (int)HttpRequestConstants.HTTP_MIMETYPE:
htc.HttpMIMEType = parms[i + 1];
htc.HttpMIMEType = parameters[i + 1];
break;
case (int)HttpRequestConstants.HTTP_BODY_MAXLENGTH:
int len;
if(int.TryParse(parms[i + 1], out len))
if(int.TryParse(parameters[i + 1], out int len))
{
if(len > HttpRequestClass.HttpBodyMaxLenMAX)
len = HttpRequestClass.HttpBodyMaxLenMAX;
if(len > m_HttpBodyMaxLenMAX)
len = m_HttpBodyMaxLenMAX;
else if(len < 64) //???
len = 64;
htc.HttpBodyMaxLen = len;
@@ -300,15 +386,14 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
break;
case (int)HttpRequestConstants.HTTP_VERIFY_CERT:
htc.HttpVerifyCert = (int.Parse(parms[i + 1]) != 0);
htc.HttpVerifyCert = (int.Parse(parameters[i + 1]) != 0);
break;
case (int)HttpRequestConstants.HTTP_VERBOSE_THROTTLE:
// TODO implement me
break;
case (int)HttpRequestConstants.HTTP_CUSTOM_HEADER:
// should not happen
//Parameters are in pairs and custom header takes
//arguments in pairs so adjust for header marker.
++i;
@@ -318,16 +403,11 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
for (int count = 1; count <= 8; ++count)
{
//Not enough parameters remaining for a header?
if (parms.Length - i < 2)
break;
if (parameters.Count - i < 2)
break;
if (htc.HttpCustomHeaders == null)
htc.HttpCustomHeaders = new List<string>();
htc.HttpCustomHeaders.Add(parms[i]);
htc.HttpCustomHeaders.Add(parms[i+1]);
int nexti = i + 2;
if (nexti >= parms.Length || Char.IsDigit(parms[nexti][0]))
if (nexti >= parameters.Count || Char.IsDigit(parameters[nexti][0]))
break;
i = nexti;
@@ -335,7 +415,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
break;
case (int)HttpRequestConstants.HTTP_PRAGMA_NO_CACHE:
htc.HttpPragmaNoCache = (int.Parse(parms[i + 1]) != 0);
htc.HttpPragmaNoCache = (int.Parse(parameters[i + 1]) != 0);
break;
}
}
@@ -346,20 +426,13 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
htc.ItemID = itemID;
htc.Url = url;
htc.ReqID = reqID;
htc.HttpTimeout = m_httpTimeout;
htc.OutboundBody = body;
htc.ResponseHeaders = headers;
htc.proxyurl = m_proxyurl;
htc.proxyexcepts = m_proxyexcepts;
// Same number as default HttpWebRequest.MaximumAutomaticRedirections
htc.MaxRedirects = 50;
lock (m_mainLock)
m_pendingRequests.Add(reqID, htc);
htc.Process();
status = HttpInitialRequestStatus.OK;
return reqID;
}
@@ -438,44 +511,60 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
public class HttpRequestClass : IServiceRequest
{
// Constants for parameters
// public const int HTTP_BODY_MAXLENGTH = 2;
// public const int HTTP_METHOD = 0;
// public const int HTTP_MIMETYPE = 1;
// public const int HTTP_VERIFY_CERT = 3;
// public const int HTTP_VERBOSE_THROTTLE = 4;
// public const int HTTP_CUSTOM_HEADER = 5;
// public const int HTTP_PRAGMA_NO_CACHE = 6;
private static readonly string[] s_wellKnownContentHeaders = {
"Content-Disposition",
"Content-Encoding",
"Content-Language",
"Content-Length",
"Content-Location",
"Content-MD5",
"Content-Range",
"Content-Type",
"Expires",
"Last-Modified"
};
private bool IsWellKnownContentHeader(string header)
{
foreach (string contentHeaderName in s_wellKnownContentHeaders)
{
if (string.Equals(header, contentHeaderName, StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
private void AddHeader(string headerName, string value, HttpRequestMessage request)
{
if (IsWellKnownContentHeader(headerName))
{
request.Content ??= new ByteArrayContent(Array.Empty<byte>());
request.Content.Headers.TryAddWithoutValidation(headerName, value);
}
else
request.Headers.TryAddWithoutValidation(headerName, value);
}
/// <summary>
/// Module that made this request.
/// </summary>
public HttpRequestModule RequestModule { get; set; }
public bool Finished { get; private set;}
public bool HttpVerifyCert = true;
public bool Removed;
public static int HttpBodyMaxLenMAX = 16384;
// Parameter members and default values
public int HttpBodyMaxLen = 2048;
public string HttpMethod = "GET";
public string HttpMIMEType = "text/plain;charset=utf-8";
public int HttpTimeout;
public bool HttpVerifyCert = true;
//public bool HttpVerboseThrottle = true; // not implemented
public List<string> HttpCustomHeaders = null;
public bool HttpPragmaNoCache = true;
public bool HttpPragmaNoCache = false;
// Request info
public bool Finished { get; }
public UUID ReqID { get; set; }
public UUID ItemID { get; set;}
public uint LocalID { get; set;}
public string proxyurl;
public string proxyexcepts;
/// <summary>
/// Number of HTTP redirects that this request has been through.
/// </summary>
@@ -484,12 +573,11 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
/// <summary>
/// Maximum number of HTTP redirects allowed for this request.
/// </summary>
public int MaxRedirects { get; set; }
public int MaxRedirects { get; set; } = 10;
public string OutboundBody;
public string ResponseBody;
public List<string> ResponseMetadata;
public Dictionary<string, string> ResponseHeaders;
public int Status;
public string Url;
@@ -499,198 +587,97 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
HttpRequestModule.m_jobEngine?.QueueJob("", SendRequest);
}
public static bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
// If this is a web request we need to check the headers first
// We may want to ignore SSL
if (sender is HttpWebRequest)
{
HttpWebRequest Request = sender as HttpWebRequest;
// We don't case about encryption, get out of here
if (Request.Headers.Get("NoVerifyCert") != null)
return true;
}
if ((((int)sslPolicyErrors) & ~4) != 0)
return false;
return true;
}
/*
* TODO: More work on the response codes. Right now
* returning 200 for success or 499 for exception
*/
public void SendRequest()
{
if(Removed)
if (Removed)
return;
HttpWebRequest Request;
HttpWebResponse response = null;
Stream resStream = null;
HttpResponseMessage responseMessage = null;
HttpRequestMessage request = null;
try
{
Request = (HttpWebRequest)WebRequest.Create(Url);
Request.ServerCertificateValidationCallback = ValidateServerCertificate;
HttpClient client = RequestModule.GetHttpClient(HttpVerifyCert);
request = new (new HttpMethod(HttpMethod), Url);
Request.AllowAutoRedirect = false;
Request.KeepAlive = false;
Request.Timeout = HttpTimeout;
//This works around some buggy HTTP Servers like Lighttpd
Request.ServicePoint.Expect100Continue = false;
Request.Method = HttpMethod;
Request.ContentType = HttpMIMEType;
if (!HttpVerifyCert)
{
// We could hijack Connection Group Name to identify
// a desired security exception. But at the moment we'll use a dummy header instead.
Request.Headers.Add("NoVerifyCert", "true");
}
// else
// {
// Request.ConnectionGroupName="Verify";
// }
if (!HttpPragmaNoCache)
{
Request.Headers.Add("Pragma", "no-cache");
}
if (HttpCustomHeaders != null)
{
for (int i = 0; i < HttpCustomHeaders.Count; i += 2)
Request.Headers.Add(HttpCustomHeaders[i],
HttpCustomHeaders[i+1]);
}
if (!string.IsNullOrEmpty(proxyurl))
{
if (!string.IsNullOrEmpty(proxyexcepts))
{
string[] elist = proxyexcepts.Split(';');
Request.Proxy = new WebProxy(proxyurl, true, elist);
}
else
{
Request.Proxy = new WebProxy(proxyurl, true);
}
}
foreach (KeyValuePair<string, string> entry in ResponseHeaders)
if (entry.Key.ToLower().Equals("user-agent"))
Request.UserAgent = entry.Value;
else
Request.Headers[entry.Key] = entry.Value;
if (Removed)
return;
// Encode outbound data
int datalen;
if (!string.IsNullOrEmpty(OutboundBody))
{
byte[] data = Util.UTF8.GetBytes(OutboundBody);
Request.ContentLength = data.Length;
using (Stream bstream = Request.GetRequestStream())
bstream.Write(data, 0, data.Length);
data = null;
}
if (Removed)
return;
try
{
// execute the request
response = (HttpWebResponse) Request.GetResponse();
}
catch (WebException e)
{
if (e.Status != WebExceptionStatus.ProtocolError)
{
throw;
}
response = (HttpWebResponse)e.Response;
}
if (Removed)
return;
Status = (int)response.StatusCode;
byte[] buf = new byte[HttpBodyMaxLenMAX + 16];
int count;
resStream = response.GetResponseStream();
int totalBodyBytes = 0;
int maxBytes = HttpBodyMaxLen;
if(maxBytes > buf.Length)
maxBytes = buf.Length;
// we need to read all allowed or UFT8 conversion may fail
do
{
// fill the buffer with data
count = resStream.Read(buf, totalBodyBytes, maxBytes - totalBodyBytes);
totalBodyBytes += count;
if (totalBodyBytes >= maxBytes)
break;
} while (count > 0); // any more data to read?
if(totalBodyBytes > 0)
{
string tempString = Util.UTF8.GetString(buf, 0, totalBodyBytes);
ResponseBody = tempString.Replace("\r", "");
datalen = data.Length;
request.Content = new ByteArrayContent(data);
}
else
ResponseBody = "";
}
catch (WebException e)
{
if (e.Status == WebExceptionStatus.ProtocolError)
datalen = -1;
foreach (KeyValuePair<string, string> entry in ResponseHeaders)
AddHeader(entry.Key, entry.Value, request);
if (HttpPragmaNoCache)
request.Headers.TryAddWithoutValidation("Pragma", "no-cache");
request.Headers.TransferEncodingChunked = false;
request.Headers.ConnectionClose = true;
if (datalen > 0)
{
HttpWebResponse webRsp = (HttpWebResponse)((WebException)e).Response;
Status = (int)webRsp.StatusCode;
try
request.Content.Headers.TryAddWithoutValidation("Content-Type", HttpMIMEType);
request.Content.Headers.TryAddWithoutValidation("Content-Length", datalen.ToString());
}
if (Removed)
return;
responseMessage = client.Send(request, HttpCompletionOption.ResponseHeadersRead);
if (Removed)
return;
Status = (int)responseMessage.StatusCode;
if (responseMessage.Content is not null)
{
int len;
if(responseMessage.Content.Headers is not null && responseMessage.Content.Headers.ContentLength is long l)
len = (int)l;
else
len = -1;
Stream resStream = responseMessage.Content.ReadAsStream();
if(resStream is not null)
{
using (Stream responseStream = webRsp.GetResponseStream())
int maxBytes = (len < 0 || len > HttpBodyMaxLen) ? HttpBodyMaxLen : len;
byte[] buf = new byte[maxBytes];
int totalBodyBytes = 0;
int count;
do
{
using (StreamReader reader = new StreamReader(responseStream))
ResponseBody = reader.ReadToEnd();
count = resStream.Read(buf, totalBodyBytes, maxBytes - totalBodyBytes);
totalBodyBytes += count;
} while (count > 0 && totalBodyBytes < maxBytes); // any more data to read?
resStream.Dispose();
if (totalBodyBytes > 0)
{
string tempString = Util.UTF8.GetString(buf, 0, totalBodyBytes);
ResponseBody = tempString.Replace("\r", "");
}
}
catch
{
ResponseBody = webRsp.StatusDescription;
}
}
else
{
Status = 499; //ClientErrorJoker;
ResponseBody = e.Message;
}
}
// catch (Exception e)
catch (HttpRequestException e)
{
Status = e.StatusCode is null ? 499 : (int)e.StatusCode;
ResponseBody = e.Message;
}
//catch (Exception e)
catch
{
// Don't crash on anything else
}
finally
{
if (resStream != null)
resStream.Close();
if(!Removed)
if (!Removed)
{
// We need to resubmit ?
if (Status == (int)HttpStatusCode.MovedPermanently ||
@@ -704,10 +691,10 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
ResponseBody = "Number of redirects exceeded max redirects";
RequestModule.GotCompletedRequest(this);
}
else
else if (responseMessage is not null && responseMessage.Headers is not null)
{
string location = response.Headers["Location"];
if (location == null)
Uri locationUri = responseMessage.Headers.Location;
if (locationUri == null)
{
Status = 499;//ClientErrorJoker;
ResponseBody = "HTTP redirect code but no location header";
@@ -715,41 +702,38 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
}
else
{
if(Uri.TryCreate(location, UriKind.RelativeOrAbsolute, out Uri locationUri))
bool validredir = true;
if(!locationUri.IsAbsoluteUri)
{
bool validredir = true;
if(!locationUri.IsAbsoluteUri)
Uri reqUri = responseMessage.RequestMessage.RequestUri;
string newloc = reqUri.Scheme +"://" + reqUri.DnsSafeHost + ":" +
reqUri.Port +"/" + locationUri.OriginalString;
if (!Uri.TryCreate(newloc, UriKind.RelativeOrAbsolute, out locationUri))
{
string newloc = response.ResponseUri.Scheme +"://" + response.ResponseUri.DnsSafeHost + ":" +
response.ResponseUri.Port +"/" + location;
if (!Uri.TryCreate(newloc, UriKind.RelativeOrAbsolute, out locationUri))
{
Status = 499;//ClientErrorJoker;
ResponseBody = "HTTP redirect code but invalid location header";
RequestModule.GotCompletedRequest(this);
validredir = false;
}
location = newloc;
Status = 499;//ClientErrorJoker;
ResponseBody = "HTTP redirect code but invalid location header";
RequestModule.GotCompletedRequest(this);
validredir = false;
}
if(validredir)
}
if(validredir)
{
if (!RequestModule.CheckAllowed(locationUri))
{
if (!RequestModule.CheckAllowed(locationUri))
{
Status = 499;//ClientErrorJoker;
ResponseBody = "URL from HTTP redirect blocked: " + location;
RequestModule.GotCompletedRequest(this);
}
else
{
Status = 0;
Url = location;
Redirects++;
ResponseBody = null;
Status = 499;//ClientErrorJoker;
ResponseBody = "URL from HTTP redirect blocked: " + locationUri.AbsoluteUri;
RequestModule.GotCompletedRequest(this);
}
else
{
Status = 0;
Url = locationUri.AbsoluteUri;
Redirects++;
ResponseBody = null;
//m_log.DebugFormat("Redirecting to [{0}]", Url);
//m_log.DebugFormat("Redirecting to [{0}]", Url);
Process();
}
Process();
}
}
else
@@ -763,13 +747,12 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
}
else
{
if (ResponseBody == null)
ResponseBody = string.Empty;
ResponseBody ??= string.Empty;
RequestModule.GotCompletedRequest(this);
}
}
if (response != null)
response.Close();
responseMessage?.Dispose();
request.Dispose();
}
}

View File

@@ -72,13 +72,9 @@ namespace OpenSim.Region.Framework.Interfaces
/// <param name="parameters">LSL parameters for the request.</param>
/// <param name="headers">Extra headers for the request.</param>
/// <param name="body">Body of the request.</param>
/// <param name="status">
/// Initial status of the request. If OK then the request is actually made to the URL. Subsequent status is
/// then returned via IServiceRequest when the response is asynchronously fetched.
/// </param>
UUID StartHttpRequest(
uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body,
out HttpInitialRequestStatus status);
UUID StartHttpRequest(uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body);
/// <summary>
/// Stop and remove all http requests for the given script.
@@ -88,5 +84,6 @@ namespace OpenSim.Region.Framework.Interfaces
IServiceRequest GetNextCompletedRequest();
void RemoveCompletedRequest(UUID id);
bool CheckThrottle(uint localID, UUID onerID);
bool CheckAllowed(Uri url);
}
}

View File

@@ -13814,6 +13814,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
return Convert.ToBase64String(data1);
}
static Regex llHTTPRequestRegex = new(@"^(https?:\/\/)(\w+):(\w+)@(.*)$", RegexOptions.Compiled);
public LSL_Key llHTTPRequest(string url, LSL_List parameters, string body)
{
IHttpRequestModule httpScriptMod = m_ScriptEngine.World.RequestModuleInterface<IHttpRequestModule>();
@@ -13823,40 +13825,48 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
if(!httpScriptMod.CheckThrottle(m_host.LocalId, m_host.OwnerID))
return ScriptBaseClass.NULL_KEY;
try
{
Uri m_checkuri = new(url);
if (m_checkuri.Scheme != Uri.UriSchemeHttp && m_checkuri.Scheme != Uri.UriSchemeHttps)
{
Error("llHTTPRequest", "Invalid url schema");
return string.Empty;
}
}
catch
if(!Uri.TryCreate(url, UriKind.Absolute, out Uri m_checkuri))
{
Error("llHTTPRequest", "Invalid url");
return string.Empty;
}
if (m_checkuri.Scheme != Uri.UriSchemeHttp && m_checkuri.Scheme != Uri.UriSchemeHttps)
{
Error("llHTTPRequest", "Invalid url schema");
return string.Empty;
}
if (!httpScriptMod.CheckAllowed(m_checkuri))
{
Error("llHttpRequest", string.Format("Request to {0} disallowed by filter", url));
return string.Empty;
}
Dictionary<string, string> httpHeaders = new();
List<string> param = new();
bool ok;
int nCustomHeaders = 0;
int flag;
for (int i = 0; i < parameters.Data.Length; i += 2)
{
ok = Int32.TryParse(parameters.Data[i].ToString(), out int flag);
if (!ok || flag < 0 ||
flag > (int)HttpRequestConstants.HTTP_PRAGMA_NO_CACHE)
object di = parameters.Data[i];
if(di is LSL_Integer li )
flag = li.value;
else if (di is int ldi)
flag = ldi;
else flag = -1;
if(flag < 0 || flag > (int)HttpRequestConstants.HTTP_PRAGMA_NO_CACHE)
{
Error("llHTTPRequest", "Parameter " + i.ToString() + " is an invalid flag");
ScriptSleep(200);
return string.Empty;
}
param.Add(parameters.Data[i].ToString()); //Add parameter flag
if (flag != (int)HttpRequestConstants.HTTP_CUSTOM_HEADER)
{
param.Add(flag.ToString()); //Add parameter flag
param.Add(parameters.Data[i+1].ToString()); //Add parameter value
}
else
@@ -13909,17 +13919,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
noskip = false;
}
string paramValue = parameters.Data[i + 1].ToString();
if(paramName.Length + paramValue.Length > 253)
{
Error("llHTTPRequest", "name and value length exceds 253 characters for custom header at parameter " + i.ToString());
return string.Empty;
}
if (noskip)
{
param.Add(paramName);
param.Add(paramValue);
string paramValue = parameters.Data[i + 1].ToString();
if (paramName.Length + paramValue.Length > 253)
{
Error("llHTTPRequest", "name and value length exceds 253 characters for custom header at parameter " + i.ToString());
return string.Empty;
}
httpHeaders[paramName] = paramValue;
nCustomHeaders++;
}
@@ -13949,8 +13957,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
RegionInfo regionInfo = World.RegionInfo;
Dictionary<string, string> httpHeaders =new();
if (!string.IsNullOrWhiteSpace(m_lsl_shard))
httpHeaders["X-SecondLife-Shard"] = m_lsl_shard;
httpHeaders["X-SecondLife-Object-Name"] = m_host.Name;
@@ -13996,17 +14002,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
url = url[..idx];
}
string authregex = @"^(https?:\/\/)(\w+):(\w+)@(.*)$";
Regex r = new(authregex);
//int[] gnums = r.GetGroupNumbers();
Match m = r.Match(url);
Match m = llHTTPRequestRegex.Match(url);
if (m.Success)
{
//for (int i = 1; i < gnums.Length; i++)
//{
//System.Text.RegularExpressions.Group g = m.Groups[gnums[i]];
//CaptureCollection cc = g.Captures;
//}
if (m.Groups.Count == 5)
{
httpHeaders["Authorization"] = String.Format("Basic {0}", Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(m.Groups[2].ToString() + ":" + m.Groups[3].ToString())));
@@ -14014,13 +14012,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
}
}
UUID reqID = httpScriptMod.StartHttpRequest(m_host.LocalId, m_item.ItemID, url, param, httpHeaders, body,
out HttpInitialRequestStatus status);
if (status == HttpInitialRequestStatus.DISALLOWED_BY_FILTER)
Error("llHttpRequest", string.Format("Request to {0} disallowed by filter", url));
return reqID.IsZero() ? "" : reqID.ToString();
UUID reqID = httpScriptMod.StartHttpRequest(m_host.LocalId, m_item.ItemID, url, param, httpHeaders, body);
return reqID.IsZero() ? string.Empty : reqID.ToString();
}