CURRENT PROJECTS
loading
CATEGORIES AND POSTS
loading
overset
DEVELOPMENT LOG FOR JIM PALMER
Posted 09/11/2010 in C#.NET


Every once and a while you need a page on one server to serve as a proxy for another page. That or proxy a request and then perform more actions after the fact as though it was all in the same request. Case in point - if you want to proxy an incoming WAN request to a server/cluster on your LAN and cache the response in the process - you'll want to use a proxy layer like this and manage the storage of the cached responses. There are many different reasons to use a code-based proxy and I had trouble locating a simple example in either C#.NET or Java/JSP. I've been meaning to post these simple scripts for those looking for a starting point.

These scripts can handle simple HTTP/1.1 or 1.0 requests of methods GET, POST, etc. They can handle passing through the correct compression headers on both the request and response. Since these initial versions were part of exploring a larger POST proxy caching system - the caching request headers are stripped out (i.e. If-Modified-Since) so handling 200 responses does not work.

C#.NET ASP simple proxy
<%@ Page Language="C#" AspCompat="false" AutoEventWireup="true" Trace="false" Debug="true" EnableSessionState="false" ValidateRequest="false" %>

<%@ Import Namespace = "System" %>
<%@ Import Namespace = "System.IO" %>
<%@ Import Namespace = "System.Net" %>
<%@ Import Namespace = "System.Text" %>
<%@ Import Namespace = "System.Text.RegularExpressions" %>
<%@ Import Namespace = "System.Web" %>
<%@ Import Namespace = "System.Web.Script.Serialization" %>

<script runat="server">

    private static string baseURL = "http://www.overset.com/";
	
	protected void Page_Load(object sender, System.EventArgs e) {
        
        Hashtable expires = new Hashtable();
        StreamReader sr = null;
        StreamWriter sw = null;
        JavaScriptSerializer JSON = new JavaScriptSerializer();
		
        try {
			
			string func = "";
			if (Request.RawUrl.IndexOf("?") >= 0)
				func = Request.RawUrl.Split('?')[1];
            // read the request body using streamreader assuming non-binary
            sr = new StreamReader(Request.InputStream);
            string requestBodyString = sr.ReadToEnd();
            sr.Close();
			
			HttpWebRequest proxyRequest = (HttpWebRequest) HttpWebRequest.Create (baseURL + func);
			proxyRequest.Method = Request.HttpMethod;
			proxyRequest.Timeout = 600000;

			// set proxy HttpWebRequest headers
			foreach (string header in Request.Headers.Keys) {
				if (!Regex.Match(header, "^(host)$", RegexOptions.IgnoreCase).Success && 
					!Regex.Match(header, "^(if-modified-since)$", RegexOptions.IgnoreCase).Success && 
					!Regex.Match(header, "^(if-none-match)$", RegexOptions.IgnoreCase).Success) {
					try {
						// manualy assign Content-Type and Accept headers because they will throw ArgumentException
						if (header == "Content-Type") {
							proxyRequest.ContentType = Request.Headers[header].ToString();
						} else if (header == "Accept") {
							proxyRequest.Accept = Request.Headers[header].ToString();
						} else {
							proxyRequest.Headers.Add(header, Request.Headers[header].ToString());
						}
					} catch (ArgumentException re) {
						// do nothing with other "restricted" headers
					} 
				}
			}

			// set proxy HttpWebRequest body
			if (proxyRequest.Method != "GET") {
				sw = new StreamWriter(proxyRequest.GetRequestStream());
				sw.Write(requestBodyString);
				sw.Close();
			}

			HttpWebResponse proxyResponse = null;
			try {
				proxyResponse = (HttpWebResponse) proxyRequest.GetResponse();
			} catch (WebException we) {
				// status != 200, get the WebException response
				proxyResponse = (HttpWebResponse) we.Response;
			}

			// read the response body using streamreader assuming non-binary
			sr = new StreamReader(proxyResponse.GetResponseStream());
			string proxyRespBodyStr = sr.ReadToEnd();
			sr.Close();

			// send proxied headers back in Response
			foreach (string header in proxyResponse.Headers.Keys) {
				try {
					Response.Headers.Add(header, proxyResponse.Headers[header].ToString());
				} catch (ArgumentException re) {
					// do nothing with "restricted" headers
				}
			}
			proxyResponse.Close();

			// write the proxied body in Response
			Response.Write(proxyRespBodyStr);
			
		} catch ( Exception ex ) {
			Response.Write(ex.Message + ": " + ex.StackTrace.ToString());
        } finally {
            if (sr != null)
                sr.Close();
            if (sw != null)
                sr.Close();
        }
        
	}
	
</script>
link directly to script for download: proxy.aspx


Java JSP simple proxy
<%@ page import="java.net.*, java.util.*, java.io.*" %>
<%

	DataInputStream sio = null;
	DataInputStream dio = null;
	DataOutputStream cos = null;
	
	String baseURL = "http://www.overset.com/";
	
	try {
		
		// get the request body = args
		String reqbody = "";
		if (request.getContentLength() > 0) {
			sio = new DataInputStream(request.getInputStream());
			byte[] reqb = new byte[request.getContentLength()];
			sio.read(reqb);
			reqbody = new String(reqb);
		}
		
		String reqPage = request.getQueryString() != null ? request.getQueryString() : "";
		URL url = new URL(baseURL + reqPage);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setDoOutput(true);
		conn.setDoInput(true);
		conn.setUseCaches(false);
		// request timeout in milliseconds
		conn.setReadTimeout(600000);
		conn.setRequestMethod(request.getMethod());
		
		// pass through headers
		Enumeration reqheaders = request.getHeaderNames();
		while ( reqheaders.hasMoreElements() ) {
			String reqheader = (String) reqheaders.nextElement();
			// exclude specific headers
			if (!reqheader.matches("(?i)(host)") && 
				!reqheader.matches("(?i)(if-modified-since)") && 
				!reqheader.matches("(?i)(if-none-match)")) {
				conn.setRequestProperty(reqheader, request.getHeader(reqheader));
			}
		}
		
		// fill the proxy content body - inputstream -> byte array -> string
		cos = new DataOutputStream(conn.getOutputStream());
		cos.writeBytes(reqbody);
		
		// make the proxy connection
		conn.connect();
		
		int respCode = conn.getResponseCode();
		// 500 status codes will throw IOExceptions on getInputStream
		if (respCode <= 400) {
			dio = new DataInputStream(conn.getInputStream());
		} else {
			dio = new DataInputStream(conn.getErrorStream());
		}
		// inputstream -> byte array -> string
		byte[] resp = new byte[conn.getContentLength()];
		dio.read(resp);
		String respMsg = new String(resp);
		
		// return headers
		Map conheaders = conn.getHeaderFields();
		Iterator cit = conheaders.keySet().iterator();
		while (cit.hasNext()) {
			String conheader = (String) cit.next();
			// ignore the status header
			if ( conheader != null ) {
				response.addHeader(conheader, conn.getHeaderField(conheader));
			}
		}
		// send the response back
		response.getWriter().print(respMsg);
		
	} catch (Exception e) {
		
		// stack trace to string
		Writer writer = new StringWriter();
		PrintWriter printWriter = new PrintWriter(writer);
		e.printStackTrace(printWriter);
		// send back error
		response.getWriter().print(e.getMessage() + ": " + writer.toString());
		
	} finally {
		
		if (sio != null)	sio.close();
		if (dio != null)	dio.close();
		if (cos != null)	cos.close();
		
	}

%>
link directly to script for download: proxy.jsp


Both these proxy scripts are written to
http://[yourhost]/proxy.aspx?[destination url]


example request:

http://[yourhost]/proxy.aspx?posts.html


will then proxy the following page:

http://www.overset.com/posts.html
comments
loading
new comment
NAME
EMAIL ME ON UPDATES
EMAIL (hidden)
URL
MESSAGE TAGS ALLOWED: <code> <a> <pre class="code [tab4|tabX|inline|bash]"> <br>
PREVIEW COMMENT
TURING TEST
gravatar