private string RequestJsonStringWithRetry( Uri uri, RouteStyle routeStyle, string httpMethod, string requestArg = "", Stream body = null) { var attempt = 0; var maxRetries = this.YfyClientConfig.HttpConfig.MaxRetries; var r = new Random(); if (routeStyle == RouteStyle.Upload) { if (body == null) { throw new ArgumentNullException(nameof(body)); } //为了重试机制,上传的流必须可以检索,否则无法重试 if (!body.CanSeek) { maxRetries = 0; } } while (true) { try { return(this.RequestJsonString(uri, routeStyle, httpMethod, requestArg, body)); } catch (RateLimitException) { throw; } catch (RetryException re) { if (++attempt > maxRetries) { throw new InternalServerException(re); } } catch (TokenRefreshedException) { } var backoff = TimeSpan.FromSeconds(Math.Pow(2, attempt) * r.NextDouble()); Thread.Sleep(backoff); if (body != null) { body.Position = 0; } } }
/// <summary> /// The add new panel. /// </summary> /// <param name="name"> /// The name. /// </param> /// <param name="description"> /// The description. /// </param> /// <param name="panelType"> /// The panel type. /// </param> /// <param name="logoImagePath"> /// The logo image path. /// </param> /// <param name="backGroundImagePath"> /// The back ground image path. /// </param> /// <returns> /// The <see cref="PanelRootObject"/>. /// </returns> public PanelRootObject AddPanel( string name, string description, PanelType panelType, string logoImagePath = "", string backGroundImagePath = "") { if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(description)) { return(_result.ErrorToObject(new PanelRootObject(), "Invalid parameter(s)")); } string files = string.Empty; RouteStyle routeStyle = RouteStyle.Rpc; if (!string.IsNullOrEmpty(logoImagePath)) { if (!File.Exists(logoImagePath)) { return(_result.ErrorToObject(new PanelRootObject(), "File not exist!")); } routeStyle = RouteStyle.Upload; var logoimage = Helper.MoveFile(logoImagePath, "Panel-logo"); // File name must contains 'logo' keyword. files = logoimage + ";"; } if (!string.IsNullOrEmpty(backGroundImagePath)) { if (!File.Exists(backGroundImagePath)) { return(_result.ErrorToObject(new PanelRootObject(), "File not exist!")); } routeStyle = RouteStyle.Upload; var bkgImage = Helper.MoveFile( backGroundImagePath, "Panel-background"); // File name must contains 'background' keyword. files = files + bkgImage; } Task <Result> x = RequestHandler.SendRequestAsync( string.Empty, "api/UserPanel/AddPanel?Name=" + name + "&Description=" + description + "&panelType=" + (Int32)panelType, HttpMethod.Post, routeStyle, string.IsNullOrEmpty(files) ? null : files); x.Wait(); return(x.Result.JsonToObject(new PanelRootObject(), "Panels")); }
/// <summary> /// Requests the JSON string. /// </summary> /// <param name="host">The host.</param> /// <param name="routeName">Name of the route.</param> /// <param name="auth">The auth type of the route.</param> /// <param name="routeStyle">The route style.</param> /// <param name="requestArg">The request argument.</param> /// <param name="body">The body to upload if <paramref name="routeStyle"/> /// is <see cref="RouteStyle.Upload"/>.</param> /// <returns>The asynchronous task with the result.</returns> private async Task <Result> RequestJsonString( string host, string routeName, string auth, RouteStyle routeStyle, string requestArg, Stream body = null) { var hostname = this.options.HostMap[host]; var uri = this.GetRouteUri(hostname, routeName); var request = new HttpRequestMessage(HttpMethod.Post, uri); if (auth == AuthType.User || auth == AuthType.Team) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this.options.OAuth2AccessToken); } else if (auth == AuthType.App) { request.Headers.Authorization = new AuthenticationHeaderValue("Basic", this.options.OAuth2AccessToken); } else if (auth == AuthType.NoAuth) { } else { throw new ArgumentException("Invalid auth type", auth); } request.Headers.TryAddWithoutValidation("User-Agent", this.options.UserAgent); if (this.selectUser != null) { request.Headers.TryAddWithoutValidation("Dropbox-Api-Select-User", this.selectUser); } if (this.selectAdmin != null) { request.Headers.TryAddWithoutValidation("Dropbox-Api-Select-Admin", this.selectAdmin); } var completionOption = HttpCompletionOption.ResponseContentRead; switch (routeStyle) { case RouteStyle.Rpc: request.Content = new StringContent(requestArg, Encoding.UTF8, "application/json"); break; case RouteStyle.Download: request.Headers.Add(DropboxApiArgHeader, requestArg); // This is required to force libcurl remove default content type header. request.Content = new StringContent(""); request.Content.Headers.ContentType = null; completionOption = HttpCompletionOption.ResponseHeadersRead; break; case RouteStyle.Upload: request.Headers.Add(DropboxApiArgHeader, requestArg); if (body == null) { throw new ArgumentNullException("body"); } request.Content = new CustomStreamContent(body); request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); break; default: throw new InvalidOperationException(string.Format( CultureInfo.InvariantCulture, "Unknown route style: {0}", routeStyle)); } var disposeResponse = true; var response = await this.getHttpClient(host).SendAsync(request, completionOption).ConfigureAwait(false); var requestId = GetRequestId(response); try { if ((int)response.StatusCode >= 500) { var text = await response.Content.ReadAsStringAsync(); text = this.CheckForError(text); throw new RetryException(requestId, (int)response.StatusCode, message: text, uri: uri); } else if (response.StatusCode == HttpStatusCode.BadRequest) { var text = await response.Content.ReadAsStringAsync(); text = this.CheckForError(text); throw new BadInputException(requestId, text, uri); } else if (response.StatusCode == HttpStatusCode.Unauthorized) { var reason = await response.Content.ReadAsStringAsync(); throw AuthException.Decode(reason, () => new AuthException(GetRequestId(response))); } else if ((int)response.StatusCode == 429) { var reason = await response.Content.ReadAsStringAsync(); throw RateLimitException.Decode(reason, () => new RateLimitException(GetRequestId(response))); } else if (response.StatusCode == HttpStatusCode.Forbidden) { var reason = await response.Content.ReadAsStringAsync(); throw AccessException.Decode(reason, () => new AccessException(GetRequestId(response))); } else if (response.StatusCode == HttpStatusCode.Conflict || response.StatusCode == HttpStatusCode.NotFound) { var reason = await response.Content.ReadAsStringAsync(); return(new Result { IsError = true, ObjectResult = reason, RequestId = GetRequestId(response) }); } else if ((int)response.StatusCode >= 200 && (int)response.StatusCode <= 299) { if (routeStyle == RouteStyle.Download) { disposeResponse = false; return(new Result { IsError = false, ObjectResult = response.Headers.GetValues(DropboxApiResultHeader).FirstOrDefault(), HttpResponse = response }); } else { return(new Result { IsError = false, ObjectResult = await response.Content.ReadAsStringAsync() }); } } else { var text = await response.Content.ReadAsStringAsync(); text = this.CheckForError(text); throw new HttpException(requestId, (int)response.StatusCode, text, uri); } } finally { if (disposeResponse) { response.Dispose(); } } }
/// <summary> /// Requests the JSON string with retry. /// </summary> /// <param name="host">The host.</param> /// <param name="auth">The auth type of the route.</param> /// <param name="routeName">Name of the route.</param> /// <param name="routeStyle">The route style.</param> /// <param name="requestArg">The request argument.</param> /// <param name="body">The body to upload if <paramref name="routeStyle"/> /// is <see cref="RouteStyle.Upload"/>.</param> /// <returns>The asynchronous task with the result.</returns> private async Task <Result> RequestJsonStringWithRetry( string host, string routeName, string auth, RouteStyle routeStyle, string requestArg, Stream body = null) { var attempt = 0; var maxRetries = this.options.MaxClientRetries; var r = new Random(); if (routeStyle == RouteStyle.Upload) { if (body == null) { throw new ArgumentNullException("body"); } // to support retry logic, the body stream must be seekable // if it isn't we won't retry if (!body.CanSeek) { maxRetries = 0; } } try { while (true) { try { return(await this.RequestJsonString(host, routeName, auth, routeStyle, requestArg, body) .ConfigureAwait(false)); } catch (RateLimitException) { throw; } catch (RetryException) { // dropbox maps 503 - ServiceUnavailable to be a rate limiting error. // do not count a rate limiting error as an attempt if (++attempt > maxRetries) { throw; } } // use exponential backoff var backoff = TimeSpan.FromSeconds(Math.Pow(2, attempt) * r.NextDouble()); #if PORTABLE40 await TaskEx.Delay(backoff); #else await Task.Delay(backoff); #endif if (body != null) { body.Position = 0; } } } finally { if (body != null) { body.Dispose(); } } }
/// <summary> /// Requests the JSON string with retry. /// </summary> /// <param name="host">The host.</param> /// <param name="auth">The auth type of the route.</param> /// <param name="routeName">Name of the route.</param> /// <param name="routeStyle">The route style.</param> /// <param name="requestArg">The request argument.</param> /// <param name="body">The body to upload if <paramref name="routeStyle"/> /// is <see cref="RouteStyle.Upload"/>.</param> /// <returns>The asynchronous task with the result.</returns> private async Task <Result> RequestJsonStringWithRetry( string host, string routeName, string auth, RouteStyle routeStyle, string requestArg, Stream body = null) { var attempt = 0; var maxRetries = this.options.MaxClientRetries; var r = new Random(); byte[] cachedBody = null; long cachedStreamStart = 0; if (routeStyle == RouteStyle.Upload) { // to support retry logic, the body stream must be seekable // if it isn't we won't retry if (!body.CanSeek) { maxRetries = 0; } else if (body is MemoryStream) { cachedStreamStart = body.Position; cachedBody = ((MemoryStream)body).ToArray(); } else { cachedStreamStart = body.Position; using (var mem = new MemoryStream()) { await body.CopyToAsync(mem).ConfigureAwait(false); cachedBody = mem.ToArray(); } } } while (true) { try { if (cachedBody == null) { return(await this.RequestJsonString(host, routeName, auth, routeStyle, requestArg, body) .ConfigureAwait(false)); } else { using (var mem = new MemoryStream(cachedBody, writable: false)) { mem.Position = cachedStreamStart; return(await this.RequestJsonString(host, routeName, auth, routeStyle, requestArg, mem) .ConfigureAwait(false)); } } } catch (RateLimitException) { throw; } catch (RetryException) { // dropbox maps 503 - ServiceUnavailable to be a rate limiting error. // do not count a rate limiting error as an attempt if (++attempt > maxRetries) { throw; } } // use exponential backoff var backoff = TimeSpan.FromSeconds(Math.Pow(2, attempt) * r.NextDouble()); #if PORTABLE40 await TaskEx.Delay(backoff); #else await Task.Delay(backoff); #endif } }
/// <summary> /// The send api request async for calling route method. /// </summary> /// <param name="host"> /// The host. /// </param> /// <param name="routeName"> /// The route name. /// </param> /// <param name="method"> /// The method. /// </param> /// <param name="routeStyle"> /// The route style. /// </param> /// <param name="requestArg"> /// The request arg. /// </param> /// <param name="body"> /// The body. /// </param> /// <returns> /// The <see cref="Task"/>. /// </returns> /// <exception cref="InvalidOperationException"> /// </exception> public async Task <Result> SendRequestAsync( string host, string routeName, HttpMethod method, RouteStyle routeStyle, string requestArg, Stream body = null) { var uri = new Uri(Hostname + routeName); var request = new HttpRequestMessage(method, uri); if (!string.IsNullOrEmpty(SessionId)) { request.Headers.Add("SessionID", SessionId); } switch (routeStyle) { case RouteStyle.Rpc: if ((method == HttpMethod.Post || method == HttpMethod.Put || method == HttpMethod.Delete) && requestArg != null) { request.Content = new StringContent(requestArg, Encoding.UTF8, "application/json"); } break; case RouteStyle.Download: if (method == HttpMethod.Post) { request.Content = new StringContent(requestArg, Encoding.UTF8, "application/json"); } break; case RouteStyle.Upload: MultipartFormDataContent multiPartContent = new MultipartFormDataContent("----MyGreatBoundary"); char[] charSeparators = new[] { ';' }; var files = requestArg.Split(charSeparators, StringSplitOptions.RemoveEmptyEntries); foreach (var file in files) { var byteArrayContent = CreateFileContent(file, out string filename); multiPartContent.Add(byteArrayContent, filename, filename); } request.Content = multiPartContent; break; default: throw new InvalidOperationException( string.Format(CultureInfo.InvariantCulture, "Unknown route style: {0}", routeStyle)); } var disposeResponse = true; var response = await HttpClient.SendAsync(request).ConfigureAwait(false); try { if ((int)response.StatusCode >= 500) { var reason = await response.Content.ReadAsStringAsync(); reason = CheckForError(reason); return(new Result { IsError = true, ObjectResult = reason, HttpResponse = response }); } else if (response.StatusCode == HttpStatusCode.BadRequest) { var reason = await response.Content.ReadAsStringAsync(); reason = CheckForError(reason); return(new Result { IsError = true, ObjectResult = reason, HttpResponse = response }); } else if (response.StatusCode == HttpStatusCode.Unauthorized) { var reason = await response.Content.ReadAsStringAsync(); reason = CheckForError(reason); return(new Result { IsError = true, ObjectResult = reason, HttpResponse = response }); } else if ((int)response.StatusCode == 429) { var reason = await response.Content.ReadAsStringAsync(); reason = CheckForError(reason); return(new Result { IsError = true, ObjectResult = reason, HttpResponse = response }); } else if (response.StatusCode == HttpStatusCode.Conflict || response.StatusCode == HttpStatusCode.Forbidden || response.StatusCode == HttpStatusCode.NotFound) { var reason = await response.Content.ReadAsStringAsync(); reason = CheckForError(reason); return(new Result { IsError = true, ObjectResult = reason, HttpResponse = response }); } else if ((int)response.StatusCode >= 200 && (int)response.StatusCode <= 299) { if (routeStyle == RouteStyle.Download) { disposeResponse = false; return(new Result { IsError = false, ObjectResult = await response.Content.ReadAsStringAsync(), HttpResponse = response }); } else { return(new Result { IsError = false, ObjectResult = await response.Content.ReadAsStringAsync(), HttpResponse = response }); } } else { var text = await response.Content.ReadAsStringAsync(); CheckForError(text); return(new Result { IsError = true, ObjectResult = await response.Content.ReadAsStringAsync(), HttpResponse = response }); } } finally { if (disposeResponse) { response.Dispose(); } } }
/// <summary> /// Requests the JSON string. /// </summary> /// <param name="host">The host.</param> /// <param name="routeName">Name of the route.</param> /// <param name="routeStyle">The route style.</param> /// <param name="requestArg">The request argument.</param> /// <param name="body">The body to upload if <paramref name="routeStyle"/> /// is <see cref="RouteStyle.Upload"/>.</param> /// <returns>The asynchronous task with the result.</returns> private async Task <Result> RequestJsonString( string host, string routeName, RouteStyle routeStyle, string requestArg, Stream body = null) { var hostname = this.hostMap[host]; var uri = this.GetRouteUri(hostname, routeName); var request = new HttpRequestMessage(HttpMethod.Post, uri); switch (routeStyle) { case RouteStyle.Rpc: request.Content = new StringContent(requestArg, Encoding.UTF8, "application/json"); break; case RouteStyle.Download: request.Headers.Add(DropboxApiArgHeader, requestArg); break; case RouteStyle.Upload: request.Headers.Add(DropboxApiArgHeader, requestArg); if (body == null) { throw new ArgumentNullException("body"); } request.Content = new StreamContent(body); request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); break; default: throw new InvalidOperationException(string.Format( CultureInfo.InvariantCulture, "Unknown route style: {0}", routeStyle)); } var disposeResponse = true; var response = await this.httpClient.SendAsync(request); try { if ((int)response.StatusCode >= 500) { var text = await response.Content.ReadAsStringAsync(); text = this.CheckForError(text); throw new RetryException((int)response.StatusCode, message: text, uri: uri); } else if (response.StatusCode == HttpStatusCode.BadRequest) { var text = await response.Content.ReadAsStringAsync(); text = this.CheckForError(text); throw new BadInputException(text, uri); } else if (response.StatusCode == HttpStatusCode.Unauthorized) { var text = await response.Content.ReadAsStringAsync(); text = this.CheckForError(text); throw new AuthException(text, uri); } else if ((int)response.StatusCode == 429) { throw new RetryException(429, uri: uri, isRateLimit: true); } else if (response.StatusCode == HttpStatusCode.Conflict || response.StatusCode == HttpStatusCode.Forbidden || response.StatusCode == HttpStatusCode.NotFound) { var reason = await response.Content.ReadAsStringAsync(); return(new Result { IsError = true, ObjectResult = reason }); } else if ((int)response.StatusCode >= 200 && (int)response.StatusCode <= 299) { if (routeStyle == RouteStyle.Download) { disposeResponse = false; return(new Result { IsError = false, ObjectResult = response.Headers.GetValues(DropboxApiResultHeader).FirstOrDefault(), HttpResponse = response }); } else { return(new Result { IsError = false, ObjectResult = await response.Content.ReadAsStringAsync() }); } } else { var text = await response.Content.ReadAsStringAsync(); text = this.CheckForError(text); throw new HttpException((int)response.StatusCode, text, uri); } } finally { if (disposeResponse) { response.Dispose(); } } }
private string RequestJsonString( Uri uri, RouteStyle routeStyle, string httpMethod, string requestArg = "", Stream body = null) { HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest; HttpWebResponse response; request.Method = httpMethod; request.Timeout = YfyClientConfig.HttpConfig.Timeout; request.Headers.Add(HttpRequestHeader.Authorization, $"Bearer {YfyClientConfig.Oauth2AccessToken}"); request.Headers.Add("X-Runtime-Version", YfySystem.CallerImageRuntimeVersion); request.UserAgent = YfyRequestHandler.UserAgent; if (YfyClientConfig.HttpConfig.Proxy != null) { request.Proxy = YfyClientConfig.HttpConfig.Proxy; } switch (routeStyle) { case RouteStyle.Rpc: { request.ContentType = "application/json"; request.Accept = $"application/{this._apiVersion}+json"; if (requestArg != "") { using (Stream bodyStream = request.GetRequestStream()) { var bodyBytes = new UTF8Encoding(false).GetBytes(requestArg); bodyStream.Write(bodyBytes, 0, bodyBytes.Length); } } } break; case RouteStyle.Upload: { if (body == null) { throw new ArgumentNullException(nameof(body)); } var boundary = YfyRequestHandler._Boundary + DateTime.Now.Ticks.ToString("x"); request.ContentType = "multipart/form-data; boundary=" + boundary; request.SendChunked = true; //重要,在上传大文件时该参数应该为false request.AllowWriteStreamBuffering = false; StringBuilder sb = new StringBuilder("--" + boundary); sb.Append("\r\n"); sb.Append("Content-Disposition: form-data; name=\"file\"; filename=\"\"\r\n"); sb.Append("Content-Type: application/octet-stream"); sb.Append("\r\n\r\n"); var bodyBytes = new UTF8Encoding(false).GetBytes(sb.ToString()); var endBody = new UTF8Encoding(false).GetBytes("\r\n" + "--" + boundary + "--"); using (Stream bodyStream = request.GetRequestStream()) { bodyStream.Write(bodyBytes, 0, bodyBytes.Length); StreamHelper.CopyStream(body, bodyStream); bodyStream.Write(endBody, 0, endBody.Length); } } break; case RouteStyle.Download: request.ContentType = null; break; default: throw new ArgumentNullException(nameof(routeStyle)); } try { response = request.GetResponse() as HttpWebResponse; string result; if (routeStyle == RouteStyle.Download) { using (Stream stream = response.GetResponseStream()) { using (FileStream fs = System.IO.File.Create(requestArg)) { StreamHelper.CopyStream(stream, fs); result = ""; } } } else { result = ReadAsStringFromResponse(response); } response.Close(); return(result); } catch (WebException we) { response = we.Response as HttpWebResponse; if (response == null) { throw; } string responseStr = ReadAsStringFromResponse(response); string requestId; this.TryGetRequestId(responseStr, out requestId); if ((int)response.StatusCode >= 500) { throw new RetryException(requestId, (int)response.StatusCode, responseStr, uri, we); } else if (response.StatusCode == HttpStatusCode.BadRequest) { CheckForError(responseStr, uri); throw new BadInputException(requestId, uri, responseStr, we); } else if (response.StatusCode == HttpStatusCode.Unauthorized) { if (RefreshToken()) { throw new TokenRefreshedException(); } throw new UnAuthorizedException(requestId, uri, responseStr, we); } else if ((int)response.StatusCode == RateLimitException.RateLimitStatusCode) { int rateLimit = Convert.ToInt32(response.Headers.Get("X-Rate-Limit-Reset")); throw new RateLimitException(requestId, rateLimit, uri, we); } else if (response.StatusCode == HttpStatusCode.PaymentRequired || response.StatusCode == HttpStatusCode.Forbidden || response.StatusCode == HttpStatusCode.Conflict) { throw new BadResponseException(requestId, (int)response.StatusCode, uri, responseStr, we); } else { throw new YfyHttpException(requestId, (int)response.StatusCode, uri, we.Message, we); } } }
public RoutingOptions(float kSpeed, RouteOrientation kOrientation, RouteStyle kRouteStyle) { this.kSpeed = kSpeed; this.kOrientation = kOrientation; this.kRouteStyle = kRouteStyle; }