/// <summary> /// Initiates an asynchronous call to the HTTP handler. /// </summary> /// <param name="context">An <see cref="T:System.Web.HttpContext"></see> object that provides references to intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> /// <param name="cb">The <see cref="T:System.AsyncCallback"></see> to call when the asynchronous method call is complete. If cb is null, the delegate is not called.</param> /// <param name="extraData">Any extra data needed to process the request.</param> /// <returns> /// An <see cref="T:System.IAsyncResult"></see> that contains information about the status of the process. /// </returns> public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { Log.Debug(typeof(ProxyHandler), "BeginProcessRequest()"); // Request URL Format: http://<load-balancer>:<port>/<tenant>/<application> Uri requestedUrl = context.Request.Url; Log.Debug(typeof(ProxyHandler), "Request: " + requestedUrl); ReverseProxyContext rpContext = new ReverseProxyContext(); rpContext.RequestedHostName = requestedUrl.Host; rpContext.RequestedPort = requestedUrl.Port; rpContext.RequestedPathAndQuery = requestedUrl.PathAndQuery; rpContext.TenantName = findTenantName(rpContext.RequestedPathAndQuery); rpContext.ApplicationName = findApplicationName(rpContext.RequestedPathAndQuery); if ((!string.IsNullOrEmpty(rpContext.TenantName)) && (!string.IsNullOrEmpty(rpContext.ApplicationName))) { rpContext.ApplicationInstance = LoadBalancerControllerWebUtil.GetApplicationInstance(rpContext.TenantName, rpContext.ApplicationName); if (rpContext.ApplicationInstance != null) { // Asynchronously add request to the load balancer request queue rpContext.AddReqCaller = new ASyncAddRequestToQueue(AddRequestToQueue); addReqResult = rpContext.AddReqCaller.BeginInvoke(context, rpContext.ApplicationInstance.NodeId, rpContext.ApplicationInstance.ApplicationId, rpContext.ApplicationInstance.Id, rpContext.ApplicationInstance.Tenant.Id, null, null); } } delegate_ = new AsyncProcessorDelegate(ReverseProxyRequest); return(delegate_.BeginInvoke(context, rpContext, cb, extraData)); }
/// <summary> /// Initiates an asynchronous call to the HTTP handler. /// </summary> /// <param name="context">An <see cref="T:System.Web.HttpContext"></see> object that provides references to intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> /// <param name="cb">The <see cref="T:System.AsyncCallback"></see> to call when the asynchronous method call is complete. If cb is null, the delegate is not called.</param> /// <param name="extraData">Any extra data needed to process the request.</param> /// <returns> /// An <see cref="T:System.IAsyncResult"></see> that contains information about the status of the process. /// </returns> public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { Log.Debug(typeof(ProxyHandler), "BeginProcessRequest()"); // Request URL Format: http://<load-balancer>:<port>/<tenant>/<application> Uri requestedUrl = context.Request.Url; Log.Debug(typeof(ProxyHandler), "Request: " + requestedUrl); ReverseProxyContext rpContext = new ReverseProxyContext(); rpContext.RequestedHostName = requestedUrl.Host; rpContext.RequestedPort = requestedUrl.Port; rpContext.RequestedPathAndQuery = requestedUrl.PathAndQuery; rpContext.TenantName = findTenantName(rpContext.RequestedPathAndQuery); rpContext.ApplicationName = findApplicationName(rpContext.RequestedPathAndQuery); if ((!string.IsNullOrEmpty(rpContext.TenantName)) && (!string.IsNullOrEmpty(rpContext.ApplicationName))) { rpContext.ApplicationInstance = LoadBalancerControllerWebUtil.GetApplicationInstance(rpContext.TenantName, rpContext.ApplicationName); if (rpContext.ApplicationInstance != null) { // Asynchronously add request to the load balancer request queue rpContext.AddReqCaller = new ASyncAddRequestToQueue(AddRequestToQueue); addReqResult = rpContext.AddReqCaller.BeginInvoke(context, rpContext.ApplicationInstance.NodeId, rpContext.ApplicationInstance.ApplicationId, rpContext.ApplicationInstance.Id, rpContext.ApplicationInstance.Tenant.Id, null, null); } } delegate_ = new AsyncProcessorDelegate(ReverseProxyRequest); return delegate_.BeginInvoke(context, rpContext, cb, extraData); }
//private bool IncrementRequestCount(ApplicationInstance instance) //{ // LbIncrementRequestCountRequest request = new LbIncrementRequestCountRequest(Settings.Credentials); // request.ApplicationId = instance.ApplicationId; // request.InstanceId = instance.Id; // EndPoints.LoadBalancerWebService.IncrementRequestCount(request); // return true; //} //private void DecrementRequestCount(ApplicationInstance instance) //{ // LbDecrementRequestCountRequest request = new LbDecrementRequestCountRequest(Settings.Credentials); // request.ApplicationId = instance.ApplicationId; // request.InstanceId = instance.Id; // EndPoints.LoadBalancerWebService.DecrementRequestCount(request); //} /// <summary> /// Sends the request to server /// Implemented using Managed Fusion URL Rewriter /// Reference: http://www.managedfusion.com/products/url-rewriter/ /// </summary> /// <param name="context">The context.</param> /// <returns></returns> private WebResponse SendRequestToTarget(HttpContext context, Uri requestUrl, ReverseProxyContext rpContext, out bool incremented) { incremented = false; // get the request WebRequest request = WebRequest.CreateDefault(requestUrl); if (request == null) throw new HttpException((int)HttpStatusCode.BadRequest, "The requested url, <" + requestUrl + ">, could not be found."); // keep the same HTTP request method request.Method = context.Request.HttpMethod; var knownVerb = KnownHttpVerb.Parse(request.Method); // depending on the type of this request specific values for an HTTP request if (request is HttpWebRequest) { var httpRequest = request as HttpWebRequest; httpRequest.AllowAutoRedirect = false; httpRequest.ServicePoint.Expect100Continue = false; // add all the headers from the other proxied session to this request foreach (string name in context.Request.Headers.AllKeys) { // add the headers that are restricted in their supported manor switch (name) { case "User-Agent": httpRequest.UserAgent = context.Request.UserAgent; break; case "Connection": string connection = context.Request.Headers[name]; if (connection.IndexOf("Keep-Alive", StringComparison.OrdinalIgnoreCase) > 0) httpRequest.KeepAlive = true; List<string> list = new List<string>(); foreach (string conn in connection.Split(',')) { string c = conn.Trim(); if (!c.Equals("Keep-Alive", StringComparison.OrdinalIgnoreCase) && !c.Equals("Close", StringComparison.OrdinalIgnoreCase)) list.Add(c); } if (list.Count > 0) httpRequest.Connection = String.Join(", ", list.ToArray()); break; case "Transfer-Encoding": httpRequest.SendChunked = true; httpRequest.TransferEncoding = context.Request.Headers[name]; break; case "Expect": httpRequest.ServicePoint.Expect100Continue = true; break; case "If-Modified-Since": DateTime ifModifiedSince; if (DateTime.TryParse(context.Request.Headers[name], out ifModifiedSince)) httpRequest.IfModifiedSince = ifModifiedSince; break; case "Content-Length": httpRequest.ContentLength = context.Request.ContentLength; break; case "Content-Type": httpRequest.ContentType = context.Request.ContentType; break; case "Accept": httpRequest.Accept = String.Join(", ", context.Request.AcceptTypes); break; case "Referer": httpRequest.Referer = context.Request.UrlReferrer.OriginalString; break; } // add to header if not restricted if (!WebHeaderCollection.IsRestricted(name, false)) { // it is nessisary to get the values for headers that are allowed to specifiy // multiple values in an instance (i.e. Cookie) string[] values = context.Request.Headers.GetValues(name); foreach (string value in values) request.Headers.Add(name, value); } } } // Add Proxy Standard Protocol Headers // http://en.wikipedia.org/wiki/X-Forwarded-For request.Headers.Add("X-Forwarded-For", context.Request.UserHostAddress); // Add Server Variables // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.45 string currentServerName = context.Request.ServerVariables["SERVER_NAME"]; string currentServerPort = context.Request.ServerVariables["SERVER_PORT"]; string currentServerProtocol = context.Request.ServerVariables["SERVER_PROTOCOL"]; if (currentServerProtocol.IndexOf("/") >= 0) currentServerProtocol = currentServerProtocol.Substring(currentServerProtocol.IndexOf("/") + 1); string currentVia = String.Format("{0} {1}:{2} ({3})", currentServerProtocol, currentServerName, currentServerPort, "Monoscape.LoadBalancerController"); request.Headers.Add("Via", currentVia); // // ContentLength is set to -1 if their is no data to send if ((request.ContentLength >= 0) && (!knownVerb.ContentBodyNotAllowed)) { int bufferSize = 64 * 1024; using (Stream requestStream = request.GetRequestStream(), bufferStream = new BufferedStream(context.Request.InputStream, bufferSize)) { byte[] buffer = new byte[bufferSize]; try { while (true) { // make sure that the stream can be read from if (!bufferStream.CanRead) break; int bytesReturned = bufferStream.Read(buffer, 0, bufferSize); // if not bytes were returned the end of the stream has been reached // and the loop should exit if (bytesReturned == 0) break; // write bytes to the response requestStream.Write(buffer, 0, bytesReturned); } } catch (Exception e) { Log.Error(typeof(ProxyHandler), "", e); } } } WebResponse response; try { // Increment application instance request count //incremented = IncrementRequestCount(rpContext.ApplicationInstance); if (addReqResult != null) { int requestId = rpContext.AddReqCaller.EndInvoke(addReqResult); // Remove request from load balancer request queue ASyncRemoveRequestFromQueue caller = new ASyncRemoveRequestFromQueue(RemoveRequestFromQueue); caller.BeginInvoke(requestId, null, null); } // Send request to the proxy target response = request.GetResponse(); } catch (WebException e) { Log.Error(typeof(ProxyHandler), "Error received from " + request.RequestUri + ": " + e.Message, e); response = e.Response; } if (response == null) { Log.Error(typeof(ProxyHandler), "The requested url " + requestUrl + " could not be found."); throw new HttpException((int)HttpStatusCode.NotFound, "The requested url could not be found."); } Log.Info(typeof(ProxyHandler), response.GetType().ToString()); if (response is HttpWebResponse) { HttpWebResponse httpResponse = response as HttpWebResponse; Log.Info(typeof(ProxyHandler), "Received '" + ((int)httpResponse.StatusCode) + " " + httpResponse.StatusDescription + "'"); } return response; }
private void ReverseProxyRequest(HttpContext context, ReverseProxyContext rpContext) { Log.Debug(typeof(ProxyHandler), "ReverseProxyRequest()"); // Request URL Format: http://<load-balancer>:<port>/<tenant>/<application> Uri requestedUrl = context.Request.Url; Log.Debug(typeof(ProxyHandler), "Request: " + requestedUrl); if ((!string.IsNullOrEmpty(rpContext.TenantName)) && (!string.IsNullOrEmpty(rpContext.ApplicationName))) { var appInstance = rpContext.ApplicationInstance; if (appInstance == null) { context.Response.Write("Application or the tenant not found"); context.Response.StatusCode = (int)HttpStatusCode.NotFound; context.Response.End(); return; } else { Uri proxyRequestUrl = null; Uri proxyResponseUrl = null; WebResponse response = null; bool incremented = false; try { // Use same path and query for the reverse proxy string pathAndQuery = rpContext.RequestedPathAndQuery; // Set Proxy Host Name/Port string proxyHostName = appInstance.IpAddress; int proxyPort = appInstance.Port; UriBuilder requestUrlBuilder = new UriBuilder(requestedUrl.Scheme, proxyHostName, proxyPort, pathAndQuery); proxyRequestUrl = requestUrlBuilder.Uri; Log.Debug(typeof(ProxyHandler), "Proxy Request: " + proxyRequestUrl); UriBuilder responseUrlBuilder = new UriBuilder(requestedUrl.Scheme, rpContext.RequestedHostName, rpContext.RequestedPort, rpContext.RequestedPathAndQuery); proxyResponseUrl = responseUrlBuilder.Uri; // Send the request to the selected application instance response = SendRequestToTarget(context, proxyRequestUrl, rpContext, out incremented); } catch (HttpException e) { Log.Error(this, "Http Error: " + e.GetHttpCode(), e); context.Response.Write(e.Message); context.Response.StatusCode = e.GetHttpCode(); context.Response.End(); return; } catch (Exception e) { Log.Error(this, e); context.Response.Write("An internal error occurred"); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; context.Response.End(); return; } finally { //if (incremented) //{ // // Decrement application instance request count // DecrementRequestCount(appInstance); //} if (response != null) { // Send the response to the client SendResponseToClient(context, response, proxyRequestUrl, proxyResponseUrl); } } } } else { context.Response.Write(String.Format("Invalid request URL, expected: http://{0}:{1}/tenant/application", rpContext.RequestedHostName, rpContext.RequestedPort)); context.Response.StatusCode = (int)HttpStatusCode.NotFound; context.Response.End(); return; } }
//private bool IncrementRequestCount(ApplicationInstance instance) //{ // LbIncrementRequestCountRequest request = new LbIncrementRequestCountRequest(Settings.Credentials); // request.ApplicationId = instance.ApplicationId; // request.InstanceId = instance.Id; // EndPoints.LoadBalancerWebService.IncrementRequestCount(request); // return true; //} //private void DecrementRequestCount(ApplicationInstance instance) //{ // LbDecrementRequestCountRequest request = new LbDecrementRequestCountRequest(Settings.Credentials); // request.ApplicationId = instance.ApplicationId; // request.InstanceId = instance.Id; // EndPoints.LoadBalancerWebService.DecrementRequestCount(request); //} /// <summary> /// Sends the request to server /// Implemented using Managed Fusion URL Rewriter /// Reference: http://www.managedfusion.com/products/url-rewriter/ /// </summary> /// <param name="context">The context.</param> /// <returns></returns> private WebResponse SendRequestToTarget(HttpContext context, Uri requestUrl, ReverseProxyContext rpContext, out bool incremented) { incremented = false; // get the request WebRequest request = WebRequest.CreateDefault(requestUrl); if (request == null) { throw new HttpException((int)HttpStatusCode.BadRequest, "The requested url, <" + requestUrl + ">, could not be found."); } // keep the same HTTP request method request.Method = context.Request.HttpMethod; var knownVerb = KnownHttpVerb.Parse(request.Method); // depending on the type of this request specific values for an HTTP request if (request is HttpWebRequest) { var httpRequest = request as HttpWebRequest; httpRequest.AllowAutoRedirect = false; httpRequest.ServicePoint.Expect100Continue = false; // add all the headers from the other proxied session to this request foreach (string name in context.Request.Headers.AllKeys) { // add the headers that are restricted in their supported manor switch (name) { case "User-Agent": httpRequest.UserAgent = context.Request.UserAgent; break; case "Connection": string connection = context.Request.Headers[name]; if (connection.IndexOf("Keep-Alive", StringComparison.OrdinalIgnoreCase) > 0) { httpRequest.KeepAlive = true; } List <string> list = new List <string>(); foreach (string conn in connection.Split(',')) { string c = conn.Trim(); if (!c.Equals("Keep-Alive", StringComparison.OrdinalIgnoreCase) && !c.Equals("Close", StringComparison.OrdinalIgnoreCase)) { list.Add(c); } } if (list.Count > 0) { httpRequest.Connection = String.Join(", ", list.ToArray()); } break; case "Transfer-Encoding": httpRequest.SendChunked = true; httpRequest.TransferEncoding = context.Request.Headers[name]; break; case "Expect": httpRequest.ServicePoint.Expect100Continue = true; break; case "If-Modified-Since": DateTime ifModifiedSince; if (DateTime.TryParse(context.Request.Headers[name], out ifModifiedSince)) { httpRequest.IfModifiedSince = ifModifiedSince; } break; case "Content-Length": httpRequest.ContentLength = context.Request.ContentLength; break; case "Content-Type": httpRequest.ContentType = context.Request.ContentType; break; case "Accept": httpRequest.Accept = String.Join(", ", context.Request.AcceptTypes); break; case "Referer": httpRequest.Referer = context.Request.UrlReferrer.OriginalString; break; } // add to header if not restricted if (!WebHeaderCollection.IsRestricted(name, false)) { // it is nessisary to get the values for headers that are allowed to specifiy // multiple values in an instance (i.e. Cookie) string[] values = context.Request.Headers.GetValues(name); foreach (string value in values) { request.Headers.Add(name, value); } } } } // Add Proxy Standard Protocol Headers // http://en.wikipedia.org/wiki/X-Forwarded-For request.Headers.Add("X-Forwarded-For", context.Request.UserHostAddress); // Add Server Variables // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.45 string currentServerName = context.Request.ServerVariables["SERVER_NAME"]; string currentServerPort = context.Request.ServerVariables["SERVER_PORT"]; string currentServerProtocol = context.Request.ServerVariables["SERVER_PROTOCOL"]; if (currentServerProtocol.IndexOf("/") >= 0) { currentServerProtocol = currentServerProtocol.Substring(currentServerProtocol.IndexOf("/") + 1); } string currentVia = String.Format("{0} {1}:{2} ({3})", currentServerProtocol, currentServerName, currentServerPort, "Monoscape.LoadBalancerController"); request.Headers.Add("Via", currentVia); // // ContentLength is set to -1 if their is no data to send if ((request.ContentLength >= 0) && (!knownVerb.ContentBodyNotAllowed)) { int bufferSize = 64 * 1024; using (Stream requestStream = request.GetRequestStream(), bufferStream = new BufferedStream(context.Request.InputStream, bufferSize)) { byte[] buffer = new byte[bufferSize]; try { while (true) { // make sure that the stream can be read from if (!bufferStream.CanRead) { break; } int bytesReturned = bufferStream.Read(buffer, 0, bufferSize); // if not bytes were returned the end of the stream has been reached // and the loop should exit if (bytesReturned == 0) { break; } // write bytes to the response requestStream.Write(buffer, 0, bytesReturned); } } catch (Exception e) { Log.Error(typeof(ProxyHandler), "", e); } } } WebResponse response; try { // Increment application instance request count //incremented = IncrementRequestCount(rpContext.ApplicationInstance); if (addReqResult != null) { int requestId = rpContext.AddReqCaller.EndInvoke(addReqResult); // Remove request from load balancer request queue ASyncRemoveRequestFromQueue caller = new ASyncRemoveRequestFromQueue(RemoveRequestFromQueue); caller.BeginInvoke(requestId, null, null); } // Send request to the proxy target response = request.GetResponse(); } catch (WebException e) { Log.Error(typeof(ProxyHandler), "Error received from " + request.RequestUri + ": " + e.Message, e); response = e.Response; } if (response == null) { Log.Error(typeof(ProxyHandler), "The requested url " + requestUrl + " could not be found."); throw new HttpException((int)HttpStatusCode.NotFound, "The requested url could not be found."); } Log.Info(typeof(ProxyHandler), response.GetType().ToString()); if (response is HttpWebResponse) { HttpWebResponse httpResponse = response as HttpWebResponse; Log.Info(typeof(ProxyHandler), "Received '" + ((int)httpResponse.StatusCode) + " " + httpResponse.StatusDescription + "'"); } return(response); }
private void ReverseProxyRequest(HttpContext context, ReverseProxyContext rpContext) { Log.Debug(typeof(ProxyHandler), "ReverseProxyRequest()"); // Request URL Format: http://<load-balancer>:<port>/<tenant>/<application> Uri requestedUrl = context.Request.Url; Log.Debug(typeof(ProxyHandler), "Request: " + requestedUrl); if ((!string.IsNullOrEmpty(rpContext.TenantName)) && (!string.IsNullOrEmpty(rpContext.ApplicationName))) { var appInstance = rpContext.ApplicationInstance; if (appInstance == null) { context.Response.Write("Application or the tenant not found"); context.Response.StatusCode = (int)HttpStatusCode.NotFound; context.Response.End(); return; } else { Uri proxyRequestUrl = null; Uri proxyResponseUrl = null; WebResponse response = null; bool incremented = false; try { // Use same path and query for the reverse proxy string pathAndQuery = rpContext.RequestedPathAndQuery; // Set Proxy Host Name/Port string proxyHostName = appInstance.IpAddress; int proxyPort = appInstance.Port; UriBuilder requestUrlBuilder = new UriBuilder(requestedUrl.Scheme, proxyHostName, proxyPort, pathAndQuery); proxyRequestUrl = requestUrlBuilder.Uri; Log.Debug(typeof(ProxyHandler), "Proxy Request: " + proxyRequestUrl); UriBuilder responseUrlBuilder = new UriBuilder(requestedUrl.Scheme, rpContext.RequestedHostName, rpContext.RequestedPort, rpContext.RequestedPathAndQuery); proxyResponseUrl = responseUrlBuilder.Uri; // Send the request to the selected application instance response = SendRequestToTarget(context, proxyRequestUrl, rpContext, out incremented); } catch (HttpException e) { Log.Error(this, "Http Error: " + e.GetHttpCode(), e); context.Response.Write(e.Message); context.Response.StatusCode = e.GetHttpCode(); context.Response.End(); return; } catch (Exception e) { Log.Error(this, e); context.Response.Write("An internal error occurred"); context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; context.Response.End(); return; } finally { //if (incremented) //{ // // Decrement application instance request count // DecrementRequestCount(appInstance); //} if (response != null) { // Send the response to the client SendResponseToClient(context, response, proxyRequestUrl, proxyResponseUrl); } } } } else { context.Response.Write(String.Format("Invalid request URL, expected: http://{0}:{1}/tenant/application", rpContext.RequestedHostName, rpContext.RequestedPort)); context.Response.StatusCode = (int)HttpStatusCode.NotFound; context.Response.End(); return; } }