Sometimes you want to give web browsers access to web resources that are best not opened indiscriminatly to the world. A good example are WMS servers: you want the users to be able to see maps, but putting a WMS server directly online is like putting a read-only sql client online.
The solution is put the web resource on a server that is not accessible by the internet, but is accessible by the web application - i.e. the limited resource is put inside the DMZ, and create some kind of proxy page that streams data from the server inside the DMZ to the outside world - after doing some validations, of course.
A setup for a proxy page (that only does the streaming, not the validation) may be as follows:
Create an aspx page MyProxyPage.aspx and add the following code:
void ProcessProxyRequest( string request, HttpResponse response)
{
HttpWebRequest webRequest = HttpWebRequest.Create(request) as HttpWebRequest;
// Important! Keeps the request from blocking after the first time!
webRequest.KeepAlive = false;
webRequest.Credentials = CredentialCache.DefaultCredentials;
using (HttpWebResponse backendResponse = (HttpWebResponse)webRequest.GetResponse())
{
Stream receiveStream = backendResponse.GetResponseStream();
// Clear whatever is already sent
response.Clear();
response.ClearContent();
response.ClearHeaders();
// Copy headers
// Check if header contains a contenth-lenght since IE
// goes bananas if this is missing
bool contentLenghtFound = false;
foreach (string header in backendResponse.Headers)
{
if (string.Compare(header, "CONTENT-LENGTH", true) == 0)
{
contentLenghtFound = true;
}
response.AppendHeader(header, backendResponse.Headers[header]);
}
// Copy content
byte[] buff = new byte[1024];
long length = 0;
int bytes;
while ((bytes = receiveStream.Read(buff, 0, 1024)) > 0)
{
length += bytes;
response.OutputStream.Write(buff, 0, bytes);
}
// Manually add content-lenght header to satisfy IE
if (!contentLenghtFound)
{
response.AppendHeader("Content-Length",
length.ToString());
}
receiveStream.Close();
backendResponse.Close();
response.Flush();
response.Close();
}
}
Now of course, in the Page_Load you have to do something first:
protected void Page_Load(object sender, EventArgs e)
{
string requestInsideDMZ = "http://somewhereinyourdmz/somepage.aspx";
ProcessProxyRequest( requestInsideDMZ,
Response);
}
If the user accesses MyProxyPage.aspx he gets the contents of http://somewhereinyourdmz/somepage.aspx instead.
1 comment:
Hi,
I am using your code and it's working very well!
I had only one little bug though that I fixed. If you want to get a page that encodes in chunks the response, you will have a problem because the headers will say "encoded" and the content won't be.
So here is the little trick:
foreach (string header in backendResponse.Headers)
{
if (string.Compare(header, "Content-Length", true) == 0)
{
contentLenghtFound = true;
}
if (string.Compare(header, "Transfer-Encoding", true) != 0)
{
response.AppendHeader(header, backendResponse.Headers[header]);
}
}
Hope it helps someone :)
Post a Comment