diff --git a/RestSharp/Enum.cs b/RestSharp/Enum.cs index ad62429e3..942dac2e3 100644 --- a/RestSharp/Enum.cs +++ b/RestSharp/Enum.cs @@ -49,7 +49,8 @@ public enum Method DELETE, HEAD, OPTIONS, - PATCH + PATCH, + MERGE, } /// diff --git a/RestSharp/Http.Async.cs b/RestSharp/Http.Async.cs index 9d1c5f867..4ccc7ed11 100644 --- a/RestSharp/Http.Async.cs +++ b/RestSharp/Http.Async.cs @@ -31,425 +31,430 @@ #if (FRAMEWORK && !MONOTOUCH && !MONODROID && !PocketPC) using System.Web; -#endif - -namespace RestSharp -{ - /// - /// HttpWebRequest wrapper (async methods) - /// - public partial class Http - { - private TimeOutState _timeoutState; - - public HttpWebRequest DeleteAsync(Action action) - { - return GetStyleMethodInternalAsync("DELETE", action); - } - - public HttpWebRequest GetAsync(Action action) - { - return GetStyleMethodInternalAsync("GET", action); - } - - public HttpWebRequest HeadAsync(Action action) - { - return GetStyleMethodInternalAsync("HEAD", action); - } - - public HttpWebRequest OptionsAsync(Action action) - { - return GetStyleMethodInternalAsync("OPTIONS", action); - } - - public HttpWebRequest PostAsync(Action action) - { - return PutPostInternalAsync("POST", action); - } - - public HttpWebRequest PutAsync(Action action) - { - return PutPostInternalAsync("PUT", action); - } - - public HttpWebRequest PatchAsync(Action action) - { - return PutPostInternalAsync("PATCH", action); - } - - /// - /// Execute an async POST-style request with the specified HTTP Method. - /// - /// The HTTP method to execute. - /// - public HttpWebRequest AsPostAsync(Action action, string httpMethod) - { +#endif + +namespace RestSharp +{ + /// + /// HttpWebRequest wrapper (async methods) + /// + public partial class Http + { + private TimeOutState _timeoutState; + + public HttpWebRequest DeleteAsync(Action action) + { + return GetStyleMethodInternalAsync("DELETE", action); + } + + public HttpWebRequest GetAsync(Action action) + { + return GetStyleMethodInternalAsync("GET", action); + } + + public HttpWebRequest HeadAsync(Action action) + { + return GetStyleMethodInternalAsync("HEAD", action); + } + + public HttpWebRequest OptionsAsync(Action action) + { + return GetStyleMethodInternalAsync("OPTIONS", action); + } + + public HttpWebRequest PostAsync(Action action) + { + return PutPostInternalAsync("POST", action); + } + + public HttpWebRequest PutAsync(Action action) + { + return PutPostInternalAsync("PUT", action); + } + + public HttpWebRequest PatchAsync(Action action) + { + return PutPostInternalAsync("PATCH", action); + } + + public HttpWebRequest MergeAsync(Action action) + { + return PutPostInternalAsync("MERGE", action); + } + + /// + /// Execute an async POST-style request with the specified HTTP Method. + /// + /// The HTTP method to execute. + /// + public HttpWebRequest AsPostAsync(Action action, string httpMethod) + { #if PocketPC return PutPostInternalAsync(httpMethod.ToUpper(), action); -#else - return PutPostInternalAsync(httpMethod.ToUpperInvariant(), action); -#endif - } - - /// - /// Execute an async GET-style request with the specified HTTP Method. - /// - /// The HTTP method to execute. - /// - public HttpWebRequest AsGetAsync(Action action, string httpMethod) - { +#else + return PutPostInternalAsync(httpMethod.ToUpperInvariant(), action); +#endif + } + + /// + /// Execute an async GET-style request with the specified HTTP Method. + /// + /// The HTTP method to execute. + /// + public HttpWebRequest AsGetAsync(Action action, string httpMethod) + { #if PocketPC return GetStyleMethodInternalAsync(httpMethod.ToUpper(), action); -#else - return GetStyleMethodInternalAsync(httpMethod.ToUpperInvariant(), action); -#endif - } - - private HttpWebRequest GetStyleMethodInternalAsync(string method, Action callback) - { - HttpWebRequest webRequest = null; - try - { - var url = Url; - webRequest = ConfigureAsyncWebRequest(method, url); - if (HasBody && (method == "DELETE" || method == "OPTIONS")) - { - webRequest.ContentType = RequestContentType; - WriteRequestBodyAsync(webRequest, callback); - } - else - { - _timeoutState = new TimeOutState { Request = webRequest }; - var asyncResult = webRequest.BeginGetResponse(result => ResponseCallback(result, callback), webRequest); - SetTimeout(asyncResult, _timeoutState); - } - } - catch(Exception ex) - { - ExecuteCallback(CreateErrorResponse(ex), callback); - } - return webRequest; - } - - private HttpResponse CreateErrorResponse(Exception ex) - { - var response = new HttpResponse(); - var webException = ex as WebException; - if (webException != null && webException.Status == WebExceptionStatus.RequestCanceled) - { - response.ResponseStatus = _timeoutState.TimedOut ? ResponseStatus.TimedOut : ResponseStatus.Aborted; - return response; - } - - response.ErrorMessage = ex.Message; - response.ErrorException = ex; - response.ResponseStatus = ResponseStatus.Error; - return response; - } - - private HttpWebRequest PutPostInternalAsync(string method, Action callback) - { - HttpWebRequest webRequest = null; - try - { - webRequest = ConfigureAsyncWebRequest(method, Url); - PreparePostBody(webRequest); - WriteRequestBodyAsync(webRequest, callback); - } - catch(Exception ex) - { - ExecuteCallback(CreateErrorResponse(ex), callback); - } - - return webRequest; - } - - private void WriteRequestBodyAsync(HttpWebRequest webRequest, Action callback) - { - IAsyncResult asyncResult; - _timeoutState = new TimeOutState { Request = webRequest }; - - if (HasBody || HasFiles || AlwaysMultipartFormData) - { -#if !WINDOWS_PHONE && !PocketPC - webRequest.ContentLength = CalculateContentLength(); -#endif - asyncResult = webRequest.BeginGetRequestStream(result => RequestStreamCallback(result, callback), webRequest); - } - - else - { - asyncResult = webRequest.BeginGetResponse(r => ResponseCallback(r, callback), webRequest); - } - - SetTimeout(asyncResult, _timeoutState); - } - - private long CalculateContentLength() - { - if (RequestBodyBytes != null) - return RequestBodyBytes.Length; - - if (!HasFiles && !AlwaysMultipartFormData) - { - return _defaultEncoding.GetByteCount(RequestBody); - } - - // calculate length for multipart form - long length = 0; - foreach (var file in Files) - { - length += _defaultEncoding.GetByteCount(GetMultipartFileHeader(file)); - length += file.ContentLength; - length += _defaultEncoding.GetByteCount(_lineBreak); - } - - foreach (var param in Parameters) - { - length += _defaultEncoding.GetByteCount(GetMultipartFormData(param)); - } - - length += _defaultEncoding.GetByteCount(GetMultipartFooter()); - return length; - } - - private void RequestStreamCallback(IAsyncResult result, Action callback) - { - var webRequest = (HttpWebRequest)result.AsyncState; - - if (_timeoutState.TimedOut) - { - var response = new HttpResponse {ResponseStatus = ResponseStatus.TimedOut}; - ExecuteCallback(response, callback); - return; - } - - // write body to request stream - try - { - using(var requestStream = webRequest.EndGetRequestStream(result)) - { - if(HasFiles || AlwaysMultipartFormData) - { - WriteMultipartFormData(requestStream); - } - else if (RequestBodyBytes != null) - { - requestStream.Write(RequestBodyBytes, 0, RequestBodyBytes.Length); - } - else - { - WriteStringTo(requestStream, RequestBody); - } - } - } - catch (Exception ex) - { - ExecuteCallback(CreateErrorResponse(ex), callback); - return; - } - - IAsyncResult asyncResult = webRequest.BeginGetResponse(r => ResponseCallback(r, callback), webRequest); - SetTimeout(asyncResult, _timeoutState); - } - - private void SetTimeout(IAsyncResult asyncResult, TimeOutState timeOutState) - { -#if FRAMEWORK && !PocketPC - if (Timeout != 0) - { - ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), timeOutState, Timeout, true); - } -#endif - } - - private static void TimeoutCallback(object state, bool timedOut) - { - if (!timedOut) - return; - - var timeoutState = state as TimeOutState; - - if (timeoutState == null) - { - return; - } - - lock (timeoutState) - { - timeoutState.TimedOut = true; - } - - if (timeoutState.Request != null) - { - timeoutState.Request.Abort(); - } - } - - private static void GetRawResponseAsync(IAsyncResult result, Action callback) - { - var response = new HttpResponse(); - response.ResponseStatus = ResponseStatus.None; - - HttpWebResponse raw = null; - - try - { - var webRequest = (HttpWebRequest)result.AsyncState; - raw = webRequest.EndGetResponse(result) as HttpWebResponse; - } - catch(WebException ex) - { - if(ex.Status == WebExceptionStatus.RequestCanceled) - { - throw ex; - } - - // Check to see if this is an HTTP error or a transport error. - // In cases where an HTTP error occurs ( status code >= 400 ) - // return the underlying HTTP response, otherwise assume a - // transport exception (ex: connection timeout) and - // rethrow the exception - - if (ex.Response is HttpWebResponse) - { - raw = ex.Response as HttpWebResponse; - } - else - { - throw ex; - } - } - - callback(raw); - raw.Close(); - } - - private void ResponseCallback(IAsyncResult result, Action callback) - { - var response = new HttpResponse {ResponseStatus = ResponseStatus.None}; - - try - { - if(_timeoutState.TimedOut) - { - response.ResponseStatus = ResponseStatus.TimedOut; - ExecuteCallback(response, callback); - return; - } - - GetRawResponseAsync(result, webResponse => - { - ExtractResponseData(response, webResponse); - ExecuteCallback(response, callback); - }); - } - catch(Exception ex) - { - ExecuteCallback(CreateErrorResponse(ex), callback); - } - } - - private static void ExecuteCallback(HttpResponse response, Action callback) - { - callback(response); - } - - partial void AddAsyncHeaderActions() - { +#else + return GetStyleMethodInternalAsync(httpMethod.ToUpperInvariant(), action); +#endif + } + + private HttpWebRequest GetStyleMethodInternalAsync(string method, Action callback) + { + HttpWebRequest webRequest = null; + try + { + var url = Url; + webRequest = ConfigureAsyncWebRequest(method, url); + if (HasBody && (method == "DELETE" || method == "OPTIONS")) + { + webRequest.ContentType = RequestContentType; + WriteRequestBodyAsync(webRequest, callback); + } + else + { + _timeoutState = new TimeOutState { Request = webRequest }; + var asyncResult = webRequest.BeginGetResponse(result => ResponseCallback(result, callback), webRequest); + SetTimeout(asyncResult, _timeoutState); + } + } + catch (Exception ex) + { + ExecuteCallback(CreateErrorResponse(ex), callback); + } + return webRequest; + } + + private HttpResponse CreateErrorResponse(Exception ex) + { + var response = new HttpResponse(); + var webException = ex as WebException; + if (webException != null && webException.Status == WebExceptionStatus.RequestCanceled) + { + response.ResponseStatus = _timeoutState.TimedOut ? ResponseStatus.TimedOut : ResponseStatus.Aborted; + return response; + } + + response.ErrorMessage = ex.Message; + response.ErrorException = ex; + response.ResponseStatus = ResponseStatus.Error; + return response; + } + + private HttpWebRequest PutPostInternalAsync(string method, Action callback) + { + HttpWebRequest webRequest = null; + try + { + webRequest = ConfigureAsyncWebRequest(method, Url); + PreparePostBody(webRequest); + WriteRequestBodyAsync(webRequest, callback); + } + catch (Exception ex) + { + ExecuteCallback(CreateErrorResponse(ex), callback); + } + + return webRequest; + } + + private void WriteRequestBodyAsync(HttpWebRequest webRequest, Action callback) + { + IAsyncResult asyncResult; + _timeoutState = new TimeOutState { Request = webRequest }; + + if (HasBody || HasFiles || AlwaysMultipartFormData) + { +#if !WINDOWS_PHONE && !PocketPC + webRequest.ContentLength = CalculateContentLength(); +#endif + asyncResult = webRequest.BeginGetRequestStream(result => RequestStreamCallback(result, callback), webRequest); + } + + else + { + asyncResult = webRequest.BeginGetResponse(r => ResponseCallback(r, callback), webRequest); + } + + SetTimeout(asyncResult, _timeoutState); + } + + private long CalculateContentLength() + { + if (RequestBodyBytes != null) + return RequestBodyBytes.Length; + + if (!HasFiles && !AlwaysMultipartFormData) + { + return _defaultEncoding.GetByteCount(RequestBody); + } + + // calculate length for multipart form + long length = 0; + foreach (var file in Files) + { + length += _defaultEncoding.GetByteCount(GetMultipartFileHeader(file)); + length += file.ContentLength; + length += _defaultEncoding.GetByteCount(_lineBreak); + } + + foreach (var param in Parameters) + { + length += _defaultEncoding.GetByteCount(GetMultipartFormData(param)); + } + + length += _defaultEncoding.GetByteCount(GetMultipartFooter()); + return length; + } + + private void RequestStreamCallback(IAsyncResult result, Action callback) + { + var webRequest = (HttpWebRequest)result.AsyncState; + + if (_timeoutState.TimedOut) + { + var response = new HttpResponse { ResponseStatus = ResponseStatus.TimedOut }; + ExecuteCallback(response, callback); + return; + } + + // write body to request stream + try + { + using (var requestStream = webRequest.EndGetRequestStream(result)) + { + if (HasFiles || AlwaysMultipartFormData) + { + WriteMultipartFormData(requestStream); + } + else if (RequestBodyBytes != null) + { + requestStream.Write(RequestBodyBytes, 0, RequestBodyBytes.Length); + } + else + { + WriteStringTo(requestStream, RequestBody); + } + } + } + catch (Exception ex) + { + ExecuteCallback(CreateErrorResponse(ex), callback); + return; + } + + IAsyncResult asyncResult = webRequest.BeginGetResponse(r => ResponseCallback(r, callback), webRequest); + SetTimeout(asyncResult, _timeoutState); + } + + private void SetTimeout(IAsyncResult asyncResult, TimeOutState timeOutState) + { +#if FRAMEWORK && !PocketPC + if (Timeout != 0) + { + ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), timeOutState, Timeout, true); + } +#endif + } + + private static void TimeoutCallback(object state, bool timedOut) + { + if (!timedOut) + return; + + var timeoutState = state as TimeOutState; + + if (timeoutState == null) + { + return; + } + + lock (timeoutState) + { + timeoutState.TimedOut = true; + } + + if (timeoutState.Request != null) + { + timeoutState.Request.Abort(); + } + } + + private static void GetRawResponseAsync(IAsyncResult result, Action callback) + { + var response = new HttpResponse(); + response.ResponseStatus = ResponseStatus.None; + + HttpWebResponse raw = null; + + try + { + var webRequest = (HttpWebRequest)result.AsyncState; + raw = webRequest.EndGetResponse(result) as HttpWebResponse; + } + catch (WebException ex) + { + if (ex.Status == WebExceptionStatus.RequestCanceled) + { + throw ex; + } + + // Check to see if this is an HTTP error or a transport error. + // In cases where an HTTP error occurs ( status code >= 400 ) + // return the underlying HTTP response, otherwise assume a + // transport exception (ex: connection timeout) and + // rethrow the exception + + if (ex.Response is HttpWebResponse) + { + raw = ex.Response as HttpWebResponse; + } + else + { + throw ex; + } + } + + callback(raw); + raw.Close(); + } + + private void ResponseCallback(IAsyncResult result, Action callback) + { + var response = new HttpResponse { ResponseStatus = ResponseStatus.None }; + + try + { + if (_timeoutState.TimedOut) + { + response.ResponseStatus = ResponseStatus.TimedOut; + ExecuteCallback(response, callback); + return; + } + + GetRawResponseAsync(result, webResponse => + { + ExtractResponseData(response, webResponse); + ExecuteCallback(response, callback); + }); + } + catch (Exception ex) + { + ExecuteCallback(CreateErrorResponse(ex), callback); + } + } + + private static void ExecuteCallback(HttpResponse response, Action callback) + { + callback(response); + } + + partial void AddAsyncHeaderActions() + { #if SILVERLIGHT _restrictedHeaderActions.Add("Content-Length", (r, v) => r.ContentLength = Convert.ToInt64(v)); -#endif +#endif #if WINDOWS_PHONE // WP7 doesn't as of Beta doesn't support a way to set Content-Length either directly // or indirectly _restrictedHeaderActions.Add("Content-Length", (r, v) => { }); -#endif - } - - // TODO: Try to merge the shared parts between ConfigureWebRequest and ConfigureAsyncWebRequest (quite a bit of code - // TODO: duplication at the moment). - private HttpWebRequest ConfigureAsyncWebRequest(string method, Uri url) - { +#endif + } + + // TODO: Try to merge the shared parts between ConfigureWebRequest and ConfigureAsyncWebRequest (quite a bit of code + // TODO: duplication at the moment). + private HttpWebRequest ConfigureAsyncWebRequest(string method, Uri url) + { #if SILVERLIGHT WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp); WebRequest.RegisterPrefix("https://", WebRequestCreator.ClientHttp); -#endif - var webRequest = (HttpWebRequest)WebRequest.Create(url); -#if !PocketPC - webRequest.UseDefaultCredentials = UseDefaultCredentials; -#endif - -#if !WINDOWS_PHONE && !SILVERLIGHT - webRequest.PreAuthenticate = PreAuthenticate; -#endif - AppendHeaders(webRequest); - AppendCookies(webRequest); - - webRequest.Method = method; - - // make sure Content-Length header is always sent since default is -1 -#if !WINDOWS_PHONE && !PocketPC - // WP7 doesn't as of Beta doesn't support a way to set this value either directly - // or indirectly - if(!HasFiles && !AlwaysMultipartFormData) - { - webRequest.ContentLength = 0; - } -#endif - - if(Credentials != null) - { - webRequest.Credentials = Credentials; - } - -#if !SILVERLIGHT - if(UserAgent.HasValue()) - { - webRequest.UserAgent = UserAgent; - } -#endif - -#if FRAMEWORK - if(ClientCertificates != null) - { - webRequest.ClientCertificates.AddRange(ClientCertificates); - } - - webRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip | DecompressionMethods.None; - ServicePointManager.Expect100Continue = false; - - if (Timeout != 0) - { - webRequest.Timeout = Timeout; - } - - if (ReadWriteTimeout != 0) - { - webRequest.ReadWriteTimeout = ReadWriteTimeout; - } - - if (Proxy != null) - { - webRequest.Proxy = Proxy; - } - - if (FollowRedirects && MaxRedirects.HasValue) - { - webRequest.MaximumAutomaticRedirections = MaxRedirects.Value; - } -#endif - -#if !SILVERLIGHT - webRequest.AllowAutoRedirect = FollowRedirects; -#endif - return webRequest; - } - - private class TimeOutState - { - public bool TimedOut { get; set; } - public HttpWebRequest Request { get; set; } - } - } -} +#endif + var webRequest = (HttpWebRequest)WebRequest.Create(url); +#if !PocketPC + webRequest.UseDefaultCredentials = UseDefaultCredentials; +#endif + +#if !WINDOWS_PHONE && !SILVERLIGHT + webRequest.PreAuthenticate = PreAuthenticate; +#endif + AppendHeaders(webRequest); + AppendCookies(webRequest); + + webRequest.Method = method; + + // make sure Content-Length header is always sent since default is -1 +#if !WINDOWS_PHONE && !PocketPC + // WP7 doesn't as of Beta doesn't support a way to set this value either directly + // or indirectly + if (!HasFiles && !AlwaysMultipartFormData) + { + webRequest.ContentLength = 0; + } +#endif + + if (Credentials != null) + { + webRequest.Credentials = Credentials; + } + +#if !SILVERLIGHT + if (UserAgent.HasValue()) + { + webRequest.UserAgent = UserAgent; + } +#endif + +#if FRAMEWORK + if (ClientCertificates != null) + { + webRequest.ClientCertificates.AddRange(ClientCertificates); + } + + webRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip | DecompressionMethods.None; + ServicePointManager.Expect100Continue = false; + + if (Timeout != 0) + { + webRequest.Timeout = Timeout; + } + + if (ReadWriteTimeout != 0) + { + webRequest.ReadWriteTimeout = ReadWriteTimeout; + } + + if (Proxy != null) + { + webRequest.Proxy = Proxy; + } + + if (FollowRedirects && MaxRedirects.HasValue) + { + webRequest.MaximumAutomaticRedirections = MaxRedirects.Value; + } +#endif + +#if !SILVERLIGHT + webRequest.AllowAutoRedirect = FollowRedirects; +#endif + return webRequest; + } + + private class TimeOutState + { + public bool TimedOut { get; set; } + public HttpWebRequest Request { get; set; } + } + } +} \ No newline at end of file diff --git a/RestSharp/Http.Sync.cs b/RestSharp/Http.Sync.cs index 72c354d0f..09a3acf49 100644 --- a/RestSharp/Http.Sync.cs +++ b/RestSharp/Http.Sync.cs @@ -22,280 +22,288 @@ using System.Web; #endif -using RestSharp.Extensions; - -namespace RestSharp -{ - /// - /// HttpWebRequest wrapper (sync methods) - /// - public partial class Http - { - /// - /// Execute a POST request - /// - public HttpResponse Post() - { - return PostPutInternal("POST"); - } - - /// - /// Execute a PUT request - /// - public HttpResponse Put() - { - return PostPutInternal("PUT"); - } - - /// - /// Execute a GET request - /// - public HttpResponse Get() - { - return GetStyleMethodInternal("GET"); - } - - /// - /// Execute a HEAD request - /// - public HttpResponse Head() - { - return GetStyleMethodInternal("HEAD"); - } - - /// - /// Execute an OPTIONS request - /// - public HttpResponse Options() - { - return GetStyleMethodInternal("OPTIONS"); - } - - /// - /// Execute a DELETE request - /// - public HttpResponse Delete() - { - return GetStyleMethodInternal("DELETE"); - } - - /// - /// Execute a PATCH request - /// - public HttpResponse Patch() - { - return PostPutInternal("PATCH"); - } - - /// - /// Execute a GET-style request with the specified HTTP Method. - /// - /// The HTTP method to execute. - /// - public HttpResponse AsGet(string httpMethod) - { +using RestSharp.Extensions; + +namespace RestSharp +{ + /// + /// HttpWebRequest wrapper (sync methods) + /// + public partial class Http + { + /// + /// Execute a POST request + /// + public HttpResponse Post() + { + return PostPutInternal("POST"); + } + + /// + /// Execute a PUT request + /// + public HttpResponse Put() + { + return PostPutInternal("PUT"); + } + + /// + /// Execute a GET request + /// + public HttpResponse Get() + { + return GetStyleMethodInternal("GET"); + } + + /// + /// Execute a HEAD request + /// + public HttpResponse Head() + { + return GetStyleMethodInternal("HEAD"); + } + + /// + /// Execute an OPTIONS request + /// + public HttpResponse Options() + { + return GetStyleMethodInternal("OPTIONS"); + } + + /// + /// Execute a DELETE request + /// + public HttpResponse Delete() + { + return GetStyleMethodInternal("DELETE"); + } + + /// + /// Execute a PATCH request + /// + public HttpResponse Patch() + { + return PostPutInternal("PATCH"); + } + + /// + /// Execute a MERGE request + /// + public HttpResponse Merge() + { + return PostPutInternal("MERGE"); + } + + /// + /// Execute a GET-style request with the specified HTTP Method. + /// + /// The HTTP method to execute. + /// + public HttpResponse AsGet(string httpMethod) + { #if PocketPC return GetStyleMethodInternal(httpMethod.ToUpper()); -#else - return GetStyleMethodInternal(httpMethod.ToUpperInvariant()); -#endif - } - - /// - /// Execute a POST-style request with the specified HTTP Method. - /// - /// The HTTP method to execute. - /// - public HttpResponse AsPost(string httpMethod) - { +#else + return GetStyleMethodInternal(httpMethod.ToUpperInvariant()); +#endif + } + + /// + /// Execute a POST-style request with the specified HTTP Method. + /// + /// The HTTP method to execute. + /// + public HttpResponse AsPost(string httpMethod) + { #if PocketPC return PostPutInternal(httpMethod.ToUpper()); -#else - return PostPutInternal(httpMethod.ToUpperInvariant()); -#endif - } - - private HttpResponse GetStyleMethodInternal(string method) - { - var webRequest = ConfigureWebRequest(method, Url); - - if (HasBody && (method == "DELETE" || method == "OPTIONS")) - { - webRequest.ContentType = RequestContentType; - WriteRequestBody(webRequest); - } - - return GetResponse(webRequest); - } - - private HttpResponse PostPutInternal(string method) - { - var webRequest = ConfigureWebRequest(method, Url); - - PreparePostData(webRequest); - - WriteRequestBody(webRequest); - return GetResponse(webRequest); - } - - partial void AddSyncHeaderActions() - { - _restrictedHeaderActions.Add("Connection", (r, v) => r.Connection = v); - _restrictedHeaderActions.Add("Content-Length", (r, v) => r.ContentLength = Convert.ToInt64(v)); - _restrictedHeaderActions.Add("Expect", (r, v) => r.Expect = v); - _restrictedHeaderActions.Add("If-Modified-Since", (r, v) => r.IfModifiedSince = Convert.ToDateTime(v)); - _restrictedHeaderActions.Add("Referer", (r, v) => r.Referer = v); - _restrictedHeaderActions.Add("Transfer-Encoding", (r, v) => { r.TransferEncoding = v; r.SendChunked = true; }); - _restrictedHeaderActions.Add("User-Agent", (r, v) => r.UserAgent = v); - } - - private void ExtractErrorResponse(HttpResponse httpResponse, Exception ex) - { - var webException = ex as WebException; - - if (webException != null && webException.Status == WebExceptionStatus.Timeout) - { - httpResponse.ResponseStatus = ResponseStatus.TimedOut; - httpResponse.ErrorMessage = ex.Message; - httpResponse.ErrorException = webException; - return; - } - - httpResponse.ErrorMessage = ex.Message; - httpResponse.ErrorException = ex; - httpResponse.ResponseStatus = ResponseStatus.Error; - } - - private HttpResponse GetResponse(HttpWebRequest request) - { - var response = new HttpResponse { ResponseStatus = ResponseStatus.None }; - - try - { - var webResponse = GetRawResponse(request); - ExtractResponseData(response, webResponse); - } - catch (Exception ex) - { - ExtractErrorResponse(response, ex); - } - - return response; - } - - private static HttpWebResponse GetRawResponse(HttpWebRequest request) - { - try - { - return (HttpWebResponse)request.GetResponse(); - } - catch (WebException ex) - { - // Check to see if this is an HTTP error or a transport error. - // In cases where an HTTP error occurs ( status code >= 400 ) - // return the underlying HTTP response, otherwise assume a - // transport exception (ex: connection timeout) and - // rethrow the exception - - if (ex.Response is HttpWebResponse) - { - return ex.Response as HttpWebResponse; - } - throw; - } - } - - private void PreparePostData(HttpWebRequest webRequest) - { - if (HasFiles || AlwaysMultipartFormData) - { - webRequest.ContentType = GetMultipartFormContentType(); - using (var requestStream = webRequest.GetRequestStream()) - { - WriteMultipartFormData(requestStream); - } - } - - PreparePostBody(webRequest); - } - - private void WriteRequestBody(HttpWebRequest webRequest) - { - if (!HasBody) - return; - - var bytes = RequestBodyBytes == null ? _defaultEncoding.GetBytes(RequestBody) : RequestBodyBytes; - - webRequest.ContentLength = bytes.Length; - - using (var requestStream = webRequest.GetRequestStream()) - { - requestStream.Write(bytes, 0, bytes.Length); - } - } - - // TODO: Try to merge the shared parts between ConfigureWebRequest and ConfigureAsyncWebRequest (quite a bit of code - // TODO: duplication at the moment). - private HttpWebRequest ConfigureWebRequest(string method, Uri url) - { - var webRequest = (HttpWebRequest)WebRequest.Create(url); -#if !PocketPC - webRequest.UseDefaultCredentials = UseDefaultCredentials; -#endif - webRequest.PreAuthenticate = PreAuthenticate; - ServicePointManager.Expect100Continue = false; - - AppendHeaders(webRequest); - AppendCookies(webRequest); - - webRequest.Method = method; - - // make sure Content-Length header is always sent since default is -1 - if (!HasFiles && !AlwaysMultipartFormData) - { - webRequest.ContentLength = 0; - } - - webRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip | DecompressionMethods.None; -#if FRAMEWORK - if(ClientCertificates != null) - { - webRequest.ClientCertificates.AddRange(ClientCertificates); - } -#endif - if(UserAgent.HasValue()) - { - webRequest.UserAgent = UserAgent; - } - - if(Timeout != 0) - { - webRequest.Timeout = Timeout; - } - - if (ReadWriteTimeout != 0) - { - webRequest.ReadWriteTimeout = ReadWriteTimeout; - } - - if(Credentials != null) - { - webRequest.Credentials = Credentials; - } - - if(Proxy != null) - { - webRequest.Proxy = Proxy; - } - - webRequest.AllowAutoRedirect = FollowRedirects; - if(FollowRedirects && MaxRedirects.HasValue) - { - webRequest.MaximumAutomaticRedirections = MaxRedirects.Value; - } - - return webRequest; - } - } +#else + return PostPutInternal(httpMethod.ToUpperInvariant()); +#endif + } + + private HttpResponse GetStyleMethodInternal(string method) + { + var webRequest = ConfigureWebRequest(method, Url); + + if (HasBody && (method == "DELETE" || method == "OPTIONS")) + { + webRequest.ContentType = RequestContentType; + WriteRequestBody(webRequest); + } + + return GetResponse(webRequest); + } + + private HttpResponse PostPutInternal(string method) + { + var webRequest = ConfigureWebRequest(method, Url); + + PreparePostData(webRequest); + + WriteRequestBody(webRequest); + return GetResponse(webRequest); + } + + partial void AddSyncHeaderActions() + { + _restrictedHeaderActions.Add("Connection", (r, v) => r.Connection = v); + _restrictedHeaderActions.Add("Content-Length", (r, v) => r.ContentLength = Convert.ToInt64(v)); + _restrictedHeaderActions.Add("Expect", (r, v) => r.Expect = v); + _restrictedHeaderActions.Add("If-Modified-Since", (r, v) => r.IfModifiedSince = Convert.ToDateTime(v)); + _restrictedHeaderActions.Add("Referer", (r, v) => r.Referer = v); + _restrictedHeaderActions.Add("Transfer-Encoding", (r, v) => { r.TransferEncoding = v; r.SendChunked = true; }); + _restrictedHeaderActions.Add("User-Agent", (r, v) => r.UserAgent = v); + } + + private void ExtractErrorResponse(HttpResponse httpResponse, Exception ex) + { + var webException = ex as WebException; + + if (webException != null && webException.Status == WebExceptionStatus.Timeout) + { + httpResponse.ResponseStatus = ResponseStatus.TimedOut; + httpResponse.ErrorMessage = ex.Message; + httpResponse.ErrorException = webException; + return; + } + + httpResponse.ErrorMessage = ex.Message; + httpResponse.ErrorException = ex; + httpResponse.ResponseStatus = ResponseStatus.Error; + } + + private HttpResponse GetResponse(HttpWebRequest request) + { + var response = new HttpResponse { ResponseStatus = ResponseStatus.None }; + + try + { + var webResponse = GetRawResponse(request); + ExtractResponseData(response, webResponse); + } + catch (Exception ex) + { + ExtractErrorResponse(response, ex); + } + + return response; + } + + private static HttpWebResponse GetRawResponse(HttpWebRequest request) + { + try + { + return (HttpWebResponse)request.GetResponse(); + } + catch (WebException ex) + { + // Check to see if this is an HTTP error or a transport error. + // In cases where an HTTP error occurs ( status code >= 400 ) + // return the underlying HTTP response, otherwise assume a + // transport exception (ex: connection timeout) and + // rethrow the exception + + if (ex.Response is HttpWebResponse) + { + return ex.Response as HttpWebResponse; + } + throw; + } + } + + private void PreparePostData(HttpWebRequest webRequest) + { + if (HasFiles || AlwaysMultipartFormData) + { + webRequest.ContentType = GetMultipartFormContentType(); + using (var requestStream = webRequest.GetRequestStream()) + { + WriteMultipartFormData(requestStream); + } + } + + PreparePostBody(webRequest); + } + + private void WriteRequestBody(HttpWebRequest webRequest) + { + if (!HasBody) + return; + + var bytes = RequestBodyBytes == null ? _defaultEncoding.GetBytes(RequestBody) : RequestBodyBytes; + + webRequest.ContentLength = bytes.Length; + + using (var requestStream = webRequest.GetRequestStream()) + { + requestStream.Write(bytes, 0, bytes.Length); + } + } + + // TODO: Try to merge the shared parts between ConfigureWebRequest and ConfigureAsyncWebRequest (quite a bit of code + // TODO: duplication at the moment). + private HttpWebRequest ConfigureWebRequest(string method, Uri url) + { + var webRequest = (HttpWebRequest)WebRequest.Create(url); +#if !PocketPC + webRequest.UseDefaultCredentials = UseDefaultCredentials; +#endif + webRequest.PreAuthenticate = PreAuthenticate; + ServicePointManager.Expect100Continue = false; + + AppendHeaders(webRequest); + AppendCookies(webRequest); + + webRequest.Method = method; + + // make sure Content-Length header is always sent since default is -1 + if (!HasFiles && !AlwaysMultipartFormData) + { + webRequest.ContentLength = 0; + } + + webRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip | DecompressionMethods.None; +#if FRAMEWORK + if (ClientCertificates != null) + { + webRequest.ClientCertificates.AddRange(ClientCertificates); + } +#endif + if (UserAgent.HasValue()) + { + webRequest.UserAgent = UserAgent; + } + + if (Timeout != 0) + { + webRequest.Timeout = Timeout; + } + + if (ReadWriteTimeout != 0) + { + webRequest.ReadWriteTimeout = ReadWriteTimeout; + } + + if (Credentials != null) + { + webRequest.Credentials = Credentials; + } + + if (Proxy != null) + { + webRequest.Proxy = Proxy; + } + + webRequest.AllowAutoRedirect = FollowRedirects; + if (FollowRedirects && MaxRedirects.HasValue) + { + webRequest.MaximumAutomaticRedirections = MaxRedirects.Value; + } + + return webRequest; + } + } } #endif \ No newline at end of file diff --git a/RestSharp/Http.cs b/RestSharp/Http.cs index 7c6763807..136f1ca1f 100644 --- a/RestSharp/Http.cs +++ b/RestSharp/Http.cs @@ -94,7 +94,6 @@ protected bool HasFiles /// Always send a multipart/form-data request - even when no Files are present. /// public bool AlwaysMultipartFormData { get; set; } - /// /// UserAgent to be sent with request /// @@ -138,7 +137,7 @@ protected bool HasFiles public X509CertificateCollection ClientCertificates { get; set; } #endif #if FRAMEWORK || PocketPC - /// + /// /// Maximum number of automatic redirects to follow if FollowRedirects is true /// public int? MaxRedirects { get; set; } @@ -177,8 +176,10 @@ protected bool HasFiles /// /// URL to call for this request /// - public Uri Url { get; set; } - + public Uri Url { get; set; } + /// + /// Flag to send authorisation header with the HttpWebRequest + /// public bool PreAuthenticate { get; set; } #if FRAMEWORK || PocketPC diff --git a/RestSharp/IHttp.cs b/RestSharp/IHttp.cs index f43bfdc33..8857f2bc1 100644 --- a/RestSharp/IHttp.cs +++ b/RestSharp/IHttp.cs @@ -69,7 +69,8 @@ public interface IHttp HttpWebRequest OptionsAsync(Action action); HttpWebRequest PostAsync(Action action); HttpWebRequest PutAsync(Action action); - HttpWebRequest PatchAsync(Action action); + HttpWebRequest PatchAsync(Action action); + HttpWebRequest MergeAsync(Action action); HttpWebRequest AsPostAsync(Action action, string httpMethod); HttpWebRequest AsGetAsync(Action action, string httpMethod); @@ -80,7 +81,8 @@ public interface IHttp HttpResponse Options(); HttpResponse Post(); HttpResponse Put(); - HttpResponse Patch(); + HttpResponse Patch(); + HttpResponse Merge(); HttpResponse AsPost(string httpMethod); HttpResponse AsGet(string httpMethod); diff --git a/RestSharp/RestClient.Async.cs b/RestSharp/RestClient.Async.cs index 73fa13144..670420cc5 100644 --- a/RestSharp/RestClient.Async.cs +++ b/RestSharp/RestClient.Async.cs @@ -34,23 +34,24 @@ public partial class RestClient /// Executes the request and callback asynchronously, authenticating if needed /// /// Request to be executed - /// Callback function to be executed upon completion providing access to the async handle. - public virtual RestRequestAsyncHandle ExecuteAsync(IRestRequest request, Action callback) - { + /// Callback function to be executed upon completion providing access to the async handle. + public virtual RestRequestAsyncHandle ExecuteAsync(IRestRequest request, Action callback) + { #if PocketPC - string method = request.Method.ToString(); -#else - string method = Enum.GetName(typeof (Method), request.Method); -#endif - switch (request.Method) - { - case Method.PATCH: - case Method.POST: - case Method.PUT: - return ExecuteAsync(request, callback, method, DoAsPostAsync); - default: - return ExecuteAsync(request, callback, method, DoAsGetAsync); - } + string method = request.Method.ToString(); +#else + string method = Enum.GetName(typeof(Method), request.Method); +#endif + switch (request.Method) + { + case Method.MERGE: + case Method.PATCH: + case Method.POST: + case Method.PUT: + return ExecuteAsync(request, callback, method, DoAsPostAsync); + default: + return ExecuteAsync(request, callback, method, DoAsGetAsync); + } } /// diff --git a/RestSharp/RestClient.Sync.cs b/RestSharp/RestClient.Sync.cs index 6351c3942..6a079c519 100644 --- a/RestSharp/RestClient.Sync.cs +++ b/RestSharp/RestClient.Sync.cs @@ -27,23 +27,24 @@ public byte[] DownloadData(IRestRequest request) /// Executes the request and returns a response, authenticating if needed /// /// Request to be executed - /// RestResponse - public virtual IRestResponse Execute(IRestRequest request) - { + /// RestResponse + public virtual IRestResponse Execute(IRestRequest request) + { #if PocketPC var method = request.Method.ToString(); -#else - var method = Enum.GetName(typeof(Method), request.Method); -#endif - switch (request.Method) - { - case Method.POST: - case Method.PUT: - case Method.PATCH: - return Execute(request, method, DoExecuteAsPost); - default: - return Execute(request, method, DoExecuteAsGet); - } +#else + var method = Enum.GetName(typeof(Method), request.Method); +#endif + switch (request.Method) + { + case Method.POST: + case Method.PUT: + case Method.PATCH: + case Method.MERGE: + return Execute(request, method, DoExecuteAsPost); + default: + return Execute(request, method, DoExecuteAsGet); + } } public IRestResponse ExecuteAsGet(IRestRequest request, string httpMethod)