thomasp85 / fiery

A flexible and lightweight web server
https://fiery.data-imaginist.com
Other
240 stars 12 forks source link

how to return an error response from inside a plugin ? #27

Open aneuraz opened 6 years ago

aneuraz commented 6 years ago

Trying to write an authentication plugin to use with fiery and and routr but I cannot figure how to block the process if a condition is not met: e.g. return an error directly from the plugin.

A very minimal example would be something like this:

Basic <- R6Class('Basic', 
                 public = list(
                   initialize = function() {

                   },
                   name = 'Basic',
                   on_attach = function(server, ...) {
                     server$on('before-request', function(request,...) {

                       if (request$headers$Auth == 'foo') {
                          # continue
                         }
                       else {
                         # return error response
                         }
                     })
                   }
                 ))

basic = Basic$new()
route <- Route$new()

route$add_handler('post', '/', function(request, response, keys, ...) {
  response$status <- 200L
  response$body <- 'request handled'
  TRUE
})

router <- RouteStack$new()
router$add_route(route, 'app_router')

app <- Fire$new()

app$attach(basic)
app$attach(router)
app$ignite(block = TRUE)

How should I do that ?

thomasp85 commented 6 years ago

Great to hear that you are working on a plugin!

The most basic answer to your question is: Add the event handler to the header event. This event will be triggered once all headers have been recieved but before the body gets loaded, so it's a natural place to put this sort of behaviour. The semantics is that if the last handler returns FALSE the response is send back without being processed in the request loop. Take a look at the size_limit route in routr for a possible approach to cutting of processing (this time based on content-length instead of authentication).

Implementation-wise, I would urge you to build this on top of routr ()if you plan to release it) as it makes it easier for others to add other pieces of functionality. The way I would do it is to check the server for existance of a header_routr plugin during attach_to. If it exists simply add the handler to the router, if not, create a new header routr with your handler and attach that to the server...

aneuraz commented 6 years ago

Thanks for your answer. After looking at sizelimit_route, I wonder if it would not be easier to write a simple function like this instead of a plugin ?

thomasp85 commented 6 years ago

Indeed, that is also possible... the upside of making it a plugin rather than a route is that the attached plugins can be queried from the app and other plugins can depend on your plugin - I agree that in this case it might be an edge case... may I ask what authentication you’re implementing?

aneuraz commented 6 years ago

it is for a JWT authentication

aneuraz commented 6 years ago

I commited a first draft of a JWT plugin here: https://github.com/aneuraz/JWTroutr what do you think ?

thomasp85 commented 6 years ago

Great - I’ll have a look through tomorrow or next week

thomasp85 commented 6 years ago

Looks exactly like I would have imagined :-) I've made a little PR with some proposed changed but these are minor...

Are you planning on adding functionality for creating new tokens?

aneuraz commented 6 years ago

Thank you for the PR. Regarding the creation of new tokens: yes! it is the next step