Skip to content

Commit

Permalink
Merge pull request #123 from Sprax2013/use-proxies
Browse files Browse the repository at this point in the history
Support proxies for outbound connections
  • Loading branch information
SpraxDev committed Jun 8, 2020
2 parents 7b33618 + 19b4c1a commit ef83468
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 20 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "api.sprax2013.de",
"version": "0.5.0",
"version": "0.5.1",
"description": "Public Minecraft related API",
"keywords": [
"minecraft",
Expand Down
2 changes: 2 additions & 0 deletions src/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export interface SpraxAPIcfg {
readonly accessLogFormat: string;
readonly discordErrorWebHookURL: string | null;
}

readonly proxies: string[];
}

export interface SpraxAPIdbCfg {
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export let cfg: SpraxAPIcfg = {
logging: {
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',
discordErrorWebHookURL: null
}
},
proxies: []
};
export let dbCfg: SpraxAPIdbCfg = {
enabled: false,
Expand Down
31 changes: 16 additions & 15 deletions src/routes/minecraft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Router, Request } from 'express';

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// Contact fallback api (should not be necessary but is better than returning an 429 or 500)
ApiError.log(`Contacting api.ashcon.app for username lookup: ${username}`);
request.get(`https://api.ashcon.app/mojang/v1/user/${username}`, { jar: true, gzip: true }, (err, httpRes, httpBody) => {
request.get(`https://api.ashcon.app/mojang/v1/user/${username}`, getRequestOptions(), (err, httpRes, httpBody) => {
if (err || (httpRes.statusCode != 200 && httpRes.statusCode != 404)) {
return callback(err || new ErrorBuilder().serverErr(`The server got rejected (${HttpError.getName(httpRes.statusCode) || httpRes.statusCode})`), null);
}
Expand Down Expand Up @@ -871,7 +872,7 @@ export function getByUUID(uuid: string, req: Request | null, callback: (err: Err
// TODO: Reduce duplicate code
if (rateLimitedNameHistory > 6) {
// Contact fallback api (should not be necessary but is better than returning an 429 or 500
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
request.get(`https://api.ashcon.app/mojang/v2/user/${mcUser.id}`, getRequestOptions(), (err, httpRes, httpBody) => { // FIXME: This api never returns legacy-field
if (err || (httpRes.statusCode != 200 && httpRes.statusCode != 404)) {
return callback(err || new ErrorBuilder().serverErr(`The server got rejected (${HttpError.getName(httpRes.statusCode) || httpRes.statusCode})`, true), null);
}
Expand All @@ -888,15 +889,15 @@ export function getByUUID(uuid: string, req: Request | null, callback: (err: Err
return callback(null, result);
});
} else {
request.get(`https://api.mojang.com/user/profiles/${mcUser.id}/names`, { jar: true, gzip: true }, (err, httpRes, httpBody) => {
request.get(`https://api.mojang.com/user/profiles/${mcUser.id}/names`, getRequestOptions(), (err, httpRes, httpBody) => {
if (err) return callback(err, null);

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

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

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

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

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

Expand Down
5 changes: 3 additions & 2 deletions src/routes/skindb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { db } from '..';
import { ErrorBuilder, restful, Image, setCaching, isNumber, generateHash, ApiError } from '../utils/utils';
import { getUserAgent, getByUUID, isUUIDCached } from './minecraft';
import { MinecraftUser, UserAgent, Skin, Cape, CapeType } from '../global';
import { getRequestOptions } from '../utils/web';

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

Expand Down Expand Up @@ -364,7 +365,7 @@ export async function importByTexture(textureValue: string, textureSignature: st
}

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 {
request.get(skinURL, { encoding: null, jar: true, gzip: true }, (err, httpRes, httpBody) => {
request.get(skinURL, Object.assign(getRequestOptions(), { encoding: null }), (err, httpRes, httpBody) => {
if (err || httpRes.statusCode != 200) return callback(err, null, false);

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

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

if (httpRes.statusCode == 200) {
Expand Down
19 changes: 19 additions & 0 deletions src/utils/web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { jar, CookieJar, CoreOptions } from 'request';
import { cfg } from '..';

let lastProxy = 0;
const proxies: { proxy: string, jar: CookieJar }[] =
cfg.proxies.length == 0 ?
[{ proxy: '', jar: jar() }] :
cfg.proxies.map((val) => { return { proxy: val.length > 0 ? `http://${val}` : val, jar: jar() } });

export function getRequestOptions(): CoreOptions {
return getNextProxy();
}

function getNextProxy(): { proxy: string, jar: CookieJar } {
if (proxies.length == 0) return proxies[0];
if (lastProxy >= proxies.length) lastProxy = 0;

return proxies[lastProxy++];
}

0 comments on commit ef83468

Please sign in to comment.