diff --git a/DESCRIPTION b/DESCRIPTION index 5a489c942a..e9551518f0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -26,7 +26,7 @@ Depends: Imports: tools, scales, - httr, + httr (>= 1.3.0), jsonlite (>= 1.6), magrittr, digest, diff --git a/NAMESPACE b/NAMESPACE index 458c99bc85..7f9ded1524 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -241,14 +241,14 @@ importFrom(htmlwidgets,saveWidget) importFrom(htmlwidgets,shinyRenderWidget) importFrom(htmlwidgets,shinyWidgetOutput) importFrom(htmlwidgets,sizingPolicy) -importFrom(httr,GET) -importFrom(httr,PATCH) -importFrom(httr,POST) +importFrom(httr,RETRY) importFrom(httr,add_headers) +importFrom(httr,authenticate) importFrom(httr,config) importFrom(httr,content) importFrom(httr,stop_for_status) importFrom(httr,warn_for_status) +importFrom(httr,write_disk) importFrom(jsonlite,parse_json) importFrom(jsonlite,read_json) importFrom(jsonlite,toJSON) diff --git a/NEWS.md b/NEWS.md index 3d64491146..762df24359 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,8 @@ ## IMPROVEMENTS +* All HTTP requests are now retried upon failure (#1656) + ## BUG FIXES * `ggplotly()` now handles `element_blank()` and `factor()` labels in positional scales correctly (#1731 and #1772). diff --git a/R/api_exports.R b/R/api_exports.R index 55238f71e1..02ae27638e 100644 --- a/R/api_exports.R +++ b/R/api_exports.R @@ -31,14 +31,14 @@ #' @param endpoint the endpoint (i.e., location) for the request. #' To see a list of all available endpoints, call `api()`. #' Any relevant query parameters should be included here (see examples). -#' @param verb name of the HTTP verb to use (as in, [httr::VERB()]). -#' @param body body of the HTTP request(as in, [httr::VERB()]). +#' @param verb name of the HTTP verb to use (as in, [httr::RETRY()]). +#' @param body body of the HTTP request(as in, [httr::RETRY()]). #' If this value is not already converted to JSON #' (via [jsonlite::toJSON()]), it uses the internal `to_JSON()` #' to ensure values are "automatically unboxed" (i.e., vec. #' #' @param ... For `api()`, these arguments are passed onto -#' [httr::VERB()]. For `api_create()`, these arguments are +#' [httr::RETRY()]. For `api_create()`, these arguments are #' included in the body of the HTTP request. #' #' @export @@ -187,9 +187,16 @@ api <- function(endpoint = "/", verb = "GET", body = NULL, ...) { body <- to_JSON(body) } - resp <- httr::VERB( - verb = verb, url = url, api_headers(), api_auth(), - body = body, ... + resp <- httr::RETRY( + verb = verb, + url = url, + api_headers(), + api_auth(), + body = body, + times = 5, + terminate_on = c(400, 401, 403, 404), + terminate_on_success = TRUE, + ... ) structure(process(resp), class = "api") diff --git a/R/imports.R b/R/imports.R index 804fc19e70..907d3ba772 100644 --- a/R/imports.R +++ b/R/imports.R @@ -6,7 +6,7 @@ #' @importFrom tidyr unnest #' @importFrom viridisLite viridis #' @importFrom jsonlite toJSON parse_json read_json -#' @importFrom httr GET POST PATCH content config add_headers stop_for_status warn_for_status +#' @importFrom httr RETRY content config add_headers authenticate stop_for_status warn_for_status write_disk #' @importFrom htmlwidgets createWidget sizingPolicy saveWidget onRender prependContent #' @importFrom lazyeval f_eval is_formula all_dots is_lang f_new #' @importFrom tibble as_tibble diff --git a/R/orca.R b/R/orca.R index d41ed26158..1a394788e4 100644 --- a/R/orca.R +++ b/R/orca.R @@ -192,9 +192,13 @@ orca_serve <- function(port = 5151, mathjax = FALSE, safe = FALSE, request_limit height = height, scale = scale ) - res <- httr::POST( - paste0("http://127.0.0.1:", port), - body = to_JSON(bod) + res <- httr::RETRY( + verb = "POST", + url = paste0("http://127.0.0.1:", port), + body = to_JSON(bod), + times = 5, + terminate_on = c(400, 401, 403, 404), + terminate_on_success = TRUE ) httr::stop_for_status(res) httr::warn_for_status(res) diff --git a/R/plotly_IMAGE.R b/R/plotly_IMAGE.R index dd3f2b20fb..188c703ce8 100644 --- a/R/plotly_IMAGE.R +++ b/R/plotly_IMAGE.R @@ -9,7 +9,7 @@ #' @param format The desired image format 'png', 'jpeg', 'svg', 'pdf', 'eps', or 'webp' #' @param scale Both png and jpeg formats will be scaled beyond the specified width and height by this number. #' @param out_file A filename for writing the image to a file. -#' @param ... arguments passed onto `httr::POST` +#' @param ... arguments passed onto `httr::RETRY` #' @export #' @examples \dontrun{ #' p <- plot_ly(x = 1:10) @@ -34,9 +34,16 @@ plotly_IMAGE <- function(x, width = 1000, height = 500, format = "png", filename = Sys.time() ) base_url <- file.path(get_domain("api"), "v2", "images") - resp <- httr::POST( - base_url, body = to_JSON(bod), api_headers(), api_auth(), - if (!missing(out_file)) httr::write_disk(out_file, overwrite = TRUE), + resp <- httr::RETRY( + verb = "POST", + url = base_url, + body = to_JSON(bod), + times = 5, + terminate_on = c(400, 401, 403, 404), + terminate_on_success = TRUE, + api_headers(), + api_auth(), + if (!missing(out_file)) httr::write_disk(out_file, overwrite = TRUE), ... ) con <- process(append_class(resp, "api_image")) diff --git a/R/signup.R b/R/signup.R index 1f9472bcdc..cbf2f748d8 100644 --- a/R/signup.R +++ b/R/signup.R @@ -45,7 +45,14 @@ signup <- function(username, email, save = TRUE) { version = as.character(packageVersion("plotly")) ) base_url <- file.path(get_domain(), "apimkacct") - resp <- httr::POST(base_url, body = bod) + resp <- httr::RETRY( + verb = "POST", + base_url, + body = bod, + times = 5, + terminate_on = c(400, 401, 403, 404), + terminate_on_success = TRUE + ) con <- process(append_class(resp, "signup")) if (save) { # store API key as an environment variable in .Rprofile diff --git a/inst/plotlyjs.R b/inst/plotlyjs.R index d29e1496d7..b67833eb07 100644 --- a/inst/plotlyjs.R +++ b/inst/plotlyjs.R @@ -1,7 +1,13 @@ library(httr) # download latest GitHub release # for a particular version: `zip <- "https://github.com/plotly/plotly.js/archive/v1.33.1.zip"` -x <- GET('https://api.github.com/repos/plotly/plotly.js/releases/latest') +x <- httr::RETRY( + verb = "GET", + url = 'https://api.github.com/repos/plotly/plotly.js/releases/latest', + times = 5, + terminate_on = c(400, 401, 403, 404), + terminate_on_success = TRUE +) zip <- content(x)$zipball_url tmp <- tempfile(fileext = ".zip") download.file(zip, tmp) diff --git a/man/api.Rd b/man/api.Rd index ef1ab1ac14..c05689caf2 100644 --- a/man/api.Rd +++ b/man/api.Rd @@ -67,7 +67,7 @@ viewing that page will be able to view the graph. You do not need to be logged in to view this plot.} \item{...}{For \code{api()}, these arguments are passed onto -\code{\link[httr:VERB]{httr::VERB()}}. For \code{api_create()}, these arguments are +\code{\link[httr:RETRY]{httr::RETRY()}}. For \code{api_create()}, these arguments are included in the body of the HTTP request.} \item{id}{a filename id.} @@ -78,9 +78,9 @@ included in the body of the HTTP request.} To see a list of all available endpoints, call \code{api()}. Any relevant query parameters should be included here (see examples).} -\item{verb}{name of the HTTP verb to use (as in, \code{\link[httr:VERB]{httr::VERB()}}).} +\item{verb}{name of the HTTP verb to use (as in, \code{\link[httr:RETRY]{httr::RETRY()}}).} -\item{body}{body of the HTTP request(as in, \code{\link[httr:VERB]{httr::VERB()}}). +\item{body}{body of the HTTP request(as in, \code{\link[httr:RETRY]{httr::RETRY()}}). If this value is not already converted to JSON (via \code{\link[jsonlite:toJSON]{jsonlite::toJSON()}}), it uses the internal \code{to_JSON()} to ensure values are "automatically unboxed" (i.e., vec.}