Skip to content

Commit

Permalink
Merge pull request #128 from ty-ras/issue/127-fix-options-behaviour
Browse files Browse the repository at this point in the history
Issue/127 fix options behaviour
  • Loading branch information
stazz authored Feb 9, 2024
2 parents cdc66f3 + 37229de commit 217e576
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 10 deletions.
2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ty-ras/server",
"version": "2.2.0",
"version": "2.2.1",
"author": {
"name": "Stanislav Muhametsin",
"email": "[email protected]",
Expand Down
110 changes: 110 additions & 0 deletions server/src/__test__/flow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,116 @@ test("Validate typicalServerFlow works when sendBody throws inside error handler
]);
});

test("Validate that handling OPTIONS works as intended by typicalServeFlow when invalid method returned", async (c) => {
c.plan(1);
const { seenCallbacks, callbacks } = flowUtil.customizeTrackingCallback({
getMethod: () => "OPTIONS",
});
const allowedMethods = ["GET", "POST"] as const;
await flowUtil.createTypicalServerFlow(
{
url: /(?<group>\/path)/,
handler: () => ({
found: "invalid-method",
allowedMethods: allowedMethods.map((method) => ({
method,
stateInformation: flowUtil.createStateValidator(),
})),
}),
},
callbacks,
undefined,
)(flowUtil.inputContext);
c.deepEqual(seenCallbacks, [
{
args: [flowUtil.seenContext],
callbackName: "getURL",
returnValue: "/path",
},
{
args: [flowUtil.seenContext],
callbackName: "getMethod",
returnValue: "OPTIONS",
},
{
args: [flowUtil.seenContext, "Allow", allowedMethods.join(",")],
callbackName: "setHeader",
returnValue: undefined,
},
{
args: [flowUtil.seenContext, 200, false],
callbackName: "setStatusCode",
returnValue: undefined,
},
]);
});

test("Validate that handling OPTIONS works as intended by typicalServeFlow when no allowed methods returned", async (c) => {
c.plan(1);
const { seenCallbacks, callbacks } = flowUtil.customizeTrackingCallback({
getMethod: () => "OPTIONS",
});
await flowUtil.createTypicalServerFlow(
{
url: /(?<group>\/path)/,
handler: () => ({
found: "invalid-method",
allowedMethods: [],
}),
},
callbacks,
undefined,
)(flowUtil.inputContext);
c.deepEqual(seenCallbacks, [
{
args: [flowUtil.seenContext],
callbackName: "getURL",
returnValue: "/path",
},
{
args: [flowUtil.seenContext],
callbackName: "getMethod",
returnValue: "OPTIONS",
},
{
args: [flowUtil.seenContext, 404, false],
callbackName: "setStatusCode",
returnValue: undefined,
},
]);
});

test("Validate that handling OPTIONS works as intended by typicalServeFlow when no handlers returned", async (c) => {
c.plan(1);
const { seenCallbacks, callbacks } = flowUtil.customizeTrackingCallback({
getURL: () => "/will-not-match",
getMethod: () => "OPTIONS",
});
await flowUtil.createTypicalServerFlow(
{
url: /(?<group>\/path)/,
handler: () => ({
found: "invalid-method",
allowedMethods: [],
}),
},
callbacks,
undefined,
)(flowUtil.inputContext);
c.deepEqual(seenCallbacks, [
{
args: [flowUtil.seenContext],
callbackName: "getURL",
returnValue: "/will-not-match",
},
{
args: [flowUtil.seenContext, 404, false],
callbackName: "setStatusCode",
returnValue: undefined,
},
]);
});

const getHumanReadableMessage = () => "";

const errorMessage = "This should never be called";
Expand Down
22 changes: 14 additions & 8 deletions server/src/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import type * as ep from "@ty-ras/endpoint";
import type * as protocol from "@ty-ras/protocol";
import * as protocol from "@ty-ras/protocol";
import * as data from "@ty-ras/data";
import * as dataBE from "@ty-ras/data-backend";
import * as server from "./utils";
Expand All @@ -17,7 +17,7 @@ import * as stream from "node:stream";
* Creates a callback which will asynchronously process each incoming HTTP request, to extract the endpoint based on URL path and method, validate all necessary inputs, invoke the endpoint, validate all necesary outputs, and write the result to HTTP response.
* This function is used by other TyRAS plugins and usually not directly by client code.
* @param endpoints All the {@link ep.AppEndpoint}s to use in returned callback.
* @param callbacks The {@see ServerFlowCallbacks} necessary to actually implement the returned callback.
* @param callbacks The {@link ServerFlowCallbacks} necessary to actually implement the returned callback.
* @param events The {@link evt.ServerEventHandler} to invoke on events.
* @returns The callback which can be used to asynchronously process incoming HTTP request, and write to outgoing HTTP response.
*/
Expand Down Expand Up @@ -70,13 +70,15 @@ export const createTypicalServerFlow = <
// We have a match -> get the handler that will handle our match
const method = cb.getMethod(ctx) as protocol.HttpMethod;
let foundHandler = handler(method, maybeEventArgs.groups);
const sendBody = method !== "HEAD";
const sendBody = method !== protocol.METHOD_HEAD;
if (
foundHandler.found !== "handler" &&
!sendBody &&
foundHandler.allowedMethods.some(({ method }) => method === "GET")
foundHandler.allowedMethods.some(
({ method }) => method === protocol.METHOD_GET,
)
) {
foundHandler = handler("GET", maybeEventArgs.groups);
foundHandler = handler(protocol.METHOD_GET, maybeEventArgs.groups);
}

if (foundHandler.found === "handler") {
Expand Down Expand Up @@ -244,7 +246,7 @@ export const createTypicalServerFlow = <
maybeEventArgs,
events,
foundHandler.allowedMethods,
method === "OPTIONS"
method === protocol.METHOD_OPTIONS
? undefined
: async (stateValidator) =>
stateValidator.validator(
Expand All @@ -254,8 +256,12 @@ export const createTypicalServerFlow = <

if (!ctx.skipSettingStatusCode) {
const statusCode =
allowedMethodsSentToClient.length > 0 ? 405 : 404;
if (statusCode === 405) {
allowedMethodsSentToClient.length > 0
? method === protocol.METHOD_OPTIONS
? 200
: 405
: 404;
if (statusCode !== 404) {
cb.setHeader(ctx, "Allow", allowedMethodsSentToClient.join(","));
}
cb.setStatusCode(ctx, statusCode, false);
Expand Down
1 change: 0 additions & 1 deletion server/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export const checkURLPathNameForHandler = <TContext>(
): evt.EventArgumentsWithoutState<TContext> | undefined => {
const pathName = (url instanceof u.URL ? url : new u.URL(url)).pathname;
const groups = regExp.exec(pathName)?.groups;
// console.log("LEL", regExp, pathName, groups);
if (!groups) {
events?.("onInvalidUrl", {
ctx,
Expand Down

0 comments on commit 217e576

Please sign in to comment.