1
0
Fork 1
mirror of https://example.com synced 2024-11-25 01:06:38 +09:00

feat (backend): permit redirects for AP object lookups

8d7d95fd23

Co-authored-by: naskya <m@naskya.net>
This commit is contained in:
Laura Hausmann 2024-03-27 07:07:19 +09:00 committed by naskya
parent 757f5f0e67
commit 519288317c
Signed by: naskya
GPG key ID: 712D413B3A9FED5C
2 changed files with 32 additions and 4 deletions

View file

@ -2,7 +2,7 @@ import * as http from "node:http";
import * as https from "node:https";
import type { URL } from "node:url";
import CacheableLookup from "cacheable-lookup";
import fetch from "node-fetch";
import fetch, { RequestRedirect } from "node-fetch";
import { HttpProxyAgent, HttpsProxyAgent } from "hpagent";
import config from "@/config/index.js";
import { isValidUrl } from "./is-valid-url.js";
@ -58,6 +58,7 @@ export async function getResponse(args: {
headers: Record<string, string>;
timeout?: number;
size?: number;
redirect?: RequestRedirect;
}) {
if (!isValidUrl(args.url)) {
throw new StatusError("Invalid URL", 400);
@ -78,8 +79,13 @@ export async function getResponse(args: {
size: args.size || 10 * 1024 * 1024,
agent: getAgentByUrl,
signal: controller.signal,
redirect: args.redirect,
});
if (args.redirect === "manual" && [301, 302, 307, 308].includes(res.status)) {
return res;
}
if (!res.ok) {
throw new StatusError(
`${res.status} ${res.statusText}`,

View file

@ -6,6 +6,7 @@ import { createSignedPost, createSignedGet } from "./ap-request.js";
import type { Response } from "node-fetch";
import type { IObject } from "./type.js";
import { isValidUrl } from "@/misc/is-valid-url.js";
import { apLogger } from "@/remote/activitypub/logger.js";
export default async (user: { id: User["id"] }, url: string, object: any) => {
const body = JSON.stringify(object);
@ -34,10 +35,15 @@ export default async (user: { id: User["id"] }, url: string, object: any) => {
/**
* Get ActivityPub object
* @param user http-signature user
* @param url URL to fetch
* @param user http-signature user
* @param redirects whether or not to accept redirects
*/
export async function apGet(url: string, user?: ILocalUser): Promise<IObject> {
export async function apGet(
url: string,
user?: ILocalUser,
redirects: boolean = true
): Promise<IObject> {
if (!isValidUrl(url)) {
throw new StatusError("Invalid URL", 400);
}
@ -61,7 +67,15 @@ export async function apGet(url: string, user?: ILocalUser): Promise<IObject> {
url,
method: req.request.method,
headers: req.request.headers,
redirect: redirects ? "manual" : "error",
});
if (redirects && [301, 302, 307, 308].includes(res.status)) {
const newUrl = res.headers.get("location");
if (newUrl == null) throw new Error("apGet got redirect but no target location");
apLogger.debug(`apGet is redirecting to ${newUrl}`);
return apGet(newUrl, user, false);
}
} else {
res = await getResponse({
url,
@ -71,12 +85,20 @@ export async function apGet(url: string, user?: ILocalUser): Promise<IObject> {
'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
"User-Agent": config.userAgent,
},
redirect: redirects ? "manual" : "error",
});
if (redirects && [301, 302, 307, 308].includes(res.status)) {
const newUrl = res.headers.get("location");
if (newUrl == null) throw new Error("apGet got redirect but no target location");
apLogger.debug(`apGet is redirecting to ${newUrl}`);
return apGet(newUrl, undefined, false);
}
}
const contentType = res.headers.get("content-type");
if (contentType == null || !validateContentType(contentType)) {
throw new Error("Invalid Content Type");
throw new Error(`apGet response had unexpected content-type: ${contentType}`);
}
if (res.body == null) throw new Error("body is null");