Оказывается, это не простое решение, и вы не меняете хост.
ARR почти сработал с некоторой изобретательностью, написав заголовок хоста в качестве имени сервера и домена, за исключением URL-адреса.перезапись работала в первый раз, но игнорировалась во второй раз, когда приложение отправляло информацию обратно на внутренний сервер.
Подошло только одно решение обратного прокси, которое было: https://gist.github.com/anth-3/6169292
, чтооснова для кода.Основная идея:
Для полноты код включен.Это несколько черновиков, но не доработано или проверено, так что ждите ошибок.Кажется, что он работает со всеми глаголами * 1, но в основном тестируется с GET, и это также работает с https * 2
* 1, если вы хотите, чтобы PUT, DELETE и другие глаголы работали, вам нужно включить это вIIS, и, похоже, вам нужно сделать это и в файле web.config
* 2, чтобы https работал, веб-сайту внешнего веб-сайта требуется действующий сертификат для его работы.Это было проверено с использованием сертификата подстановочного знака на внешнем и внутреннем веб-сайте в течение https://443
Это позволило мне выполнить первоначальный POC.
Единственные страницы в пустом проекте:
global.asax
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext urlRequestContext = HttpContext.Current;
if (!(urlRequestContext.Request.Url.AbsolutePath.ToLower().StartsWith("/errorpages/")))
{
try
{
string hostingAuth = urlRequestContext.Request.Headers.GetValues("hostingauth").FirstOrDefault();
if (hostingAuth == "company2secret")
{
string newHost = "company2internal.validdomain.com";
reverseProxy.ProcessRequest(urlRequestContext, newHost);
}
}
catch (System.Threading.ThreadAbortException)
{
// ignore it
}
catch (Exception ex)
{
Response.Redirect("/errorpages/missingtoken.aspx", true);
}
}
}
.
class1.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
namespace redirect
{
public class reverseProxy
{
//public static bool AcceptAllCertifications(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certification, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
//{
// return true;
//}
public static void ProcessRequest(HttpContext Context, string newHost)
{
//ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AcceptAllCertifications);
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
/* Create variables to hold the request and response. */
HttpRequest Request = Context.Request;
HttpResponse Response = Context.Response;
string URI = null;
URI = Request.Url.Scheme.ToString() + "://" + newHost + Request.Url.PathAndQuery;
/* Create an HttpWebRequest to send the URI on and process results. */
System.Net.HttpWebRequest ProxyRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(URI);
/* Set the same requests to our request as in the incoming request */
ProxyRequest.Method = Request.HttpMethod.ToUpper();
ProxyRequest.ServicePoint.Expect100Continue = false;
ProxyRequest.Accept = Request.Headers["Accept"];
//ProxyRequest.TransferEncoding = Request.Headers["Accept-encoding"];
ProxyRequest.SendChunked = false;
//ProxyRequest.Date = Request.Headers["Date"];
ProxyRequest.Expect = Request.Headers["Expect"];
//ProxyRequest.IfModifiedSince = Request.Headers["If-Modified-Since"];
//ProxyRequest.Range = Request.Headers["Range"];
ProxyRequest.Referer = Request.Headers["Referer"];
ProxyRequest.TransferEncoding = Request.Headers["Transfer-Encoding"];
ProxyRequest.UserAgent = Request.Headers["User-Agent"];
//set the same headers except for certain ones as they need to be set not in this way
foreach (string strKey in Request.Headers.AllKeys)
{
if ((strKey != "Accept") && (strKey != "Connection") && (strKey != "Content-Length") && (strKey != "Content-Type") && (strKey != "Date") && (strKey != "Expect") && (strKey != "Host") && (strKey != "If-Modified-Since") && (strKey != "Range") && (strKey != "Referer") && (strKey != "Transfer-Encoding") && (strKey != "User-Agent") && (strKey != "Proxy-Connection") && (strKey != "hostingauth"))
ProxyRequest.Headers.Add(strKey, Request.Headers[strKey]);
}
if (Request.InputStream.Length > 0)
{
/*
* Since we are using the same request method as the original request, and that is
* a POST, the values to send on in the new request must be grabbed from the
* original POSTed request.
*/
byte[] Bytes = new byte[Request.InputStream.Length];
Request.InputStream.Read(Bytes, 0, (int)Request.InputStream.Length);
ProxyRequest.ContentLength = Bytes.Length;
string ContentType = Request.ContentType;
if (String.IsNullOrEmpty(ContentType))
{
ProxyRequest.ContentType = "application/x-www-form-urlencoded";
}
else
{
ProxyRequest.ContentType = ContentType;
}
using (Stream OutputStream = ProxyRequest.GetRequestStream())
{
OutputStream.Write(Bytes, 0, Bytes.Length);
}
}
//else
//{
// /*
// * When the original request is a GET, things are much easier, as we need only to
// * pass the URI we collected earlier which will still have any parameters
// * associated with the request attached to it.
// */
// //ProxyRequest.Method = "GET";
//}
System.Net.WebResponse ServerResponse = null;
/* Send the proxy request to the remote server or fail. */
try
{
//even if it isn't gzipped it tries but ignores if it fails
ProxyRequest.AutomaticDecompression = DecompressionMethods.GZip;
ServerResponse = ProxyRequest.GetResponse();
}
catch (System.Net.WebException WebEx)
{
#region exceptionError
Response.StatusCode = 500;
Response.StatusDescription = WebEx.Status.ToString();
Response.Write(WebEx.Message);
Response.Write("\r\n");
Response.Write(((System.Net.HttpWebResponse)WebEx.Response).ResponseUri);
Response.Write("\r\n");
Response.Write(((System.Net.HttpWebResponse)WebEx.Response).Method);
Response.Write("\r\n");
Response.Write("Headers\r\n");
foreach (string strKey in Request.Headers.AllKeys)
{
Response.Write(strKey + ": " +Request.Headers[strKey]);
Response.Write("\r\n");
}
Response.End();
#endregion
return;
}
/* Set up the response to the client if there is one to set up. */
if (ServerResponse != null)
{
Response.ContentType = ServerResponse.ContentType;
using (Stream ByteStream = ServerResponse.GetResponseStream())
{
/* What is the response type? */
if (ServerResponse.ContentType.Contains("text") ||
ServerResponse.ContentType.Contains("json") ||
ServerResponse.ContentType.Contains("xml"))
{
/* These "text" types are easy to handle. */
using (StreamReader Reader = new StreamReader(ByteStream))
{
string ResponseString = Reader.ReadToEnd();
/*
* Tell the client not to cache the response since it
* could easily be dynamic, and we do not want to mess
* that up!
*/
Response.CacheControl = "no-cache";
//If the request came with a gzip request, send it back gzipped
if (Request.Headers["Accept-encoding"].Contains("gzip"))
{
Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
System.IO.Compression.CompressionMode.Compress);
Response.AppendHeader("Content-Encoding", "gzip");
}
//write webpage/results back
Response.Write(ResponseString);
}
}
else
{
//This is completely untested
/*
* Handle binary responses (image, layer file, other binary
* files) differently than text.
*/
BinaryReader BinReader = new BinaryReader(ByteStream);
byte[] BinaryOutputs = BinReader.ReadBytes((int)ServerResponse.ContentLength);
BinReader.Close();
/*
* Tell the client not to cache the response since it could
* easily be dynamic, and we do not want to mess that up!
*/
Response.CacheControl = "no-cache";
//could this make it more efficient - untested
if (Request.Headers["Accept-encoding"].Contains("gzip"))
{
Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
System.IO.Compression.CompressionMode.Compress);
Response.AppendHeader("Content-Encoding", "gzip");
}
/*
* Send the binary response to the client.
* (Note: if large images/files are sent, we could modify this to
* send back in chunks instead...something to think about for
* future.)
*/
Response.OutputStream.Write(BinaryOutputs, 0, BinaryOutputs.Length);
}
ServerResponse.Close();
}
}
//done
Response.End();
}
}
}
.
В файле web.config показано переопределение глагола для PUT и DELETE для работы.Я знаю, что я бы обратный прокси aspx и API без расширения.Это, вероятно, излишне и неэффективно, но работает
web.config
<!--
For more information on how to configure your ASP.NET application, please visit
https://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.6.1"/>
<httpRuntime targetFramework="4.6.1"/>
</system.web>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701"/>
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+"/>
</compilers>
</system.codedom>
<system.webServer>
<handlers>
<remove name="PageHandlerFactory-ISAPI-2.0-64" />
<remove name="PageHandlerFactory-ISAPI-2.0" />
<remove name="PageHandlerFactory-Integrated-4.0" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="PageHandlerFactory-ISAPI-4.0_32bit" />
<remove name="PageHandlerFactory-ISAPI-4.0_64bit" />
<remove name="PageHandlerFactory-Integrated" />
<add name="PageHandlerFactory-Integrated" path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv2.0" />
<add name="PageHandlerFactory-ISAPI-4.0_64bit" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="PageHandlerFactory-ISAPI-4.0_32bit" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="PageHandlerFactory-Integrated-4.0" path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode,runtimeVersionv4.0" />
<add name="PageHandlerFactory-ISAPI-2.0" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness32" responseBufferLimit="0" />
<add name="PageHandlerFactory-ISAPI-2.0-64" path="*.aspx" verb="*" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv2.0,bitness64" responseBufferLimit="0" />
</handlers>
</system.webServer>
</configuration>
/ errorpages / missingtoken.aspx
<body>
the token is missing
</body>
Если вам интересно узнать speed , Почтальон давал мне время
- 350-600ms прямо через Интернет для отображения страницы .aspx.
- 600-900мс через обратный прокси-сервер (2 разных местоположения. RP дома, внутренний сервер (представлен снаружи) на сайте).Я подозреваю, что это происходит из-за многократного распаковывания и повторного распаковывания.
- 800 мс-1 с, если он проходил через RP, но GZIPping не выполнялся.
Если мне не нужно было разархивировать, чтобы разархивировать, я подозреваю, что время будет ближе кпрямое время
Если бы я мог развернуть RP на месте и запросить его у внутреннего сервера в разархивированном виде и представить обратно в заархивированном виде, это могло бы быть и быстрее.YMMV
.
Если вам интересно, зачем это нужно, - это вставка аутентификации в запрос к API, который обычно находится в белом списке IP.RP не будет внесен в белый список, и у меня нет доступа к коду API, чтобы изменить его.