Skip to content

Commit 217e576

Browse files
authored
Merge pull request #128 from ty-ras/issue/127-fix-options-behaviour
Issue/127 fix options behaviour
2 parents cdc66f3 + 37229de commit 217e576

File tree

4 files changed

+125
-10
lines changed

4 files changed

+125
-10
lines changed

server/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ty-ras/server",
3-
"version": "2.2.0",
3+
"version": "2.2.1",
44
"author": {
55
"name": "Stanislav Muhametsin",
66
"email": "[email protected]",

server/src/__test__/flow.spec.ts

+110
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,116 @@ test("Validate typicalServerFlow works when sendBody throws inside error handler
13461346
]);
13471347
});
13481348

1349+
test("Validate that handling OPTIONS works as intended by typicalServeFlow when invalid method returned", async (c) => {
1350+
c.plan(1);
1351+
const { seenCallbacks, callbacks } = flowUtil.customizeTrackingCallback({
1352+
getMethod: () => "OPTIONS",
1353+
});
1354+
const allowedMethods = ["GET", "POST"] as const;
1355+
await flowUtil.createTypicalServerFlow(
1356+
{
1357+
url: /(?<group>\/path)/,
1358+
handler: () => ({
1359+
found: "invalid-method",
1360+
allowedMethods: allowedMethods.map((method) => ({
1361+
method,
1362+
stateInformation: flowUtil.createStateValidator(),
1363+
})),
1364+
}),
1365+
},
1366+
callbacks,
1367+
undefined,
1368+
)(flowUtil.inputContext);
1369+
c.deepEqual(seenCallbacks, [
1370+
{
1371+
args: [flowUtil.seenContext],
1372+
callbackName: "getURL",
1373+
returnValue: "/path",
1374+
},
1375+
{
1376+
args: [flowUtil.seenContext],
1377+
callbackName: "getMethod",
1378+
returnValue: "OPTIONS",
1379+
},
1380+
{
1381+
args: [flowUtil.seenContext, "Allow", allowedMethods.join(",")],
1382+
callbackName: "setHeader",
1383+
returnValue: undefined,
1384+
},
1385+
{
1386+
args: [flowUtil.seenContext, 200, false],
1387+
callbackName: "setStatusCode",
1388+
returnValue: undefined,
1389+
},
1390+
]);
1391+
});
1392+
1393+
test("Validate that handling OPTIONS works as intended by typicalServeFlow when no allowed methods returned", async (c) => {
1394+
c.plan(1);
1395+
const { seenCallbacks, callbacks } = flowUtil.customizeTrackingCallback({
1396+
getMethod: () => "OPTIONS",
1397+
});
1398+
await flowUtil.createTypicalServerFlow(
1399+
{
1400+
url: /(?<group>\/path)/,
1401+
handler: () => ({
1402+
found: "invalid-method",
1403+
allowedMethods: [],
1404+
}),
1405+
},
1406+
callbacks,
1407+
undefined,
1408+
)(flowUtil.inputContext);
1409+
c.deepEqual(seenCallbacks, [
1410+
{
1411+
args: [flowUtil.seenContext],
1412+
callbackName: "getURL",
1413+
returnValue: "/path",
1414+
},
1415+
{
1416+
args: [flowUtil.seenContext],
1417+
callbackName: "getMethod",
1418+
returnValue: "OPTIONS",
1419+
},
1420+
{
1421+
args: [flowUtil.seenContext, 404, false],
1422+
callbackName: "setStatusCode",
1423+
returnValue: undefined,
1424+
},
1425+
]);
1426+
});
1427+
1428+
test("Validate that handling OPTIONS works as intended by typicalServeFlow when no handlers returned", async (c) => {
1429+
c.plan(1);
1430+
const { seenCallbacks, callbacks } = flowUtil.customizeTrackingCallback({
1431+
getURL: () => "/will-not-match",
1432+
getMethod: () => "OPTIONS",
1433+
});
1434+
await flowUtil.createTypicalServerFlow(
1435+
{
1436+
url: /(?<group>\/path)/,
1437+
handler: () => ({
1438+
found: "invalid-method",
1439+
allowedMethods: [],
1440+
}),
1441+
},
1442+
callbacks,
1443+
undefined,
1444+
)(flowUtil.inputContext);
1445+
c.deepEqual(seenCallbacks, [
1446+
{
1447+
args: [flowUtil.seenContext],
1448+
callbackName: "getURL",
1449+
returnValue: "/will-not-match",
1450+
},
1451+
{
1452+
args: [flowUtil.seenContext, 404, false],
1453+
callbackName: "setStatusCode",
1454+
returnValue: undefined,
1455+
},
1456+
]);
1457+
});
1458+
13491459
const getHumanReadableMessage = () => "";
13501460

13511461
const errorMessage = "This should never be called";

server/src/flow.ts

+14-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
import type * as ep from "@ty-ras/endpoint";
6-
import type * as protocol from "@ty-ras/protocol";
6+
import * as protocol from "@ty-ras/protocol";
77
import * as data from "@ty-ras/data";
88
import * as dataBE from "@ty-ras/data-backend";
99
import * as server from "./utils";
@@ -17,7 +17,7 @@ import * as stream from "node:stream";
1717
* 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.
1818
* This function is used by other TyRAS plugins and usually not directly by client code.
1919
* @param endpoints All the {@link ep.AppEndpoint}s to use in returned callback.
20-
* @param callbacks The {@see ServerFlowCallbacks} necessary to actually implement the returned callback.
20+
* @param callbacks The {@link ServerFlowCallbacks} necessary to actually implement the returned callback.
2121
* @param events The {@link evt.ServerEventHandler} to invoke on events.
2222
* @returns The callback which can be used to asynchronously process incoming HTTP request, and write to outgoing HTTP response.
2323
*/
@@ -70,13 +70,15 @@ export const createTypicalServerFlow = <
7070
// We have a match -> get the handler that will handle our match
7171
const method = cb.getMethod(ctx) as protocol.HttpMethod;
7272
let foundHandler = handler(method, maybeEventArgs.groups);
73-
const sendBody = method !== "HEAD";
73+
const sendBody = method !== protocol.METHOD_HEAD;
7474
if (
7575
foundHandler.found !== "handler" &&
7676
!sendBody &&
77-
foundHandler.allowedMethods.some(({ method }) => method === "GET")
77+
foundHandler.allowedMethods.some(
78+
({ method }) => method === protocol.METHOD_GET,
79+
)
7880
) {
79-
foundHandler = handler("GET", maybeEventArgs.groups);
81+
foundHandler = handler(protocol.METHOD_GET, maybeEventArgs.groups);
8082
}
8183

8284
if (foundHandler.found === "handler") {
@@ -244,7 +246,7 @@ export const createTypicalServerFlow = <
244246
maybeEventArgs,
245247
events,
246248
foundHandler.allowedMethods,
247-
method === "OPTIONS"
249+
method === protocol.METHOD_OPTIONS
248250
? undefined
249251
: async (stateValidator) =>
250252
stateValidator.validator(
@@ -254,8 +256,12 @@ export const createTypicalServerFlow = <
254256

255257
if (!ctx.skipSettingStatusCode) {
256258
const statusCode =
257-
allowedMethodsSentToClient.length > 0 ? 405 : 404;
258-
if (statusCode === 405) {
259+
allowedMethodsSentToClient.length > 0
260+
? method === protocol.METHOD_OPTIONS
261+
? 200
262+
: 405
263+
: 404;
264+
if (statusCode !== 404) {
259265
cb.setHeader(ctx, "Allow", allowedMethodsSentToClient.join(","));
260266
}
261267
cb.setStatusCode(ctx, statusCode, false);

server/src/utils.ts

-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ export const checkURLPathNameForHandler = <TContext>(
2929
): evt.EventArgumentsWithoutState<TContext> | undefined => {
3030
const pathName = (url instanceof u.URL ? url : new u.URL(url)).pathname;
3131
const groups = regExp.exec(pathName)?.groups;
32-
// console.log("LEL", regExp, pathName, groups);
3332
if (!groups) {
3433
events?.("onInvalidUrl", {
3534
ctx,

0 commit comments

Comments
 (0)