// Wrapper for Run which takes care of CloudFlare challenges, calls RunCurl override protected async Task <WebClientByteResult> Run(WebRequest request) { WebClientByteResult result = await RunCurl(request); // check if we've received a CloudFlare challenge string[] server; if (result.Status == HttpStatusCode.ServiceUnavailable && result.Headers.TryGetValue("server", out server) && (server[0] == "cloudflare-nginx" || server[0] == "cloudflare")) { logger.Info("UnixLibCurlWebClient: Received a new CloudFlare challenge"); // solve the challenge string pageContent = Encoding.UTF8.GetString(result.Content); Uri uri = new Uri(request.Url); string clearanceUri = CloudFlareChallengeSolverSolve(pageContent, uri); logger.Info(string.Format("UnixLibCurlWebClient: CloudFlare clearanceUri: {0}", clearanceUri)); // wait... await Task.Delay(5000); // request clearanceUri to get cf_clearance cookie var response = await CurlHelper.GetAsync(clearanceUri, serverConfig, request.Cookies, request.Referer); logger.Info(string.Format("UnixLibCurlWebClient: received CloudFlare clearance cookie: {0}", response.Cookies)); // add new cf_clearance cookies to the original request request.Cookies = response.Cookies + request.Cookies; // re-run the original request with updated cf_clearance cookie result = await RunCurl(request); // add cf_clearance cookie to the final result so we update the config for the next request result.Cookies = response.Cookies + " " + result.Cookies; } return(result); }
override protected async Task <WebClientByteResult> Run(WebRequest webRequest) { ServicePointManager.SecurityProtocol = (SecurityProtocolType)192 | (SecurityProtocolType)768 | (SecurityProtocolType)3072; var cookies = new CookieContainer(); if (!string.IsNullOrEmpty(webRequest.Cookies)) { var uri = new Uri(webRequest.Url); var cookieUrl = new Uri(uri.Scheme + "://" + uri.Host); // don't include the path, Scheme is needed for mono compatibility foreach (var c in webRequest.Cookies.Split(';')) { try { cookies.SetCookies(cookieUrl, c.Trim()); } catch (CookieException ex) { logger.Info("(Non-critical) Problem loading cookie {0}, {1}, {2}", uri, c, ex.Message); } } } using (ClearanceHandler clearanceHandlr = new ClearanceHandler()) { clearanceHandlr.ClearanceDelay = 7000; // 2018/03/22: something odd is going on with cloudflare, for a few users higher delays are needed (depending on which server you end up?) using (HttpClientHandler clientHandlr = new HttpClientHandler { CookieContainer = cookies, AllowAutoRedirect = false, // Do not use this - Bugs ahoy! Lost cookies and more. UseCookies = true, Proxy = webProxy, UseProxy = (webProxy != null), AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }) { clearanceHandlr.InnerHandler = clientHandlr; using (var client = new HttpClient(clearanceHandlr)) { if (webRequest.EmulateBrowser == true) { client.DefaultRequestHeaders.Add("User-Agent", BrowserUtil.ChromeUserAgent); } else { client.DefaultRequestHeaders.Add("User-Agent", "Jackett/" + configService.GetVersion()); } HttpResponseMessage response = null; using (var request = new HttpRequestMessage()) { request.Headers.ExpectContinue = false; request.RequestUri = new Uri(webRequest.Url); if (webRequest.Headers != null) { foreach (var header in webRequest.Headers) { if (header.Key != "Content-Type") { request.Headers.TryAddWithoutValidation(header.Key, header.Value); } } } if (!string.IsNullOrEmpty(webRequest.Referer)) { request.Headers.Referrer = new Uri(webRequest.Referer); } if (!string.IsNullOrEmpty(webRequest.RawBody)) { var type = webRequest.Headers.Where(h => h.Key == "Content-Type").Cast <KeyValuePair <string, string>?>().FirstOrDefault(); if (type.HasValue) { var str = new StringContent(webRequest.RawBody); str.Headers.Remove("Content-Type"); str.Headers.Add("Content-Type", type.Value.Value); request.Content = str; } else { request.Content = new StringContent(webRequest.RawBody); } request.Method = HttpMethod.Post; } else if (webRequest.Type == RequestType.POST) { if (webRequest.PostData != null) { request.Content = new FormUrlEncodedContent(webRequest.PostData); } request.Method = HttpMethod.Post; } else { request.Method = HttpMethod.Get; } using (response = await client.SendAsync(request)) { var result = new WebClientByteResult { Content = await response.Content.ReadAsByteArrayAsync() }; foreach (var header in response.Headers) { IEnumerable <string> value = header.Value; result.Headers[header.Key.ToLowerInvariant()] = value.ToArray(); } // some cloudflare clients are using a refresh header // Pull it out manually if (response.StatusCode == HttpStatusCode.ServiceUnavailable && response.Headers.Contains("Refresh")) { var refreshHeaders = response.Headers.GetValues("Refresh"); var redirval = ""; var redirtime = 0; if (refreshHeaders != null) { foreach (var value in refreshHeaders) { var start = value.IndexOf("="); var end = value.IndexOf(";"); var len = value.Length; if (start > -1) { redirval = value.Substring(start + 1); result.RedirectingTo = redirval; // normally we don't want a serviceunavailable (503) to be a redirect, but that's the nature // of this cloudflare approach..don't want to alter BaseWebResult.IsRedirect because normally // it shoudln't include service unavailable..only if we have this redirect header. response.StatusCode = System.Net.HttpStatusCode.Redirect; redirtime = Int32.Parse(value.Substring(0, end)); System.Threading.Thread.Sleep(redirtime * 1000); } } } } if (response.Headers.Location != null) { result.RedirectingTo = response.Headers.Location.ToString(); } // Mono won't add the baseurl to relative redirects. // e.g. a "Location: /index.php" header will result in the Uri "file:///index.php" // See issue #1200 if (result.RedirectingTo != null && result.RedirectingTo.StartsWith("file://")) { var newRedirectingTo = result.RedirectingTo.Replace("file://", request.RequestUri.Scheme + "://" + request.RequestUri.Host); logger.Debug("[MONO relative redirect bug] Rewriting relative redirect URL from " + result.RedirectingTo + " to " + newRedirectingTo); result.RedirectingTo = newRedirectingTo; } result.Status = response.StatusCode; // Compatiblity issue between the cookie format and httpclient // Pull it out manually ignoring the expiry date then set it manually // http://stackoverflow.com/questions/14681144/httpclient-not-storing-cookies-in-cookiecontainer IEnumerable <string> cookieHeaders; var responseCookies = new List <Tuple <string, string> >(); if (response.Headers.TryGetValues("set-cookie", out cookieHeaders)) { foreach (var value in cookieHeaders) { var nameSplit = value.IndexOf('='); if (nameSplit > -1) { responseCookies.Add(new Tuple <string, string>(value.Substring(0, nameSplit), value.Substring(0, value.IndexOf(';') == -1 ? value.Length : (value.IndexOf(';'))) + ";")); } } var cookieBuilder = new StringBuilder(); foreach (var cookieGroup in responseCookies.GroupBy(c => c.Item1)) { cookieBuilder.AppendFormat("{0} ", cookieGroup.Last().Item2); } result.Cookies = cookieBuilder.ToString().Trim(); } ServerUtil.ResureRedirectIsFullyQualified(webRequest, result); return(result); } } } } } }
override protected async Task <WebClientByteResult> Run(WebRequest webRequest) { HttpResponseMessage response = null; var request = new HttpRequestMessage(); request.Headers.ExpectContinue = false; request.RequestUri = new Uri(webRequest.Url); if (webRequest.EmulateBrowser == true) { request.Headers.UserAgent.ParseAdd(BrowserUtil.ChromeUserAgent); } else { request.Headers.UserAgent.ParseAdd("Jackett/" + configService.GetVersion()); } // clear cookies from cookiecontainer var oldCookies = cookies.GetCookies(request.RequestUri); foreach (Cookie oldCookie in oldCookies) { oldCookie.Expired = true; } if (!string.IsNullOrEmpty(webRequest.Cookies)) { // add cookies to cookiecontainer var cookieUrl = new Uri(request.RequestUri.Scheme + "://" + request.RequestUri.Host); // don't include the path, Scheme is needed for mono compatibility foreach (var ccookiestr in webRequest.Cookies.Split(';')) { var cookiestrparts = ccookiestr.Split('='); var name = cookiestrparts[0].Trim(); if (string.IsNullOrWhiteSpace(name)) { continue; } var value = ""; if (cookiestrparts.Length >= 2) { value = cookiestrparts[1].Trim(); } var cookie = new Cookie(name, value); cookies.Add(cookieUrl, cookie); } } if (webRequest.Headers != null) { foreach (var header in webRequest.Headers) { if (header.Key != "Content-Type") { request.Headers.TryAddWithoutValidation(header.Key, header.Value); } } } if (!string.IsNullOrEmpty(webRequest.Referer)) { request.Headers.Referrer = new Uri(webRequest.Referer); } if (!string.IsNullOrEmpty(webRequest.RawBody)) { var type = webRequest.Headers.Where(h => h.Key == "Content-Type").Cast <KeyValuePair <string, string>?>().FirstOrDefault(); if (type.HasValue) { var str = new StringContent(webRequest.RawBody); str.Headers.Remove("Content-Type"); str.Headers.Add("Content-Type", type.Value.Value); request.Content = str; } else { request.Content = new StringContent(webRequest.RawBody); } request.Method = HttpMethod.Post; } else if (webRequest.Type == RequestType.POST) { if (webRequest.PostData != null) { request.Content = new FormUrlEncodedContent(webRequest.PostData); } request.Method = HttpMethod.Post; } else { request.Method = HttpMethod.Get; } response = await client.SendAsync(request); var result = new WebClientByteResult(); result.Content = await response.Content.ReadAsByteArrayAsync(); foreach (var header in response.Headers) { IEnumerable <string> value = header.Value; result.Headers[header.Key.ToLowerInvariant()] = value.ToArray(); } // some cloudflare clients are using a refresh header // Pull it out manually if (response.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable && response.Headers.Contains("Refresh")) { var refreshHeaders = response.Headers.GetValues("Refresh"); var redirval = ""; var redirtime = 0; if (refreshHeaders != null) { foreach (var value in refreshHeaders) { var start = value.IndexOf("="); var end = value.IndexOf(";"); var len = value.Length; if (start > -1) { redirval = value.Substring(start + 1); result.RedirectingTo = redirval; // normally we don't want a serviceunavailable (503) to be a redirect, but that's the nature // of this cloudflare approach..don't want to alter BaseWebResult.IsRedirect because normally // it shoudln't include service unavailable..only if we have this redirect header. response.StatusCode = System.Net.HttpStatusCode.Redirect; redirtime = Int32.Parse(value.Substring(0, end)); System.Threading.Thread.Sleep(redirtime * 1000); } } } } if (response.Headers.Location != null) { result.RedirectingTo = response.Headers.Location.ToString(); } // Mono won't add the baseurl to relative redirects. // e.g. a "Location: /index.php" header will result in the Uri "file:///index.php" // See issue #1200 if (result.RedirectingTo != null && result.RedirectingTo.StartsWith("file://")) { // URL decoding apparently is needed to, without it e.g. Demonoid download is broken // TODO: is it always needed (not just for relative redirects)? var newRedirectingTo = WebUtilityHelpers.UrlDecode(result.RedirectingTo, webRequest.Encoding); newRedirectingTo = newRedirectingTo.Replace("file://", request.RequestUri.Scheme + "://" + request.RequestUri.Host); logger.Debug("[MONO relative redirect bug] Rewriting relative redirect URL from " + result.RedirectingTo + " to " + newRedirectingTo); result.RedirectingTo = newRedirectingTo; } result.Status = response.StatusCode; // Compatiblity issue between the cookie format and httpclient // Pull it out manually ignoring the expiry date then set it manually // http://stackoverflow.com/questions/14681144/httpclient-not-storing-cookies-in-cookiecontainer IEnumerable <string> cookieHeaders; var responseCookies = new List <Tuple <string, string> >(); if (response.Headers.TryGetValues("set-cookie", out cookieHeaders)) { foreach (var value in cookieHeaders) { logger.Debug(value); var nameSplit = value.IndexOf('='); if (nameSplit > -1) { responseCookies.Add(new Tuple <string, string>(value.Substring(0, nameSplit), value.Substring(0, value.IndexOf(';') == -1 ? value.Length : (value.IndexOf(';'))) + ";")); } } var cookieBuilder = new StringBuilder(); foreach (var cookieGroup in responseCookies.GroupBy(c => c.Item1)) { cookieBuilder.AppendFormat("{0} ", cookieGroup.Last().Item2); } result.Cookies = cookieBuilder.ToString().Trim(); } ServerUtil.ResureRedirectIsFullyQualified(webRequest, result); return(result); }
protected async Task <WebClientByteResult> RunCurl(WebRequest request) { CurlHelper.CurlResponse response; if (request.Type == RequestType.GET) { response = await CurlHelper.GetAsync(request.Url, serverConfig, request.Cookies, request.Referer, request.Headers); } else { if (!string.IsNullOrEmpty(request.RawBody)) { logger.Debug("UnixLibCurlWebClient: Posting " + request.RawBody); } else if (request.PostData != null && request.PostData.Count() > 0) { logger.Debug("UnixLibCurlWebClient: Posting " + StringUtil.PostDataFromDict(request.PostData)); } response = await CurlHelper.PostAsync(request.Url, serverConfig, request.PostData, request.Cookies, request.Referer, request.Headers, request.RawBody); } var result = new WebClientByteResult() { Content = response.Content, Cookies = response.Cookies, Status = response.Status }; if (response.HeaderList != null) { foreach (var header in response.HeaderList) { var key = header[0].ToLowerInvariant(); result.Headers[key] = new string[] { header[1] }; // doesn't support multiple identical headers? switch (key) { case "location": result.RedirectingTo = header[1]; break; case "refresh": if (response.Status == System.Net.HttpStatusCode.ServiceUnavailable) { //"Refresh: 8;URL=/cdn-cgi/l/chk_jschl?pass=1451000679.092-1vJFUJLb9R" var redirval = ""; var value = header[1]; var start = value.IndexOf("="); var end = value.IndexOf(";"); var len = value.Length; if (start > -1) { redirval = value.Substring(start + 1); result.RedirectingTo = redirval; // normally we don't want a serviceunavailable (503) to be a redirect, but that's the nature // of this cloudflare approach..don't want to alter BaseWebResult.IsRedirect because normally // it shoudln't include service unavailable..only if we have this redirect header. result.Status = System.Net.HttpStatusCode.Redirect; var redirtime = Int32.Parse(value.Substring(0, end)); System.Threading.Thread.Sleep(redirtime * 1000); } } break; } } } ServerUtil.ResureRedirectIsFullyQualified(request, result); return(result); }
override protected async Task <WebClientByteResult> Run(WebRequest request) { var args = new StringBuilder(); var proxy = serverConfig.GetProxyUrl(true); if (proxy != null) { args.AppendFormat("-x '" + proxy + "' "); } args.AppendFormat("--url \"{0}\" ", request.Url); if (request.EmulateBrowser == true) { args.AppendFormat("-i -sS --user-agent \"{0}\" ", BrowserUtil.ChromeUserAgent); } else { args.AppendFormat("-i -sS --user-agent \"{0}\" ", "Jackett/" + configService.GetVersion()); } if (!string.IsNullOrWhiteSpace(request.Cookies)) { args.AppendFormat("--cookie \"{0}\" ", request.Cookies); } if (!string.IsNullOrWhiteSpace(request.Referer)) { args.AppendFormat("--referer \"{0}\" ", request.Referer); } if (!string.IsNullOrEmpty(request.RawBody)) { var postString = StringUtil.PostDataFromDict(request.PostData); args.AppendFormat("--data \"{0}\" ", request.RawBody.Replace("\"", "\\\"")); } else if (request.PostData != null && request.PostData.Count() > 0) { var postString = StringUtil.PostDataFromDict(request.PostData); args.AppendFormat("--data \"{0}\" ", postString); } var tempFile = Path.GetTempFileName(); args.AppendFormat("--output \"{0}\" ", tempFile); if (serverConfig.RuntimeSettings.DoSSLFix == true) { // http://stackoverflow.com/questions/31107851/how-to-fix-curl-35-cannot-communicate-securely-with-peer-no-common-encryptio // https://git.fedorahosted.org/cgit/mod_nss.git/plain/docs/mod_nss.html args.Append("--cipher " + SSLFix.CipherList); } if (serverConfig.RuntimeSettings.IgnoreSslErrors == true) { args.Append("-k "); } args.Append("-H \"Accept-Language: en-US,en\" "); args.Append("-H \"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\" "); string stdout = null; await Task.Run(() => { stdout = processService.StartProcessAndGetOutput(System.Environment.OSVersion.Platform == PlatformID.Unix ? "curl" : "curl.exe", args.ToString(), true); }); var outputData = File.ReadAllBytes(tempFile); File.Delete(tempFile); stdout = Encoding.UTF8.GetString(outputData); var result = new WebClientByteResult(); var headSplit = stdout.IndexOf("\r\n\r\n"); if (headSplit < 0) { throw new Exception("Invalid response"); } var headers = stdout.Substring(0, headSplit); if (serverConfig.RuntimeSettings.ProxyConnection != null) { // the proxy provided headers too so we need to split headers again var headSplit1 = stdout.IndexOf("\r\n\r\n", headSplit + 4); if (headSplit1 > 0) { headers = stdout.Substring(headSplit + 4, headSplit1 - (headSplit + 4)); headSplit = headSplit1; } } var headerCount = 0; var cookieBuilder = new StringBuilder(); var cookies = new List <Tuple <string, string> >(); foreach (var header in headers.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)) { if (headerCount == 0) { var responseCode = int.Parse(header.Split(' ')[1]); result.Status = (HttpStatusCode)responseCode; } else { var headerSplitIndex = header.IndexOf(':'); if (headerSplitIndex > 0) { var name = header.Substring(0, headerSplitIndex).ToLowerInvariant(); var value = header.Substring(headerSplitIndex + 1); switch (name) { case "set-cookie": var nameSplit = value.IndexOf('='); if (nameSplit > -1) { cookies.Add(new Tuple <string, string>(value.Substring(0, nameSplit), value.Substring(0, value.IndexOf(';') + 1))); } break; case "location": result.RedirectingTo = value.Trim(); break; case "refresh": //"Refresh: 8;URL=/cdn-cgi/l/chk_jschl?pass=1451000679.092-1vJFUJLb9R" var redirval = ""; var start = value.IndexOf("="); var end = value.IndexOf(";"); var len = value.Length; if (start > -1) { redirval = value.Substring(start + 1); result.RedirectingTo = redirval; // normally we don't want a serviceunavailable (503) to be a redirect, but that's the nature // of this cloudflare approach..don't want to alter BaseWebResult.IsRedirect because normally // it shoudln't include service unavailable..only if we have this redirect header. result.Status = System.Net.HttpStatusCode.Redirect; var redirtime = Int32.Parse(value.Substring(0, end)); System.Threading.Thread.Sleep(redirtime * 1000); } break; } } } headerCount++; } foreach (var cookieGroup in cookies.GroupBy(c => c.Item1)) { cookieBuilder.AppendFormat("{0} ", cookieGroup.Last().Item2); } result.Cookies = cookieBuilder.ToString().Trim(); result.Content = new byte[outputData.Length - (headSplit + 3)]; var dest = 0; for (int i = headSplit + 4; i < outputData.Length; i++) { result.Content[dest] = outputData[i]; dest++; } logger.Debug("WebClientByteResult returned " + result.Status); ServerUtil.ResureRedirectIsFullyQualified(request, result); return(result); }
protected override async Task <WebClientByteResult> Run(WebRequest webRequest) { ServicePointManager.SecurityProtocol = (SecurityProtocolType)192 | (SecurityProtocolType)768 | (SecurityProtocolType)3072; var cookies = new CookieContainer(); if (!string.IsNullOrWhiteSpace(webRequest.Cookies)) { // don't include the path, Scheme is needed for mono compatibility var requestUri = new Uri(webRequest.Url); var cookieUrl = new Uri(requestUri.Scheme + "://" + requestUri.Host); var cookieDictionary = CookieUtil.CookieHeaderToDictionary(webRequest.Cookies); foreach (var kv in cookieDictionary) { cookies.Add(cookieUrl, new Cookie(kv.Key, kv.Value)); } } var userAgent = webRequest.EmulateBrowser.Value ? BrowserUtil.ChromeUserAgent : "Jackett/" + configService.GetVersion(); using (var clearanceHandlr = new ClearanceHandler(userAgent)) { clearanceHandlr.MaxTries = 10; using (var clientHandlr = new HttpClientHandler { CookieContainer = cookies, AllowAutoRedirect = false, // Do not use this - Bugs ahoy! Lost cookies and more. UseCookies = true, Proxy = webProxy, UseProxy = (webProxy != null), AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }) { // custom certificate validation handler (netcore version) clientHandlr.ServerCertificateCustomValidationCallback = ValidateCertificate; clearanceHandlr.InnerHandler = clientHandlr; using (var client = new HttpClient(clearanceHandlr)) { //if (webRequest.EmulateBrowser == true) // client.DefaultRequestHeaders.Add("User-Agent", BrowserUtil.ChromeUserAgent); //else // client.DefaultRequestHeaders.Add("User-Agent", "Jackett/" + configService.GetVersion()); HttpResponseMessage response = null; using (var request = new HttpRequestMessage()) { request.Headers.ExpectContinue = false; request.RequestUri = new Uri(webRequest.Url); if (webRequest.Headers != null) { foreach (var header in webRequest.Headers) { if (header.Key != "Content-Type") { request.Headers.TryAddWithoutValidation(header.Key, header.Value); } } } if (!string.IsNullOrEmpty(webRequest.Referer)) { request.Headers.Referrer = new Uri(webRequest.Referer); } if (!string.IsNullOrEmpty(webRequest.RawBody)) { var type = webRequest.Headers.Where(h => h.Key == "Content-Type").Cast <KeyValuePair <string, string>?>().FirstOrDefault(); if (type.HasValue) { var str = new StringContent(webRequest.RawBody); str.Headers.Remove("Content-Type"); str.Headers.Add("Content-Type", type.Value.Value); request.Content = str; } else { request.Content = new StringContent(webRequest.RawBody); } request.Method = HttpMethod.Post; } else if (webRequest.Type == RequestType.POST) { if (webRequest.PostData != null) { request.Content = FormUrlEncodedContentWithEncoding(webRequest.PostData, webRequest.Encoding); } request.Method = HttpMethod.Post; } else { request.Method = HttpMethod.Get; } using (response = await client.SendAsync(request)) { var result = new WebClientByteResult { Content = await response.Content.ReadAsByteArrayAsync() }; foreach (var header in response.Headers) { var value = header.Value; result.Headers[header.Key.ToLowerInvariant()] = value.ToArray(); } // some cloudflare clients are using a refresh header // Pull it out manually if (response.StatusCode == HttpStatusCode.ServiceUnavailable && response.Headers.Contains("Refresh")) { var refreshHeaders = response.Headers.GetValues("Refresh"); var redirval = ""; var redirtime = 0; if (refreshHeaders != null) { foreach (var value in refreshHeaders) { var start = value.IndexOf("="); var end = value.IndexOf(";"); var len = value.Length; if (start > -1) { redirval = value.Substring(start + 1); result.RedirectingTo = redirval; // normally we don't want a serviceunavailable (503) to be a redirect, but that's the nature // of this cloudflare approach..don't want to alter BaseWebResult.IsRedirect because normally // it shoudln't include service unavailable..only if we have this redirect header. response.StatusCode = System.Net.HttpStatusCode.Redirect; redirtime = int.Parse(value.Substring(0, end)); System.Threading.Thread.Sleep(redirtime * 1000); } } } } if (response.Headers.Location != null) { result.RedirectingTo = response.Headers.Location.ToString(); } // Mono won't add the baseurl to relative redirects. // e.g. a "Location: /index.php" header will result in the Uri "file:///index.php" // See issue #1200 if (result.RedirectingTo != null && result.RedirectingTo.StartsWith("file://")) { // URL decoding apparently is needed to, without it e.g. Demonoid download is broken // TODO: is it always needed (not just for relative redirects)? var newRedirectingTo = WebUtilityHelpers.UrlDecode(result.RedirectingTo, webRequest.Encoding); if (newRedirectingTo.StartsWith("file:////")) // Location without protocol but with host (only add scheme) { newRedirectingTo = newRedirectingTo.Replace("file://", request.RequestUri.Scheme + ":"); } else { newRedirectingTo = newRedirectingTo.Replace("file://", request.RequestUri.Scheme + "://" + request.RequestUri.Host); } logger.Debug("[MONO relative redirect bug] Rewriting relative redirect URL from " + result.RedirectingTo + " to " + newRedirectingTo); result.RedirectingTo = newRedirectingTo; } result.Status = response.StatusCode; // Compatiblity issue between the cookie format and httpclient // Pull it out manually ignoring the expiry date then set it manually // http://stackoverflow.com/questions/14681144/httpclient-not-storing-cookies-in-cookiecontainer var responseCookies = new List <Tuple <string, string> >(); if (response.Headers.TryGetValues("set-cookie", out var cookieHeaders)) { foreach (var value in cookieHeaders) { var nameSplit = value.IndexOf('='); if (nameSplit > -1) { responseCookies.Add(new Tuple <string, string>(value.Substring(0, nameSplit), value.Substring(0, value.IndexOf(';') == -1 ? value.Length : (value.IndexOf(';'))) + ";")); } } var cookieBuilder = new StringBuilder(); foreach (var cookieGroup in responseCookies.GroupBy(c => c.Item1)) { cookieBuilder.AppendFormat("{0} ", cookieGroup.Last().Item2); } result.Cookies = cookieBuilder.ToString().Trim(); } ServerUtil.ResureRedirectIsFullyQualified(webRequest, result); return(result); } } } } } }