Skip to content

Commit ef83468

Browse files
authored
Merge pull request #123 from Sprax2013/use-proxies
Support proxies for outbound connections
2 parents 7b33618 + 19b4c1a commit ef83468

File tree

7 files changed

+44
-20
lines changed

7 files changed

+44
-20
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "api.sprax2013.de",
3-
"version": "0.5.0",
3+
"version": "0.5.1",
44
"description": "Public Minecraft related API",
55
"keywords": [
66
"minecraft",

src/global.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export interface SpraxAPIcfg {
1616
readonly accessLogFormat: string;
1717
readonly discordErrorWebHookURL: string | null;
1818
}
19+
20+
readonly proxies: string[];
1921
}
2022

2123
export interface SpraxAPIdbCfg {

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ export let cfg: SpraxAPIcfg = {
2222
logging: {
2323
accessLogFormat: '[:date[web]] :remote-addr by :remote-user | :method :url :status with :res[content-length] bytes | ":user-agent" referred from ":referrer" | :response-time[3] ms',
2424
discordErrorWebHookURL: null
25-
}
25+
},
26+
proxies: []
2627
};
2728
export let dbCfg: SpraxAPIdbCfg = {
2829
enabled: false,

src/routes/minecraft.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Router, Request } from 'express';
88

99
import { createCamera, createModel } from '../utils/modelRender';
1010
import { db } from '../index';
11+
import { getRequestOptions } from '../utils/web';
1112
import { importByTexture, importCapeByURL } from './skindb';
1213
import { MinecraftProfile, MinecraftUser, MinecraftNameHistoryElement, UserAgent, CapeType, SkinArea } from '../global';
1314
import { restful, isUUID, toBoolean, Image, ErrorBuilder, ApiError, HttpError, setCaching, isNumber, toInt, isHttpURL, getFileNameFromURL, generateHash } from '../utils/utils';
@@ -357,7 +358,7 @@ router.all('/skin/:user?', (req, res, next) => {
357358
const skinURL = mcUser.getSecureSkinURL();
358359

359360
if (skinURL) {
360-
request.get(skinURL, { encoding: null, jar: true, gzip: true }, (err, httpRes, httpBody) => {
361+
request.get(skinURL, Object.assign(getRequestOptions(), { encoding: null }), (err, httpRes, httpBody) => {
361362
if (err) return next(err);
362363

363364
if (httpRes.statusCode == 200) {
@@ -393,7 +394,7 @@ router.all('/skin/:user?', (req, res, next) => {
393394
} else {
394395
const skinURL: string = MinecraftUser.getSecureURL(req.query.url as string);
395396

396-
request.get(skinURL, { encoding: null, jar: true, gzip: true }, (err, httpRes, httpBody) => {
397+
request.get(skinURL, Object.assign(getRequestOptions(), { encoding: null }), (err, httpRes, httpBody) => {
397398
if (err) return next(err);
398399

399400
if (httpRes.statusCode == 200) {
@@ -459,7 +460,7 @@ router.all('/skin/:user?/:skinArea?/:3d?', (req, res, next) => {
459460
const skinURL = mcUser.getSecureSkinURL();
460461

461462
if (skinURL) {
462-
request.get(skinURL, { encoding: null, jar: true, gzip: true }, (err, httpRes, httpBody) => {
463+
request.get(skinURL, Object.assign(getRequestOptions(), { encoding: null }), (err, httpRes, httpBody) => {
463464
if (err) return next(err);
464465

465466
if (httpRes.statusCode != 200 && httpRes.statusCode != 404) ApiError.log(`${skinURL} returned HTTP-Code ${httpRes.statusCode}`);
@@ -497,7 +498,7 @@ router.all('/skin/:user?/:skinArea?/:3d?', (req, res, next) => {
497498
// if (skinURL.toLowerCase().startsWith('https://cdn.skindb.net/skins/')) {
498499
// }
499500

500-
request.get(skinURL, { encoding: null, jar: true, gzip: true }, (err, httpRes, httpBody) => {
501+
request.get(skinURL, Object.assign(getRequestOptions(), { encoding: null }), (err, httpRes, httpBody) => {
501502
if (err) return next(err);
502503

503504
if (httpRes.statusCode == 200) {
@@ -540,7 +541,7 @@ router.all('/capes/:capeType/:user?', (req, res, next) => {
540541
capeType == CapeType.LABYMOD ? mcUser.getLabyModCapeURL() : null;
541542

542543
if (capeURL) {
543-
request.get(capeURL, { encoding: null, jar: true, gzip: true }, (err, httpRes, httpBody) => {
544+
request.get(capeURL, Object.assign(getRequestOptions(), { encoding: null }), (err, httpRes, httpBody) => {
544545
if (err) return next(err);
545546

546547
if (httpRes.statusCode == 200) {
@@ -631,7 +632,7 @@ router.all('/capes/:capeType/:user?/render', (req, res, next) => {
631632
capeType == CapeType.LABYMOD ? mcUser.getLabyModCapeURL() : null;
632633

633634
if (capeURL) {
634-
request.get(capeURL, { encoding: null, jar: true, gzip: true }, (err, httpRes, httpBody) => {
635+
request.get(capeURL, Object.assign(getRequestOptions(), { encoding: null }), (err, httpRes, httpBody) => {
635636
if (err) return next(err);
636637

637638
if (httpRes.statusCode != 200 && httpRes.statusCode != 404) ApiError.log(`${capeURL} returned HTTP-Code ${httpRes.statusCode}`);
@@ -651,7 +652,7 @@ router.all('/capes/:capeType/:user?/render', (req, res, next) => {
651652
} else {
652653
const capeURL: string = MinecraftUser.getSecureURL(req.query.url as string);
653654

654-
request.get(capeURL, { encoding: null, jar: true, gzip: true }, (err, httpRes, httpBody) => {
655+
request.get(capeURL, Object.assign(getRequestOptions(), { encoding: null }), (err, httpRes, httpBody) => {
655656
if (err) return next(err);
656657

657658
if (httpRes.statusCode == 200) {
@@ -795,7 +796,7 @@ export function getByUsername(username: string, at: number | string | null = nul
795796
const get = (callback: (err: Error | null, apiRes: { id: string, name: string } | null) => void) => {
796797
const cacheValue: { id: string, name: string } | Error | null | undefined = uuidCache.get(cacheKey);
797798
if (cacheValue == undefined) {
798-
request.get(`https://api.mojang.com/users/profiles/minecraft/${username}${at != null ? `?at=${at}` : ''}`, { jar: true, gzip: true }, (err, httpRes, httpBody) => {
799+
request.get(`https://api.mojang.com/users/profiles/minecraft/${username}${at != null ? `?at=${at}` : ''}`, getRequestOptions(), (err, httpRes, httpBody) => {
799800
if (err) {
800801
uuidCache.set(cacheKey, err);
801802
return callback(err, null);
@@ -808,7 +809,7 @@ export function getByUsername(username: string, at: number | string | null = nul
808809

809810
// Contact fallback api (should not be necessary but is better than returning an 429 or 500)
810811
ApiError.log(`Contacting api.ashcon.app for username lookup: ${username}`);
811-
request.get(`https://api.ashcon.app/mojang/v1/user/${username}`, { jar: true, gzip: true }, (err, httpRes, httpBody) => {
812+
request.get(`https://api.ashcon.app/mojang/v1/user/${username}`, getRequestOptions(), (err, httpRes, httpBody) => {
812813
if (err || (httpRes.statusCode != 200 && httpRes.statusCode != 404)) {
813814
return callback(err || new ErrorBuilder().serverErr(`The server got rejected (${HttpError.getName(httpRes.statusCode) || httpRes.statusCode})`), null);
814815
}
@@ -871,7 +872,7 @@ export function getByUUID(uuid: string, req: Request | null, callback: (err: Err
871872
// TODO: Reduce duplicate code
872873
if (rateLimitedNameHistory > 6) {
873874
// Contact fallback api (should not be necessary but is better than returning an 429 or 500
874-
request.get(`https://api.ashcon.app/mojang/v2/user/${mcUser.id}`, { jar: true, gzip: true }, (err, httpRes, httpBody) => { // FIXME: This api never returns legacy-field
875+
request.get(`https://api.ashcon.app/mojang/v2/user/${mcUser.id}`, getRequestOptions(), (err, httpRes, httpBody) => { // FIXME: This api never returns legacy-field
875876
if (err || (httpRes.statusCode != 200 && httpRes.statusCode != 404)) {
876877
return callback(err || new ErrorBuilder().serverErr(`The server got rejected (${HttpError.getName(httpRes.statusCode) || httpRes.statusCode})`, true), null);
877878
}
@@ -888,15 +889,15 @@ export function getByUUID(uuid: string, req: Request | null, callback: (err: Err
888889
return callback(null, result);
889890
});
890891
} else {
891-
request.get(`https://api.mojang.com/user/profiles/${mcUser.id}/names`, { jar: true, gzip: true }, (err, httpRes, httpBody) => {
892+
request.get(`https://api.mojang.com/user/profiles/${mcUser.id}/names`, getRequestOptions(), (err, httpRes, httpBody) => {
892893
if (err) return callback(err, null);
893894

894895
if (httpRes.statusCode != 200 && httpRes.statusCode != 204) {
895896
// Contact fallback api (should not be necessary but is better than returning an 429 or 500
896897
ApiError.log(`Mojang returned ${httpRes.statusCode} on name history lookup for ${mcUser.id}`);
897898
rateLimitedNameHistory++;
898899

899-
request.get(`https://api.ashcon.app/mojang/v2/user/${mcUser.id}`, { jar: true, gzip: true }, (err, httpRes, httpBody) => { // FIXME: This api never returns legacy-field
900+
request.get(`https://api.ashcon.app/mojang/v2/user/${mcUser.id}`, getRequestOptions(), (err, httpRes, httpBody) => { // FIXME: This api never returns legacy-field
900901
if (err || (httpRes.statusCode != 200 && httpRes.statusCode != 404)) {
901902
return callback(err || new ErrorBuilder().serverErr(`The server got rejected (${HttpError.getName(httpRes.statusCode) || httpRes.statusCode})`, true), null);
902903
}
@@ -930,7 +931,7 @@ export function getByUUID(uuid: string, req: Request | null, callback: (err: Err
930931
}
931932
};
932933

933-
request.get(`https://sessionserver.mojang.com/session/minecraft/profile/${uuid}?unsigned=false`, { jar: true, gzip: true }, (err, httpRes, httpBody) => {
934+
request.get(`https://sessionserver.mojang.com/session/minecraft/profile/${uuid}?unsigned=false`, getRequestOptions(), (err, httpRes, httpBody) => {
934935
if (err) {
935936
userCache.set(uuid, err);
936937
return callback(err, null);
@@ -968,7 +969,7 @@ export function getByUUID(uuid: string, req: Request | null, callback: (err: Err
968969
ApiError.log(`Contacting api.ashcon.app for profile lookup: ${uuid}`);
969970

970971
// Contact fallback api (should not be necessary but is better than returning an 429 or 500
971-
request.get(`https://api.ashcon.app/mojang/v2/user/${uuid}`, { jar: true, gzip: true }, (err, httpRes, httpBody) => { // FIXME: This api never returns legacy-field
972+
request.get(`https://api.ashcon.app/mojang/v2/user/${uuid}`, getRequestOptions(), (err, httpRes, httpBody) => { // FIXME: This api never returns legacy-field
972973
if (err || (httpRes.statusCode != 200 && httpRes.statusCode != 404)) {
973974
return callback(err || new ErrorBuilder().serverErr(`The server got rejected (${HttpError.getName(httpRes.statusCode) || httpRes.statusCode})`, true), null);
974975
}
@@ -1079,7 +1080,7 @@ export function isUUIDCached(uuid: string): boolean {
10791080
}
10801081

10811082
function getBlockedServers(callback: (err: Error | null, hashes: string[] | null) => void): void {
1082-
request.get(`https://sessionserver.mojang.com/blockedservers`, { jar: true, gzip: true }, (err, httpRes, httpBody) => {
1083+
request.get(`https://sessionserver.mojang.com/blockedservers`, getRequestOptions(), (err, httpRes, httpBody) => {
10831084
if (err) return callback(err, null);
10841085
if (httpRes.statusCode != 200) return callback(null, null);
10851086

src/routes/skindb.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { db } from '..';
1010
import { ErrorBuilder, restful, Image, setCaching, isNumber, generateHash, ApiError } from '../utils/utils';
1111
import { getUserAgent, getByUUID, isUUIDCached } from './minecraft';
1212
import { MinecraftUser, UserAgent, Skin, Cape, CapeType } from '../global';
13+
import { getRequestOptions } from '../utils/web';
1314

1415
const yggdrasilPublicKey = fs.readFileSync(path.join(__dirname, '..', '..', 'resources', 'yggdrasil_session_pubkey.pem'));
1516

@@ -364,7 +365,7 @@ export async function importByTexture(textureValue: string, textureSignature: st
364365
}
365366

366367
export function importSkinByURL(skinURL: string, userAgent: UserAgent, callback: (err: Error | null, skin: Skin | null, exactMatch: boolean) => void, textureValue: string | null = null, textureSignature: string | null = null): void {
367-
request.get(skinURL, { encoding: null, jar: true, gzip: true }, (err, httpRes, httpBody) => {
368+
request.get(skinURL, Object.assign(getRequestOptions(), { encoding: null }), (err, httpRes, httpBody) => {
368369
if (err || httpRes.statusCode != 200) return callback(err, null, false);
369370

370371
return importSkinByBuffer(httpBody, skinURL, userAgent, callback, textureValue, textureSignature);
@@ -415,7 +416,7 @@ export function importSkinByBuffer(skinBuffer: Buffer, skinURL: string | null, u
415416

416417
export function importCapeByURL(capeURL: string, capeType: CapeType, userAgent: UserAgent, textureValue?: string, textureSignature?: string): Promise<Cape | null> {
417418
return new Promise((resolve, reject) => {
418-
request.get(capeURL, { encoding: null, jar: true, gzip: true }, (err, httpRes, httpBody) => {
419+
request.get(capeURL, Object.assign(getRequestOptions(), { encoding: null }), (err, httpRes, httpBody) => {
419420
if (err) return reject(err);
420421

421422
if (httpRes.statusCode == 200) {

src/utils/web.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { jar, CookieJar, CoreOptions } from 'request';
2+
import { cfg } from '..';
3+
4+
let lastProxy = 0;
5+
const proxies: { proxy: string, jar: CookieJar }[] =
6+
cfg.proxies.length == 0 ?
7+
[{ proxy: '', jar: jar() }] :
8+
cfg.proxies.map((val) => { return { proxy: val.length > 0 ? `http://${val}` : val, jar: jar() } });
9+
10+
export function getRequestOptions(): CoreOptions {
11+
return getNextProxy();
12+
}
13+
14+
function getNextProxy(): { proxy: string, jar: CookieJar } {
15+
if (proxies.length == 0) return proxies[0];
16+
if (lastProxy >= proxies.length) lastProxy = 0;
17+
18+
return proxies[lastProxy++];
19+
}

0 commit comments

Comments
 (0)