Skip to content

Commit

Permalink
refactor: consolidate HTTP-related utilities (denoland#452)
Browse files Browse the repository at this point in the history
  • Loading branch information
iuioiua authored Aug 30, 2023
1 parent 4ec7d5e commit 3e357fc
Show file tree
Hide file tree
Showing 13 changed files with 79 additions and 85 deletions.
2 changes: 1 addition & 1 deletion routes/_middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import { MiddlewareHandlerContext } from "$fresh/server.ts";
import { getSessionId } from "kv_oauth";
import { redirect } from "@/utils/redirect.ts";
import { redirect } from "@/utils/http.ts";
import { Status } from "std/http/http_status.ts";
import { getUserBySession, ifUserHasNotifications } from "@/utils/db.ts";
import { incrVisitsCountByDay } from "@/utils/db.ts";
Expand Down
2 changes: 1 addition & 1 deletion routes/account/manage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import type { RouteContext } from "$fresh/server.ts";
import { stripe } from "@/utils/payments.ts";
import type { SignedInState } from "@/utils/middleware.ts";
import { redirect } from "@/utils/redirect.ts";
import { redirect } from "@/utils/http.ts";

export default async function AccountManagePage(
_req: Request,
Expand Down
2 changes: 1 addition & 1 deletion routes/account/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import type { RouteContext } from "$fresh/server.ts";
import { stripe } from "@/utils/payments.ts";
import type { SignedInState } from "@/utils/middleware.ts";
import { redirect } from "@/utils/redirect.ts";
import { redirect } from "@/utils/http.ts";

const STRIPE_PREMIUM_PLAN_PRICE_ID = Deno.env.get(
"STRIPE_PREMIUM_PLAN_PRICE_ID",
Expand Down
2 changes: 1 addition & 1 deletion routes/dashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import { redirect } from "@/utils/redirect.ts";
import { redirect } from "@/utils/http.ts";

// deno-lint-ignore require-await
export default async function DashboardPage() {
Expand Down
2 changes: 1 addition & 1 deletion routes/items/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
newNotificationProps,
Notification,
} from "@/utils/db.ts";
import { redirect } from "@/utils/redirect.ts";
import { redirect } from "@/utils/http.ts";
import Head from "@/components/Head.tsx";
import { SignedInState } from "@/utils/middleware.ts";
import CommentsList from "@/islands/CommentsList.tsx";
Expand Down
2 changes: 1 addition & 1 deletion routes/notifications/[id].ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import type { RouteContext } from "$fresh/server.ts";
import { deleteNotification, getNotification } from "@/utils/db.ts";
import { redirect } from "@/utils/redirect.ts";
import { redirect } from "@/utils/http.ts";
import { SignedInState } from "@/utils/middleware.ts";

export default async function NotificationsNotificationPage(
Expand Down
3 changes: 1 addition & 2 deletions routes/submit/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ import {
listItemsByUser,
newItemProps,
} from "@/utils/db.ts";
import { redirect } from "@/utils/redirect.ts";
import { isPublicUrl, isValidUrl, redirect } from "@/utils/http.ts";
import Head from "@/components/Head.tsx";
import IconCheckCircle from "tabler_icons_tsx/circle-check.tsx";
import IconCircleX from "tabler_icons_tsx/circle-x.tsx";
import { SignedInState } from "@/utils/middleware.ts";
import { isPublicUrl, isValidUrl } from "@/utils/url_validation.ts";

export const handler: Handlers<SignedInState, SignedInState> = {
async POST(req, ctx) {
Expand Down
47 changes: 47 additions & 0 deletions utils/http.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,55 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import type { RedirectStatus, Status } from "std/http/http_status.ts";

export async function fetchValues<T>(endpoint: string, cursor: string) {
let url = endpoint;
if (cursor !== "") url += "?cursor=" + cursor;
const resp = await fetch(url);
if (!resp.ok) throw new Error(`Request failed: GET ${url}`);
return await resp.json() as { values: T[]; cursor: string };
}

/**
* @param location A relative (to the request URL) or absolute URL.
* @param status HTTP status
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location}
*/
export function redirect(
location: string,
status: Status.Created | RedirectStatus = 303,
) {
return new Response(null, {
headers: {
location,
},
status,
});
}

export function isValidUrl(string: string): boolean {
try {
const { protocol } = new URL(string);
return protocol.startsWith("http");
} catch {
return false;
}
}

export function isPublicUrl(string: string): boolean {
try {
const { hostname } = new URL(string);
const ranges = [
/^localhost$/,
/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
/^::1$/,
/^0:0:0:0:0:0:0:1$/,
/^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
/^172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.\d{1,3}$/,
/^192\.168\.\d{1,3}\.\d{1,3}$/,
];

return !ranges.some((range) => range.test(hostname));
} catch (_) {
return false;
}
}
28 changes: 24 additions & 4 deletions utils/url_validation_test.ts → utils/http_test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import { isPublicUrl, isValidUrl, redirect } from "./http.ts";
import { assert, assertEquals } from "std/testing/asserts.ts";

import { assertEquals } from "std/testing/asserts.ts";
import { isPublicUrl, isValidUrl } from "./url_validation.ts";
Deno.test("[http] redirect() defaults", () => {
const location = "/hello-there";

Deno.test("[url_validation] isValidUrl()", () => {
const resp = redirect(location);
assert(!resp.ok);
assertEquals(resp.body, null);
assertEquals(resp.headers.get("location"), location);
assertEquals(resp.status, 303);
});

Deno.test("[http] redirect()", () => {
const location = "/hello-there";
const status = 302;

const resp = redirect(location, status);
assert(!resp.ok);
assertEquals(resp.body, null);
assertEquals(resp.headers.get("location"), location);
assertEquals(resp.status, status);
});

Deno.test("[http] isValidUrl()", () => {
assertEquals(isValidUrl("https://hunt.deno.land/"), true);
assertEquals(isValidUrl("http://hunt.deno.land/"), true);
assertEquals(isValidUrl("ws://hunt.deno.land/"), false);
assertEquals(isValidUrl("wss://hunt.deno.land/"), false);
assertEquals(isValidUrl("invalidurl"), false);
});

Deno.test("[url_validation] isPublicUrl()", () => {
Deno.test("[http] isPublicUrl()", () => {
assertEquals(isPublicUrl("https://hunt.deno.land/"), true);
assertEquals(isPublicUrl("http://hunt.deno.land/"), true);
assertEquals(isPublicUrl("ws://hunt.deno.land/"), true);
Expand Down
2 changes: 1 addition & 1 deletion utils/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import type { State } from "@/routes/_middleware.ts";
import type { MiddlewareHandlerContext } from "$fresh/server.ts";
import { getUserBySession, User } from "@/utils/db.ts";
import { redirect } from "@/utils/redirect.ts";
import { redirect } from "@/utils/http.ts";

export interface SignedInState extends State {
sessionId: string;
Expand Down
19 changes: 0 additions & 19 deletions utils/redirect.ts

This file was deleted.

24 changes: 0 additions & 24 deletions utils/redirect_test.ts

This file was deleted.

29 changes: 0 additions & 29 deletions utils/url_validation.ts

This file was deleted.

0 comments on commit 3e357fc

Please sign in to comment.