aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--package.json2
-rw-r--r--server/helpers/proxy.ts14
-rw-r--r--server/helpers/requests.ts32
-rw-r--r--server/tests/api/server/index.ts1
-rw-r--r--server/tests/api/server/proxy.ts72
-rw-r--r--shared/extra-utils/mock-servers/mock-proxy.ts27
-rw-r--r--yarn.lock46
7 files changed, 191 insertions, 3 deletions
diff --git a/package.json b/package.json
index 18bce1123..dc15405ce 100644
--- a/package.json
+++ b/package.json
@@ -99,6 +99,7 @@
99 "fs-extra": "^10.0.0", 99 "fs-extra": "^10.0.0",
100 "got": "^11.8.2", 100 "got": "^11.8.2",
101 "helmet": "^4.1.0", 101 "helmet": "^4.1.0",
102 "hpagent": "^0.1.2",
102 "http-problem-details": "^0.1.5", 103 "http-problem-details": "^0.1.5",
103 "http-signature": "1.3.5", 104 "http-signature": "1.3.5",
104 "ip-anonymize": "^0.1.0", 105 "ip-anonymize": "^0.1.0",
@@ -199,6 +200,7 @@
199 "marked-man": "^0.7.0", 200 "marked-man": "^0.7.0",
200 "mocha": "^9.0.0", 201 "mocha": "^9.0.0",
201 "nodemon": "^2.0.1", 202 "nodemon": "^2.0.1",
203 "proxy": "^1.0.2",
202 "socket.io-client": "^4.0.1", 204 "socket.io-client": "^4.0.1",
203 "source-map-support": "^0.5.0", 205 "source-map-support": "^0.5.0",
204 "supertest": "^6.0.1", 206 "supertest": "^6.0.1",
diff --git a/server/helpers/proxy.ts b/server/helpers/proxy.ts
new file mode 100644
index 000000000..8b82ccae0
--- /dev/null
+++ b/server/helpers/proxy.ts
@@ -0,0 +1,14 @@
1function getProxy () {
2 return process.env.HTTPS_PROXY ||
3 process.env.HTTP_PROXY ||
4 undefined
5}
6
7function isProxyEnabled () {
8 return !!getProxy()
9}
10
11export {
12 getProxy,
13 isProxyEnabled
14}
diff --git a/server/helpers/requests.ts b/server/helpers/requests.ts
index 36e69458e..e09e23086 100644
--- a/server/helpers/requests.ts
+++ b/server/helpers/requests.ts
@@ -1,19 +1,21 @@
1import { createWriteStream, remove } from 'fs-extra' 1import { createWriteStream, remove } from 'fs-extra'
2import got, { CancelableRequest, Options as GotOptions, RequestError } from 'got' 2import got, { CancelableRequest, Options as GotOptions, RequestError } from 'got'
3import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'
3import { join } from 'path' 4import { join } from 'path'
4import { CONFIG } from '../initializers/config' 5import { CONFIG } from '../initializers/config'
5import { ACTIVITY_PUB, PEERTUBE_VERSION, REQUEST_TIMEOUT, WEBSERVER } from '../initializers/constants' 6import { ACTIVITY_PUB, PEERTUBE_VERSION, REQUEST_TIMEOUT, WEBSERVER } from '../initializers/constants'
6import { pipelinePromise } from './core-utils' 7import { pipelinePromise } from './core-utils'
7import { processImage } from './image-utils' 8import { processImage } from './image-utils'
8import { logger } from './logger' 9import { logger } from './logger'
10import { getProxy, isProxyEnabled } from './proxy'
11
12const httpSignature = require('http-signature')
9 13
10export interface PeerTubeRequestError extends Error { 14export interface PeerTubeRequestError extends Error {
11 statusCode?: number 15 statusCode?: number
12 responseBody?: any 16 responseBody?: any
13} 17}
14 18
15const httpSignature = require('http-signature')
16
17type PeerTubeRequestOptions = { 19type PeerTubeRequestOptions = {
18 activityPub?: boolean 20 activityPub?: boolean
19 bodyKBLimit?: number // 1MB 21 bodyKBLimit?: number // 1MB
@@ -29,6 +31,8 @@ type PeerTubeRequestOptions = {
29} & Pick<GotOptions, 'headers' | 'json' | 'method' | 'searchParams'> 31} & Pick<GotOptions, 'headers' | 'json' | 'method' | 'searchParams'>
30 32
31const peertubeGot = got.extend({ 33const peertubeGot = got.extend({
34 ...getAgent(),
35
32 headers: { 36 headers: {
33 'user-agent': getUserAgent() 37 'user-agent': getUserAgent()
34 }, 38 },
@@ -153,6 +157,30 @@ async function downloadImage (url: string, destDir: string, destName: string, si
153 } 157 }
154} 158}
155 159
160function getAgent () {
161 if (!isProxyEnabled()) return {}
162
163 const proxy = getProxy()
164
165 logger.info('Using proxy %s.', proxy)
166
167 const proxyAgentOptions = {
168 keepAlive: true,
169 keepAliveMsecs: 1000,
170 maxSockets: 256,
171 maxFreeSockets: 256,
172 scheduling: 'lifo' as 'lifo',
173 proxy
174 }
175
176 return {
177 agent: {
178 http: new HttpProxyAgent(proxyAgentOptions),
179 https: new HttpsProxyAgent(proxyAgentOptions)
180 }
181 }
182}
183
156function getUserAgent () { 184function getUserAgent () {
157 return `PeerTube/${PEERTUBE_VERSION} (+${WEBSERVER.URL})` 185 return `PeerTube/${PEERTUBE_VERSION} (+${WEBSERVER.URL})`
158} 186}
diff --git a/server/tests/api/server/index.ts b/server/tests/api/server/index.ts
index 56e6eb5da..b16a22ee7 100644
--- a/server/tests/api/server/index.ts
+++ b/server/tests/api/server/index.ts
@@ -15,3 +15,4 @@ import './stats'
15import './tracker' 15import './tracker'
16import './no-client' 16import './no-client'
17import './plugins' 17import './plugins'
18import './proxy'
diff --git a/server/tests/api/server/proxy.ts b/server/tests/api/server/proxy.ts
new file mode 100644
index 000000000..d5042ef27
--- /dev/null
+++ b/server/tests/api/server/proxy.ts
@@ -0,0 +1,72 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import 'mocha'
4import * as chai from 'chai'
5import { cleanupTests, createMultipleServers, doubleFollow, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/extra-utils'
6import { MockProxy } from '@shared/extra-utils/mock-servers/mock-proxy'
7
8const expect = chai.expect
9
10describe('Test proxy', function () {
11 let servers: PeerTubeServer[] = []
12 let proxy: MockProxy
13
14 const goodEnv = { HTTP_PROXY: '' }
15 const badEnv = { HTTP_PROXY: 'http://localhost:9000' }
16
17 before(async function () {
18 this.timeout(120000)
19
20 proxy = new MockProxy()
21
22 const proxyPort = await proxy.initialize()
23 servers = await createMultipleServers(2)
24
25 goodEnv.HTTP_PROXY = 'http://localhost:' + proxyPort
26
27 await setAccessTokensToServers(servers)
28 await doubleFollow(servers[0], servers[1])
29 })
30
31 it('Should succeed federation with the appropriate proxy config', async function () {
32 await servers[0].kill()
33 await servers[0].run({}, { env: goodEnv })
34
35 await servers[0].videos.quickUpload({ name: 'video 1' })
36
37 await waitJobs(servers)
38
39 for (const server of servers) {
40 const { total, data } = await server.videos.list()
41 expect(total).to.equal(1)
42 expect(data).to.have.lengthOf(1)
43 }
44 })
45
46 it('Should fail federation with a wrong proxy config', async function () {
47 await servers[0].kill()
48 await servers[0].run({}, { env: badEnv })
49
50 await servers[0].videos.quickUpload({ name: 'video 2' })
51
52 await waitJobs(servers)
53
54 {
55 const { total, data } = await servers[0].videos.list()
56 expect(total).to.equal(2)
57 expect(data).to.have.lengthOf(2)
58 }
59
60 {
61 const { total, data } = await servers[1].videos.list()
62 expect(total).to.equal(1)
63 expect(data).to.have.lengthOf(1)
64 }
65 })
66
67 after(async function () {
68 proxy.terminate()
69
70 await cleanupTests(servers)
71 })
72})
diff --git a/shared/extra-utils/mock-servers/mock-proxy.ts b/shared/extra-utils/mock-servers/mock-proxy.ts
new file mode 100644
index 000000000..5365f87d1
--- /dev/null
+++ b/shared/extra-utils/mock-servers/mock-proxy.ts
@@ -0,0 +1,27 @@
1
2import { createServer, Server } from 'http'
3import * as proxy from 'proxy'
4import { randomInt } from '@shared/core-utils'
5
6class MockProxy {
7 private server: Server
8
9 initialize () {
10 return new Promise<number>(res => {
11 const port = 42501 + randomInt(1, 100)
12
13 this.server = proxy(createServer())
14 this.server.listen(port, () => res(port))
15 })
16 }
17
18 terminate () {
19 if (this.server) this.server.close()
20 }
21}
22
23// ---------------------------------------------------------------------------
24
25export {
26 MockProxy
27}
diff --git a/yarn.lock b/yarn.lock
index 0f4fe3938..1179f273b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2002,6 +2002,16 @@ argparse@^2.0.1:
2002 resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" 2002 resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
2003 integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 2003 integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
2004 2004
2005args@5.0.1:
2006 version "5.0.1"
2007 resolved "https://registry.yarnpkg.com/args/-/args-5.0.1.tgz#4bf298df90a4799a09521362c579278cc2fdd761"
2008 integrity sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==
2009 dependencies:
2010 camelcase "5.0.0"
2011 chalk "2.4.2"
2012 leven "2.1.0"
2013 mri "1.1.4"
2014
2005array-differ@^3.0.0: 2015array-differ@^3.0.0:
2006 version "3.0.0" 2016 version "3.0.0"
2007 resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" 2017 resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b"
@@ -2200,6 +2210,11 @@ basic-auth-connect@^1.0.0:
2200 resolved "https://registry.yarnpkg.com/basic-auth-connect/-/basic-auth-connect-1.0.0.tgz#fdb0b43962ca7b40456a7c2bb48fe173da2d2122" 2210 resolved "https://registry.yarnpkg.com/basic-auth-connect/-/basic-auth-connect-1.0.0.tgz#fdb0b43962ca7b40456a7c2bb48fe173da2d2122"
2201 integrity sha1-/bC0OWLKe0BFanwrtI/hc9otISI= 2211 integrity sha1-/bC0OWLKe0BFanwrtI/hc9otISI=
2202 2212
2213basic-auth-parser@0.0.2:
2214 version "0.0.2"
2215 resolved "https://registry.yarnpkg.com/basic-auth-parser/-/basic-auth-parser-0.0.2.tgz#ce9e71a77f23c1279eecd2659b2a46244c156e41"
2216 integrity sha1-zp5xp38jwSee7NJlmypGJEwVbkE=
2217
2203basic-auth@2.0.1, basic-auth@~2.0.1: 2218basic-auth@2.0.1, basic-auth@~2.0.1:
2204 version "2.0.1" 2219 version "2.0.1"
2205 resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" 2220 resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
@@ -2616,6 +2631,11 @@ camelcase-keys@^4.0.0:
2616 map-obj "^2.0.0" 2631 map-obj "^2.0.0"
2617 quick-lru "^1.0.0" 2632 quick-lru "^1.0.0"
2618 2633
2634camelcase@5.0.0:
2635 version "5.0.0"
2636 resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42"
2637 integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==
2638
2619camelcase@^4.1.0: 2639camelcase@^4.1.0:
2620 version "4.1.0" 2640 version "4.1.0"
2621 resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" 2641 resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
@@ -2663,7 +2683,7 @@ chai@^4.1.1:
2663 pathval "^1.1.1" 2683 pathval "^1.1.1"
2664 type-detect "^4.0.5" 2684 type-detect "^4.0.5"
2665 2685
2666chalk@^2.0.0, chalk@^2.4.2: 2686chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.2:
2667 version "2.4.2" 2687 version "2.4.2"
2668 resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" 2688 resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
2669 integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== 2689 integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -4692,6 +4712,11 @@ hosted-git-info@^2.1.4:
4692 resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" 4712 resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
4693 integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== 4713 integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
4694 4714
4715hpagent@^0.1.2:
4716 version "0.1.2"
4717 resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-0.1.2.tgz#cab39c66d4df2d4377dbd212295d878deb9bdaa9"
4718 integrity sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==
4719
4695html-to-text@8.0.0: 4720html-to-text@8.0.0:
4696 version "8.0.0" 4721 version "8.0.0"
4697 resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-8.0.0.tgz#5848681a5a38d657a7bb58cf5006d1c29fe64ce3" 4722 resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-8.0.0.tgz#5848681a5a38d657a7bb58cf5006d1c29fe64ce3"
@@ -5548,6 +5573,11 @@ latest-version@^5.0.0:
5548 dependencies: 5573 dependencies:
5549 package-json "^6.3.0" 5574 package-json "^6.3.0"
5550 5575
5576leven@2.1.0:
5577 version "2.1.0"
5578 resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
5579 integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA=
5580
5551levn@^0.4.1: 5581levn@^0.4.1:
5552 version "0.4.1" 5582 version "0.4.1"
5553 resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" 5583 resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
@@ -6196,6 +6226,11 @@ mp4-stream@^3.0.0:
6196 queue-microtask "^1.2.2" 6226 queue-microtask "^1.2.2"
6197 readable-stream "^3.0.6" 6227 readable-stream "^3.0.6"
6198 6228
6229mri@1.1.4:
6230 version "1.1.4"
6231 resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a"
6232 integrity sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==
6233
6199ms@2.0.0: 6234ms@2.0.0:
6200 version "2.0.0" 6235 version "2.0.0"
6201 resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 6236 resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -7131,6 +7166,15 @@ proxy-addr@~2.0.5:
7131 forwarded "0.2.0" 7166 forwarded "0.2.0"
7132 ipaddr.js "1.9.1" 7167 ipaddr.js "1.9.1"
7133 7168
7169proxy@^1.0.2:
7170 version "1.0.2"
7171 resolved "https://registry.yarnpkg.com/proxy/-/proxy-1.0.2.tgz#e0cfbe11c0a7a8b238fd2d7134de4e2867578e7f"
7172 integrity sha512-KNac2ueWRpjbUh77OAFPZuNdfEqNynm9DD4xHT14CccGpW8wKZwEkN0yjlb7X9G9Z9F55N0Q+1z+WfgAhwYdzQ==
7173 dependencies:
7174 args "5.0.1"
7175 basic-auth-parser "0.0.2"
7176 debug "^4.1.1"
7177
7134pseudomap@^1.0.2: 7178pseudomap@^1.0.2:
7135 version "1.0.2" 7179 version "1.0.2"
7136 resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" 7180 resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"