Skip to content

Commit

Permalink
Use const instead of let.
Browse files Browse the repository at this point in the history
Update dependencies.
Fix typos in the readme.
Code cleanup to clarify different matcher purposes.
Fixed wildCardRouteMatcher to match whole URL. Was missing the beginning match token.
  • Loading branch information
mattbishop committed Aug 8, 2022
1 parent 00cd5dd commit 99a6195
Show file tree
Hide file tree
Showing 6 changed files with 562 additions and 276 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Fastify Allow Plugin
The HTTP 1.1 specification has an [`Allow` header](https://datatracker.ietf.org/doc/html/rfc7231#section-7.4.1) for resources to include in client responses, indicating all the methods the resource supports. All resource requests return the `Allow` header so client develpers can discover all of the allowable methods on the resource. If a resource does not support a method, for instance, `DELETE`, then the response status will be `405 Not Allowed` along with the `Allow` header.
The HTTP 1.1 specification has an [`Allow` header](https://datatracker.ietf.org/doc/html/rfc7231#section-7.4.1) for resources to include in client responses, indicating all the methods the resource supports. All resource requests return the `Allow` header so client developers can discover all the allowable methods on the resource. If a resource does not support a method, for instance, `DELETE`, then the response status will be `405 Not Allowed` along with the `Allow` header.

This plugin adds an `Allow` header to all responses with routes that have registered handlers, regardless of the method they handle. It returns a `405 Not Allowed` response when a route has no supported method handler. This behaviour is different from Fastify's default behaviour, which is to return a `404 Not Found` for unhandled methodson a route.
This plugin adds an `Allow` header to all responses with routes that have registered handlers, regardless of the method they handle. It returns a `405 Not Allowed` response when a route has no supported method handler. This behaviour is different from Fastify's default behaviour, which is to return a `404 Not Found` for unhandled methods on a route.

If a route has no registered method handlers, fastify-allow will send the usual `404 Not Found` response.

Expand Down Expand Up @@ -57,9 +57,9 @@ fastify.options('*', optionsHandler)

if `send405` is set to false, then `send405ForWildcard` is ignored.

| Option | Description | Default value |
| -------------------- | ------------------------------------------------------------ | ------------- |
| `send405` | Controls whether or not to send `405 Not Allowed` status codes for request that have handlers for other methods, just not the one being sent. | `true` |
| Option | Description | Default value |
|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
| `send405` | Controls whether or not to send `405 Not Allowed` status codes for request that have handlers for other methods, just not the one being sent. | `true` |
| `send405ForWildcard` | Only applies when `send405` is true. Wildcard routes that have no non-wildcard route handlers will still return 405 `Not Allowed` for requests that have a matching wildcard handler. | `false` |


Expand All @@ -78,4 +78,4 @@ const allowOpts: AllowOptions = { send405: false, send405ForWildcard: true }
fastify.register(allowPlugin, allowOpts)

// now register handlers
```
```
41 changes: 23 additions & 18 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,29 +41,28 @@ function comparePaths(p1, p2) {
function captureRouteMethod(caseSensitive, ignoreTrailingSlash, ctx, routeOptions) {
const { send405ForWildcard, routeMethods } = ctx;
const { method, url } = routeOptions;
const wildcardPattern = url.replace(/\*/g, ".+");
const isWildcard = wildcardPattern !== url;
const pattern = wildcardPattern.replace(/\/:[^/]+/g, "/[^/]+");
const flags = caseSensitive ? "" : "i";
const trailingSlash = ignoreTrailingSlash ? "/?" : "";
const urlMatcher = new RegExp(`^${pattern}${trailingSlash}$`, flags);
const urlPattern = url.replace(/\*/g, ".+");
const isWildcard = urlPattern !== url;
const urlMatcher = buildUrlMatcher(urlPattern, caseSensitive, ignoreTrailingSlash);
let urlMethods = routeMethods.get(url) || "";
if (urlMethods) {
urlMethods += ", ";
}
urlMethods += method;
const wildcardMatcher = isWildcard ? new RegExp(wildcardPattern) : false;
for (const [key, value] of routeMethods.entries()) {
// 1. Is this url a wildcard url? Yes, are there urls that this one covers? Add method to their methods
if (wildcardMatcher
&& wildcardMatcher.test(key)
&& !value.includes(method)) {
routeMethods.set(key, `${value}, ${method}`);
const wildcardRouteMatcher = isWildcard
? new RegExp(`^${urlPattern}`)
: false;
for (const [aRoute, aRouteMethods] of routeMethods.entries()) {
// 1. Is this url a wildcard route? Yes, are there urls that this one covers? Add method to their methods
if (wildcardRouteMatcher
&& wildcardRouteMatcher.test(aRoute)
&& !aRouteMethods.includes(method)) {
routeMethods.set(aRoute, `${aRouteMethods}, ${method}`);
}
// 2. Are any existing urls wildcards that cover this url? Add their missing methods to your methods.
if (key.endsWith("*")
&& url.startsWith(key.slice(0, key.length - 1))) {
const otherMethods = value.split(", ");
if (aRoute.endsWith("*")
&& url.startsWith(aRoute.slice(0, aRoute.length - 1))) {
const otherMethods = aRouteMethods.split(", ");
urlMethods = otherMethods.reduce((acc, m) => {
if (!acc.includes(m)) {
acc = `${acc}, ${m}`;
Expand All @@ -77,9 +76,15 @@ function captureRouteMethod(caseSensitive, ignoreTrailingSlash, ctx, routeOption
addSortedMatcher(ctx, urlMatcher, url);
}
}
function buildUrlMatcher(wildcardPattern, caseSensitive, ignoreTrailingSlash) {
const pattern = wildcardPattern.replace(/\/:[^/]+/g, "/[^/]+");
const flags = caseSensitive ? "" : "i";
const trailingSlash = ignoreTrailingSlash ? "/?" : "";
return new RegExp(`^${pattern}${trailingSlash}$`, flags);
}
function handleRequest(ctx, request, reply, done) {
const { routeMethods, matcherRoutes, send405 } = ctx;
let { url, method, routerPath = findUrlRoute(matcherRoutes, url) || "" } = request;
const { url, method, routerPath = findUrlRoute(matcherRoutes, url) || "" } = request;
const methods = routeMethods.get(routerPath);
if (methods) {
reply.header("allow", methods);
Expand Down Expand Up @@ -116,7 +121,7 @@ function plugin(fastify, opts, done) {
fastify.addHook("onRequest", (q, p, d) => handleRequest(ctx, q, p, d));
done();
}
exports.default = fastify_plugin_1.default(plugin, {
exports.default = (0, fastify_plugin_1.default)(plugin, {
name: "fastify-allow",
fastify: ">=3.x"
});
Loading

0 comments on commit 99a6195

Please # to comment.