/// <summary> /// Executes the provided request asynchronously, returning the response object. /// </summary> /// <param name="request"><see cref="ISteamRequest"/> object for execution.</param> /// <returns><see cref="ISteamResponse"/> object containing the result of the request.</returns> public async Task<ISteamResponse> ExecuteAsync( ISteamRequest request ) { AuthenticateClient( this, request ); HttpRequestMessage httpRequest = BuildHttpRequest( request ); CookieContainer cookieContainer = new CookieContainer(); if( request.Cookies == null || request.Cookies.Count > 0 ) { foreach( Cookie cookie in request.Cookies ) cookieContainer.Add( httpRequest.RequestUri, cookie ); } HttpClientHandler httpHandler = new HttpClientHandler(); httpHandler.CookieContainer = cookieContainer; using( var httpClient = new HttpClient( httpHandler ) ){ httpClient.Timeout = TimeSpan.FromMilliseconds( ( ( request.Timeout > 0 ) ? request.Timeout : this.Timeout ) ); try { request.IncreaseNumAttempts(); return ConvertToResponse( request, await httpClient.SendAsync( httpRequest ), cookieContainer ); }catch( Exception ex ) { if( ex.InnerException != null && ex.InnerException is WebException ) return CreateErrorResponse( request, ex.InnerException ); return CreateErrorResponse( request, ex ); } } }
/// <summary> /// Method invoked by the library in order to authenticate for a resource. /// Should not be called directly by consumer code. /// </summary> /// <param name="client">SteamClient instance to authenticate.</param> /// <param name="request">Request requiring authentication.</param> public void Authenticate( SteamClient client, ISteamRequest request ) { if( AccessToken != null ) request.AddParameter( "access_token", AccessToken, ParameterType.GetOrPost ); }
/// <summary> /// Method invoked by the library in order to authenticate for a resource. /// Should not be called directly by consumer code. /// </summary> /// <param name="client">SteamClient instance to authenticate.</param> /// <param name="request">Request requiring authentication.</param> public void Authenticate( SteamClient client, ISteamRequest request ) { request.AddParameter( "key", ApiKey, ParameterType.QueryString ); }
/// <summary> /// Method invoked by the library in order to authenticate for a resource. /// Should not be called directly by consumer code. /// </summary> /// <param name="client">SteamClient instance to authenticate.</param> /// <param name="request">Request requiring authentication.</param> public void Authenticate( SteamClient client, ISteamRequest request ) { if( AuthCookie != null ) request.AddCookie( AuthCookie ); }
/// <summary> /// Authenticates the client with the Steam API. /// </summary> /// <param name="client">SteamClient instance to be authenticated.</param> /// <param name="request">Request requiring authentication.</param> private void AuthenticateClient( SteamClient client, ISteamRequest request ) { if( Authenticator != null ) { Authenticator.Authenticate( client, request ); } }
/// <summary> /// Utility method for POST requests. We must produce an object array such that it can be serialized (non-Raw POST style requests). /// </summary> /// <param name="request">Request to be evaluated.</param> /// <param name="body">Current parameter intended for addition to the body of the request.</param> /// <returns>Content which can be sent over the HTTP Request, including all parameters (both body and GetOrPost).</returns> private string SerializeBodyWithParameters( ISteamRequest request, SteamRequestParameter body, PostDataFormat format ) { Dictionary<string, object> output = new Dictionary<string, object>(); IEnumerable<SteamRequestParameter> parameters = request.Parameters.Where( p => p.Type == ParameterType.GetOrPost ); foreach( var p in parameters ) { output.Add( p.Name, p.Value ); } if( format == PostDataFormat.Json ) { if( body != null && output.Count < 1 ) return JsonConvert.SerializeObject( body.Value ); else if( body != null ) { output.Add( body.Name, body.Value ); return JsonConvert.SerializeObject( output ); } else { return JsonConvert.SerializeObject( output ); } } else { var payload = new StringBuilder(); foreach( SteamRequestParameter p in parameters ) { if( payload.Length > 1 ) payload.Append( "&" ); string appendValue = ( p.IsUrlEncoded ) ? p.Value.ToString() : Uri.EscapeDataString( p.Value.ToString() ); payload.AppendFormat( "{0}={1}", Uri.EscapeDataString( p.Name ), appendValue ); } if( body != null && body.Value != null ) payload.AppendFormat( "{0}={1}", body.Name, body.Value.ToString() ); return payload.ToString(); } }
protected SteamResponse CreateErrorResponse( ISteamRequest request, Exception ex ) { SteamResponse response = new SteamResponse(); response.Request = request; response.IsSuccessful = false; response.ErrorMessage = ex.Message; if( ex is WebException ) { var webException = ex as WebException; if( webException != null ) { if( webException.Status == WebExceptionStatus.RequestCanceled ) response.ResponseStatus = ResponseStatus.Aborted; else response.ResponseStatus = ResponseStatus.Error; response.ErrorException = ex; return response; } } else if( ex is TaskCanceledException ) { response.ErrorMessage = "The request timed out."; response.ResponseStatus = ResponseStatus.TimedOut; return response; } response.ErrorException = ex; response.ResponseStatus = ResponseStatus.Error; return response; }
protected SteamResponse ConvertToResponse( ISteamRequest request, HttpResponseMessage response, CookieContainer cookies ) { var cookieJar = new Dictionary<string,Cookie>(); if( cookies != null ) { foreach( Cookie cookie in cookies.GetCookies( response.RequestMessage.RequestUri ).Cast<Cookie>() ) { cookieJar.Add( cookie.Name, cookie ); } } SteamResponse steamResponse = new SteamResponse { HttpResponse = response, Request = request, RequestUri = response.RequestMessage.RequestUri, Cookies = cookieJar, ResponseStatus = SteamSharp.ResponseStatus.Completed, StatusCode = response.StatusCode, StatusDescription = response.StatusCode.ToString(), IsSuccessful = response.IsSuccessStatusCode }; if( !steamResponse.IsSuccessful && response.ReasonPhrase != null ) steamResponse.ErrorMessage = response.ReasonPhrase; StreamReader stream = new StreamReader( response.Content.ReadAsStreamAsync().Result, System.Text.Encoding.UTF8 ); steamResponse.Content = stream.ReadToEnd(); return steamResponse; }
/// <summary> /// Constructs the <see cref="HttpWebRequest" /> object which will be used to execute the web request. /// </summary> /// <param name="request">Request for execution.</param> protected HttpRequestMessage BuildHttpRequest( ISteamRequest request ) { // Add any Default client parameters (if it exists in the request, the request wins) foreach( var param in DefaultParameters ) { if( !request.Parameters.Any( p => p.Name == param.Name && p.Type == param.Type ) ) request.AddParameter( param ); } HttpRequestMessage httpRequest = new HttpRequestMessage( request.Method, BuildUri( request ) ); // HEADERS // -- Add UserAgent header, if it does not already exist in both the request and the standard request message (shouldn't overwrite valuable system/platform data) if( !request.Parameters.Any( p => p.Name == "User-Agent" && p.Type == ParameterType.HttpHeader ) && !httpRequest.Headers.Any( h => h.Key == "User-Agent" ) ) request.Parameters.Add( new SteamRequestParameter { Name = "User-Agent", Value = ( ( DefaultUserAgent == null ) ? "SteamSharp/" + AssemblyVersion : DefaultUserAgent ), Type = ParameterType.HttpHeader } ); // -- Currently we only accept and deserialize JSON responses request.Parameters.Add( new SteamRequestParameter { Name = "Accept", Value = "application/json", Type = ParameterType.HttpHeader } ); IEnumerable<SteamRequestParameter> headers = request.Parameters.Where( p => p.Type == ParameterType.HttpHeader ); foreach( var header in headers ) { if( httpRequest.Headers.Contains( header.Name ) ) httpRequest.Headers.Remove( header.Name ); httpRequest.Headers.Add( header.Name, header.Value.ToString() ); } // BODY -- Only evaluate if post if( request.Method == HttpMethod.Post ) { var body = request.Parameters.FirstOrDefault( p => p.Type == ParameterType.RequestBody ); HttpContent content; switch( request.DataFormat ) { case PostDataFormat.Json: content = new StringContent( SerializeBodyWithParameters( request, body, PostDataFormat.Json ) ); content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse( "application/json" ); break; case PostDataFormat.FormUrlEncoded: content = new StringContent( SerializeBodyWithParameters( request, body, PostDataFormat.FormUrlEncoded ) ); content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse( "application/x-www-form-urlencoded" ); break; default: content = new StringContent( ( ( body != null ) ? body.Value.ToString() : String.Empty ) ); break; } httpRequest.Content = content; } return httpRequest; }
/// <summary> /// Helper method which creates the final Uri used in the HTTP request. /// </summary> /// <param name="request">Request for execution.</param> /// <returns>Well formed Uri for use in an <see cref="System.Net.Http.HttpRequestMessage"/>.</returns> public Uri BuildUri( ISteamRequest request ) { string destination = request.Resource; // Add trailing slash if none exists if( !BaseAPIEndpoint.EndsWith( "/" ) ) BaseAPIEndpoint = BaseAPIEndpoint + "/"; if( !Uri.IsWellFormedUriString( BaseAPIEndpoint, UriKind.RelativeOrAbsolute ) ) throw new FormatException( "BaseAPIEndpoint specified does not conform to a valid Uri format. BaseAPIEndpoint specified: " + BaseAPIEndpoint ); // URL Segement replacement is only valid if the Resource URI is non-empty if( !String.IsNullOrEmpty( destination ) ) { // To prefix or not to prefix - that is the question! (The answer is don't. Obviously.) if( destination.StartsWith( "/" ) ) destination = destination.Substring( 1 ); var urlParams = request.Parameters.Where( p => p.Type == ParameterType.UrlSegment ); foreach( SteamRequestParameter p in urlParams ) destination = destination.Replace( "{" + p.Name + "}", p.Value.ToString() ); destination = BaseAPIEndpoint + destination; } else destination = BaseAPIEndpoint; IEnumerable<SteamRequestParameter> parameters = null; if( request.DataFormat != PostDataFormat.Raw && ( request.Method == HttpMethod.Post || request.Method == HttpMethod.Put ) ) { // This conforms to a POST-style request and the output will be serialized (i.e. not Raw) parameters = request.Parameters.Where( p => p.Type == ParameterType.QueryString ); } else { // This conforms to a GET-style request parameters = request.Parameters.Where( p => p.Type == ParameterType.GetOrPost || p.Type == ParameterType.QueryString ); } var queryString = new StringBuilder(); if( parameters != null && parameters.Any() ) { foreach( SteamRequestParameter p in parameters ) { if( queryString.Length > 1 ) queryString.Append( "&" ); string appendValue = ( p.IsUrlEncoded ) ? p.Value.ToString() : Uri.EscapeDataString( p.Value.ToString() ); queryString.AppendFormat( "{0}={1}", Uri.EscapeDataString( p.Name ), appendValue ); } destination = destination + "?" + queryString; } return new Uri( destination ); }
/// <summary> /// Executes the provided request synchronously, returning the response object. /// </summary> /// <param name="request"><see cref="ISteamRequest"/> object for execution.</param> /// <returns><see cref="ISteamResponse"/> object containing the result of the request.</returns> public ISteamResponse Execute( ISteamRequest request ) { return ExecuteAsync( request ).Result; }