Skip to content

Add 405 http code handling #554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Jul 3, 2020
Merged

Add 405 http code handling #554

merged 19 commits into from
Jul 3, 2020

Conversation

meztez
Copy link
Collaborator

@meztez meztez commented Jun 19, 2020

Fixes #493
Fixes #310

PR task list:

  • Update NEWS
  • Add tests
  • Update documentation with devtools::document()

@schloerke schloerke added this to the v0.5.0 - Next CRAN release milestone Jun 22, 2020
Copy link
Collaborator

@schloerke schloerke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(See comments)

Bruno Tremblay added 3 commits June 22, 2020 15:13
Merge remote-tracking branch 'upstream/master' into http_405

# Conflicts:
#	NEWS.md
@schloerke
Copy link
Collaborator

This is much better. Thank you.

It still feels weird overloading the $canServe() method by adding to the req object. Don't know if we need to just recursively reinspect at default404Handler() time. Will talk with @cpsievert later.

meztez and others added 4 commits June 23, 2020 14:10
Co-authored-by: Barret Schloerke <barret@rstudio.com>
Co-authored-by: Barret Schloerke <barret@rstudio.com>
Merge remote-tracking branch 'upstream/master' into http_405

# Conflicts:
#	NEWS.md
Merge remote-tracking branch 'upstream/master' into http_405

# Conflicts:
#	NEWS.md
@schloerke
Copy link
Collaborator

schloerke commented Jun 25, 2020

Ok. I believe I have a solution that we can use inside default404handler(). It just needs access to the pr object which we can get from req$pr.


# this function works under the assumption that the regular route was not found.  
# Here we are only trying to find if there are other routes that can be 
allowed_verbs <- function(pr, path_to_find) {

  verbs_allowed <- c()

  # look at all possible endpoints
  for (endpoint_group in pr$endpoints) {
    for (endpoint in endpoint_group) {
      if (endpoint$matchesPath(path_to_find)) {
        verbs_allowed <- c(verbs_allowed, endpoint$verbs)
      }
    }
  }

  # look at all possible mounts
  for (i in seq_along(pr$mounts)) {
    mount <- pr$mounts[[i]]
    mount_path <- sub("/$", "", names(pr$mounts)[i]) # trim trailing `/`

    # if the front of the urls don't match, move on to next mount
    if (!identical(
      substr(path_to_find, 0, nchar(mount_path)),
      mount_path
    )) {
      next
    }

    # remove path and recurse
    mount_path_to_find <- substr(path_to_find, nchar(mount_path) + 1, nchar(path_to_find))
    m_verbs <- allowed_verbs(mount, mount_path_to_find)

    # add any verbs found
    if (length(m_verbs) > 0) {
      verbs_allowed <- c(verbs_allowed, m_verbs)
    }
  }

  # return verbs
  return(verbs_allowed)
}

pr <- plumber$new()
sub <- plumber$new()
sub$handle("GET", "/test", force)
pr$mount("/barret", sub)


allowed_verbs(pr, path_to_find = "/barret/test")        # "GET"
allowed_verbs(pr, path_to_find = "/subroute/not_found") # NULL
allowed_verbs(pr, path_to_find = "/barret/")            # NULL
allowed_verbs(pr, path_to_find = "/barret/wrong")       # NULL


####################


is_405 <- function(pr, path_to_find, verb_to_find) {
  verbs_allowed <- allowed_verbs(pr, path_to_find)
  
  # nothing found, not 405
  if (length(verbs_allowed) == 0) return(FALSE)

  !(verb_to_find %in% verbs_allowed)
}

is_405(pr, path_to_find = "/barret/test", "GET")  # FALSE
is_405(pr, path_to_find = "/barret/test", "POST") # TRUE
is_405(pr, path_to_find = "/subroute/not_found")  # FALSE

Will need a new method (as you said) to match the path.

# plumber-step.R
# PlumberEndpoint public defs
    #' @description ability to serve request
    #' @param req a request object
    #' @return a logical. `TRUE` when endpoint can serve request.
    canServe = function(req){
      req$REQUEST_METHOD %in% self$verbs && self$matchesRoute(req$PATH_INFO)
    },
    #' @description determines if route matches requested path
    #' @param path a url path
    #' @return a logical. `TRUE` when endpoint matches the requested path.
    matchesPath = function(path) {
      !is.na(stri_match_first_regex(path, private$regex$regex)[1,1])
    },

Untested 404 handler update...

  if (is_405(req$pr, req$PATH_INFO, req$REQUEST_METHOD)) {
    res$status <- 405L
    # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Allow
    res$setHeader("Allow", paste(req$verbsAllowed, collapse = ", "))
    return(list(error = "405 - Method Not Allowed"))
  }

...woot

@schloerke
Copy link
Collaborator

(done with updates in the above comment)

@meztez
Copy link
Collaborator Author

meztez commented Jun 25, 2020

pr is not a value of req, did you expect it to be or should I add it?

@schloerke
Copy link
Collaborator

schloerke commented Jun 25, 2020

Sorry. It is currently not implemented.

See #310

Want to add it to this PR? Or can make separate PR

@schloerke
Copy link
Collaborator

The pr$req could prolly be added here:

@meztez
Copy link
Collaborator Author

meztez commented Jun 25, 2020

I added it here :

req$.internal <- new.env()
, above route

@meztez
Copy link
Collaborator Author

meztez commented Jun 25, 2020

Im finishing up, just need to fix test because we use a mock req with make_req that does not have a router

@schloerke schloerke self-requested a review June 26, 2020 21:08
@schloerke schloerke requested a review from cpsievert June 26, 2020 21:08
Co-authored-by: Barret Schloerke <barret@rstudio.com>
@schloerke
Copy link
Collaborator

@meztez Is this PR ready for review / merging?

@schloerke
Copy link
Collaborator

@meztez Is this PR ready for review / merging?
(resending again as it was close to the other PR. Sorry for the spam).

@meztez
Copy link
Collaborator Author

meztez commented Jul 2, 2020

@schloerke yes

Co-authored-by: Carson Sievert <cpsievert1@gmail.com>
Co-authored-by: Carson Sievert <cpsievert1@gmail.com>
Co-authored-by: Carson Sievert <cpsievert1@gmail.com>
Co-authored-by: Carson Sievert <cpsievert1@gmail.com>
@cpsievert cpsievert self-requested a review July 2, 2020 20:13
Copy link
Contributor

@cpsievert cpsievert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM pending test

@schloerke schloerke changed the title add 405 http code handling Add 405 http code handling Jul 3, 2020
@schloerke schloerke merged commit 36e8760 into rstudio:master Jul 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Requests to valid endpoints with an incorrect request method should return HTTP 405 instead of HTTP 404 Expose pr object to filters
3 participants