📜 ⬆️ ⬇️

Azure. Debugging applications on the 80th port

Developing an application on the Windows Azure platform, we had to put up with the feature of running debug builds on different ports. But when we began to actively debug cross-domain AJAX, this problem became particularly acute. Because the scripts required the use of an absolute URL in the text of the js-script. We had to write the port number in the URL: http://127.0.0.1:81/bla-bla-bla. When the port was changed when the application was restarted, the devfabric had to be restarted in order for the assembly to run on port 81. The restart took away precious time, the irritation increased.
At one point, patience broke and we decided to create a tool for debugging the application on one port. It is an ASPX application that receives requests and redirects them to the running Azure instance. This allows us to not worry about which port Azure is currently running on.

Presetting


During the development process, an idea was born to write something like a Reverse Proxy for the Azure instance to be available at azureproxy.com . To do this, we use IIS 7.5, which still turns on the machine.
In IIS Manager, add AzureProxy application pool
Adding an application pool
We specify the fourth version of the .Net Framework environment.
And add a website
Add site
Here you need to specify the site name "AzureProxy", application pool "AzureProxy", the physical path - any (here D: \ AzureProxy \). You also need to specify a binding: IP address - 127.0.0.1, Port - 80, Host name - azureproxy.com .
We also need the Url Rewrite module for IIS. If you have not yet installed it, you need to visit www.iis.net/download/URLRewrite and install it.
The next step is to add azureproxy.com to the hosts file.
In the editor, open the file C: \ Windows \ System32 \ drivers \ etc \ hosts and add the line to it
127.0.0.1 azureproxy.com
Preliminary stage is over.

We program


It is time to open Visual Studio 2010.
Create a new project
Creating a new project
The project type is ASP .NET Empty Web Application, the rest is optional.
Rule the Web.Config file
<? xml version ="1.0" ? >
< configuration >
< system.web >
< compilation debug ="true" targetFramework ="4.0" />
< pages enableViewStateMac ="false" />
</ system.web >
< system.webServer >
< modules runAllManagedModulesForAllRequests ="true" >
< add name ="AzureProxyModule" type ="AzureProxy.AzureProxyModule, AzureProxy" />
</ modules >
< rewrite >
< rules >
< rule name ="All" stopProcessing ="true" >
< match url ="^(.*)$" />
< action type ="Rewrite" url ="/Default.aspx?url={HtmlEncode:{R:0}}" />
</ rule >
</ rules >
</ rewrite >
</ system.webServer >
</ configuration >

* This source code was highlighted with Source Code Highlighter .


Add a new web form
New web form
named Default.aspx
And leave it only
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="AzureProxy.Default" %>


Next, add a new class.
New class
with the name AzureProxyModule.cs as follows
')
using System;
using System.Net;
using System.IO;
using System.Configuration;
using System.Collections. Generic ;
using System.Linq;
using System.Web;
using System.Threading;

namespace AzureProxy
{
public class AzureProxyModule : IHttpModule
{

const string Domain = "127.0.0.1" ;
const int MaxPort = 110;

public static string Port = "" ;
public Queue< int > ports = new Queue< int >();
public List <Thread> threads = new List <Thread>();
public bool ThreadsLive = false ;

public AzureProxyModule()
{
}

public String ModuleName
{
get { return "AzureProxyModule" ; }
}

public void Init(HttpApplication httpApp)
{

httpApp.BeginRequest +=
new EventHandler( this .OnBeginRequest);

httpApp.EndRequest +=
new EventHandler( this .OnEndRequest);
}

public void OnBeginRequest( object o, EventArgs ea)
{
}

public void OnEndRequest( object o, EventArgs ea)
{
HttpApplication httpApp = (HttpApplication)o;
if (Port.Length == 0)
{
SearchPort();
}

DoRequest();

}

public void DoRequest()
{

byte [] buffer = new byte [4096];
HttpContext ctx;
ctx = HttpContext .Current;
if (ctx.Request.QueryString[ "url" ] == null ) return ;

HttpWebRequest myHttp = (HttpWebRequest)HttpWebRequest.Create( "http://" + Domain + ":" + Port + ctx.Request.RawUrl.ToString());
myHttp.AllowAutoRedirect = false ;
myHttp.KeepAlive = true ;
myHttp.CookieContainer = new CookieContainer();
myHttp.UserAgent = ctx.Request.UserAgent;
foreach ( string CookieName in ctx.Request.Cookies.AllKeys)
{
if (ctx.Request.Cookies[CookieName].Domain != null )
{
myHttp.CookieContainer.Add( new Cookie(ctx.Request.Cookies[CookieName].Name, ctx.Request.Cookies[CookieName].Value, ctx.Request.Cookies[CookieName].Path, ctx.Request.Cookies[CookieName].Domain));
}
else
{
myHttp.CookieContainer.Add( new Cookie(ctx.Request.Cookies[CookieName].Name, ctx.Request.Cookies[CookieName].Value, ctx.Request.Cookies[CookieName].Path, Domain));
}
}
myHttp.ContentType = ctx.Request.ContentType;

if (ctx.Request.HttpMethod == "POST" )
{
myHttp.Method = "POST" ;
myHttp.AllowWriteStreamBuffering = true ;
myHttp.ContentLength = ctx.Request.InputStream.Length;
Stream requestStream = myHttp.GetRequestStream();
int length = 0;
while ((length = ctx.Request.InputStream.Read(buffer, 0, buffer.Length)) > 0)
{
requestStream.Write(buffer, 0, length);
}
}

try
{
WebResponse response = myHttp.GetResponse();

if (response.Headers[ "Location" ] != null )
{
ctx.Response.Redirect(response.Headers[ "Location" ]);
}

using ( Stream stream = response.GetResponseStream())
{
using (MemoryStream memoryStream = new MemoryStream())
{
int count = 0;
do
{
count = stream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, count);

} while (count != 0);

ctx.Response.ContentType = response.ContentType;
foreach ( string HeaderKey in response.Headers.Keys)
{
ctx.Response.AddHeader(HeaderKey, response.Headers[HeaderKey]);
}

ctx.Response.BinaryWrite(memoryStream.ToArray());

}
}
}
catch (WebException code)
{
switch (code.Message)
{
case "Unable to connect to the remote server" :
SearchPort();
DoRequest();
break ;
case "The remote server returned an error: (404) Not Found." :
ctx.Response.Status = "404 File Not Found" ;
break ;
}
return ;
}
}

public void SearchPort()
{
Port = "" ;
for ( int port = 81; port < MaxPort; port++)
{
ports.Enqueue(port);
threads.Add( new Thread( new ThreadStart(PortTest)));
}
threads.ForEach( new Action<Thread>(ThreadStart));

while (TreadsIsLive() && (Port.Length == 0))
{
//
}

//
threads.ForEach( new Action<Thread>(ThreadAbort));
}

public void Dispose() { }

public bool TreadsIsLive()
{
ThreadsLive = false ;
threads.ForEach( new Action<Thread>(ThreadTest));
return true ;
}

public void ThreadTest(Thread t)
{
ThreadsLive = ThreadsLive || t.IsAlive;
}

protected void ThreadStart(Thread t)
{
try
{
ThreadsLive = true ;
if (!t.IsAlive) t.Start();
}
catch { }
}

protected void ThreadAbort(Thread t)
{
t.Abort();
}

protected void PortTest()
{
int port;
try
{
port = ports.Dequeue();

HttpWebRequest myHttpWebRequest =
(HttpWebRequest)HttpWebRequest.Create( "http://" + Domain + ":" + port.ToString());
try
{
HttpWebResponse myHttpWebResponse =
(HttpWebResponse)myHttpWebRequest.GetResponse();
Port = port.ToString();
return ;
}
catch { }
}
catch { }
}
}
}

* This source code was highlighted with Source Code Highlighter .


We assemble application, and we publish it in IIS
Publication

Azure instance is now available at azureproxy.com.

Conclusion


The technique used allows you to proxy requests to running Azure instances from a single address. The search for an available port is carried out in child trades in parallel, which allows you to quickly find the port. Both GET and POST requests are proxied, the incoming stream is completely transmitted to the Azure instance, which allows you to upload files.

PS The article provides an abbreviated step-by-step guide to creating AzureProxy. If necessary - we can highlight in more detail the problem areas that arose in the process of writing the application.

Source: https://habr.com/ru/post/111100/


All Articles