Return(HttpOptions options, Func <HttpConfig, HttpConfig> configurer, Func <IHttpObservable, IObservable <HttpFetch <HttpContent> > > query, Func <HttpFetchInfo, bool> predicate) => new Impl(query, options, configurer, predicate);
public IHttpObservable WithOptions(HttpOptions options) => new Impl(_query, options, Configurer, FilterPredicate);
static async Task <T> HttpFetchAsync <T>(IHttpClient http, HttpConfig config, HttpMethod method, Uri url, HttpContent content, HttpOptions options, Func <HttpConfig, HttpResponseMessage, T> responseSelector, Func <HttpConfig, HttpMethod, Uri, HttpContent, T> redirectionSelector) { var request = new HttpRequestMessage { Method = method, RequestUri = url, Content = content }; HttpResponseMessage response = null; try { response = await http.SendAsync(request, config) .DontContinueOnCapturedContext(); IEnumerable <string> setCookies; if (response.Headers.TryGetValues("Set-Cookie", out setCookies)) { var cc = new CookieContainer(); foreach (var cookie in setCookies) { try { cc.SetCookies(url, cookie); } catch (CookieException) { /* ignore bad cookies */ } } var mergedCookies = from cookies in new[] { http.Config.Cookies ?? Enumerable.Empty <Cookie>(), cc.GetCookies(url).Cast <Cookie>(), } from c in cookies // // According to RFC 6265[1], "cookies for a given host // are shared across all the ports on that host" so // don't take Cookie.Port into account when grouping. // It is also assumed that Cookie.Domain // // [1] https://tools.ietf.org/html/rfc6265#section-1 // group c by new { c.Name, Domain = c.Domain.ToLowerInvariant(), c.Path } into g select g.OrderByDescending(e => e.TimeStamp).First(); config = config.WithCookies(mergedCookies.ToArray()); } // Source: // https://referencesource.microsoft.com/#System/net/System/Net/HttpWebRequest.cs,5669 // // Check for Redirection // // Table View: // Method 301 302 303 307 // * * * GET * // POST GET GET GET POST // // Put another way: // 301 & 302 - All methods are redirected to the same method but POST. POST is redirected to a GET. // 303 - All methods are redirected to GET // 307 - All methods are redirected to the same method. // var sc = response.StatusCode; if (sc == HttpStatusCode.Ambiguous || // 300 sc == HttpStatusCode.Moved || // 301 sc == HttpStatusCode.Redirect || // 302 sc == HttpStatusCode.RedirectMethod || // 303 sc == HttpStatusCode.RedirectKeepVerb) // 307 { var redirectionUrl = response.Headers.Location?.AsRelativeTo(response.RequestMessage.RequestUri); if (redirectionUrl == null) { // 300 // If the server has a preferred choice of representation, // it SHOULD include the specific URI for that // representation in the Location field; user agents MAY // use the Location field value for automatic redirection. if (sc != HttpStatusCode.Ambiguous) { throw new ProtocolViolationException("Server did not supply a URL for a redirection response."); } } else { if (redirectionUrl.Scheme == "ws" || redirectionUrl.Scheme == "wss") { throw new NotSupportedException($"Redirection to a WebSocket URL ({redirectionUrl}) is not supported."); } if (redirectionUrl.Scheme != Uri.UriSchemeHttp && redirectionUrl.Scheme != Uri.UriSchemeHttps) { throw new ProtocolViolationException( $"Server sent a redirection response where the redirection URL ({redirectionUrl}) scheme was neither HTTP nor HTTPS."); } return(sc == HttpStatusCode.RedirectMethod || method == HttpMethod.Post && (sc == HttpStatusCode.Moved || sc == HttpStatusCode.Redirect) ? redirectionSelector(config, HttpMethod.Get, redirectionUrl, null) : redirectionSelector(config, method, redirectionUrl, content)); } } if (!options.ReturnErroneousFetch) { response.EnsureSuccessStatusCode(); } var result = responseSelector(config, response); response = null; // disown return(result); } finally { response?.Dispose(); } }
static async Task <HttpFetch <HttpContent> > SendAsync(IHttpClient http, HttpConfig config, int id, HttpMethod method, Uri url, HttpContent content = null, HttpOptions options = null) { http = http.WithConfig(config); for (var redirections = 0; ; redirections++) { if (redirections > MaximumAutomaticRedirections) { throw new Exception("The maximum number of redirection responses permitted has been exceeded."); } var result = await HttpFetchAsync(http, http.Config, method, url, content, options, (cfg, rsp) => new { Config = cfg, Method = default(HttpMethod), Url = default(Uri), Content = default(HttpContent), Response = rsp, }, (cfg, rm, rl, rc) => new { Config = cfg, Method = rm, Url = rl, Content = rc, Response = default(HttpResponseMessage), }) .DontContinueOnCapturedContext(); if (result.Response != null) { return(result.Response.ToHttpFetch(id, http.WithConfig(result.Config))); } // TODO tail call recursion? http = http.WithConfig(result.Config); method = result.Method; url = result.Url; content = result.Content; } }
static async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpConfig config, HttpOptions options) { var hwreq = WebRequest.CreateHttp(request.RequestUri); hwreq.Method = request.Method.Method; hwreq.Timeout = (int)config.Timeout.TotalMilliseconds; hwreq.CookieContainer = config.Cookies; hwreq.Credentials = config.Credentials; hwreq.UseDefaultCredentials = config.UseDefaultCredentials; var userAgent = request.Headers.UserAgent.ToString(); hwreq.UserAgent = userAgent.Length > 0 ? userAgent : config.UserAgent; var referrer = request.Headers.Referrer; if (referrer != null) { hwreq.Referer = referrer.ToString(); } var accept = request.Headers.Accept.ToString(); if (accept.Length > 0) { hwreq.Accept = accept; } var content = request.Content; foreach (var e in from e in request.Headers.Concat(content?.Headers ?? Enumerable.Empty <KeyValuePair <string, IEnumerable <string> > >()) where !e.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase) && !e.Key.Equals("User-Agent", StringComparison.OrdinalIgnoreCase) && !e.Key.Equals("Referer", StringComparison.OrdinalIgnoreCase) && !e.Key.Equals("Accept", StringComparison.OrdinalIgnoreCase) from v in e.Value select e.Key.AsKeyTo(v)) { hwreq.Headers.Add(e.Key, e.Value); } HttpWebResponse hwrsp = null; try { if (content != null) { // ReSharper disable once PossibleNullReferenceException hwreq.ContentType = content.Headers.ContentType.ToString(); using (var s = hwreq.GetRequestStream()) await content.CopyToAsync(s); } return(await CreateResponse(hwreq, hwrsp = (HttpWebResponse)hwreq.GetResponse())); } catch (WebException e) when(e.Status == WebExceptionStatus.ProtocolError) { if (!options.ReturnErrorneousFetch) { throw; } return(await CreateResponse(hwreq, hwrsp = (HttpWebResponse)e.Response)); } finally { hwrsp?.Dispose(); } }
public virtual HttpResponseMessage Send(HttpRequestMessage request, HttpConfig config, HttpOptions options) => SendAsync(request, config, options).Result;