snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
205
apps/meteor/server/lib/http/call.ts
Normal file
205
apps/meteor/server/lib/http/call.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
import { serverFetch as fetch } from '@rocket.chat/server-fetch';
|
||||
import { HTTP } from 'meteor/http';
|
||||
import { URL } from 'meteor/url';
|
||||
|
||||
import { truncate } from '../../../lib/utils/stringUtils';
|
||||
|
||||
// Code extracted from https://github.com/meteor/meteor/blob/master/packages/deprecated/http
|
||||
// Modified to:
|
||||
// - Respect proxy envvars such as HTTP_PROXY and NO_PROXY
|
||||
// - Respect HTTP_DEFAULT_TIMEOUT envvar or use 20s when it is not set
|
||||
|
||||
const envTimeout = parseInt(process.env.HTTP_DEFAULT_TIMEOUT || '', 10);
|
||||
const defaultTimeout = !isNaN(envTimeout) ? envTimeout : 20000;
|
||||
|
||||
type HttpCallOptions = {
|
||||
content?: string | URLSearchParams;
|
||||
data?: Record<string, any>;
|
||||
query?: string;
|
||||
params?: Record<string, string>;
|
||||
auth?: string;
|
||||
headers?: Record<string, string>;
|
||||
timeout?: number;
|
||||
followRedirects?: boolean;
|
||||
referrer?: string;
|
||||
integrity?: string;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
interface HTTPResponse {
|
||||
statusCode?: number;
|
||||
headers?: { [id: string]: string };
|
||||
content?: string;
|
||||
data?: any;
|
||||
ok?: boolean;
|
||||
redirected?: boolean;
|
||||
}
|
||||
|
||||
type callbackFn = (error: Error | undefined, result?: HTTPResponse) => void;
|
||||
|
||||
// Fill in `response.data` if the content-type is JSON.
|
||||
function populateData(response: Record<string, any>): void {
|
||||
// Read Content-Type header, up to a ';' if there is one.
|
||||
// A typical header might be "application/json; charset=utf-8"
|
||||
// or just "application/json".
|
||||
const contentType = (response.headers['content-type'] || ';').split(';')[0];
|
||||
|
||||
// Only try to parse data as JSON if server sets correct content type.
|
||||
if (['application/json', 'text/javascript', 'application/javascript', 'application/x-javascript'].indexOf(contentType) >= 0) {
|
||||
try {
|
||||
response.data = JSON.parse(response.content);
|
||||
} catch (err) {
|
||||
response.data = null;
|
||||
}
|
||||
} else {
|
||||
response.data = null;
|
||||
}
|
||||
}
|
||||
|
||||
function makeErrorByStatus(statusCode: number, content: string): Error {
|
||||
let message = `failed [${statusCode}]`;
|
||||
|
||||
if (content) {
|
||||
message += `${truncate(content.replace(/\n/g, ' '), 500)}`;
|
||||
}
|
||||
|
||||
return new Error(message);
|
||||
}
|
||||
|
||||
function _call(httpMethod: string, url: string, options: HttpCallOptions, callback: callbackFn): void {
|
||||
const method = (httpMethod || '').toUpperCase();
|
||||
|
||||
if (!/^https?:\/\//.test(url)) {
|
||||
throw new Error('url must be absolute and start with http:// or https://');
|
||||
}
|
||||
|
||||
const headers: Record<string, string> = {};
|
||||
let { content } = options;
|
||||
|
||||
if (!('timeout' in options)) {
|
||||
options.timeout = defaultTimeout;
|
||||
}
|
||||
|
||||
if (options.data) {
|
||||
content = JSON.stringify(options.data);
|
||||
headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
let paramsForUrl;
|
||||
let paramsForBody;
|
||||
|
||||
if (content || method === 'GET' || method === 'HEAD') {
|
||||
paramsForUrl = options.params;
|
||||
} else {
|
||||
paramsForBody = options.params;
|
||||
}
|
||||
|
||||
const newUrl = URL._constructUrl(url, options.query, paramsForUrl);
|
||||
|
||||
if (options.auth) {
|
||||
if (options.auth.indexOf(':') < 0) {
|
||||
throw new Error('auth option should be of the form "username:password"');
|
||||
}
|
||||
|
||||
const base64 = Buffer.from(options.auth, 'ascii').toString('base64');
|
||||
headers.Authorization = `Basic ${base64}`;
|
||||
}
|
||||
|
||||
if (paramsForBody) {
|
||||
const data = new URLSearchParams();
|
||||
Object.entries(paramsForBody).forEach(([key, value]) => {
|
||||
data.append(key, value);
|
||||
});
|
||||
content = data;
|
||||
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
|
||||
const { headers: receivedHeaders } = options;
|
||||
|
||||
if (receivedHeaders) {
|
||||
Object.keys(receivedHeaders).forEach((key) => {
|
||||
headers[key] = receivedHeaders[key];
|
||||
});
|
||||
}
|
||||
|
||||
// wrap callback to add a 'response' property on an error, in case
|
||||
// we have both (http 4xx/5xx error, which has a response payload)
|
||||
const wrappedCallback = ((cb: callbackFn): { (error: Error | undefined, response?: HTTPResponse): void } => {
|
||||
let called = false;
|
||||
return (error: Error | undefined, response?: HTTPResponse): void => {
|
||||
if (!called) {
|
||||
called = true;
|
||||
if (error && response) {
|
||||
(error as any).response = response;
|
||||
}
|
||||
cb(error, response);
|
||||
}
|
||||
};
|
||||
})(callback);
|
||||
|
||||
// is false if false, otherwise always true
|
||||
const followRedirects = options.followRedirects === false ? 'manual' : 'follow';
|
||||
|
||||
const requestOptions = {
|
||||
method,
|
||||
jar: false,
|
||||
timeout: options.timeout,
|
||||
body: content,
|
||||
redirect: followRedirects,
|
||||
referrer: options.referrer,
|
||||
integrity: options.integrity,
|
||||
headers,
|
||||
ignoreSsrfValidation: true,
|
||||
} as const;
|
||||
|
||||
fetch(newUrl, requestOptions)
|
||||
.then(async (res) => {
|
||||
const content = await res.text();
|
||||
const response: HTTPResponse = {};
|
||||
response.statusCode = res.status;
|
||||
response.content = `${content}`;
|
||||
|
||||
// fetch headers don't allow simple read using bracket notation
|
||||
// so we iterate their entries and assign them to a new Object
|
||||
response.headers = {};
|
||||
for (const entry of (res.headers as any).entries()) {
|
||||
const [key, val] = entry;
|
||||
response.headers[key] = val;
|
||||
}
|
||||
|
||||
response.ok = res.ok;
|
||||
response.redirected = res.redirected;
|
||||
|
||||
populateData(response);
|
||||
|
||||
if (response.statusCode >= 400) {
|
||||
const error = makeErrorByStatus(response.statusCode, response.content);
|
||||
wrappedCallback(error, response);
|
||||
} else {
|
||||
wrappedCallback(undefined, response);
|
||||
}
|
||||
})
|
||||
.catch((err) => wrappedCallback(err));
|
||||
}
|
||||
|
||||
function httpCallAsync(httpMethod: string, url: string, options: HttpCallOptions, callback: callbackFn): void;
|
||||
function httpCallAsync(httpMethod: string, url: string, callback: callbackFn): void;
|
||||
function httpCallAsync(httpMethod: string, url: string, optionsOrCallback: HttpCallOptions | callbackFn = {}, callback?: callbackFn): void {
|
||||
// If the options argument was omitted, adjust the arguments:
|
||||
if (!callback && typeof optionsOrCallback === 'function') {
|
||||
return _call(httpMethod, url, {}, optionsOrCallback);
|
||||
}
|
||||
|
||||
return _call(httpMethod, url, optionsOrCallback as HttpCallOptions, callback as callbackFn);
|
||||
}
|
||||
|
||||
export const httpCall = async (httpMethod: string, url: string, options: HttpCallOptions) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
httpCallAsync.bind(HTTP)(httpMethod, url, options, (error, result) => {
|
||||
if (error) {
|
||||
return reject(error);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user