diff --git a/DotNet/proxy.ashx b/DotNet/proxy.ashx index b81da7ea..eb72ccf3 100644 --- a/DotNet/proxy.ashx +++ b/DotNet/proxy.ashx @@ -279,7 +279,7 @@ public class proxy : IHttpHandler { //forwarding original request System.Net.WebResponse serverResponse = null; try { - serverResponse = forwardToServer(context, addTokenToUri(requestUri, token, tokenParamName), postBody, credentials); + serverResponse = forwardToServer(context.Request, addTokenToUri(requestUri, token, tokenParamName), postBody, credentials); } catch (System.Net.WebException webExc) { string errorMsg = webExc.Message + " " + uri; @@ -287,7 +287,7 @@ public class proxy : IHttpHandler { if (webExc.Response != null) { - copyHeaders(webExc.Response as System.Net.HttpWebResponse, context.Response); + copyResponseHeaders(webExc.Response as System.Net.HttpWebResponse, context.Response); using (Stream responseStream = webExc.Response.GetResponseStream()) { @@ -328,7 +328,7 @@ public class proxy : IHttpHandler { //server returned error - potential cause: token has expired. //we'll do second attempt to call the server with renewed token: token = getNewTokenIfCredentialsAreSpecified(serverUrl, requestUri); - serverResponse = forwardToServer(context, addTokenToUri(requestUri, token, tokenParamName), postBody); + serverResponse = forwardToServer(context.Request, addTokenToUri(requestUri, token, tokenParamName), postBody); //storing the token in Application scope, to do not waste time on requesting new one untill it expires or the app is restarted. context.Application.Lock(); @@ -367,12 +367,25 @@ public class proxy : IHttpHandler { return new byte[0]; } - private System.Net.WebResponse forwardToServer(HttpContext context, string uri, byte[] postBody, System.Net.NetworkCredential credentials = null) + private void writeRequestPostBody(System.Net.HttpWebRequest req, byte[] bytes) { - return - postBody.Length > 0? - doHTTPRequest(uri, postBody, "POST", context.Request.Headers["referer"], context.Request.ContentType, credentials): - doHTTPRequest(uri, context.Request.HttpMethod, credentials); + if (bytes != null && bytes.Length > 0) + { + req.ContentLength = bytes.Length; + using (Stream outputStream = req.GetRequestStream()) + { + outputStream.Write(bytes, 0, bytes.Length); + } + } + } + + private System.Net.WebResponse forwardToServer(HttpRequest req, string uri, byte[] postBody, System.Net.NetworkCredential credentials = null) + { + string method = postBody.Length > 0 ? "POST" : req.HttpMethod; + System.Net.HttpWebRequest forwardReq = createHTTPRequest(uri, method, req.ContentType, credentials); + copyRequestHeaders(req, forwardReq); + writeRequestPostBody(forwardReq, postBody); + return forwardReq.GetResponse(); } /// @@ -380,7 +393,7 @@ public class proxy : IHttpHandler { /// /// The response that we are copying the headers from /// The response that we are copying the headers to - private void copyHeaders(System.Net.WebResponse fromResponse, HttpResponse toResponse) + private void copyResponseHeaders(System.Net.WebResponse fromResponse, HttpResponse toResponse) { foreach (var headerKey in fromResponse.Headers.AllKeys) { @@ -410,6 +423,73 @@ public class proxy : IHttpHandler { } } + private void copyRequestHeaders(HttpRequest fromRequest, System.Net.HttpWebRequest toRequest) + { + foreach (var headerKey in fromRequest.Headers.AllKeys) + { + string headerValue = fromRequest.Headers[headerKey]; + string headerKeyLower = headerKey.ToLower(); + + switch (headerKeyLower) + { + case "accept-encoding": + case "proxy-connection": + continue; + case "range": + setRangeHeader(toRequest, headerValue); + break; + case "accept": + toRequest.Accept = headerValue; + break; + case "if-modified-since": + DateTime modDT; + if (DateTime.TryParse(headerValue, out modDT)) + toRequest.IfModifiedSince = modDT; + break; + case "referer": + toRequest.Referer = headerValue; + break; + case "user-agent": + toRequest.UserAgent = headerValue; + break; + default: + // Some headers are restricted and would throw an exception: + // http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.headers(v=vs.100).aspx + // Also check for our custom list of headers that should not be sent (https://github.com/Esri/resource-proxy/issues/362) + if (!System.Net.WebHeaderCollection.IsRestricted(headerKey) && + headerKeyLower != "accept-encoding" && + headerKeyLower != "proxy-connection" && + headerKeyLower != "connection" && + headerKeyLower != "keep-alive" && + headerKeyLower != "proxy-authenticate" && + headerKeyLower != "proxy-authorization" && + headerKeyLower != "transfer-encoding" && + headerKeyLower != "te" && + headerKeyLower != "trailer" && + headerKeyLower != "upgrade" && + toRequest.Headers[headerKey] == null) + toRequest.Headers[headerKey] = headerValue; + break; + } + } + } + + private void setRangeHeader(System.Net.HttpWebRequest req, string range) + { + string[] specifierAndRange = range.Split('='); + if (specifierAndRange.Length == 2) + { + string specifier = specifierAndRange[0]; + string[] fromAndTo = specifierAndRange[1].Split('-'); + if (fromAndTo.Length == 2) + { + int from, to; + if (int.TryParse(fromAndTo[0], out from) && int.TryParse(fromAndTo[1], out to)) + req.AddRange(specifier, from, to); + } + } + } + private bool fetchAndPassBackToClient(System.Net.WebResponse serverResponse, HttpResponse clientResponse, bool ignoreAuthenticationErrors) { if (serverResponse != null) { using (Stream byteStream = serverResponse.GetResponseStream()) { @@ -427,14 +507,14 @@ public class proxy : IHttpHandler { return true; //Copy the header info and the content to the reponse to client - copyHeaders(serverResponse, clientResponse); + copyResponseHeaders(serverResponse, clientResponse); clientResponse.Write(strResponse); } } else { // Binary response (image, lyr file, other binary file) //Copy the header info to the reponse to client - copyHeaders(serverResponse, clientResponse); + copyResponseHeaders(serverResponse, clientResponse); // Tell client not to cache the image since it's dynamic clientResponse.CacheControl = "no-cache"; byte[] buffer = new byte[32768]; @@ -470,16 +550,20 @@ public class proxy : IHttpHandler { } } - return doHTTPRequest(uri, bytes, method, PROXY_REFERER, contentType, credentials); + System.Net.HttpWebRequest req = createHTTPRequest(uri, method, contentType, credentials); + req.Referer = PROXY_REFERER; + writeRequestPostBody(req, bytes); + return req.GetResponse(); } - private System.Net.WebResponse doHTTPRequest(string uri, byte[] bytes, string method, string referer, string contentType, System.Net.NetworkCredential credentials = null) + private System.Net.HttpWebRequest createHTTPRequest(string uri, string method, string contentType, System.Net.NetworkCredential credentials = null) { ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(uri); req.ServicePoint.Expect100Continue = false; - req.Referer = referer; req.Method = method; + if (method == "POST") + req.ContentType = string.IsNullOrEmpty(contentType) ? "application/x-www-form-urlencoded" : contentType; // Use the default system proxy req.Proxy = SYSTEM_PROXY; @@ -487,16 +571,7 @@ public class proxy : IHttpHandler { if (credentials != null) req.Credentials = credentials; - if (bytes != null && bytes.Length > 0 || method == "POST") { - req.Method = "POST"; - req.ContentType = string.IsNullOrEmpty(contentType) ? "application/x-www-form-urlencoded" : contentType; - if (bytes != null && bytes.Length > 0) - req.ContentLength = bytes.Length; - using (Stream outputStream = req.GetRequestStream()) { - outputStream.Write(bytes, 0, bytes.Length); - } - } - return req.GetResponse(); + return req; } private string webResponseToString(System.Net.WebResponse serverResponse) {