// Copyright (c) ServiceStack, Inc. All Rights Reserved. // License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Threading.Tasks; using System.Reflection; using System.Threading; using ServiceStack.Auth; using ServiceStack.Logging; using ServiceStack.Messaging; using ServiceStack.Text; using ServiceStack.Web; #if !(__IOS__ || SL5) #endif #if SL5SendOneWay using ServiceStack.Text; #endif namespace ServiceStack { /** * Need to provide async request options * http://msdn.microsoft.com/en-us/library/86wf6409(VS.71).aspx */ public abstract class ServiceClientBase : IServiceClient, IMessageProducer, IHasCookieContainer, IServiceClientMeta { private static readonly ILog log = LogManager.GetLogger(typeof(ServiceClientBase)); private AuthenticationInfo authInfo = null; /// /// The request filter is called before any request. /// This request filter is executed globally. /// private static Action globalRequestFilter; public static Action GlobalRequestFilter { get { return globalRequestFilter; } set { globalRequestFilter = value; AsyncServiceClient.GlobalRequestFilter = value; } } /// /// The response action is called once the server response is available. /// It will allow you to access raw response information. /// This response action is executed globally. /// Note that you should NOT consume the response stream as this is handled by ServiceStack /// private static Action globalResponseFilter; public static Action GlobalResponseFilter { get { return globalResponseFilter; } set { globalResponseFilter = value; AsyncServiceClient.GlobalResponseFilter = value; } } /// /// Gets the collection of headers to be added to outgoing requests. /// public INameValueCollection Headers { get; private set; } public const string DefaultHttpMethod = HttpMethods.Post; public static string DefaultUserAgent = "ServiceStack .NET Client " + Env.ServiceStackVersion; readonly AsyncServiceClient asyncClient; protected ServiceClientBase() { this.HttpMethod = DefaultHttpMethod; this.Headers = PclExportClient.Instance.NewNameValueCollection(); asyncClient = new AsyncServiceClient { ContentType = ContentType, StreamSerializer = AsyncSerializeToStream, StreamDeserializer = AsyncDeserializeFromStream, UserName = this.UserName, Password = this.Password, RequestFilter = this.RequestFilter, ResponseFilter = this.ResponseFilter, ResultsFilter = this.ResultsFilter, Headers = this.Headers, }; this.CookieContainer = new CookieContainer(); this.StoreCookies = true; //leave this.UserAgent = DefaultUserAgent; asyncClient.HandleCallbackOnUiThread = this.HandleCallbackOnUiThread = true; asyncClient.ShareCookiesWithBrowser = this.ShareCookiesWithBrowser = true; JsConfig.InitStatics(); } protected ServiceClientBase(string syncReplyBaseUri, string asyncOneWayBaseUri) : this() { this.SyncReplyBaseUri = syncReplyBaseUri; this.AsyncOneWayBaseUri = asyncOneWayBaseUri; } /// /// Sets all baseUri properties, using the Format property for the SyncReplyBaseUri and AsyncOneWayBaseUri /// /// Base URI of the service public void SetBaseUri(string baseUri) { this.BaseUri = baseUri; this.asyncClient.BaseUri = baseUri; this.SyncReplyBaseUri = baseUri.WithTrailingSlash() + Format + "/reply/"; this.AsyncOneWayBaseUri = baseUri.WithTrailingSlash() + Format + "/oneway/"; } private class AccessToken { private string token; internal static readonly AccessToken __accessToken = new AccessToken("lUjBZNG56eE9yd3FQdVFSTy9qeGl5dlI5RmZwamc4U05udl000"); private AccessToken(string token) { this.token = token; } } /// /// Whether to Accept Gzip,Deflate Content-Encoding and to auto decompress responses /// private bool disableAutoCompression; public bool DisableAutoCompression { get { return disableAutoCompression; } set { disableAutoCompression = value; asyncClient.DisableAutoCompression = value; } } public string RequestCompressionType { get; set; } /// /// The user name for basic authentication /// private string username; public string UserName { get { return username; } set { username = value; asyncClient.UserName = value; } } /// /// The password for basic authentication /// private string password; public string Password { get { return password; } set { password = value; asyncClient.Password = value; } } /// /// Sets the username and the password for basic authentication. /// public void SetCredentials(string userName, string password) { this.UserName = userName; this.Password = password; } /// /// The Authorization Bearer Token to send with this request /// private string bearerToken; public string BearerToken { get { return bearerToken; } set { bearerToken = value; asyncClient.BearerToken = value; } } public string BaseUri { get; set; } public abstract string Format { get; } public string SyncReplyBaseUri { get; set; } public string AsyncOneWayBaseUri { get; set; } public int Version { get; set; } public string SessionId { get; set; } private string userAgent; public string UserAgent { get { return userAgent; } set { userAgent = value; asyncClient.UserAgent = value; } } private TimeSpan? timeout; public TimeSpan? Timeout { get { return this.timeout; } set { this.timeout = value; this.asyncClient.Timeout = value; } } private TimeSpan? readWriteTimeout; public TimeSpan? ReadWriteTimeout { get { return this.readWriteTimeout; } set { this.readWriteTimeout = value; // TODO implement ReadWriteTimeout in asyncClient //this.asyncClient.ReadWriteTimeout = value; } } public virtual string Accept { get { return ContentType; } } public abstract string ContentType { get; } public string HttpMethod { get; set; } /// /// Whether to execute async callbacks on the same Synchronization Context it was called from. /// public bool CaptureSynchronizationContext { get { return asyncClient.CaptureSynchronizationContext; } set { asyncClient.CaptureSynchronizationContext = value; } } public bool HandleCallbackOnUiThread { get { return asyncClient.HandleCallbackOnUiThread; } set { asyncClient.HandleCallbackOnUiThread = value; } } public bool EmulateHttpViaPost { get { return asyncClient.EmulateHttpViaPost; } set { asyncClient.EmulateHttpViaPost = value; } } public ProgressDelegate OnDownloadProgress { get { return asyncClient.OnDownloadProgress; } set { asyncClient.OnDownloadProgress = value; } } public ProgressDelegate OnUploadProgress { get { return asyncClient.OnUploadProgress; } set { asyncClient.OnUploadProgress = value; } } private bool shareCookiesWithBrowser; public bool ShareCookiesWithBrowser { get { return this.shareCookiesWithBrowser; } set { asyncClient.ShareCookiesWithBrowser = this.shareCookiesWithBrowser = value; } } #if !SL5 public IWebProxy Proxy { get; set; } #endif private ICredentials credentials; /// /// Gets or sets authentication information for the request. /// Warning: It's recommened to use and for basic auth. /// This property is only used for IIS level authentication. /// public ICredentials Credentials { get { return this.credentials; } set { this.credentials = value; this.asyncClient.Credentials = value; } } /// /// Determines if the basic auth header should be sent with every request. /// By default, the basic auth header is only sent when "401 Unauthorized" is returned. /// private bool alwaysSendBasicAuthHeader; public bool AlwaysSendBasicAuthHeader { get { return alwaysSendBasicAuthHeader; } set { asyncClient.AlwaysSendBasicAuthHeader = alwaysSendBasicAuthHeader = value; } } /// /// Specifies if cookies should be stored /// private bool storeCookies; public bool StoreCookies { get { return storeCookies; } set { asyncClient.StoreCookies = storeCookies = value; } } private CookieContainer cookieContainer; public CookieContainer CookieContainer { get { return cookieContainer; } set { asyncClient.CookieContainer = cookieContainer = value; } } private bool allowAutoRedirect = true; public bool AllowAutoRedirect { get { return allowAutoRedirect; } set { allowAutoRedirect = value; // TODO: Implement for async client. // asyncClient.AllowAutoRedirect = value; } } /// /// Called before request resend, when the initial request required authentication /// private Action onAuthenticationRequired { get; set; } public Action OnAuthenticationRequired { get { return onAuthenticationRequired; } set { onAuthenticationRequired = value; asyncClient.OnAuthenticationRequired = value; } } /// /// The request filter is called before any request. /// This request filter only works with the instance where it was set (not global). /// private Action requestFilter { get; set; } public Action RequestFilter { get { return requestFilter; } set { requestFilter = value; asyncClient.RequestFilter = value; } } /// /// The ResultsFilter is called before the Request is sent allowing you to return a cached response. /// private ResultsFilterDelegate resultsFilter; public ResultsFilterDelegate ResultsFilter { get { return resultsFilter; } set { resultsFilter = value; asyncClient.ResultsFilter = value; } } /// /// The ResultsFilterResponse is called before returning the response allowing responses to be cached. /// private ResultsFilterResponseDelegate resultsFilterResponse; public ResultsFilterResponseDelegate ResultsFilterResponse { get { return resultsFilterResponse; } set { resultsFilterResponse = value; asyncClient.ResultsFilterResponse = value; } } /// /// Called with requestUri, ResponseType when server returns 304 NotModified /// public ExceptionFilterDelegate exceptionFilter; public ExceptionFilterDelegate ExceptionFilter { get { return exceptionFilter; } set { exceptionFilter = value; asyncClient.ExceptionFilter = value; } } /// /// The response action is called once the server response is available. /// It will allow you to access raw response information. /// Note that you should NOT consume the response stream as this is handled by ServiceStack /// private Action responseFilter { get; set; } public Action ResponseFilter { get { return responseFilter; } set { responseFilter = value; asyncClient.ResponseFilter = value; } } public UrlResolverDelegate UrlResolver { get; set; } public TypedUrlResolverDelegate TypedUrlResolver { get; set; } public virtual string ToAbsoluteUrl(string relativeOrAbsoluteUrl) { return relativeOrAbsoluteUrl.StartsWith("http:") || relativeOrAbsoluteUrl.StartsWith("https:") ? relativeOrAbsoluteUrl : this.BaseUri.CombineWith(relativeOrAbsoluteUrl); } public virtual string ResolveUrl(string httpMethod, string relativeOrAbsoluteUrl) { return ToAbsoluteUrl((UrlResolver != null ? UrlResolver(this, httpMethod, relativeOrAbsoluteUrl) : null) ?? relativeOrAbsoluteUrl); } public virtual string ResolveTypedUrl(string httpMethod, object requestDto) { return ToAbsoluteUrl((TypedUrlResolver != null ? TypedUrlResolver(this, httpMethod, requestDto) : null) ?? requestDto.ToUrl(httpMethod, Format)); } [Obsolete("Renamed to ToAbsoluteUrl")] public virtual string GetUrl(string relativeOrAbsoluteUrl) { return ToAbsoluteUrl(relativeOrAbsoluteUrl); } internal void AsyncSerializeToStream(IRequest requestContext, object request, Stream stream) { SerializeRequestToStream(requestContext, request, stream); } public abstract void SerializeToStream(IRequest requestContext, object request, Stream stream); public abstract T DeserializeFromStream(Stream stream); public abstract StreamDeserializerDelegate StreamDeserializer { get; } internal object AsyncDeserializeFromStream(Type type, Stream fromStream) { return StreamDeserializer(type, fromStream); } protected T Deserialize(string text) { using (var ms = MemoryStreamFactory.GetStream(text.ToUtf8Bytes())) { return DeserializeFromStream(ms); } } public virtual List SendAll(IEnumerable requests) { var elType = requests.GetType().GetCollectionType(); var requestUri = this.SyncReplyBaseUri.WithTrailingSlash() + elType.Name + "[]"; var client = SendRequest(HttpMethods.Post, ResolveUrl(HttpMethods.Post, requestUri), requests); try { var webResponse = PclExport.Instance.GetResponse(client); return HandleResponse>(webResponse); } catch (Exception ex) { List response; if (!HandleResponseException(ex, requests, requestUri, () => SendRequest(HttpMethods.Post, requestUri, requests), c => PclExport.Instance.GetResponse(c), out response)) { throw; } return response; } } public virtual TResponse Send(object request) { if (typeof(TResponse) == typeof(object)) return (TResponse)this.Send(this.GetResponseType(request), request); if (request is IVerb) { if (request is IGet) return Get(request); if (request is IPost) return Post(request); if (request is IPut) return Put(request); if (request is IDelete) return Delete(request); if (request is IPatch) return Patch(request); } var httpMethod = HttpMethod ?? DefaultHttpMethod; var requestUri = ResolveUrl(httpMethod, UrlResolver == null ? this.SyncReplyBaseUri.WithTrailingSlash() + request.GetType().Name : Format + "/reply/" + request.GetType().Name); if (ResultsFilter != null) { var response = ResultsFilter(typeof(TResponse), httpMethod, requestUri, request); if (response is TResponse) return (TResponse)response; } var client = SendRequest(httpMethod, requestUri, request); try { var webResponse = PclExport.Instance.GetResponse(client); ApplyWebResponseFilters(webResponse); var response = GetResponse(webResponse); if (ResultsFilterResponse != null) { ResultsFilterResponse(webResponse, response, httpMethod, requestUri, request); } DisposeIfRequired(webResponse); return response; } catch (Exception ex) { TResponse response; if (!HandleResponseException(ex, request, requestUri, () => SendRequest(HttpMethods.Post, requestUri, request), c => PclExport.Instance.GetResponse(c), out response)) { throw; } return response; } } /// /// Called by Send method if an exception occurs, for instance a System.Net.WebException because the server /// returned an HTTP error code. Override if you want to handle specific exceptions or always want to parse the /// response to a custom ErrorResponse DTO type instead of ServiceStack's ErrorResponse class. In case ex is a /// System.Net.WebException, do not use /// createWebRequest/getResponse/HandleResponse<TResponse> to parse the response /// because that will result in the same exception again. Use /// ThrowWebServiceException<YourErrorResponseType> to parse the response and to throw a /// WebServiceException containing the parsed DTO. Then override Send to handle that exception. /// protected virtual bool HandleResponseException(Exception ex, object request, string requestUri, Func createWebRequest, Func getResponse, out TResponse response) { var webEx = ex as WebException; try { if (WebRequestUtils.ShouldAuthenticate(webEx, (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) || credentials != null || bearerToken != null || OnAuthenticationRequired != null)) { if (OnAuthenticationRequired != null) OnAuthenticationRequired(); var client = createWebRequest(); HandleAuthException(ex, client); var webResponse = getResponse(client); response = HandleResponse(webResponse); return true; } } catch (Exception subEx) { // Since we are effectively re-executing the call, // the new exception should be shown to the caller rather // than the old one. // The new exception is either this one or the one thrown // by the following method. ThrowResponseTypeException(request, subEx, requestUri); throw; } if (ExceptionFilter != null && webEx != null && webEx.Response != null) { var cachedResponse = ExceptionFilter(webEx, webEx.Response, requestUri, typeof(TResponse)); if (cachedResponse is TResponse) { response = (TResponse)cachedResponse; return true; } } // If this doesn't throw, the calling method // should rethrow the original exception upon // return value of false. ThrowResponseTypeException(request, ex, requestUri); response = default(TResponse); return false; } private void HandleAuthException(Exception ex, WebRequest client) { var webEx = ex as WebException; if (webEx != null && webEx.Response != null) { var headers = ((HttpWebResponse)webEx.Response).Headers; var doAuthHeader = PclExportClient.Instance.GetHeader(headers, HttpHeaders.WwwAuthenticate, x => x.Contains("realm")); if (doAuthHeader == null) { client.AddBasicAuth(this.UserName, this.Password); } else { this.authInfo = new AuthenticationInfo(doAuthHeader); client.AddAuthInfo(this.UserName, this.Password, authInfo); } } } readonly ConcurrentDictionary> ResponseHandlers = new ConcurrentDictionary>(); private void ThrowResponseTypeException(object request, Exception ex, string requestUri) { var responseType = WebRequestUtils.GetErrorResponseDtoType(request); Action responseHandler; if (!ResponseHandlers.TryGetValue(responseType, out responseHandler)) { var mi = GetType().GetInstanceMethod("ThrowWebServiceException") .MakeGenericMethod(new[] { responseType }); responseHandler = (Action)mi.CreateDelegate( typeof(Action), this); ResponseHandlers[responseType] = responseHandler; } responseHandler(ex, requestUri); } public void ThrowWebServiceException(Exception ex, string requestUri) { var webEx = ex as WebException; if (webEx != null && webEx.Response != null #if !(SL5 || PCL || NETSTANDARD1_1) && webEx.Status == WebExceptionStatus.ProtocolError #endif ) { var errorResponse = ((HttpWebResponse)webEx.Response); log.Error(webEx); if (log.IsDebugEnabled) { log.DebugFormat("Status Code : {0}", errorResponse.StatusCode); log.DebugFormat("Status Description : {0}", errorResponse.StatusDescription); } var serviceEx = new WebServiceException(errorResponse.StatusDescription) { StatusCode = (int)errorResponse.StatusCode, StatusDescription = errorResponse.StatusDescription, ResponseHeaders = errorResponse.Headers }; try { if (string.IsNullOrEmpty(errorResponse.ContentType) || errorResponse.ContentType.MatchesContentType(ContentType)) { var bytes = errorResponse.GetResponseStream().ReadFully(); var stream = MemoryStreamFactory.GetStream(bytes); serviceEx.ResponseBody = bytes.FromUtf8Bytes(); serviceEx.ResponseDto = DeserializeFromStream(stream); if (stream.CanRead) stream.Dispose(); //alt ms throws when you dispose twice } else { serviceEx.ResponseBody = errorResponse.GetResponseStream().ToUtf8String(); } } catch (Exception innerEx) { // Oh, well, we tried throw new WebServiceException(errorResponse.StatusDescription, innerEx) { StatusCode = (int)errorResponse.StatusCode, StatusDescription = errorResponse.StatusDescription, ResponseBody = serviceEx.ResponseBody }; } //Escape deserialize exception handling and throw here throw serviceEx; } var authEx = ex as AuthenticationException; if (authEx != null) { throw WebRequestUtils.CreateCustomException(requestUri, authEx); } } private WebRequest SendRequest(string httpMethod, string requestUri, object request) { return PrepareWebRequest(httpMethod, requestUri, request, client => { using (var requestStream = PclExport.Instance.GetRequestStream(client)) { SerializeRequestToStream(null, request, requestStream); } }); } private void SerializeRequestToStream(IRequest requestContext, object request, Stream requestStream) { var str = request as string; var bytes = request as byte[]; var stream = request as Stream; if (str != null) { requestStream.Write(str); } else if (bytes != null) { requestStream.Write(bytes, 0, bytes.Length); } else if (stream != null) { stream.WriteTo(requestStream); } else { #if !SL5 if (RequestCompressionType == CompressionTypes.Deflate) { requestStream = new System.IO.Compression.DeflateStream(requestStream, System.IO.Compression.CompressionMode.Compress); } else if (RequestCompressionType == CompressionTypes.GZip) { requestStream = new System.IO.Compression.GZipStream(requestStream, System.IO.Compression.CompressionMode.Compress); } #endif SerializeToStream(null, request, requestStream); requestStream.Close(); } } private WebRequest PrepareWebRequest(string httpMethod, string requestUri, object request, Action sendRequestAction) { if (httpMethod == null) throw new ArgumentNullException("httpMethod"); this.PopulateRequestMetadata(request); if (!httpMethod.HasRequestBody() && request != null) { var queryString = QueryStringSerializer.SerializeToString(request); if (!string.IsNullOrEmpty(queryString)) { requestUri += "?" + queryString; } } var client = PclExport.Instance.CreateWebRequest(requestUri, emulateHttpViaPost: EmulateHttpViaPost); try { client.Accept = Accept; client.Method = httpMethod; PclExportClient.Instance.AddHeader(client, Headers); #if !SL5 if (Proxy != null) client.Proxy = Proxy; #endif PclExport.Instance.Config(client, allowAutoRedirect: AllowAutoRedirect, timeout: this.Timeout, readWriteTimeout: ReadWriteTimeout, userAgent: UserAgent); if (this.authInfo != null && !string.IsNullOrEmpty(this.UserName)) client.AddAuthInfo(this.UserName, this.Password, authInfo); else if (this.BearerToken != null) client.Headers[HttpHeaders.Authorization] = "Bearer " + this.BearerToken; else if (this.Credentials != null) client.Credentials = this.Credentials; else if (this.AlwaysSendBasicAuthHeader) client.AddBasicAuth(this.UserName, this.Password); if (!DisableAutoCompression) { PclExport.Instance.AddCompression(client); } if (StoreCookies) { PclExportClient.Instance.SetCookieContainer(client, this); } ApplyWebRequestFilters(client); if (httpMethod.HasRequestBody()) { client.ContentType = ContentType; if (RequestCompressionType != null) client.Headers[HttpHeaders.ContentEncoding] = RequestCompressionType; if (sendRequestAction != null) sendRequestAction(client); } } catch (AuthenticationException ex) { throw WebRequestUtils.CreateCustomException(requestUri, ex) ?? ex; } return client; } private void ApplyWebResponseFilters(WebResponse webResponse) { if (!(webResponse is HttpWebResponse)) return; if (ResponseFilter != null) ResponseFilter((HttpWebResponse)webResponse); if (GlobalResponseFilter != null) GlobalResponseFilter((HttpWebResponse)webResponse); } private void ApplyWebRequestFilters(HttpWebRequest client) { if (RequestFilter != null) RequestFilter(client); if (GlobalRequestFilter != null) GlobalRequestFilter(client); } private byte[] DownloadBytes(string httpMethod, string requestUri, object request) { var webRequest = SendRequest(httpMethod, requestUri, request); using (var response = PclExport.Instance.GetResponse(webRequest)) { ApplyWebResponseFilters(response); using (var stream = response.GetResponseStream()) return stream.ReadFully(); } } public virtual void Publish(object requestDto) { SendOneWay(requestDto); } public void PublishAll(IEnumerable requests) { var elType = requests.GetType().GetCollectionType(); var requestUri = this.AsyncOneWayBaseUri.WithTrailingSlash() + elType.Name + "[]"; SendOneWay(HttpMethods.Post, ResolveUrl(HttpMethods.Post, requestUri), requests); } public void Publish(T requestDto) { SendOneWay(requestDto); } public void Publish(IMessage message) { var requestDto = message.GetBody(); if (message.CreatedDate != default(DateTime)) Headers.Set("X-CreatedDate", message.CreatedDate.ToJsv()); if (message.Priority != default(int)) Headers.Set("X-Priority", message.Priority.ToString()); if (message.RetryAttempts != default(int)) Headers.Set("X-RetryAttempts", message.RetryAttempts.ToString()); if (message.ReplyId != null) Headers.Set("X-ReplyId", message.ReplyId.Value.ToString()); if (message.ReplyTo != null) Headers.Set("X-ReplyTo", message.ReplyTo); if (message.Tag != null) Headers.Set("X-Tag", message.Tag); SendOneWay(requestDto); } public static string GetExplicitMethod(object request) { if (!(request is IVerb)) return null; return request is IGet ? HttpMethods.Get : request is IPost ? HttpMethods.Post : request is IPut ? HttpMethods.Put : request is IDelete ? HttpMethods.Delete : request is IPatch ? HttpMethods.Patch : null; } public virtual void SendOneWay(object request) { var requestUri = this.AsyncOneWayBaseUri.WithTrailingSlash() + request.GetType().Name; var httpMethod = GetExplicitMethod(request) ?? HttpMethod ?? DefaultHttpMethod; SendOneWay(httpMethod, ResolveUrl(httpMethod, requestUri), request); } public virtual void SendOneWay(string relativeOrAbsoluteUrl, object request) { var httpMethod = GetExplicitMethod(request) ?? HttpMethod ?? DefaultHttpMethod; SendOneWay(httpMethod, ResolveUrl(httpMethod, relativeOrAbsoluteUrl), request); } public virtual void SendAllOneWay(IEnumerable requests) { PublishAll(requests); } public virtual void SendOneWay(string httpMethod, string relativeOrAbsoluteUrl, object requestDto) { var requestUri = ToAbsoluteUrl(relativeOrAbsoluteUrl); try { DownloadBytes(httpMethod, requestUri, requestDto); } catch (Exception ex) { HttpWebResponse response; if (!HandleResponseException( ex, requestDto, requestUri, () => SendRequest(httpMethod, requestUri, requestDto), c => PclExport.Instance.GetResponse(c), out response)) { throw; } using (response) { } //auto dispose } } public virtual Task SendAsync(object request, CancellationToken token) { if (typeof(TResponse) == typeof(object)) return this.SendAsync(this.GetResponseType(request), request, token) .ContinueWith(t => (TResponse)t.Result, token); if (request is IVerb) { if (request is IGet) return GetAsync(request); if (request is IPost) return PostAsync(request); if (request is IPut) return PutAsync(request); if (request is IDelete) return DeleteAsync(request); if (request is IPatch) return PatchAsync(request); } var httpMethod = HttpMethod ?? DefaultHttpMethod; var requestUri = ResolveUrl(httpMethod, UrlResolver == null ? this.SyncReplyBaseUri.WithTrailingSlash() + request.GetType().Name : Format + "/reply/" + request.GetType().Name); return asyncClient.SendAsync(httpMethod, requestUri, request, token); } public Task> SendAllAsync(IEnumerable requests, CancellationToken token) { var elType = requests.GetType().GetCollectionType(); var requestUri = this.SyncReplyBaseUri.WithTrailingSlash() + elType.Name + "[]"; return asyncClient.SendAsync>(HttpMethods.Post, ResolveUrl(HttpMethods.Post, requestUri), requests, token); } public Task PublishAsync(object request, CancellationToken token) { var requestUri = this.AsyncOneWayBaseUri.WithTrailingSlash() + request.GetType().Name; return asyncClient.SendAsync(HttpMethods.Post, ResolveUrl(HttpMethods.Post, requestUri), request, token); } public Task PublishAllAsync(IEnumerable requests, CancellationToken token) { var elType = requests.GetType().GetCollectionType(); var requestUri = this.AsyncOneWayBaseUri.WithTrailingSlash() + elType.Name + "[]"; return asyncClient.SendAsync(HttpMethods.Post, ResolveUrl(HttpMethods.Post, requestUri), requests, token); } public virtual Task SendAsync(object request) { return SendAsync(request, default(CancellationToken)); } public virtual Task GetAsync(IReturn requestDto) { return asyncClient.SendAsync(HttpMethods.Get, ResolveTypedUrl(HttpMethods.Get, requestDto), null); } public virtual Task GetAsync(object requestDto) { return asyncClient.SendAsync(HttpMethods.Get, ResolveTypedUrl(HttpMethods.Get, requestDto), null); } public virtual Task GetAsync(string relativeOrAbsoluteUrl) { return asyncClient.SendAsync(HttpMethods.Get, ResolveUrl(HttpMethods.Get, relativeOrAbsoluteUrl), null); } public virtual Task GetAsync(IReturnVoid requestDto) { return asyncClient.SendAsync(HttpMethods.Get, ResolveTypedUrl(HttpMethods.Get, requestDto), null); } public virtual Task DeleteAsync(IReturn requestDto) { return asyncClient.SendAsync(HttpMethods.Delete, ResolveTypedUrl(HttpMethods.Delete, requestDto), null); } public virtual Task DeleteAsync(object requestDto) { return asyncClient.SendAsync(HttpMethods.Delete, ResolveTypedUrl(HttpMethods.Delete, requestDto), null); } public virtual Task DeleteAsync(string relativeOrAbsoluteUrl) { return asyncClient.SendAsync(HttpMethods.Delete, ResolveUrl(HttpMethods.Delete, relativeOrAbsoluteUrl), null); } public virtual Task DeleteAsync(IReturnVoid requestDto) { return asyncClient.SendAsync(HttpMethods.Delete, ResolveTypedUrl(HttpMethods.Delete, requestDto), null); } public virtual Task PostAsync(IReturn requestDto) { return asyncClient.SendAsync(HttpMethods.Post, ResolveTypedUrl(HttpMethods.Post, requestDto), requestDto); } public virtual Task PostAsync(object requestDto) { return asyncClient.SendAsync(HttpMethods.Post, ResolveTypedUrl(HttpMethods.Post, requestDto), requestDto); } public virtual Task PostAsync(string relativeOrAbsoluteUrl, object request) { return asyncClient.SendAsync(HttpMethods.Post, ResolveUrl(HttpMethods.Post, relativeOrAbsoluteUrl), request); } public virtual Task PostAsync(IReturnVoid requestDto) { return asyncClient.SendAsync(HttpMethods.Post, ResolveTypedUrl(HttpMethods.Post, requestDto), requestDto); } public virtual Task PutAsync(IReturn requestDto) { return asyncClient.SendAsync(HttpMethods.Put, ResolveTypedUrl(HttpMethods.Put, requestDto), requestDto); } public virtual Task PutAsync(object requestDto) { return asyncClient.SendAsync(HttpMethods.Put, ResolveTypedUrl(HttpMethods.Put, requestDto), requestDto); } public virtual Task PutAsync(string relativeOrAbsoluteUrl, object request) { return asyncClient.SendAsync(HttpMethods.Put, ResolveUrl(HttpMethods.Put, relativeOrAbsoluteUrl), request); } public virtual Task PutAsync(IReturnVoid requestDto) { return asyncClient.SendAsync(HttpMethods.Put, ResolveTypedUrl(HttpMethods.Put, requestDto), requestDto); } public virtual Task PatchAsync(IReturn requestDto) { return asyncClient.SendAsync(HttpMethods.Patch, ResolveTypedUrl(HttpMethods.Patch, requestDto), requestDto); } public virtual Task PatchAsync(object requestDto) { return asyncClient.SendAsync(HttpMethods.Patch, ResolveTypedUrl(HttpMethods.Patch, requestDto), requestDto); } public virtual Task PatchAsync(string relativeOrAbsoluteUrl, object request) { return asyncClient.SendAsync(HttpMethods.Patch, ResolveUrl(HttpMethods.Patch, relativeOrAbsoluteUrl), request); } public virtual Task PatchAsync(IReturnVoid requestDto) { return asyncClient.SendAsync(HttpMethods.Patch, ResolveTypedUrl(HttpMethods.Patch, requestDto), requestDto); } public virtual Task CustomMethodAsync(string httpVerb, IReturn requestDto) { return CustomMethodAsync(httpVerb, ResolveTypedUrl(httpVerb, requestDto), requestDto); } public virtual Task CustomMethodAsync(string httpVerb, object requestDto) { return CustomMethodAsync(httpVerb, ResolveTypedUrl(httpVerb, requestDto), requestDto); } public virtual Task CustomMethodAsync(string httpVerb, string relativeOrAbsoluteUrl, object request) { if (!HttpMethods.HasVerb(httpVerb)) throw new NotSupportedException("Unknown HTTP Method is not supported: " + httpVerb); var requestBody = httpVerb.HasRequestBody() ? request : null; return asyncClient.SendAsync(httpVerb, ResolveUrl(httpVerb, relativeOrAbsoluteUrl), requestBody); } public virtual Task CustomMethodAsync(string httpVerb, IReturnVoid requestDto) { if (!HttpMethods.HasVerb(httpVerb)) throw new NotSupportedException("Unknown HTTP Method is not supported: " + httpVerb); var requestBody = httpVerb.HasRequestBody() ? requestDto : null; return asyncClient.SendAsync(httpVerb, ResolveTypedUrl(httpVerb, requestDto), requestBody); } public virtual void CancelAsync() { asyncClient.CancelAsync(); } public virtual TResponse Send(string httpMethod, string relativeOrAbsoluteUrl, object request) { var requestUri = ToAbsoluteUrl(relativeOrAbsoluteUrl); if (ResultsFilter != null) { var response = ResultsFilter(typeof(TResponse), httpMethod, requestUri, request); if (response is TResponse) return (TResponse)response; } var client = SendRequest(httpMethod, requestUri, request); try { var webResponse = PclExport.Instance.GetResponse(client); ApplyWebResponseFilters(webResponse); var response = GetResponse(webResponse); if (ResultsFilterResponse != null) { ResultsFilterResponse(webResponse, response, httpMethod, requestUri, request); } DisposeIfRequired(webResponse); return response; } catch (Exception ex) { TResponse response; if (!HandleResponseException( ex, request, requestUri, () => SendRequest(httpMethod, requestUri, request), c => PclExport.Instance.GetResponse(c), out response)) { throw; } return response; } } public void AddHeader(string name, string value) { Headers[name] = value; } public void ClearCookies() { CookieContainer = new CookieContainer(); } public Dictionary GetCookieValues() { return CookieContainer.ToDictionary(BaseUri); } public void SetCookie(string name, string value, TimeSpan? expiresIn = null) { this.SetCookie(new Uri(BaseUri), name, value, expiresIn != null ? DateTime.UtcNow.Add(expiresIn.Value) : (DateTime?)null); } public virtual void Get(IReturnVoid requestDto) { Send(HttpMethods.Get, ResolveTypedUrl(HttpMethods.Get, requestDto), null); } /// /// APIs returning HttpWebResponse must be explicitly Disposed, e.g using (var res = client.Get(url)) { ... } /// public virtual HttpWebResponse Get(object requestDto) { return Send(HttpMethods.Get, ResolveTypedUrl(HttpMethods.Get, requestDto), null); } /// /// APIs returning HttpWebResponse must be explicitly Disposed, e.g using (var res = client.Get(url)) { ... } /// public virtual HttpWebResponse Get(string relativeOrAbsoluteUrl) { return Send(HttpMethods.Get, ResolveUrl(HttpMethods.Get, relativeOrAbsoluteUrl), null); } public virtual TResponse Get(IReturn requestDto) { return Send(HttpMethods.Get, ResolveTypedUrl(HttpMethods.Get, requestDto), null); } public virtual TResponse Get(object requestDto) { return Send(HttpMethods.Get, ResolveTypedUrl(HttpMethods.Get, requestDto), null); } public virtual TResponse Get(string relativeOrAbsoluteUrl) { return Send(HttpMethods.Get, ResolveUrl(HttpMethods.Get, relativeOrAbsoluteUrl), null); } public virtual IEnumerable GetLazy(IReturn> queryDto) { var query = (IQuery)queryDto; QueryResponse response; do { response = Send>(HttpMethods.Get, ResolveTypedUrl(HttpMethods.Get, queryDto), null); foreach (var result in response.Results) { yield return result; } query.Skip = query.Skip.GetValueOrDefault(0) + response.Results.Count; } while (response.Results.Count + response.Offset < response.Total); } public virtual void Delete(IReturnVoid requestDto) { Send(HttpMethods.Delete, ResolveTypedUrl(HttpMethods.Delete, requestDto), null); } public virtual HttpWebResponse Delete(object requestDto) { return Send(HttpMethods.Delete, ResolveTypedUrl(HttpMethods.Delete, requestDto), null); } public virtual HttpWebResponse Delete(string relativeOrAbsoluteUrl) { return Send(HttpMethods.Delete, ResolveUrl(HttpMethods.Delete, relativeOrAbsoluteUrl), null); } public virtual TResponse Delete(IReturn requestDto) { return Send(HttpMethods.Delete, ResolveTypedUrl(HttpMethods.Delete, requestDto), null); } public virtual TResponse Delete(object requestDto) { return Send(HttpMethods.Delete, ResolveTypedUrl(HttpMethods.Delete, requestDto), null); } public virtual TResponse Delete(string relativeOrAbsoluteUrl) { return Send(HttpMethods.Delete, ResolveUrl(HttpMethods.Delete, relativeOrAbsoluteUrl), null); } public virtual void Post(IReturnVoid requestDto) { Send(HttpMethods.Post, ResolveTypedUrl(HttpMethods.Post, requestDto), requestDto); } public virtual HttpWebResponse Post(object requestDto) { return Send(HttpMethods.Post, ResolveTypedUrl(HttpMethods.Post, requestDto), requestDto); } public virtual TResponse Post(IReturn requestDto) { return Send(HttpMethods.Post, ResolveTypedUrl(HttpMethods.Post, requestDto), requestDto); } public virtual TResponse Post(object requestDto) { return Send(HttpMethods.Post, ResolveTypedUrl(HttpMethods.Post, requestDto), requestDto); } public virtual TResponse Post(string relativeOrAbsoluteUrl, object requestDto) { return Send(HttpMethods.Post, ResolveUrl(HttpMethods.Post, relativeOrAbsoluteUrl), requestDto); } public virtual void Put(IReturnVoid requestDto) { Send(HttpMethods.Put, ResolveTypedUrl(HttpMethods.Put, requestDto), requestDto); } public virtual HttpWebResponse Put(object requestDto) { return Send(HttpMethods.Put, ResolveTypedUrl(HttpMethods.Put, requestDto), requestDto); } public virtual TResponse Put(IReturn requestDto) { return Send(HttpMethods.Put, ResolveTypedUrl(HttpMethods.Put, requestDto), requestDto); } public virtual TResponse Put(object requestDto) { return Send(HttpMethods.Put, ResolveTypedUrl(HttpMethods.Put, requestDto), requestDto); } public virtual TResponse Put(string relativeOrAbsoluteUrl, object requestDto) { return Send(HttpMethods.Put, ResolveUrl(HttpMethods.Put, relativeOrAbsoluteUrl), requestDto); } public virtual void Patch(IReturnVoid requestDto) { Send(HttpMethods.Patch, ResolveTypedUrl(HttpMethods.Patch, requestDto), requestDto); } public virtual HttpWebResponse Patch(object requestDto) { return Send(HttpMethods.Patch, ResolveTypedUrl(HttpMethods.Patch, requestDto), requestDto); } public virtual TResponse Patch(IReturn requestDto) { return Send(HttpMethods.Patch, ResolveTypedUrl(HttpMethods.Patch, requestDto), requestDto); } public virtual TResponse Patch(object requestDto) { return Send(HttpMethods.Patch, ResolveTypedUrl(HttpMethods.Patch, requestDto), requestDto); } public virtual TResponse Patch(string relativeOrAbsoluteUrl, object requestDto) { return Send(HttpMethods.Patch, ResolveUrl(HttpMethods.Patch, relativeOrAbsoluteUrl), requestDto); } public virtual void CustomMethod(string httpVerb, IReturnVoid requestDto) { Send(httpVerb, ResolveTypedUrl(httpVerb, requestDto), requestDto); } public virtual HttpWebResponse CustomMethod(string httpVerb, object requestDto) { var requestBody = httpVerb.HasRequestBody() ? requestDto : null; return Send(httpVerb, ResolveTypedUrl(httpVerb, requestDto), requestBody); } public virtual HttpWebResponse CustomMethod(string httpVerb, string relativeOrAbsoluteUrl, object requestDto) { if (!HttpMethods.AllVerbs.Contains(httpVerb.ToUpper())) throw new NotSupportedException("Unknown HTTP Method is not supported: " + httpVerb); return Send(httpVerb, ResolveUrl(httpVerb, relativeOrAbsoluteUrl), requestDto); } public virtual TResponse CustomMethod(string httpVerb, IReturn requestDto) { var requestBody = httpVerb.HasRequestBody() ? requestDto : null; return Send(httpVerb, ResolveTypedUrl(httpVerb, requestDto), requestBody); } public virtual TResponse CustomMethod(string httpVerb, object requestDto) { var requestBody = httpVerb.HasRequestBody() ? requestDto : null; return CustomMethod(httpVerb, ResolveTypedUrl(httpVerb, requestDto), requestBody); } public virtual TResponse CustomMethod(string httpVerb, string relativeOrAbsoluteUrl, object requestDto = null) { if (!HttpMethods.AllVerbs.Contains(httpVerb.ToUpper())) throw new NotSupportedException("Unknown HTTP Method is not supported: " + httpVerb); return Send(httpVerb, relativeOrAbsoluteUrl, requestDto); } public virtual HttpWebResponse Head(IReturn requestDto) { return Send(HttpMethods.Head, ResolveTypedUrl(HttpMethods.Head, requestDto), requestDto); } public virtual HttpWebResponse Head(object requestDto) { return Send(HttpMethods.Head, ResolveTypedUrl(HttpMethods.Head, requestDto), requestDto); } public virtual HttpWebResponse Head(string relativeOrAbsoluteUrl) { return Send(HttpMethods.Head, ResolveUrl(HttpMethods.Head, relativeOrAbsoluteUrl), null); } public virtual TResponse PostFilesWithRequest(object request, IEnumerable files) { return PostFilesWithRequest(ResolveTypedUrl(HttpMethods.Post, request), request, files.ToArray()); } public virtual TResponse PostFilesWithRequest(string relativeOrAbsoluteUrl, object request, IEnumerable files) { return PostFilesWithRequest(ResolveUrl(HttpMethods.Post, relativeOrAbsoluteUrl), request, files.ToArray()); } private TResponse PostFilesWithRequest(string requestUri, object request, UploadFile[] files) { var fileCount = 0; long currentStreamPosition = 0; Func createWebRequest = () => { var webRequest = PrepareWebRequest(HttpMethods.Post, requestUri, null, null); var queryString = QueryStringSerializer.SerializeToString(request); var nameValueCollection = PclExportClient.Instance.ParseQueryString(queryString); var boundary = Guid.NewGuid().ToString("N"); webRequest.ContentType = "multipart/form-data; boundary=\"" + boundary + "\""; boundary = "--" + boundary; var newLine = "\r\n"; using (var outputStream = PclExport.Instance.GetRequestStream(webRequest)) { foreach (var key in nameValueCollection.AllKeys) { outputStream.Write(boundary + newLine); outputStream.Write($"Content-Disposition: form-data;name=\"{key}\"{newLine}"); outputStream.Write($"Content-Type: text/plain;charset=utf-8{newLine}{newLine}"); outputStream.Write(nameValueCollection[key] + newLine); } var buffer = new byte[4096]; for (fileCount = 0; fileCount < files.Length; fileCount++) { var file = files[fileCount]; currentStreamPosition = file.Stream.Position; outputStream.Write(boundary + newLine); var fileName = file.FileName ?? $"upload{fileCount}"; var fieldName = file.FieldName ?? $"upload{fileCount}"; outputStream.Write($"Content-Disposition: form-data;name=\"{fieldName}\";filename=\"{fileName}\"{newLine}Content-Type: application/octet-stream{newLine}{newLine}"); int byteCount; int bytesWritten = 0; while ((byteCount = file.Stream.Read(buffer, 0, 4096)) > 0) { outputStream.Write(buffer, 0, byteCount); if (OnUploadProgress != null) { bytesWritten += byteCount; OnUploadProgress(bytesWritten, file.Stream.Length); } } outputStream.Write(newLine); if (fileCount == files.Length - 1) outputStream.Write(boundary + "--"); } } return webRequest; }; try { var webRequest = createWebRequest(); var webResponse = PclExport.Instance.GetResponse(webRequest); return HandleResponse(webResponse); } catch (Exception ex) { TResponse response; // restore original position before retry files[fileCount - 1].Stream.Seek(currentStreamPosition, SeekOrigin.Begin); if (!HandleResponseException( ex, request, requestUri, createWebRequest, c => PclExport.Instance.GetResponse(c), out response)) { throw; } return response; } } public virtual TResponse PostFileWithRequest(Stream fileToUpload, string fileName, object request, string fieldName = "upload") { return PostFileWithRequest(ResolveTypedUrl(HttpMethods.Post, request), fileToUpload, fileName, request, fieldName); } public virtual TResponse PostFileWithRequest(string relativeOrAbsoluteUrl, Stream fileToUpload, string fileName, object request, string fieldName = "upload") { var requestUri = ResolveUrl(HttpMethods.Post, relativeOrAbsoluteUrl); var currentStreamPosition = fileToUpload.Position; Func createWebRequest = () => { var webRequest = PrepareWebRequest(HttpMethods.Post, requestUri, null, null); var queryString = QueryStringSerializer.SerializeToString(request); var nameValueCollection = PclExportClient.Instance.ParseQueryString(queryString); var boundary = "----------------------------" + Guid.NewGuid().ToString("N"); webRequest.ContentType = "multipart/form-data; boundary=" + boundary; boundary = "--" + boundary; var newLine = "\r\n"; using (var outputStream = PclExport.Instance.GetRequestStream(webRequest)) { foreach (var key in nameValueCollection.AllKeys) { outputStream.Write(boundary + newLine); outputStream.Write($"Content-Disposition: form-data;name=\"{key}\"{newLine}"); outputStream.Write($"Content-Type: text/plain;charset=utf-8{newLine}{newLine}"); outputStream.Write(nameValueCollection[key] + newLine); } outputStream.Write(boundary + newLine); outputStream.Write($"Content-Disposition: form-data;name=\"{fieldName}\";filename=\"{fileName}\"{newLine}{newLine}"); var buffer = new byte[4096]; int byteCount; int bytesWritten = 0; while ((byteCount = fileToUpload.Read(buffer, 0, 4096)) > 0) { outputStream.Write(buffer, 0, byteCount); if (OnUploadProgress != null) { bytesWritten += byteCount; OnUploadProgress(bytesWritten, fileToUpload.Length); } } outputStream.Write(newLine); outputStream.Write(boundary + "--"); } return webRequest; }; try { var webRequest = createWebRequest(); var webResponse = PclExport.Instance.GetResponse(webRequest); return HandleResponse(webResponse); } catch (Exception ex) { TResponse response; // restore original position before retry fileToUpload.Seek(currentStreamPosition, SeekOrigin.Begin); if (!HandleResponseException( ex, request, requestUri, createWebRequest, c => PclExport.Instance.GetResponse(c), out response)) { throw; } return response; } } public virtual TResponse PostFile(string relativeOrAbsoluteUrl, Stream fileToUpload, string fileName, string mimeType) { var currentStreamPosition = fileToUpload.Position; var requestUri = ResolveUrl(HttpMethods.Post, relativeOrAbsoluteUrl); Func createWebRequest = () => PrepareWebRequest(HttpMethods.Post, requestUri, null, null); try { var webRequest = createWebRequest(); webRequest.UploadFile(fileToUpload, fileName, mimeType); var webResponse = PclExport.Instance.GetResponse(webRequest); return HandleResponse(webResponse); } catch (Exception ex) { TResponse response; // restore original position before retry fileToUpload.Seek(currentStreamPosition, SeekOrigin.Begin); if (!HandleResponseException(ex, null, requestUri, createWebRequest, c => { c.UploadFile(fileToUpload, fileName, mimeType); return PclExport.Instance.GetResponse(c); }, out response)) { throw; } return response; } } private TResponse HandleResponse(WebResponse webResponse) { ApplyWebResponseFilters(webResponse); var response = GetResponse(webResponse); DisposeIfRequired(webResponse); return response; } private static void DisposeIfRequired(WebResponse webResponse) { if (typeof(TResponse) == typeof(HttpWebResponse) && webResponse is HttpWebResponse) return; if (typeof(TResponse) == typeof(Stream)) return; using (webResponse) {} } protected TResponse GetResponse(WebResponse webResponse) { #if NETSTANDARD1_1 || NETSTANDARD1_6 var compressionType = webResponse.Headers[HttpHeaders.ContentEncoding]; #endif //Callee Needs to dispose of response manually if (typeof(TResponse) == typeof(HttpWebResponse) && webResponse is HttpWebResponse) { return (TResponse)Convert.ChangeType(webResponse, typeof(TResponse), null); } if (typeof(TResponse) == typeof(Stream)) { #if NETSTANDARD1_1 || NETSTANDARD1_6 return (TResponse)(object)webResponse.GetResponseStream().Decompress(compressionType); #else return (TResponse)(object)webResponse.GetResponseStream(); #endif } #if NETSTANDARD1_1 || NETSTANDARD1_6 using (var responseStream = webResponse.GetResponseStream().Decompress(compressionType)) #else using (var responseStream = webResponse.GetResponseStream()) #endif { if (typeof(TResponse) == typeof(string)) { using (var reader = new StreamReader(responseStream)) { return (TResponse)(object)reader.ReadToEnd(); } } if (typeof(TResponse) == typeof(byte[])) { return (TResponse)(object)responseStream.ReadFully(); } var response = DeserializeFromStream(responseStream); return response; } } public void Dispose() { } } public static partial class ServiceClientExtensions { #if !(NETFX_CORE || SL5 || PCL || NETSTANDARD1_1) public static TResponse PostFile(this IRestClient client, string relativeOrAbsoluteUrl, FileInfo fileToUpload, string mimeType) { using (FileStream fileStream = fileToUpload.OpenRead()) { return client.PostFile(relativeOrAbsoluteUrl, fileStream, fileToUpload.Name, mimeType); } } public static TResponse PostFileWithRequest(this IRestClient client, FileInfo fileToUpload, object request, string fieldName = "upload") { return client.PostFileWithRequest(request.ToPostUrl(), fileToUpload, request, fieldName); } public static TResponse PostFileWithRequest(this IRestClient client, string relativeOrAbsoluteUrl, FileInfo fileToUpload, object request, string fieldName = "upload") { using (FileStream fileStream = fileToUpload.OpenRead()) { return client.PostFileWithRequest(relativeOrAbsoluteUrl, fileStream, fileToUpload.Name, request, fieldName); } } #endif public static void PopulateRequestMetadata(this IHasSessionId client, object request) { if (client.SessionId != null) { var hasSession = request as IHasSessionId; if (hasSession != null && hasSession.SessionId == null) hasSession.SessionId = client.SessionId; } var clientVersion = client as IHasVersion; if (clientVersion != null && clientVersion.Version > 0) { var hasVersion = request as IHasVersion; if (hasVersion != null && hasVersion.Version <= 0) hasVersion.Version = clientVersion.Version; } } public static Dictionary ToDictionary(this CookieContainer cookies, string baseUri) { var to = new Dictionary(); if (cookies == null) return to; foreach (Cookie cookie in cookies.GetCookies(new Uri(baseUri))) { to[cookie.Name] = cookie.Value; } return to; } [Obsolete("Use: using (client.Get(request) { }")] public static HttpWebResponse Get(this IRestClient client, object request) { var c = client as ServiceClientBase; if (c == null) throw new NotSupportedException(); return c.Get(request); } [Obsolete("Use: using (client.Delete(request) { }")] public static HttpWebResponse Delete(this IRestClient client, object request) { var c = client as ServiceClientBase; if (c == null) throw new NotSupportedException(); return c.Delete(request); } [Obsolete("Use: using (client.Post(request) { }")] public static HttpWebResponse Post(this IRestClient client, object request) { var c = client as ServiceClientBase; if (c == null) throw new NotSupportedException(); return c.Post(request); } [Obsolete("Use: using (client.Put(request) { }")] public static HttpWebResponse Put(this IRestClient client, object request) { var c = client as ServiceClientBase; if (c == null) throw new NotSupportedException(); return c.Put(request); } [Obsolete("Use: using (client.Patch(request) { }")] public static HttpWebResponse Patch(this IRestClient client, object request) { var c = client as ServiceClientBase; if (c == null) throw new NotSupportedException(); return c.Patch(request); } [Obsolete("Use: using (client.CustomMethod(httpVerb, request) { }")] public static HttpWebResponse CustomMethod(this IRestClient client, string httpVerb, object requestDto) { var c = client as ServiceClientBase; if (c == null) throw new NotSupportedException(); return c.CustomMethod(httpVerb, requestDto); } [Obsolete("Use: using (client.Head(request) { }")] public static HttpWebResponse Head(this IRestClient client, IReturn requestDto) { var c = client as ServiceClientBase; if (c == null) throw new NotSupportedException(); return c.Head(requestDto); } [Obsolete("Use: using (client.Head(request) { }")] public static HttpWebResponse Head(this IRestClient client, object requestDto) { var c = client as ServiceClientBase; if (c == null) throw new NotSupportedException(); return c.Head(requestDto); } [Obsolete("Use: using (client.Head(relativeOrAbsoluteUrl) { }")] public static HttpWebResponse Head(this IRestClient client, string relativeOrAbsoluteUrl) { var c = client as ServiceClientBase; if (c == null) throw new NotSupportedException(); return c.Head(relativeOrAbsoluteUrl); } public static void SetCookie(this IServiceClient client, Uri baseUri, string name, string value, DateTime? expiresAt = null, string path = "/", bool? httpOnly = null, bool? secure = null) { var hasCookies = client as IHasCookieContainer; if (hasCookies == null) throw new NotSupportedException("Client does not implement IHasCookieContainer"); var cookie = new Cookie(name, value, path); if (expiresAt != null) cookie.Expires = expiresAt.Value; if (path != null) cookie.Path = path; if (httpOnly != null) cookie.HttpOnly = httpOnly.Value; if (secure != null) cookie.Secure = secure.Value; hasCookies.CookieContainer.Add(baseUri, cookie); } public static string GetSessionId(this IServiceClient client) { string sessionId; client.GetCookieValues().TryGetValue("ss-id", out sessionId); return sessionId; } public static string GetPermanentSessionId(this IServiceClient client) { string sessionId; client.GetCookieValues().TryGetValue("ss-pid", out sessionId); return sessionId; } public static void SetSessionId(this IServiceClient client, string sessionId) { if (sessionId == null) return; client.SetCookie("ss-id", sessionId); } public static void SetPermanentSessionId(this IServiceClient client, string sessionId) { if (sessionId == null) return; client.SetCookie("ss-pid", sessionId, expiresIn:TimeSpan.FromDays(365 * 20)); } public static string GetTokenCookie(this IServiceClient client) { string token; client.GetCookieValues().TryGetValue("ss-tok", out token); return token; } public static void SetTokenCookie(this IServiceClient client, string token) { if (token == null) return; client.SetCookie("ss-tok", token, expiresIn: TimeSpan.FromDays(365 * 20)); } public static void SetUserAgent(this HttpWebRequest req, string userAgent) { #if !(PCL || NETSTANDARD1_1 || NETSTANDARD1_6) req.UserAgent = userAgent; #else req.Headers[HttpRequestHeader.UserAgent] = userAgent; #endif } } public interface IHasCookieContainer { CookieContainer CookieContainer { get; } } public interface IServiceClientMeta { string Format { get; } string BaseUri { get; set; } string SyncReplyBaseUri { get; } string AsyncOneWayBaseUri { get; } string UserName { get; } string Password { get; } bool AlwaysSendBasicAuthHeader { get; } int Version { get; } string SessionId { get; } } public delegate string UrlResolverDelegate(IServiceClientMeta client, string httpMethod, string relativeOrAbsoluteUrl); public delegate string TypedUrlResolverDelegate(IServiceClientMeta client, string httpMethod, object requestDto); public delegate object ResultsFilterDelegate(Type responseType, string httpMethod, string requestUri, object request); public delegate void ResultsFilterResponseDelegate(WebResponse webResponse, object response, string httpMethod, string requestUri, object request); public delegate object ExceptionFilterDelegate(WebException webEx, WebResponse webResponse, string requestUri, Type responseType); }