/// <summary> /// Execute a request for the given requestModel asynchronously, executing the given callback upon completions /// other than cancellations (gui updates for cancellation should be done at point of cancellation). /// </summary> /// <param name="requestModel"></param> /// <param name="callback"></param> /// <returns></returns> public CancellationTokenSource ExecuteAsync(RequestModel requestModel, Action <ResponseModel> callback) { //todo: add using statements for disposible objects var hasCookies = requestModel.RequestHeaders.ContainsKey("cookie"); var hasExpect100ContinueHeader = requestModel.RequestHeaders.ContainsKey("expect") && requestModel.RequestHeaders["expect"].Equals("100-continue", StringComparison.OrdinalIgnoreCase); var handler = new HttpClientHandler { AllowAutoRedirect = followRedirects, UseCookies = hasCookies, UseDefaultCredentials = true }; if (!proxyServer.IsBlank()) { handler.Proxy = new WebProxy(proxyServer, false); //make second arg a config option. handler.UseProxy = true; } if (enableAutomaticContentDecompression) { foreach (var enc in requestModel.AcceptEncodings) { switch (enc) { case "gzip": handler.AutomaticDecompression |= DecompressionMethods.GZip; break; case "deflate": handler.AutomaticDecompression |= DecompressionMethods.Deflate; break; default: log.Warn("Automatic decompression of '{0}' content specified by the Accept-Encoding request header is not supported", enc); break; } } } // 20191203 William Velasquez http://creativosdigitales.co // Force TLS 1.2 Connection ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; var client = new HttpClient(handler); var request = new HttpRequestMessage { RequestUri = requestModel.Url, Method = requestModel.Method }; foreach (var header in requestModel.RequestHeaders) { if (header.Key.Equals("cookie", StringComparison.OrdinalIgnoreCase)) { handler.CookieContainer.SetCookies(requestModel.Url, header.Value.Replace("; ", ", ")); } else { request.Headers.Add(header.Key, header.Value); } } request.Headers.ExpectContinue = hasExpect100ContinueHeader; if (!requestModel.Url.UserInfo.IsBlank()) { var userInfoBase64Text = Base64EncodeUrlUserInfo(requestModel.Url); request.Headers.Authorization = new AuthenticationHeaderValue("Basic", userInfoBase64Text); } if (requestModel.Method != HttpMethod.Get && requestModel.Method != HttpMethod.Head && !requestModel.Body.IsBlank()) { //default content-type: http://mattryall.net/blog/2008/03/default-content-type string textCt; requestModel.ContentHeaders.TryGetValue("Content-Type", out textCt); textCt = textCt.IsBlank() ? defaultRequestContentType : textCt; //then try settings supplied textCt = textCt.IsBlank() ? "application/octet-stream" : textCt; // then try w3 spec default //get encoding var ct = new ContentType(textCt); //write content w/ BOM if needed var contentBytes = GetEncodedBytes(requestModel.Body, ct.CharSet, includeUtf8Bom); var content = new ByteArrayContent(contentBytes); foreach (var header in requestModel.ContentHeaders) { content.Headers.Remove(header.Key); //remove defaults if (String.Equals(header.Key, "content-type", StringComparison.OrdinalIgnoreCase)) //treat special w/ defaults, etc. { content.Headers.Add(header.Key, textCt); } else { content.Headers.Add(header.Key, header.Value); } } request.Content = content; } var start = DateTime.Now; var ctokenSource = new CancellationTokenSource(); var ctoken = ctokenSource.Token; client.SendAsync(request, ctoken).ContinueWith(responseTask => { try { var end = DateTime.Now; switch (responseTask.Status) { case TaskStatus.RanToCompletion: { var response = responseTask.Result; var responseModel = new ResponseModel(response, start, end); callback(responseModel); break; } case TaskStatus.Canceled: { log.Info("request canceled by user"); break; } case TaskStatus.Faulted: { var aggException = responseTask.Exception.Flatten(); foreach (var exception in aggException.InnerExceptions) { log.Error(exception, "request terminated with an error"); } string errMessage = String.Join(Environment.NewLine, aggException.InnerExceptions); var responseModel = new ResponseModel(errMessage, start, end); callback(responseModel); break; } default: { var errMessage = String.Format("The request terminated with an unexpected status={0}", responseTask.Status); log.Warn(errMessage); var responseModel = new ResponseModel(errMessage, start, end); callback(responseModel); break; } } } catch (Exception ex) { log.Error(ex, "exception raised in request continuation, application will proceed in a corrupt state until Task is disposed, at which point the application will shut down with a fatal exception"); throw; } }); return(ctokenSource); }
private void bind(ResponseModel responseVm) { this.lastResponseModel = responseVm; lblResponseStatusValue.Text = responseVm.Status; lnkResponseStatusInfo.Visible = !responseVm.ErrorMessage.IsBlank(); lnkCancelRequest.Visible = responseVm.Status == ResponseModel.Loading.Status; txtResponseHeaders.Text = responseVm.Headers; var elapsedMs = responseVm.ElapsedMilliseconds; lblResponseTimeValue.Text = elapsedMs == 0 ? "" : elapsedMs + " ms"; updateResponseBodyOutput(); }
/// <summary> /// Execute a request for the given requestModel asynchronously, executing the given callback upon completions /// other than cancellations (gui updates for cancellation should be done at point of cancellation). /// </summary> /// <param name="requestModel"></param> /// <param name="callback"></param> /// <returns></returns> public CancellationTokenSource ExecuteAsync(RequestModel requestModel, Action<ResponseModel> callback) { //todo: add using statements for disposible objects var hasCookies = requestModel.RequestHeaders.ContainsKey("cookie"); var hasExpect100ContinueHeader = requestModel.RequestHeaders.ContainsKey("expect") && requestModel.RequestHeaders["expect"].Equals("100-continue", StringComparison.OrdinalIgnoreCase); var handler = new HttpClientHandler { AllowAutoRedirect = followRedirects, UseCookies = hasCookies }; if (!proxyServer.IsBlank()) { handler.Proxy = new WebProxy(proxyServer, false); //make second arg a config option. handler.UseProxy = true; } if (enableAutomaticContentDecompression) { foreach (var enc in requestModel.AcceptEncodings) { switch (enc) { case "gzip": handler.AutomaticDecompression |= DecompressionMethods.GZip; break; case "deflate": handler.AutomaticDecompression |= DecompressionMethods.Deflate; break; default: log.Warn("Automatic decompression of '{0}' content specified by the Accept-Encoding request header is not supported", enc); break; } } } var client = new HttpClient(handler); var request = new HttpRequestMessage { RequestUri = requestModel.Url, Method = requestModel.Method }; foreach (var header in requestModel.RequestHeaders) { if(header.Key.Equals("cookie", StringComparison.OrdinalIgnoreCase)) handler.CookieContainer.SetCookies(requestModel.Url, header.Value.Replace("; ", ", ")); else request.Headers.Add(header.Key, header.Value); } request.Headers.ExpectContinue = hasExpect100ContinueHeader; if (!requestModel.Url.UserInfo.IsBlank()) { var userInfoBase64Text = Base64EncodeUrlUserInfo(requestModel.Url); request.Headers.Authorization = new AuthenticationHeaderValue("Basic", userInfoBase64Text); } if (requestModel.Method != HttpMethod.Get && requestModel.Method != HttpMethod.Head && !requestModel.Body.IsBlank()) { //default content-type: http://mattryall.net/blog/2008/03/default-content-type string textCt; requestModel.ContentHeaders.TryGetValue("Content-Type", out textCt); textCt = textCt.IsBlank() ? defaultRequestContentType : textCt; //then try settings supplied textCt = textCt.IsBlank() ? "application/octet-stream" : textCt; // then try w3 spec default //get encoding var ct = new ContentType(textCt); //write content w/ BOM if needed var contentBytes = GetEncodedBytes(requestModel.Body, ct.CharSet, includeUtf8Bom); var content = new ByteArrayContent(contentBytes); foreach (var header in requestModel.ContentHeaders) { content.Headers.Remove(header.Key); //remove defaults if (String.Equals(header.Key, "content-type", StringComparison.OrdinalIgnoreCase)) //treat special w/ defaults, etc. content.Headers.Add(header.Key, textCt); else content.Headers.Add(header.Key, header.Value); } request.Content = content; } var start = DateTime.Now; var ctokenSource = new CancellationTokenSource(); var ctoken = ctokenSource.Token; client.SendAsync(request,ctoken).ContinueWith(responseTask => { try { var end = DateTime.Now; switch (responseTask.Status) { case TaskStatus.RanToCompletion: { var response = responseTask.Result; var responseModel = new ResponseModel(response, start, end); callback(responseModel); break; } case TaskStatus.Canceled: { log.Info("request canceled by user"); break; } case TaskStatus.Faulted: { var aggException = responseTask.Exception.Flatten(); foreach (var exception in aggException.InnerExceptions) log.Error("request terminated with an error", exception); string errMessage = String.Join(Environment.NewLine, aggException.InnerExceptions); var responseModel = new ResponseModel(errMessage, start, end); callback(responseModel); break; } default: { var errMessage = String.Format("The request terminated with an unexpected status={0}", responseTask.Status); log.Warn(errMessage); var responseModel = new ResponseModel(errMessage, start, end); callback(responseModel); break; } } } catch(Exception ex) { log.Error("exception raised in request continuation, application will proceed in a corrupt state until Task is disposed, at which point the application will shut down with a fatal exception", ex); throw; } }); return ctokenSource; }
private void addRequestResponseSnapshot(RequestViewModel requestVm, ResponseModel responseModel) { //update the model snapshots.Add(new RequestResponseSnapshot() { request = requestVm, response = responseModel }); //update the view bindSnapshots(); }