mirror of
https://github.com/http-party/node-http-proxy.git
synced 2025-12-08 20:59:18 +00:00
Compare commits
637 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b96cd7251 | ||
|
|
335aeeba2f | ||
|
|
dba39668ba | ||
|
|
9bbe486c5e | ||
|
|
6e4bef4d1c | ||
|
|
d05624167c | ||
|
|
244303b994 | ||
|
|
b4028ba78b | ||
|
|
77a98159d2 | ||
|
|
c662f9ebcd | ||
|
|
806e4927c9 | ||
|
|
c8fa599983 | ||
|
|
0d9ed366b1 | ||
|
|
9d75b981a1 | ||
|
|
192b2b980b | ||
|
|
4a657a7126 | ||
|
|
7a154f81d1 | ||
|
|
749eec65c3 | ||
|
|
e588213644 | ||
|
|
59c4403e9d | ||
|
|
dd1d08b631 | ||
|
|
16d4f8a951 | ||
|
|
fc93520d74 | ||
|
|
7e4a0e511b | ||
|
|
a9b09cce43 | ||
|
|
b00911c937 | ||
|
|
36bfe566a7 | ||
|
|
91fee3e943 | ||
|
|
235f0aa047 | ||
|
|
acdbec09c6 | ||
|
|
569e2ac4fb | ||
|
|
cb3171abfa | ||
|
|
a3fe02d651 | ||
|
|
42e8e1e099 | ||
|
|
e94d52973a | ||
|
|
4a37175a52 | ||
|
|
e5c02b8a8a | ||
|
|
2c44039a7c | ||
|
|
8097ae237e | ||
|
|
abf882e03c | ||
|
|
bab02e909e | ||
|
|
c9a556cfa5 | ||
|
|
6f88caf6e4 | ||
|
|
d2f9db8241 | ||
|
|
8231984fb0 | ||
|
|
81d58c531b | ||
|
|
d533a1be43 | ||
|
|
89f9ef87e0 | ||
|
|
c5d8466483 | ||
|
|
107c18720c | ||
|
|
de1b80851a | ||
|
|
bc6a23709c | ||
|
|
f5c2381395 | ||
|
|
50f58b4cd9 | ||
|
|
2c98416ac2 | ||
|
|
543636d0f6 | ||
|
|
a4bccc332d | ||
|
|
f4ff1006b9 | ||
|
|
09dcb98456 | ||
|
|
812757541d | ||
|
|
c979ba9f2c | ||
|
|
e6f24ba617 | ||
|
|
d73f1ee873 | ||
|
|
a539f3cbc1 | ||
|
|
d4d85ac5c4 | ||
|
|
c1fb596b85 | ||
|
|
961f457622 | ||
|
|
ac1a01b1f3 | ||
|
|
8cb451f20c | ||
|
|
c252b32f6c | ||
|
|
927357bedc | ||
|
|
4edbb62cc5 | ||
|
|
69cf892519 | ||
|
|
2f7f03778c | ||
|
|
d8223884f6 | ||
|
|
d0f1dfeb82 | ||
|
|
220f5fb795 | ||
|
|
d48f67eb90 | ||
|
|
61c2889109 | ||
|
|
fbc266809c | ||
|
|
8eddf45f2a | ||
|
|
f5217d6c20 | ||
|
|
2d01edc5a5 | ||
|
|
d0a1588639 | ||
|
|
912cd3acae | ||
|
|
84087208dd | ||
|
|
b98c75b1ff | ||
|
|
b781af641a | ||
|
|
cbd5777060 | ||
|
|
d8fb344715 | ||
|
|
1e52f660f0 | ||
|
|
9d06ca99d3 | ||
|
|
d0e000e1f9 | ||
|
|
183b5bb4fc | ||
|
|
3a347af543 | ||
|
|
fcfb0b37f6 | ||
|
|
42df703830 | ||
|
|
3e966361bc | ||
|
|
5082acc067 | ||
|
|
433a7408cf | ||
|
|
6baf1498cb | ||
|
|
284903d379 | ||
|
|
b223275ebb | ||
|
|
ecdfff408f | ||
|
|
28cecb5a96 | ||
|
|
bdb3492b21 | ||
|
|
a4b2857216 | ||
|
|
820fc5987c | ||
|
|
f345a1ac2d | ||
|
|
018352955e | ||
|
|
f8d605d53f | ||
|
|
c1440b58f1 | ||
|
|
ef86b50427 | ||
|
|
e1b2f4c31b | ||
|
|
820f198729 | ||
|
|
d2df053677 | ||
|
|
7357200334 | ||
|
|
d19be83858 | ||
|
|
570cf3b4f9 | ||
|
|
5c46e4b754 | ||
|
|
25e56d0182 | ||
|
|
9d9fa940cf | ||
|
|
2831d9a4f9 | ||
|
|
a379d160f3 | ||
|
|
7aed1f4559 | ||
|
|
bfcab93d8d | ||
|
|
7bad3fbca4 | ||
|
|
268994ea45 | ||
|
|
58c0fdc761 | ||
|
|
d73b0d312a | ||
|
|
a76e226221 | ||
|
|
db576d75c1 | ||
|
|
e017fa8189 | ||
|
|
646a7493a4 | ||
|
|
3b39d2c3dc | ||
|
|
0cb1d3c68e | ||
|
|
f9540de7b1 | ||
|
|
f2093b5313 | ||
|
|
41414a56a1 | ||
|
|
6371231086 | ||
|
|
6c83980f29 | ||
|
|
e4760727f1 | ||
|
|
222a4d0fa1 | ||
|
|
6106d4c32f | ||
|
|
cd1d7776e8 | ||
|
|
642e9cc2cb | ||
|
|
eea79cab53 | ||
|
|
f82ce18d2f | ||
|
|
470475b896 | ||
|
|
2a5e69d48c | ||
|
|
9ea1e89a2f | ||
|
|
ab42124d54 | ||
|
|
5d593e8ef1 | ||
|
|
64fa520789 | ||
|
|
b5a6d0e583 | ||
|
|
0bc4c783ca | ||
|
|
9752652e76 | ||
|
|
a05fc2d169 | ||
|
|
60baca5aed | ||
|
|
6b65c428b3 | ||
|
|
eb97bf5423 | ||
|
|
ec64e4f597 | ||
|
|
e666a4e07d | ||
|
|
c86ae51bb9 | ||
|
|
302d981dd2 | ||
|
|
30e3b371de | ||
|
|
cea0e8676b | ||
|
|
3d2350c54f | ||
|
|
ca73208749 | ||
|
|
da674ec4df | ||
|
|
855cebdac4 | ||
|
|
8bfd90c4d9 | ||
|
|
931f73dc98 | ||
|
|
349b843731 | ||
|
|
2e3eb0950d | ||
|
|
1f4fccd2c6 | ||
|
|
ecbba1a008 | ||
|
|
fe3dd8363f | ||
|
|
38864d0167 | ||
|
|
76051032e7 | ||
|
|
7e82a04a16 | ||
|
|
1912b62ddb | ||
|
|
3f997b9289 | ||
|
|
7e6c66a7e4 | ||
|
|
d26ef56e1b | ||
|
|
607f96c00c | ||
|
|
18c77cafc7 | ||
|
|
934e6c4d54 | ||
|
|
0db8f195d7 | ||
|
|
0bd446c680 | ||
|
|
a6ae6c4997 | ||
|
|
1b89bc9a76 | ||
|
|
c6dfb04a67 | ||
|
|
6201ac76f7 | ||
|
|
5f14bcaa70 | ||
|
|
0ee314c436 | ||
|
|
c33d1616cd | ||
|
|
1dabda241f | ||
|
|
5a969d077b | ||
|
|
21b30b754d | ||
|
|
aa8f3e9a6e | ||
|
|
ab37a224aa | ||
|
|
d145152655 | ||
|
|
87a92a7280 | ||
|
|
507f818df5 | ||
|
|
e907d7bb2a | ||
|
|
7298510e91 | ||
|
|
63c9262df5 | ||
|
|
ff1626f071 | ||
|
|
df158bfc53 | ||
|
|
f55ffa356a | ||
|
|
ab5c3e5c81 | ||
|
|
245d73ae6c | ||
|
|
6d074eff47 | ||
|
|
26029ba7ac | ||
|
|
14415a5074 | ||
|
|
62e4b75101 | ||
|
|
7f2f3ac35c | ||
|
|
9eefd4678e | ||
|
|
9ece52fac4 | ||
|
|
32aa10dfe2 | ||
|
|
c82c9ece8a | ||
|
|
3311106c2c | ||
|
|
402ab05734 | ||
|
|
f0db5b3f70 | ||
|
|
f30486195c | ||
|
|
ea0a4ded80 | ||
|
|
8a8a894092 | ||
|
|
c62610e8e4 | ||
|
|
05d18a4e1b | ||
|
|
8bff3ddc12 | ||
|
|
26537866b3 | ||
|
|
f92a1b6839 | ||
|
|
c1a94176a8 | ||
|
|
099eb86948 | ||
|
|
4bd3c4671f | ||
|
|
6a330ff904 | ||
|
|
89f9ca1e89 | ||
|
|
c04485671a | ||
|
|
2086e4917c | ||
|
|
71a06aab02 | ||
|
|
501e8c2a9b | ||
|
|
81874f795b | ||
|
|
75a9a2de61 | ||
|
|
aeb42a3614 | ||
|
|
56a7b77645 | ||
|
|
9c0b8697bc | ||
|
|
410a8ce94c | ||
|
|
d98d9516ea | ||
|
|
70ed1c4273 | ||
|
|
361d4e3b00 | ||
|
|
276f65a3b8 | ||
|
|
8d68ac0e0f | ||
|
|
48ae5d828c | ||
|
|
95a588706b | ||
|
|
3194d819b4 | ||
|
|
8a24a1e18f | ||
|
|
add81338a9 | ||
|
|
daf66a7a88 | ||
|
|
aba505d159 | ||
|
|
84036e9ddd | ||
|
|
daa2ce0ee3 | ||
|
|
69a693034e | ||
|
|
9ba8311343 | ||
|
|
709b3e9656 | ||
|
|
3f19e6e178 | ||
|
|
7c5e40a429 | ||
|
|
eb95660822 | ||
|
|
4a2b870cc9 | ||
|
|
fa797fca90 | ||
|
|
57329b6327 | ||
|
|
d1eabccf93 | ||
|
|
60bd697051 | ||
|
|
1af8224cc1 | ||
|
|
43641b00b3 | ||
|
|
70fa8ad58c | ||
|
|
796ab0bcc5 | ||
|
|
f70015f001 | ||
|
|
f64954a08b | ||
|
|
9f684d0439 | ||
|
|
3768cce4ca | ||
|
|
d46e876e27 | ||
|
|
0378b03e53 | ||
|
|
04c45a022c | ||
|
|
9577a0faf2 | ||
|
|
fcdbf46e4d | ||
|
|
c62766391e | ||
|
|
43c6f0c7c0 | ||
|
|
b065d92908 | ||
|
|
bcd8a564a8 | ||
|
|
77305489d9 | ||
|
|
f0bf741815 | ||
|
|
10a294af4d | ||
|
|
232258b6ec | ||
|
|
9210b56c9e | ||
|
|
000eb533de | ||
|
|
2aa3b84d7b | ||
|
|
a4ca578b44 | ||
|
|
e7d50b1a37 | ||
|
|
c0a796b3e3 | ||
|
|
90d40d6a6a | ||
|
|
45cf95a82e | ||
|
|
42c35aedca | ||
|
|
ec683b924b | ||
|
|
65a21bce6d | ||
|
|
51eeebef68 | ||
|
|
554f59c518 | ||
|
|
73e8a4cdd5 | ||
|
|
3ab6e9591e | ||
|
|
a934cb6a46 | ||
|
|
107b9da256 | ||
|
|
73d865bc9f | ||
|
|
df12aeb12d | ||
|
|
ed73f06ed3 | ||
|
|
d5c656bceb | ||
|
|
dceef407a1 | ||
|
|
e44fabe58a | ||
|
|
9a534c6ff6 | ||
|
|
d1facd52c3 | ||
|
|
5568cb5575 | ||
|
|
6ac05259ca | ||
|
|
0e64568b4e | ||
|
|
fc73828035 | ||
|
|
814fbd254d | ||
|
|
a65021d52b | ||
|
|
511b7b3d47 | ||
|
|
6b83ae47bb | ||
|
|
d16062bba2 | ||
|
|
49a0de1e7c | ||
|
|
05f0b891a6 | ||
|
|
261742a429 | ||
|
|
f92f7aea9b | ||
|
|
8be9d945d0 | ||
|
|
0a6b424e2c | ||
|
|
37036dd325 | ||
|
|
a3d02196c5 | ||
|
|
63c53a1772 | ||
|
|
3e8ee042fb | ||
|
|
db5f2954b2 | ||
|
|
ed9e12b0ed | ||
|
|
5f838541cb | ||
|
|
e336b52629 | ||
|
|
7104a7c023 | ||
|
|
d1baa3684e | ||
|
|
e50846b967 | ||
|
|
9df4bc1e12 | ||
|
|
f6bac7b257 | ||
|
|
48d46e6750 | ||
|
|
7b79a7409a | ||
|
|
4193d3bd74 | ||
|
|
159ca83652 | ||
|
|
0f243516e1 | ||
|
|
431aba79d8 | ||
|
|
7cb98a4e41 | ||
|
|
5ebf9833c8 | ||
|
|
1385635e18 | ||
|
|
1213e46b1b | ||
|
|
c472527ea6 | ||
|
|
ccad177954 | ||
|
|
896ee7c9c3 | ||
|
|
8aa7c519b1 | ||
|
|
d637b96420 | ||
|
|
4947484806 | ||
|
|
c54278bd3b | ||
|
|
61c8734e8b | ||
|
|
77a1cff9bc | ||
|
|
d908e2ad61 | ||
|
|
4f07dc220d | ||
|
|
97ceeb37d0 | ||
|
|
8b48a9fdab | ||
|
|
c6b7a7919f | ||
|
|
7e5feec34f | ||
|
|
d658c9fa39 | ||
|
|
eca765a856 | ||
|
|
07fceb7c7a | ||
|
|
ece85b4e1b | ||
|
|
5251a238e7 | ||
|
|
89a22bc003 | ||
|
|
a7b16eb136 | ||
|
|
c22610af75 | ||
|
|
99f757251b | ||
|
|
e633b0f7e4 | ||
|
|
10670540ac | ||
|
|
68fa17bbca | ||
|
|
7923cf3c6b | ||
|
|
81daf26a43 | ||
|
|
80f645c7c0 | ||
|
|
4bdc3e4f45 | ||
|
|
4c3ba74c4e | ||
|
|
daad4703f3 | ||
|
|
b60673522b | ||
|
|
4ecc6e26ce | ||
|
|
8004f4e5fc | ||
|
|
3b2178d1f7 | ||
|
|
0b223abb65 | ||
|
|
7a53ec2fd6 | ||
|
|
96ea631fb6 | ||
|
|
3e47c2485c | ||
|
|
c5ec1836b2 | ||
|
|
e936d186b6 | ||
|
|
68c5512303 | ||
|
|
689459fe46 | ||
|
|
53a2653b5e | ||
|
|
3330e125aa | ||
|
|
d6d2d0c882 | ||
|
|
d4942e52e7 | ||
|
|
d23353d980 | ||
|
|
4351ed1c86 | ||
|
|
0ba4fa8e5e | ||
|
|
969a623542 | ||
|
|
0b642d4cf7 | ||
|
|
a4ee8f9d82 | ||
|
|
2c8edc170d | ||
|
|
c47adac391 | ||
|
|
c17b591b7d | ||
|
|
6fa23e11f6 | ||
|
|
182c76cd23 | ||
|
|
e5991519db | ||
|
|
162a42f58f | ||
|
|
9243444ac0 | ||
|
|
db12f6c24e | ||
|
|
16828a9915 | ||
|
|
e2a5d513ca | ||
|
|
d83fdf69a1 | ||
|
|
bc236d7e95 | ||
|
|
c82ff2c3c0 | ||
|
|
d7064f2e1e | ||
|
|
de3ff11656 | ||
|
|
2142c506e0 | ||
|
|
e592c53d1a | ||
|
|
d85ccdd333 | ||
|
|
831a44b3c8 | ||
|
|
ed8c9eeba9 | ||
|
|
588327c2c4 | ||
|
|
e02317ce86 | ||
|
|
b726116134 | ||
|
|
7e44d3669b | ||
|
|
bdeabb767a | ||
|
|
cfd417de23 | ||
|
|
c4d56a5faf | ||
|
|
9e74a633a7 | ||
|
|
97e4600e94 | ||
|
|
03880d8d06 | ||
|
|
0393b5da99 | ||
|
|
ae0faef5aa | ||
|
|
584ce76e75 | ||
|
|
10c0f11b68 | ||
|
|
83367e7e91 | ||
|
|
cb7af4f4d7 | ||
|
|
dcb873ad99 | ||
|
|
54eceb4a86 | ||
|
|
7a3f6dfbcc | ||
|
|
04c10113f7 | ||
|
|
961d2f9400 | ||
|
|
bbe3bfdf98 | ||
|
|
31d919b0a3 | ||
|
|
6a6dfbb79d | ||
|
|
a467b7b4a9 | ||
|
|
fd42dcef01 | ||
|
|
a2b1f0a4c9 | ||
|
|
d0862aff0c | ||
|
|
cde08fb2ee | ||
|
|
590bb604da | ||
|
|
b8c6397a94 | ||
|
|
dda6f7a45a | ||
|
|
8085178dc2 | ||
|
|
1204a35e46 | ||
|
|
f720e36b42 | ||
|
|
33a2462d28 | ||
|
|
e3f8d5fdbe | ||
|
|
a1b25a123b | ||
|
|
920f1e7707 | ||
|
|
c75d06c5f9 | ||
|
|
d60353f80b | ||
|
|
02df9a33c5 | ||
|
|
881c7e62e0 | ||
|
|
cc09ae6a34 | ||
|
|
7c72f3b407 | ||
|
|
0bfb9be418 | ||
|
|
5d66ce11bb | ||
|
|
5e130de854 | ||
|
|
babdf531fe | ||
|
|
2bf20d61d5 | ||
|
|
52ecd52ee5 | ||
|
|
9b3e1eb247 | ||
|
|
90fb01d38a | ||
|
|
1d1ee88582 | ||
|
|
e4450132e2 | ||
|
|
bbe2b2788a | ||
|
|
86750c7e59 | ||
|
|
c65ffbb976 | ||
|
|
a7042132c8 | ||
|
|
b333e63648 | ||
|
|
c9f5772fc1 | ||
|
|
a9f9e21eda | ||
|
|
3d8e5383cd | ||
|
|
5a1504f076 | ||
|
|
c7924e01f9 | ||
|
|
601dbcbfe9 | ||
|
|
b79bd29d5e | ||
|
|
a51b062278 | ||
|
|
8269eca2bb | ||
|
|
1436b715ae | ||
|
|
bb0d28c587 | ||
|
|
f7f5fa727e | ||
|
|
455f97e14c | ||
|
|
17399e7c3e | ||
|
|
0aeaba7fe6 | ||
|
|
94ec6fa5ce | ||
|
|
32a40889ce | ||
|
|
32dcb0449c | ||
|
|
4a517fbe6e | ||
|
|
5dcdf2b36c | ||
|
|
7ad5c0f993 | ||
|
|
39b0c46a69 | ||
|
|
a350fadea6 | ||
|
|
1b5fb1d8fc | ||
|
|
12cda561af | ||
|
|
f566a42e51 | ||
|
|
4ee96ddd66 | ||
|
|
1c7ace26c5 | ||
|
|
7d840d3515 | ||
|
|
427d8d8536 | ||
|
|
13741a823f | ||
|
|
69f126b34c | ||
|
|
72a89eab8b | ||
|
|
1ceea3e5f9 | ||
|
|
f36cb4d5a1 | ||
|
|
ca092635e7 | ||
|
|
8663ac1c43 | ||
|
|
afc4d0931f | ||
|
|
6b61878759 | ||
|
|
16a4d9da11 | ||
|
|
2c10f256b6 | ||
|
|
031452e400 | ||
|
|
9efa40a9d2 | ||
|
|
8eff1a1f26 | ||
|
|
10a0db4f0d | ||
|
|
0602500230 | ||
|
|
02007ed0fb | ||
|
|
40902506af | ||
|
|
7e25bded27 | ||
|
|
c01fd2c54e | ||
|
|
dc9d7e5452 | ||
|
|
7b9169c8c5 | ||
|
|
1cb967b90a | ||
|
|
5bb83b967e | ||
|
|
275a5192fa | ||
|
|
f1aeb0500c | ||
|
|
da9de7034a | ||
|
|
38e6d7cd54 | ||
|
|
469b0d4e9f | ||
|
|
9f3508805a | ||
|
|
dfdedf23b7 | ||
|
|
bd106d69f0 | ||
|
|
07091b5077 | ||
|
|
edd8e2f04e | ||
|
|
ef946a7697 | ||
|
|
268afe34bb | ||
|
|
1b867a7f59 | ||
|
|
29afab4488 | ||
|
|
e08d4edad3 | ||
|
|
60de543d04 | ||
|
|
adc5be020c | ||
|
|
0637322d96 | ||
|
|
4d3a4e1ee7 | ||
|
|
dd0f7b8876 | ||
|
|
d7078e2fdd | ||
|
|
ee3cc38066 | ||
|
|
aaff1966e4 | ||
|
|
57abb7f26c | ||
|
|
8b05626eed | ||
|
|
98f29bdcfc | ||
|
|
4c2f2f3b9a | ||
|
|
c9945dd370 | ||
|
|
5823842194 | ||
|
|
d1663549ec | ||
|
|
18341d5597 | ||
|
|
26c4c43a06 | ||
|
|
3c91ed3d26 | ||
|
|
46fe81e11a | ||
|
|
92de6a6f95 | ||
|
|
6e77cd3909 | ||
|
|
7599cee3fd | ||
|
|
dad211e71c | ||
|
|
63b016c8ef | ||
|
|
ec981c5b74 | ||
|
|
c4ddc4edd3 | ||
|
|
886a870707 | ||
|
|
8c8c455541 | ||
|
|
031aa0fbf3 | ||
|
|
07cfa6b981 | ||
|
|
7d71a867a8 | ||
|
|
6a03e5f7cf | ||
|
|
2a593664a5 | ||
|
|
893100972c | ||
|
|
79a14acfd2 | ||
|
|
f97c0c6167 | ||
|
|
1a7bef0cda | ||
|
|
e45bfd66a2 | ||
|
|
a74cd85c8a | ||
|
|
4a4607d075 | ||
|
|
0fb33810f5 | ||
|
|
c9cd6d2ad3 | ||
|
|
8b3fe32f6a | ||
|
|
e0faaaf811 | ||
|
|
1993faf8a4 | ||
|
|
3a39e444ff | ||
|
|
07551c63e4 | ||
|
|
79f7f99528 | ||
|
|
4480699d3a | ||
|
|
a6256cac1d | ||
|
|
b85aa16e75 | ||
|
|
27df8d72ad | ||
|
|
abf1d90fdf | ||
|
|
c9612798f1 | ||
|
|
8fc3389367 | ||
|
|
f4e9945856 | ||
|
|
2fac7b9b00 | ||
|
|
c02b721321 | ||
|
|
d40e4beb62 | ||
|
|
356f43d719 | ||
|
|
d4f0da898e | ||
|
|
9ab8749a9b | ||
|
|
bd3df45010 | ||
|
|
2e7343d728 | ||
|
|
6a4294cbdf | ||
|
|
4f24664e8a | ||
|
|
cedc5c4bd2 | ||
|
|
335af81d02 | ||
|
|
a255f984fe | ||
|
|
16eacfa961 | ||
|
|
004a46c09d | ||
|
|
34f16e7464 | ||
|
|
d05af4af60 | ||
|
|
8273cb6461 | ||
|
|
4d13156721 |
6
.auto-changelog
Normal file
6
.auto-changelog
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"output": "CHANGELOG.md",
|
||||
"template": "keepachangelog",
|
||||
"unreleased": true,
|
||||
"commitLimit": false
|
||||
}
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
package-lock.json binary
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,3 +1,10 @@
|
||||
config.json
|
||||
node_modules/
|
||||
node_modules
|
||||
*.swp
|
||||
cov
|
||||
atest.js
|
||||
notes
|
||||
primus-proxy.js
|
||||
tes.js
|
||||
npm-debug.log
|
||||
.nyc_output
|
||||
coverage
|
||||
|
||||
9
.npmignore
Normal file
9
.npmignore
Normal file
@ -0,0 +1,9 @@
|
||||
test
|
||||
examples
|
||||
doc
|
||||
benchmark
|
||||
coverage
|
||||
.nyc_output
|
||||
.travis.yml
|
||||
CHANGELOG.md
|
||||
UPGRADING.md
|
||||
18
.travis.yml
18
.travis.yml
@ -1,10 +1,12 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- travis@nodejitsu.com
|
||||
irc: "irc.freenode.org#nodejitsu"
|
||||
- "8"
|
||||
- "10"
|
||||
- "12"
|
||||
script:
|
||||
- npm test
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
1937
CHANGELOG.md
1937
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
74
CODE_OF_CONDUCT.md
Normal file
74
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,74 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at <https://github.com/http-party/node-http-proxy>. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
4
LICENSE
4
LICENSE
@ -1,7 +1,7 @@
|
||||
|
||||
node-http-proxy
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires
|
||||
Copyright (c) 2010-2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -20,4 +20,4 @@
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
97
UPGRADING.md
Normal file
97
UPGRADING.md
Normal file
@ -0,0 +1,97 @@
|
||||
Looking to upgrade from `http-proxy@0.x.x` to `http-proxy@1.0`? You've come to the right place!
|
||||
`http-proxy@1.0` is a from-scratch implementation of `http-proxy` and, as such
|
||||
brings some breaking changes to APIs.
|
||||
|
||||
## Server creation
|
||||
|
||||
Available through `.createServer()` or `.createProxyServer()`.
|
||||
|
||||
```javascript
|
||||
httpProxy.createServer({
|
||||
target:'http://localhost:9003'
|
||||
}).listen(8003);
|
||||
```
|
||||
|
||||
Check the [README.md](https://github.com/http-party/node-http-proxy/blob/caronte/README.md) for a more detailed explanation of the parameters.
|
||||
|
||||
## Proxying
|
||||
|
||||
Web proxying is done by calling the `.web()` method on a Proxy instance. You can check among some use cases in the [examples folder](https://github.com/http-party/node-http-proxy/tree/caronte/examples/http)
|
||||
|
||||
```javascript
|
||||
//
|
||||
// Create a HTTP Proxy server with a HTTPS target
|
||||
//
|
||||
httpProxy.createProxyServer({
|
||||
target: 'https://google.com',
|
||||
agent : https.globalAgent,
|
||||
headers: {
|
||||
host: 'google.com'
|
||||
}
|
||||
}).listen(8011);
|
||||
|
||||
```
|
||||
|
||||
Websockets are proxied by the `.ws()` method. The [examples folder](https://github.com/http-party/node-http-proxy/tree/caronte/examples/websocket) again provides a lot of useful snippets!
|
||||
|
||||
```javascript
|
||||
var proxy = new httpProxy.createProxyServer({
|
||||
target: {
|
||||
host: 'localhost',
|
||||
port: 9015
|
||||
}
|
||||
});
|
||||
var proxyServer = http.createServer(function (req, res) {
|
||||
proxy.web(req, res);
|
||||
});
|
||||
|
||||
//
|
||||
// Listen to the `upgrade` event and proxy the
|
||||
// WebSocket requests as well.
|
||||
//
|
||||
proxyServer.on('upgrade', function (req, socket, head) {
|
||||
proxy.ws(req, socket, head);
|
||||
});
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
It is possible to listen globally on the `error` event on the server. In alternative, a
|
||||
callback passed to `.web()` or `.ws()` as last parameter is also accepted.
|
||||
|
||||
```javascript
|
||||
var proxy = httpProxy.createServer({
|
||||
target:'http://localhost:9005'
|
||||
});
|
||||
|
||||
//
|
||||
// Tell the proxy to listen on port 8000
|
||||
//
|
||||
proxy.listen(8005);
|
||||
|
||||
//
|
||||
// Listen for the `error` event on `proxy`.
|
||||
proxy.on('error', function (err, req, res) {
|
||||
res.writeHead(500, {
|
||||
'Content-Type': 'text/plain'
|
||||
});
|
||||
|
||||
res.end('Something went wrong. And we are reporting a custom error message.');
|
||||
});
|
||||
```
|
||||
|
||||
## Dropped
|
||||
|
||||
Since the API was rewritten to be extremely flexible we decided to drop some features
|
||||
which were in the core and delegate them to eventual "userland" modules.
|
||||
|
||||
- Middleware API
|
||||
- ProxyTable API
|
||||
|
||||
### Middleware API
|
||||
|
||||
The new API makes it really easy to implement code that behaves like the old Middleware API. You can check some examples [here](https://github.com/http-party/node-http-proxy/tree/caronte/examples/middleware)
|
||||
|
||||
### ProxyTable API
|
||||
|
||||
See this [link](https://github.com/donasaur/http-proxy-rules/) for an add-on proxy rules module that you can use to simulate the old ProxyTable API.
|
||||
37
benchmark/README.md
Normal file
37
benchmark/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# Benchmarking `node-http-proxy`
|
||||
|
||||
The long-term goal of these scripts and documentation is to provide a consistent and well understood benchmarking process for `node-http-proxy` so that performance does not degrade over time. They were initially created to compare the performance of `v0.10.3` and `v1.0.0` (which was a significant rewrite).
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
All benchmarking shall be done with [wrk](https://github.com/wg/wrk) which _is the same tool used for performance testing by the node.js core team._ **Make sure you have `wrk` installed before continuing**.
|
||||
|
||||
```
|
||||
$ wrk
|
||||
Usage: wrk <options> <url>
|
||||
Options:
|
||||
-c, --connections <N> Connections to keep open
|
||||
-d, --duration <T> Duration of test
|
||||
-t, --threads <N> Number of threads to use
|
||||
|
||||
-s, --script <S> Load Lua script file
|
||||
-H, --header <H> Add header to request
|
||||
--latency Print latency statistics
|
||||
--timeout <T> Socket/request timeout
|
||||
-v, --version Print version details
|
||||
|
||||
Numeric arguments may include a SI unit (1k, 1M, 1G)
|
||||
Time arguments may include a time unit (2s, 2m, 2h)
|
||||
```
|
||||
|
||||
## Benchmarks
|
||||
|
||||
1. [Simple HTTP benchmark](#simple-http)
|
||||
|
||||
### Simple HTTP
|
||||
|
||||
_This benchmark requires three terminals running:_
|
||||
|
||||
1. **A proxy server:** `node benchmark/scripts/proxy.js`
|
||||
2. **A target server:** `node benchmark/scripts/hello.js`
|
||||
3. **A wrk process:** `wrk -c 20 -d5m -t 2 http://127.0.0.1:8000`
|
||||
3
benchmark/scripts/hello.js
Normal file
3
benchmark/scripts/hello.js
Normal file
@ -0,0 +1,3 @@
|
||||
require('http').createServer(function(req, res) {
|
||||
res.end('Hello world!');
|
||||
}).listen(9000);
|
||||
6
benchmark/scripts/proxy.js
Normal file
6
benchmark/scripts/proxy.js
Normal file
@ -0,0 +1,6 @@
|
||||
var http = require('http'),
|
||||
httpProxy = require('../../');
|
||||
//
|
||||
// Create your proxy server
|
||||
//
|
||||
httpProxy.createProxyServer({ target: 'http://localhost:9000' }).listen(8000);
|
||||
@ -1,7 +1,7 @@
|
||||
var crypto = require('crypto'),
|
||||
WebSocket = require('ws'),
|
||||
async = require('async'),
|
||||
httpProxy = require('../');
|
||||
httpProxy = require('../../');
|
||||
|
||||
var SERVER_PORT = 8415,
|
||||
PROXY_PORT = 8514;
|
||||
@ -1,113 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var path = require('path'),
|
||||
fs = require('fs'),
|
||||
util = require('util'),
|
||||
argv = require('optimist').argv,
|
||||
httpProxy = require('../lib/node-http-proxy');
|
||||
|
||||
var help = [
|
||||
"usage: node-http-proxy [options] ",
|
||||
"",
|
||||
"Starts a node-http-proxy server using the specified command-line options",
|
||||
"",
|
||||
"options:",
|
||||
" --port PORT Port that the proxy server should run on",
|
||||
" --host HOST Host that the proxy server should run on",
|
||||
" --target HOST:PORT Location of the server the proxy will target",
|
||||
" --config OUTFILE Location of the configuration file for the proxy server",
|
||||
" --silent Silence the log output from the proxy server",
|
||||
" --user USER User to drop privileges to once server socket is bound",
|
||||
" -h, --help You're staring at it"
|
||||
].join('\n');
|
||||
|
||||
if (argv.h || argv.help || Object.keys(argv).length === 2) {
|
||||
return util.puts(help);
|
||||
}
|
||||
|
||||
var location, config = {},
|
||||
port = argv.port || 80,
|
||||
host = argv.host || undefined,
|
||||
target = argv.target;
|
||||
user = argv.user;
|
||||
|
||||
//
|
||||
// If we were passed a config, parse it
|
||||
//
|
||||
if (argv.config) {
|
||||
try {
|
||||
var data = fs.readFileSync(argv.config);
|
||||
config = JSON.parse(data.toString());
|
||||
} catch (ex) {
|
||||
util.puts('Error starting node-http-proxy: ' + ex);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// If `config.https` is set, then load the required file contents into the config options.
|
||||
//
|
||||
if (config.https) {
|
||||
Object.keys(config.https).forEach(function (key) {
|
||||
// If CA certs are specified, load those too.
|
||||
if (key === "ca") {
|
||||
for (var i=0; i < config.https.ca.length; i++) {
|
||||
if (config.https.ca === undefined) {
|
||||
config.https.ca = [];
|
||||
}
|
||||
config.https.ca[i] = fs.readFileSync(config.https[key][i], 'utf8');
|
||||
}
|
||||
} else {
|
||||
config.https[key] = fs.readFileSync(config.https[key], 'utf8');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Check to see if we should silence the logs
|
||||
//
|
||||
config.silent = typeof argv.silent !== 'undefined' ? argv.silent : config.silent;
|
||||
|
||||
//
|
||||
// If we were passed a target, parse the url string
|
||||
//
|
||||
if (typeof target === 'string') location = target.split(':');
|
||||
|
||||
//
|
||||
// Create the server with the specified options
|
||||
//
|
||||
var server;
|
||||
if (location) {
|
||||
var targetPort = location.length === 1 ? 80 : parseInt(location[1]);
|
||||
server = httpProxy.createServer(targetPort, location[0], config);
|
||||
}
|
||||
else if (config.router) {
|
||||
server = httpProxy.createServer(config);
|
||||
}
|
||||
else {
|
||||
return util.puts(help);
|
||||
}
|
||||
|
||||
//
|
||||
// Start the server
|
||||
//
|
||||
if (host) {
|
||||
server.listen(port, host);
|
||||
} else {
|
||||
server.listen(port);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Drop privileges if requested
|
||||
//
|
||||
if (typeof user === 'string') {
|
||||
process.setuid(user);
|
||||
}
|
||||
|
||||
//
|
||||
// Notify that the server is started
|
||||
//
|
||||
if (!config.silent) {
|
||||
util.puts('node-http-proxy server now listening on port: ' + port);
|
||||
}
|
||||
10
codecov.yml
Normal file
10
codecov.yml
Normal file
@ -0,0 +1,10 @@
|
||||
coverage:
|
||||
parsers:
|
||||
javascript:
|
||||
enable_partials: yes
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: "70%"
|
||||
patch:
|
||||
enabled: false
|
||||
@ -1,10 +0,0 @@
|
||||
{
|
||||
"silent": false,
|
||||
"router": {
|
||||
"localhost": "localhost:9000"
|
||||
},
|
||||
"forward": {
|
||||
"port": 9001,
|
||||
"host": "localhost"
|
||||
}
|
||||
}
|
||||
BIN
doc/logo.png
Normal file
BIN
doc/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
@ -1,9 +1,35 @@
|
||||
/*
|
||||
simple-balancer.js: Example of a simple round robin balancer for websockets
|
||||
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
var http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
//
|
||||
// A simple round-robin load balancing strategy.
|
||||
//
|
||||
//
|
||||
// First, list the servers you want to use in your rotation.
|
||||
//
|
||||
var addresses = [
|
||||
@ -22,7 +48,7 @@ var addresses = [
|
||||
//
|
||||
|
||||
var proxies = addresses.map(function (target) {
|
||||
return new httpProxy.HttpProxy({
|
||||
return new httpProxy.createProxyServer({
|
||||
target: target
|
||||
});
|
||||
});
|
||||
@ -38,21 +64,20 @@ function nextProxy() {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
//
|
||||
// Get the 'next' proxy and send the http request
|
||||
//
|
||||
// Get the 'next' proxy and send the http request
|
||||
//
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
nextProxy().proxyRequest(req, res);
|
||||
var server = http.createServer(function (req, res) {
|
||||
nextProxy().web(req, res);
|
||||
});
|
||||
|
||||
//
|
||||
// Get the 'next' proxy and send the upgrade request
|
||||
//
|
||||
// Get the 'next' proxy and send the upgrade request
|
||||
//
|
||||
|
||||
server.on('upgrade', function (req, socket, head) {
|
||||
nextProxy().proxyWebSocketRequest(req, socket, head);
|
||||
nextProxy().ws(req, socket, head);
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
|
||||
server.listen(8001);
|
||||
|
||||
@ -1,7 +1,34 @@
|
||||
var httpProxy = require('../../lib/node-http-proxy');
|
||||
/*
|
||||
simple-balancer.js: Example of a simple round robin balancer
|
||||
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
var http = require('http'),
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
//
|
||||
// A simple round-robin load balancing strategy.
|
||||
//
|
||||
//
|
||||
// First, list the servers you want to use in your rotation.
|
||||
//
|
||||
var addresses = [
|
||||
@ -14,23 +41,24 @@ var addresses = [
|
||||
port: 80
|
||||
}
|
||||
];
|
||||
var proxy = httpProxy.createServer();
|
||||
|
||||
httpProxy.createServer(function (req, res, proxy) {
|
||||
http.createServer(function (req, res) {
|
||||
//
|
||||
// On each request, get the first location from the list...
|
||||
//
|
||||
var target = addresses.shift();
|
||||
var target = { target: addresses.shift() };
|
||||
|
||||
//
|
||||
// ...then proxy to the server whose 'turn' it is...
|
||||
//
|
||||
console.log('balancing request to: ', target);
|
||||
proxy.proxyRequest(req, res, target);
|
||||
proxy.web(req, res, target);
|
||||
|
||||
//
|
||||
// ...and then the server you just used becomes the last item in the list.
|
||||
//
|
||||
addresses.push(target);
|
||||
}).listen(8000);
|
||||
addresses.push(target.target);
|
||||
}).listen(8021);
|
||||
|
||||
// Rinse; repeat; enjoy.
|
||||
@ -2,7 +2,7 @@
|
||||
//
|
||||
// just to make these example a little bit interesting,
|
||||
// make a little key value store with an http interface
|
||||
// (see couchbd for a grown-up version of this)
|
||||
// (see couchdb for a grown-up version of this)
|
||||
//
|
||||
// API:
|
||||
// GET /
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
basic-proxy.js: Basic example of proxying over HTTP
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,14 +27,14 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
var welcome = [
|
||||
'# # ##### ##### ##### ##### ##### #### # # # #',
|
||||
'# # # # # # # # # # # # # # # # ',
|
||||
'###### # # # # ##### # # # # # # ## # ',
|
||||
'# # # # ##### ##### ##### # # ## # ',
|
||||
'# # # # # # # # # # # # # ',
|
||||
'# # # # # # # # # # # # # # # # ',
|
||||
'###### # # # # ##### # # # # # # ## # ',
|
||||
'# # # # ##### ##### ##### # # ## # ',
|
||||
'# # # # # # # # # # # # # ',
|
||||
'# # # # # # # # #### # # # '
|
||||
].join('\n');
|
||||
|
||||
@ -43,7 +43,9 @@ util.puts(welcome.rainbow.bold);
|
||||
//
|
||||
// Basic Http Proxy Server
|
||||
//
|
||||
httpProxy.createServer(9000, 'localhost').listen(8000);
|
||||
httpProxy.createServer({
|
||||
target:'http://localhost:9003'
|
||||
}).listen(8003);
|
||||
|
||||
//
|
||||
// Target Http Server
|
||||
@ -52,7 +54,7 @@ http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
}).listen(9000);
|
||||
}).listen(9003);
|
||||
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8003'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9003 '.yellow);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
concurrent-proxy.js: check levelof concurrency through proxy.
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,12 +27,14 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
//
|
||||
// Basic Http Proxy Server
|
||||
//
|
||||
httpProxy.createServer(9000, 'localhost').listen(8000);
|
||||
httpProxy.createServer({
|
||||
target:'http://localhost:9004'
|
||||
}).listen(8004);
|
||||
|
||||
//
|
||||
// Target Http Server
|
||||
@ -42,7 +44,7 @@ httpProxy.createServer(9000, 'localhost').listen(8000);
|
||||
//
|
||||
|
||||
|
||||
var connections = [],
|
||||
var connections = [],
|
||||
go;
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
@ -51,16 +53,16 @@ http.createServer(function (req, res) {
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
});
|
||||
|
||||
|
||||
process.stdout.write(connections.length + ', ');
|
||||
|
||||
|
||||
if (connections.length > 110 || go) {
|
||||
go = true;
|
||||
while (connections.length) {
|
||||
connections.shift()();
|
||||
}
|
||||
}
|
||||
}).listen(9000);
|
||||
}).listen(9004);
|
||||
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8004'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9004 '.yellow);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
custom-proxy-error.js: Example of using the custom `proxyError` event.
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,28 +27,29 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
//
|
||||
// Http Proxy Server with Latency
|
||||
// Http Proxy Server with bad target
|
||||
//
|
||||
var server = httpProxy.createServer(9000, 'localhost');
|
||||
var proxy = httpProxy.createServer({
|
||||
target:'http://localhost:9005'
|
||||
});
|
||||
|
||||
//
|
||||
// Tell the server to listen on port 8002
|
||||
// Tell the proxy to listen on port 8000
|
||||
//
|
||||
server.listen(8002);
|
||||
proxy.listen(8005);
|
||||
|
||||
//
|
||||
// Listen for the `proxyError` event on `server.proxy`. _It will not
|
||||
// be raised on the server itself._
|
||||
server.proxy.on('proxyError', function (err, req, res) {
|
||||
// Listen for the `error` event on `proxy`.
|
||||
proxy.on('error', function (err, req, res) {
|
||||
res.writeHead(500, {
|
||||
'Content-Type': 'text/plain'
|
||||
});
|
||||
|
||||
|
||||
res.end('Something went wrong. And we are reporting a custom error message.');
|
||||
});
|
||||
|
||||
|
||||
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with custom error message'.magenta.underline);
|
||||
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8005 '.yellow + 'with custom error message'.magenta.underline);
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
url-middleware.js: Example of a simple url routing middleware for node-http-proxy
|
||||
error-handling.js: Example of handle erros for HTTP and WebSockets
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,32 +27,37 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
//
|
||||
// Now we set up our proxy.
|
||||
// HTTP Proxy Server
|
||||
//
|
||||
httpProxy.createServer(
|
||||
//
|
||||
// This is where our middlewares go, with any options desired - in this case,
|
||||
// the list of routes/URLs and their destinations.
|
||||
//
|
||||
require('proxy-by-url')({
|
||||
'/hello': { port: 9000, host: 'localhost' },
|
||||
'/charlie': { port: 80, host: 'charlieistheman.com' },
|
||||
'/google': { port: 80, host: 'google.com' }
|
||||
var proxy = httpProxy.createProxyServer({target:'http://localhost:9000', ws:true});
|
||||
|
||||
//
|
||||
// Example of error handling
|
||||
//
|
||||
function requestHandler(req, res) {
|
||||
// Pass a callback to the web proxy method
|
||||
// and catch the error there.
|
||||
proxy.web(req, res, function (err) {
|
||||
// Now you can get the err
|
||||
// and handle it by your self
|
||||
// if (err) throw err;
|
||||
res.writeHead(502);
|
||||
res.end("There was an error proxying your request");
|
||||
});
|
||||
|
||||
// In a websocket request case
|
||||
req.on('upgrade', function (req, socket, head) {
|
||||
proxy.ws(req, socket, head, function (err) {
|
||||
// Now you can get the err
|
||||
// and handle it by your self
|
||||
// if (err) throw err;
|
||||
socket.close();
|
||||
})
|
||||
})
|
||||
).listen(8000);
|
||||
}
|
||||
|
||||
//
|
||||
// Target Http Server (to listen for requests on 'localhost')
|
||||
//
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
}).listen(9000);
|
||||
|
||||
// And finally, some colored startup output.
|
||||
http.createServer(requestHandler).listen(8000);
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
proxy-table.js: Example of proxying over HTTP with proxy table
|
||||
forward-and-target-proxy.js: Example of proxying over HTTP with additional forward proxy
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,16 +27,21 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
//
|
||||
// Http Proxy Server with Proxy Table
|
||||
// Setup proxy server with forwarding
|
||||
//
|
||||
httpProxy.createServer({
|
||||
router: {
|
||||
'localhost': 'localhost:9000'
|
||||
target: {
|
||||
port: 9006,
|
||||
host: 'localhost'
|
||||
},
|
||||
forward: {
|
||||
port: 9007,
|
||||
host: 'localhost'
|
||||
}
|
||||
}).listen(8001);
|
||||
}).listen(8006);
|
||||
|
||||
//
|
||||
// Target Http Server
|
||||
@ -45,7 +50,18 @@ http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
}).listen(9000);
|
||||
}).listen(9006);
|
||||
|
||||
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8001 '.yellow + 'with proxy table'.magenta.underline);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
|
||||
//
|
||||
// Target Http Forwarding Server
|
||||
//
|
||||
http.createServer(function (req, res) {
|
||||
util.puts('Receiving forward for: ' + req.url);
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully forwarded to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
}).listen(9007);
|
||||
|
||||
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8006 '.yellow + 'with forward proxy'.magenta.underline);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9006 '.yellow);
|
||||
util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9007 '.yellow);
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
forward-proxy.js: Example of proxying over HTTP with additional forward proxy
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,26 +27,17 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
//
|
||||
// Setup proxy server with forwarding
|
||||
//
|
||||
httpProxy.createServer(9000, 'localhost', {
|
||||
httpProxy.createServer({
|
||||
forward: {
|
||||
port: 9001,
|
||||
port: 9019,
|
||||
host: 'localhost'
|
||||
}
|
||||
}).listen(8003);
|
||||
|
||||
//
|
||||
// Target Http Server
|
||||
//
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
}).listen(9000);
|
||||
}).listen(8019);
|
||||
|
||||
//
|
||||
// Target Http Forwarding Server
|
||||
@ -56,8 +47,7 @@ http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully forwarded to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
}).listen(9001);
|
||||
}).listen(9019);
|
||||
|
||||
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8003 '.yellow + 'with forward proxy'.magenta.underline);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
|
||||
util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9001 '.yellow);
|
||||
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8019 '.yellow + 'with forward proxy'.magenta.underline);
|
||||
util.puts('http forward server '.blue + 'started '.green.bold + 'on port '.blue + '9019 '.yellow);
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
latent-proxy.js: Example of proxying over HTTP with latency
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,21 +27,19 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
//
|
||||
// Http Proxy Server with Latency
|
||||
//
|
||||
httpProxy.createServer(function (req, res, proxy) {
|
||||
var buffer = httpProxy.buffer(req);
|
||||
var proxy = httpProxy.createProxyServer();
|
||||
http.createServer(function (req, res) {
|
||||
setTimeout(function () {
|
||||
proxy.proxyRequest(req, res, {
|
||||
port: 9000,
|
||||
host: 'localhost',
|
||||
buffer: buffer
|
||||
proxy.web(req, res, {
|
||||
target: 'http://localhost:9008'
|
||||
});
|
||||
}, 200);
|
||||
}).listen(8002);
|
||||
}, 500);
|
||||
}).listen(8008);
|
||||
|
||||
//
|
||||
// Target Http Server
|
||||
@ -50,7 +48,7 @@ http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
}).listen(9000);
|
||||
}).listen(9008);
|
||||
|
||||
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with latency'.magenta.underline);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
|
||||
util.puts('http proxy server '.blue + 'started '.green.bold + 'on port '.blue + '8008 '.yellow + 'with latency'.magenta.underline);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9008 '.yellow);
|
||||
|
||||
26
examples/http/ntlm-authentication.js
Normal file
26
examples/http/ntlm-authentication.js
Normal file
@ -0,0 +1,26 @@
|
||||
var httpProxy = require('../../lib/http-proxy');
|
||||
var Agent = require('agentkeepalive');
|
||||
|
||||
var agent = new Agent({
|
||||
maxSockets: 100,
|
||||
keepAlive: true,
|
||||
maxFreeSockets: 10,
|
||||
keepAliveMsecs:1000,
|
||||
timeout: 60000,
|
||||
keepAliveTimeout: 30000 // free socket keepalive for 30 seconds
|
||||
});
|
||||
|
||||
var proxy = httpProxy.createProxy({ target: 'http://whatever.com', agent: agent });
|
||||
|
||||
//
|
||||
// Modify headers of the response before it gets sent
|
||||
// So that we handle the NLTM authentication response
|
||||
//
|
||||
proxy.on('proxyRes', function (proxyRes) {
|
||||
var key = 'www-authenticate';
|
||||
proxyRes.headers[key] = proxyRes.headers[key] && proxyRes.headers[key].split(',');
|
||||
});
|
||||
|
||||
require('http').createServer(function (req, res) {
|
||||
proxy.web(req, res);
|
||||
}).listen(3000);
|
||||
46
examples/http/proxy-http-to-https.js
Normal file
46
examples/http/proxy-http-to-https.js
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
proxy-http-to-https.js: Basic example of proxying over HTTP to a target HTTPS server
|
||||
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
var https = require('https'),
|
||||
http = require('http'),
|
||||
util = require('util'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
colors = require('colors'),
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
//
|
||||
// Create a HTTP Proxy server with a HTTPS target
|
||||
//
|
||||
httpProxy.createProxyServer({
|
||||
target: 'https://google.com',
|
||||
agent : https.globalAgent,
|
||||
headers: {
|
||||
host: 'google.com'
|
||||
}
|
||||
}).listen(8011);
|
||||
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8011'.yellow);
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
proxy-https-to-http.js: Basic example of proxying over HTTPS to a target HTTP server
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -25,27 +25,36 @@
|
||||
*/
|
||||
|
||||
var https = require('https'),
|
||||
http = require('http'),
|
||||
util = require('util'),
|
||||
http = require('http'),
|
||||
util = require('util'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
colors = require('colors'),
|
||||
httpProxy = require('../../lib/node-http-proxy'),
|
||||
helpers = require('../../test/helpers');
|
||||
|
||||
httpProxy = require('../../lib/http-proxy'),
|
||||
fixturesDir = path.join(__dirname, '..', '..', 'test', 'fixtures');
|
||||
|
||||
//
|
||||
// Create the target HTTPS server
|
||||
// Create the target HTTP server
|
||||
//
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('hello http over https\n');
|
||||
res.end();
|
||||
}).listen(8000);
|
||||
res.end();
|
||||
}).listen(9009);
|
||||
|
||||
//
|
||||
// Create the proxy server listening on port 443
|
||||
// Create the HTTPS proxy server listening on port 8000
|
||||
//
|
||||
httpProxy.createServer(8000, 'localhost', {
|
||||
https: helpers.https
|
||||
}).listen(8080);
|
||||
httpProxy.createServer({
|
||||
target: {
|
||||
host: 'localhost',
|
||||
port: 9009
|
||||
},
|
||||
ssl: {
|
||||
key: fs.readFileSync(path.join(fixturesDir, 'agent2-key.pem'), 'utf8'),
|
||||
cert: fs.readFileSync(path.join(fixturesDir, 'agent2-cert.pem'), 'utf8')
|
||||
}
|
||||
}).listen(8009);
|
||||
|
||||
util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8080'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow);
|
||||
util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8009'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9009 '.yellow);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
proxy-https-to-https.js: Basic example of proxying over HTTPS to a target HTTPS server
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,29 +27,33 @@
|
||||
var https = require('https'),
|
||||
http = require('http'),
|
||||
util = require('util'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
colors = require('colors'),
|
||||
httpProxy = require('../../lib/node-http-proxy'),
|
||||
helpers = require('../../test/helpers');
|
||||
|
||||
httpProxy = require('../../lib/http-proxy'),
|
||||
fixturesDir = path.join(__dirname, '..', '..', 'test', 'fixtures'),
|
||||
httpsOpts = {
|
||||
key: fs.readFileSync(path.join(fixturesDir, 'agent2-key.pem'), 'utf8'),
|
||||
cert: fs.readFileSync(path.join(fixturesDir, 'agent2-cert.pem'), 'utf8')
|
||||
};
|
||||
|
||||
//
|
||||
// Create the target HTTPS server
|
||||
// Create the target HTTPS server
|
||||
//
|
||||
https.createServer(helpers.https, function (req, res) {
|
||||
https.createServer(httpsOpts, function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('hello https\n');
|
||||
res.end();
|
||||
}).listen(8000);
|
||||
res.end();
|
||||
}).listen(9010);
|
||||
|
||||
//
|
||||
// Create the proxy server listening on port 443
|
||||
// Create the proxy server listening on port 8010
|
||||
//
|
||||
httpProxy.createServer(8000, 'localhost', {
|
||||
https: helpers.https,
|
||||
target: {
|
||||
https: true,
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
}).listen(8080);
|
||||
httpProxy.createServer({
|
||||
ssl: httpsOpts,
|
||||
target: 'https://localhost:9010',
|
||||
secure: false
|
||||
}).listen(8010);
|
||||
|
||||
util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8080'.yellow);
|
||||
util.puts('https server '.blue + 'started '.green.bold + 'on port '.blue + '8000 '.yellow);
|
||||
util.puts('https proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8010'.yellow);
|
||||
util.puts('https server '.blue + 'started '.green.bold + 'on port '.blue + '9010 '.yellow);
|
||||
|
||||
55
examples/http/reverse-proxy.js
Normal file
55
examples/http/reverse-proxy.js
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
reverse-proxy.js: Example of reverse proxying (with HTTPS support)
|
||||
Copyright (c) 2015 Alberto Pose <albertopose@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
var http = require('http'),
|
||||
net = require('net'),
|
||||
httpProxy = require('../../lib/http-proxy'),
|
||||
url = require('url'),
|
||||
util = require('util');
|
||||
|
||||
var proxy = httpProxy.createServer();
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
util.puts('Receiving reverse proxy request for:' + req.url);
|
||||
var parsedUrl = url.parse(req.url);
|
||||
var target = parsedUrl.protocol + '//' + parsedUrl.hostname;
|
||||
proxy.web(req, res, {target: target, secure: false});
|
||||
}).listen(8213);
|
||||
|
||||
server.on('connect', function (req, socket) {
|
||||
util.puts('Receiving reverse proxy request for:' + req.url);
|
||||
|
||||
var serverUrl = url.parse('https://' + req.url);
|
||||
|
||||
var srvSocket = net.connect(serverUrl.port, serverUrl.hostname, function() {
|
||||
socket.write('HTTP/1.1 200 Connection Established\r\n' +
|
||||
'Proxy-agent: Node-Proxy\r\n' +
|
||||
'\r\n');
|
||||
srvSocket.pipe(socket);
|
||||
socket.pipe(srvSocket);
|
||||
});
|
||||
});
|
||||
|
||||
// Test with:
|
||||
// curl -vv -x http://127.0.0.1:8213 https://www.google.com
|
||||
// curl -vv -x http://127.0.0.1:8213 http://www.google.com
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
gzip-middleware-proxytable.js: Basic example of `connect-gzip` middleware in node-http-proxy
|
||||
sse.js: Basic example of proxying over HTTP
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, Marak Squires, & Dominic Tarr.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,28 +27,41 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy'),
|
||||
SSE = require('sse');
|
||||
|
||||
//
|
||||
// Basic Http Proxy Server
|
||||
//
|
||||
httpProxy.createServer(
|
||||
require('connect-gzip').gzip({ matchType: /.?/ }),
|
||||
{
|
||||
router: {
|
||||
"localhost/fun": "localhost:9000"
|
||||
}
|
||||
}
|
||||
).listen(8000);
|
||||
var proxy = new httpProxy.createProxyServer();
|
||||
http.createServer(function (req, res) {
|
||||
proxy.web(req, res, {
|
||||
target: 'http://localhost:9003'
|
||||
});
|
||||
}).listen(8003);
|
||||
|
||||
//
|
||||
// Target Http Server
|
||||
//
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
var server = http.createServer(function(req, res) {
|
||||
res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
}).listen(9000);
|
||||
});
|
||||
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
|
||||
//
|
||||
// Use SSE
|
||||
//
|
||||
|
||||
var sse = new SSE(server, {path: '/'});
|
||||
sse.on('connection', function(client) {
|
||||
var count = 0;
|
||||
setInterval(function(){
|
||||
client.send('message #' + count++);
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
server.listen(9003);
|
||||
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8003'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9003 '.yellow);
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
standalone-proxy.js: Example of proxying over HTTP with a standalone HTTP server.
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,22 +27,19 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
//
|
||||
// Http Server with proxyRequest Handler and Latency
|
||||
//
|
||||
var proxy = new httpProxy.RoutingProxy();
|
||||
var proxy = new httpProxy.createProxyServer();
|
||||
http.createServer(function (req, res) {
|
||||
var buffer = httpProxy.buffer(req);
|
||||
setTimeout(function () {
|
||||
proxy.proxyRequest(req, res, {
|
||||
port: 9000,
|
||||
host: 'localhost',
|
||||
buffer: buffer
|
||||
proxy.web(req, res, {
|
||||
target: 'http://localhost:9002'
|
||||
});
|
||||
}, 200);
|
||||
}).listen(8004);
|
||||
}).listen(8002);
|
||||
|
||||
//
|
||||
// Target Http Server
|
||||
@ -51,7 +48,7 @@ http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
}).listen(9000);
|
||||
}).listen(9002);
|
||||
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8004 '.yellow + 'with proxyRequest handler'.cyan.underline + ' and latency'.magenta);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '8002 '.yellow + 'with proxy.web() handler'.cyan.underline + ' and latency'.magenta);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9002 '.yellow);
|
||||
|
||||
@ -1,87 +1,108 @@
|
||||
/*
|
||||
bodyDecoder-middleware.js: Basic example of `connect.bodyParser()` middleware in node-http-proxy
|
||||
|
||||
var Store = require('../helpers/store')
|
||||
, http = require('http')
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
var http = require('http'),
|
||||
connect = require('connect'),
|
||||
request = require('request'),
|
||||
colors = require('colors'),
|
||||
util = require('util'),
|
||||
queryString = require('querystring'),
|
||||
bodyParser = require('body-parser'),
|
||||
httpProxy = require('../../lib/http-proxy'),
|
||||
proxy = httpProxy.createProxyServer({});
|
||||
|
||||
|
||||
//restream parsed body before proxying
|
||||
proxy.on('proxyReq', function(proxyReq, req, res, options) {
|
||||
if (!req.body || !Object.keys(req.body).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var contentType = proxyReq.getHeader('Content-Type');
|
||||
var bodyData;
|
||||
|
||||
if (contentType === 'application/json') {
|
||||
bodyData = JSON.stringify(req.body);
|
||||
}
|
||||
|
||||
if (contentType === 'application/x-www-form-urlencoded') {
|
||||
bodyData = queryString.stringify(req.body);
|
||||
}
|
||||
|
||||
if (bodyData) {
|
||||
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
|
||||
proxyReq.write(bodyData);
|
||||
}
|
||||
});
|
||||
|
||||
http.createServer(new Store().handler()).listen(7531, function () {
|
||||
//try these commands:
|
||||
// get index:
|
||||
// curl localhost:7531
|
||||
// []
|
||||
//
|
||||
// get a doc:
|
||||
// curl localhost:7531/foo
|
||||
// {"error":"not_found"}
|
||||
//
|
||||
// post an doc:
|
||||
// curl -X POST localhost:7531/foo -d '{"content": "hello", "type": "greeting"}'
|
||||
// {"ok":true}
|
||||
//
|
||||
// get index (now, not empty)
|
||||
// curl localhost:7531
|
||||
// ["/foo"]
|
||||
//
|
||||
// get doc
|
||||
// curl localhost:7531/foo
|
||||
// {"content": "hello", "type": "greeting"}
|
||||
|
||||
//
|
||||
// now, suppose we wanted to direct all objects where type == "greeting" to a different store
|
||||
// than where type == "insult"
|
||||
// Basic Http Proxy Server
|
||||
//
|
||||
// we can use connect connect-bodyDecoder and some custom logic to send insults to another Store.
|
||||
|
||||
//insult server:
|
||||
|
||||
http.createServer(new Store().handler()).listen(2600, function () {
|
||||
|
||||
//greetings -> 7531, insults-> 2600
|
||||
|
||||
// now, start a proxy server.
|
||||
|
||||
var bodyParser = require('connect/lib/middleware/bodyParser')
|
||||
//don't worry about incoming contont type
|
||||
//bodyParser.parse[''] = JSON.parse
|
||||
|
||||
require('../../lib/node-http-proxy').createServer(
|
||||
//refactor the body parser and re-streamer into a separate package
|
||||
bodyParser(),
|
||||
//body parser absorbs the data and end events before passing control to the next
|
||||
// middleware. if we want to proxy it, we'll need to re-emit these events after
|
||||
//passing control to the middleware.
|
||||
require('connect-restreamer')(),
|
||||
function (req, res, proxy) {
|
||||
//if your posting an obect which contains type: "insult"
|
||||
//it will get redirected to port 2600.
|
||||
//normal get requests will go to 7531 nad will not return insults.
|
||||
var port = (req.body && req.body.type === 'insult' ? 2600 : 7531)
|
||||
proxy.proxyRequest(req, res, {host: 'localhost', port: port})
|
||||
}
|
||||
).listen(1337, function () {
|
||||
var request = require('request')
|
||||
//bodyParser needs content-type set to application/json
|
||||
//if we use request, it will set automatically if we use the 'json:' field.
|
||||
function post (greeting, type) {
|
||||
request.post({
|
||||
url: 'http://localhost:1337/' + greeting,
|
||||
json: {content: greeting, type: type || "greeting"}
|
||||
})
|
||||
}
|
||||
post("hello")
|
||||
post("g'day")
|
||||
post("kiora")
|
||||
post("houdy")
|
||||
post("java", "insult")
|
||||
|
||||
//now, the insult should have been proxied to 2600
|
||||
|
||||
//curl localhost:2600
|
||||
//["/java"]
|
||||
|
||||
//but the greetings will be sent to 7531
|
||||
|
||||
//curl localhost:7531
|
||||
//["/hello","/g%27day","/kiora","/houdy"]
|
||||
|
||||
var app = connect()
|
||||
.use(bodyParser.json())//json parser
|
||||
.use(bodyParser.urlencoded())//urlencoded parser
|
||||
.use(function(req, res){
|
||||
// modify body here,
|
||||
// eg: req.body = {a: 1}.
|
||||
console.log('proxy body:',req.body)
|
||||
proxy.web(req, res, {
|
||||
target: 'http://127.0.0.1:9013'
|
||||
})
|
||||
});
|
||||
|
||||
http.createServer(app).listen(8013, function(){
|
||||
console.log('proxy linsten 8013');
|
||||
});
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Target Http Server
|
||||
//
|
||||
var app1 = connect()
|
||||
.use(bodyParser.json())
|
||||
.use(function(req, res){
|
||||
console.log('app1:',req.body)
|
||||
res.end('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
});
|
||||
http.createServer(app1).listen(9013, function(){
|
||||
//request to 8013 to proxy
|
||||
request.post({//
|
||||
url: 'http://127.0.0.1:8013',
|
||||
json: {content: 123, type: "greeting from json request"}
|
||||
},function(err, res,data){
|
||||
console.log('return for json request:' ,err, data)
|
||||
})
|
||||
})
|
||||
|
||||
// application/x-www-form-urlencoded request
|
||||
request.post({//
|
||||
url: 'http://127.0.0.1:8013',
|
||||
form: {content: 123, type: "greeting from urlencoded request"}
|
||||
},function(err, res,data){
|
||||
console.log('return for urlencoded request:' ,err, data)
|
||||
})
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
gzip-middleware.js: Basic example of `connect-gzip` middleware in node-http-proxy
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, Marak Squires, & Dominic Tarr.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,15 +27,30 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
connect = require('connect'),
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
//
|
||||
// Basic Connect App
|
||||
//
|
||||
connect.createServer(
|
||||
connect.compress({
|
||||
// Pass to connect.compress() the options
|
||||
// that you need, just for show the example
|
||||
// we use threshold to 1
|
||||
threshold: 1
|
||||
}),
|
||||
function (req, res) {
|
||||
proxy.web(req, res);
|
||||
}
|
||||
).listen(8012);
|
||||
|
||||
//
|
||||
// Basic Http Proxy Server
|
||||
//
|
||||
httpProxy.createServer(
|
||||
require('connect-gzip').gzip({ matchType: /.?/ }),
|
||||
9000, 'localhost'
|
||||
).listen(8000);
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://localhost:9012'
|
||||
});
|
||||
|
||||
//
|
||||
// Target Http Server
|
||||
@ -44,7 +59,7 @@ http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
}).listen(9000);
|
||||
}).listen(9012);
|
||||
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8012'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9012 '.yellow);
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
var Store = require('../helpers/store')
|
||||
, http = require('http')
|
||||
|
||||
//
|
||||
// jsonp is a handy technique for getting around the limitations of the same-origin policy.
|
||||
// (http://en.wikipedia.org/wiki/Same_origin_policy)
|
||||
//
|
||||
// normally, to dynamically update a page you use an XmlHttpRequest. this has flakey support
|
||||
// is some browsers and is restricted by the same origin policy. you cannot perform XHR requests to
|
||||
// someone else's server. one way around this would be to proxy requests to all the servers you want
|
||||
// to xhr to, and your core server - so that everything has the same port and host.
|
||||
//
|
||||
// another way, is to turn json into javascript. (which is exempt from the same origin policy)
|
||||
// this is done by wrapping the json object in a function call, and then including a script tag.
|
||||
//
|
||||
// here we're proxing our own JSON returning server, but we could proxy any server on the internet,
|
||||
// and our client side app would be slurping down JSONP from anywhere.
|
||||
//
|
||||
// curl localhost:1337/whatever?callback=alert
|
||||
// alert([]) //which is valid javascript!
|
||||
//
|
||||
// also see http://en.wikipedia.org/wiki/JSONP#JSONP
|
||||
//
|
||||
|
||||
http.createServer(new Store().handler()).listen(7531)
|
||||
|
||||
require('../../lib/node-http-proxy').createServer(
|
||||
require('connect-jsonp')(true),
|
||||
'localhost', 7531
|
||||
).listen(1337)
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
modifyBody-middleware.js: Example of middleware which modifies response
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, Marak Squires, & Dominic Tarr.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,31 +27,43 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
connect = require('connect'),
|
||||
app = connect(),
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
//
|
||||
// Basic Connect App
|
||||
//
|
||||
app.use(function (req, res, next) {
|
||||
var _write = res.write;
|
||||
|
||||
res.write = function (data) {
|
||||
_write.call(res, data.toString().replace("Ruby", "http-party"));
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(function (req, res) {
|
||||
proxy.web(req, res)
|
||||
});
|
||||
|
||||
http.createServer(app).listen(8013);
|
||||
|
||||
//
|
||||
// Basic Http Proxy Server
|
||||
//
|
||||
httpProxy.createServer(
|
||||
function (req, res, next) {
|
||||
var _write = res.write;
|
||||
|
||||
res.write = function (data) {
|
||||
_write.call(res, data.toString().replace("Ruby", "nodejitsu"));
|
||||
}
|
||||
next();
|
||||
},
|
||||
9000, 'localhost'
|
||||
).listen(8000);
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://localhost:9013'
|
||||
});
|
||||
|
||||
//
|
||||
// Target Http Server
|
||||
//
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end('Hello, I know Ruby\n');
|
||||
}).listen(9000);
|
||||
res.end('Hello, I love Ruby\n');
|
||||
}).listen(9013);
|
||||
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8013'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9013 '.yellow);
|
||||
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
var util = require('util'),
|
||||
colors = require('colors'),
|
||||
http = require('http'),
|
||||
httpProxy = require('../../lib/node-http-proxy'),
|
||||
Store = require('../helpers/store')
|
||||
|
||||
http.createServer(new Store().handler()).listen(7531)
|
||||
|
||||
// Now we set up our proxy.
|
||||
httpProxy.createServer(
|
||||
// This is where our middlewares go, with any options desired - in this case,
|
||||
// the list of routes/URLs and their destinations.
|
||||
require('proxy-by-url')({
|
||||
'/store': { port: 7531, host: 'localhost' },
|
||||
'/': { port: 9000, host: 'localhost' }
|
||||
})
|
||||
).listen(8000);
|
||||
|
||||
//
|
||||
// Target Http Server (to listen for requests on 'localhost')
|
||||
//
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write('request successfully proxied to: ' + req.url + '\n' + JSON.stringify(req.headers, true, 2));
|
||||
res.end();
|
||||
}).listen(9000);
|
||||
|
||||
// And finally, some colored startup output.
|
||||
util.puts('http proxy server'.blue + ' started '.green.bold + 'on port '.blue + '8000'.yellow);
|
||||
util.puts('http server '.blue + 'started '.green.bold + 'on port '.blue + '9000 '.yellow);
|
||||
@ -1,12 +1,14 @@
|
||||
{
|
||||
"name": "http-proxy-examples",
|
||||
"description": "packages required to run the examples",
|
||||
"name": "http-proxy-examples",
|
||||
"description": "packages required to run the examples",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"connect": "1.6",
|
||||
"connect-gzip": "0.1",
|
||||
"connect-jsonp": "0.0.5",
|
||||
"connect-restreamer": "1",
|
||||
"proxy-by-url": ">= 0.0.1"
|
||||
"agentkeepalive": "^4.0.0",
|
||||
"colors": "~1.3.0",
|
||||
"connect-restreamer": "~1.0.0",
|
||||
"request": "~2.88.0",
|
||||
"socket.io": "~0.9.16",
|
||||
"socket.io-client": "~0.9.16",
|
||||
"sse": "0.0.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
standalone-websocket-proxy.js: Example of proxying websockets over HTTP with a standalone HTTP server.
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,7 +27,7 @@
|
||||
var util = require('util'),
|
||||
http = require('http'),
|
||||
colors = require('colors'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
try {
|
||||
var io = require('socket.io'),
|
||||
@ -43,7 +43,7 @@ catch (ex) {
|
||||
// Create the target HTTP server and setup
|
||||
// socket.io on it.
|
||||
//
|
||||
var server = io.listen(8080);
|
||||
var server = io.listen(9016);
|
||||
server.sockets.on('connection', function (client) {
|
||||
util.debug('Got websocket connection');
|
||||
|
||||
@ -57,36 +57,35 @@ server.sockets.on('connection', function (client) {
|
||||
//
|
||||
// Setup our server to proxy standard HTTP requests
|
||||
//
|
||||
var proxy = new httpProxy.HttpProxy({
|
||||
var proxy = new httpProxy.createProxyServer({
|
||||
target: {
|
||||
host: 'localhost',
|
||||
port: 8080
|
||||
host: 'localhost',
|
||||
port: 9016
|
||||
}
|
||||
});
|
||||
|
||||
var proxyServer = http.createServer(function (req, res) {
|
||||
proxy.proxyRequest(req, res);
|
||||
proxy.web(req, res);
|
||||
});
|
||||
|
||||
//
|
||||
// Listen to the `upgrade` event and proxy the
|
||||
// Listen to the `upgrade` event and proxy the
|
||||
// WebSocket requests as well.
|
||||
//
|
||||
proxyServer.on('upgrade', function (req, socket, head) {
|
||||
var buffer = httpProxy.buffer(socket);
|
||||
|
||||
setTimeout(function () {
|
||||
proxy.proxyWebSocketRequest(req, socket, head, buffer);
|
||||
proxy.ws(req, socket, head);
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
proxyServer.listen(8081);
|
||||
proxyServer.listen(8016);
|
||||
|
||||
//
|
||||
// Setup the socket.io client against our proxy
|
||||
//
|
||||
var ws = client.connect('ws://localhost:8081');
|
||||
var ws = client.connect('ws://localhost:8016');
|
||||
|
||||
ws.on('message', function (msg) {
|
||||
util.debug('Got message: ' + msg);
|
||||
ws.send('I am the client');
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
standalone-websocket-proxy.js: Example of proxying websockets over HTTP with a standalone HTTP server.
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,7 +27,7 @@
|
||||
var util = require('util'),
|
||||
http = require('http'),
|
||||
colors = require('colors'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
try {
|
||||
var io = require('socket.io'),
|
||||
@ -43,7 +43,7 @@ catch (ex) {
|
||||
// Create the target HTTP server and setup
|
||||
// socket.io on it.
|
||||
//
|
||||
var server = io.listen(8080);
|
||||
var server = io.listen(9015);
|
||||
server.sockets.on('connection', function (client) {
|
||||
util.debug('Got websocket connection');
|
||||
|
||||
@ -57,31 +57,32 @@ server.sockets.on('connection', function (client) {
|
||||
//
|
||||
// Setup our server to proxy standard HTTP requests
|
||||
//
|
||||
var proxy = new httpProxy.HttpProxy({
|
||||
var proxy = new httpProxy.createProxyServer({
|
||||
target: {
|
||||
host: 'localhost',
|
||||
port: 8080
|
||||
host: 'localhost',
|
||||
port: 9015
|
||||
}
|
||||
});
|
||||
var proxyServer = http.createServer(function (req, res) {
|
||||
proxy.proxyRequest(req, res);
|
||||
proxy.web(req, res);
|
||||
});
|
||||
|
||||
//
|
||||
// Listen to the `upgrade` event and proxy the
|
||||
// Listen to the `upgrade` event and proxy the
|
||||
// WebSocket requests as well.
|
||||
//
|
||||
proxyServer.on('upgrade', function (req, socket, head) {
|
||||
proxy.proxyWebSocketRequest(req, socket, head);
|
||||
proxy.ws(req, socket, head);
|
||||
});
|
||||
|
||||
proxyServer.listen(8081);
|
||||
proxyServer.listen(8015);
|
||||
|
||||
//
|
||||
// Setup the socket.io client against our proxy
|
||||
//
|
||||
var ws = client.connect('ws://localhost:8081');
|
||||
var ws = client.connect('ws://localhost:8015');
|
||||
|
||||
ws.on('message', function (msg) {
|
||||
util.debug('Got message: ' + msg);
|
||||
ws.send('I am the client');
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/*
|
||||
web-socket-proxy.js: Example of proxying over HTTP and WebSockets.
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Fedor Indutny, & Marak Squires.
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@ -27,7 +27,7 @@
|
||||
var util = require('util'),
|
||||
http = require('http'),
|
||||
colors = require('colors'),
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
httpProxy = require('../../lib/http-proxy');
|
||||
|
||||
try {
|
||||
var io = require('socket.io'),
|
||||
@ -43,7 +43,7 @@ catch (ex) {
|
||||
// Create the target HTTP server and setup
|
||||
// socket.io on it.
|
||||
//
|
||||
var server = io.listen(8080);
|
||||
var server = io.listen(9014);
|
||||
server.sockets.on('connection', function (client) {
|
||||
util.debug('Got websocket connection');
|
||||
|
||||
@ -57,13 +57,14 @@ server.sockets.on('connection', function (client) {
|
||||
//
|
||||
// Create a proxy server with node-http-proxy
|
||||
//
|
||||
httpProxy.createServer(8080, 'localhost').listen(8081);
|
||||
httpProxy.createServer({ target: 'ws://localhost:9014', ws: true }).listen(8014);
|
||||
|
||||
//
|
||||
// Setup the socket.io client against our proxy
|
||||
//
|
||||
var ws = client.connect('ws://localhost:8081');
|
||||
var ws = client.connect('ws://localhost:8014');
|
||||
|
||||
ws.on('message', function (msg) {
|
||||
util.debug('Got message: ' + msg);
|
||||
ws.send('I am the client');
|
||||
});
|
||||
|
||||
13
index.js
Normal file
13
index.js
Normal file
@ -0,0 +1,13 @@
|
||||
/*!
|
||||
* Caron dimonio, con occhi di bragia
|
||||
* loro accennando, tutte le raccoglie;
|
||||
* batte col remo qualunque s’adagia
|
||||
*
|
||||
* Charon the demon, with the eyes of glede,
|
||||
* Beckoning to them, collects them all together,
|
||||
* Beats with his oar whoever lags behind
|
||||
*
|
||||
* Dante - The Divine Comedy (Canto III)
|
||||
*/
|
||||
|
||||
module.exports = require('./lib/http-proxy');
|
||||
66
lib/http-proxy.js
Normal file
66
lib/http-proxy.js
Normal file
@ -0,0 +1,66 @@
|
||||
// Use explicit /index.js to help browserify negociation in require '/lib/http-proxy' (!)
|
||||
var ProxyServer = require('./http-proxy/index.js').Server;
|
||||
|
||||
|
||||
/**
|
||||
* Creates the proxy server.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* httpProxy.createProxyServer({ .. }, 8000)
|
||||
* // => '{ web: [Function], ws: [Function] ... }'
|
||||
*
|
||||
* @param {Object} Options Config object passed to the proxy
|
||||
*
|
||||
* @return {Object} Proxy Proxy object with handlers for `ws` and `web` requests
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
|
||||
function createProxyServer(options) {
|
||||
/*
|
||||
* `options` is needed and it must have the following layout:
|
||||
*
|
||||
* {
|
||||
* target : <url string to be parsed with the url module>
|
||||
* forward: <url string to be parsed with the url module>
|
||||
* agent : <object to be passed to http(s).request>
|
||||
* ssl : <object to be passed to https.createServer()>
|
||||
* ws : <true/false, if you want to proxy websockets>
|
||||
* xfwd : <true/false, adds x-forward headers>
|
||||
* secure : <true/false, verify SSL certificate>
|
||||
* toProxy: <true/false, explicitly specify if we are proxying to another proxy>
|
||||
* prependPath: <true/false, Default: true - specify whether you want to prepend the target's path to the proxy path>
|
||||
* ignorePath: <true/false, Default: false - specify whether you want to ignore the proxy path of the incoming request>
|
||||
* localAddress : <Local interface string to bind for outgoing connections>
|
||||
* changeOrigin: <true/false, Default: false - changes the origin of the host header to the target URL>
|
||||
* preserveHeaderKeyCase: <true/false, Default: false - specify whether you want to keep letter case of response header key >
|
||||
* auth : Basic authentication i.e. 'user:password' to compute an Authorization header.
|
||||
* hostRewrite: rewrites the location hostname on (201/301/302/307/308) redirects, Default: null.
|
||||
* autoRewrite: rewrites the location host/port on (201/301/302/307/308) redirects based on requested host/port. Default: false.
|
||||
* protocolRewrite: rewrites the location protocol on (201/301/302/307/308) redirects to 'http' or 'https'. Default: null.
|
||||
* }
|
||||
*
|
||||
* NOTE: `options.ws` and `options.ssl` are optional.
|
||||
* `options.target and `options.forward` cannot be
|
||||
* both missing
|
||||
* }
|
||||
*/
|
||||
|
||||
return new ProxyServer(options);
|
||||
}
|
||||
|
||||
|
||||
ProxyServer.createProxyServer = createProxyServer;
|
||||
ProxyServer.createServer = createProxyServer;
|
||||
ProxyServer.createProxy = createProxyServer;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Export the proxy "Server" as the main export.
|
||||
*/
|
||||
module.exports = ProxyServer;
|
||||
|
||||
248
lib/http-proxy/common.js
Normal file
248
lib/http-proxy/common.js
Normal file
@ -0,0 +1,248 @@
|
||||
var common = exports,
|
||||
url = require('url'),
|
||||
extend = require('util')._extend,
|
||||
required = require('requires-port');
|
||||
|
||||
var upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i,
|
||||
isSSL = /^https|wss/;
|
||||
|
||||
/**
|
||||
* Simple Regex for testing if protocol is https
|
||||
*/
|
||||
common.isSSL = isSSL;
|
||||
/**
|
||||
* Copies the right headers from `options` and `req` to
|
||||
* `outgoing` which is then used to fire the proxied
|
||||
* request.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* common.setupOutgoing(outgoing, options, req)
|
||||
* // => { host: ..., hostname: ...}
|
||||
*
|
||||
* @param {Object} Outgoing Base object to be filled with required properties
|
||||
* @param {Object} Options Config object passed to the proxy
|
||||
* @param {ClientRequest} Req Request Object
|
||||
* @param {String} Forward String to select forward or target
|
||||
*
|
||||
* @return {Object} Outgoing Object with all required properties set
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
common.setupOutgoing = function(outgoing, options, req, forward) {
|
||||
outgoing.port = options[forward || 'target'].port ||
|
||||
(isSSL.test(options[forward || 'target'].protocol) ? 443 : 80);
|
||||
|
||||
['host', 'hostname', 'socketPath', 'pfx', 'key',
|
||||
'passphrase', 'cert', 'ca', 'ciphers', 'secureProtocol'].forEach(
|
||||
function(e) { outgoing[e] = options[forward || 'target'][e]; }
|
||||
);
|
||||
|
||||
outgoing.method = options.method || req.method;
|
||||
outgoing.headers = extend({}, req.headers);
|
||||
|
||||
if (options.headers){
|
||||
extend(outgoing.headers, options.headers);
|
||||
}
|
||||
|
||||
if (options.auth) {
|
||||
outgoing.auth = options.auth;
|
||||
}
|
||||
|
||||
if (options.ca) {
|
||||
outgoing.ca = options.ca;
|
||||
}
|
||||
|
||||
if (isSSL.test(options[forward || 'target'].protocol)) {
|
||||
outgoing.rejectUnauthorized = (typeof options.secure === "undefined") ? true : options.secure;
|
||||
}
|
||||
|
||||
|
||||
outgoing.agent = options.agent || false;
|
||||
outgoing.localAddress = options.localAddress;
|
||||
|
||||
//
|
||||
// Remark: If we are false and not upgrading, set the connection: close. This is the right thing to do
|
||||
// as node core doesn't handle this COMPLETELY properly yet.
|
||||
//
|
||||
if (!outgoing.agent) {
|
||||
outgoing.headers = outgoing.headers || {};
|
||||
if (typeof outgoing.headers.connection !== 'string'
|
||||
|| !upgradeHeader.test(outgoing.headers.connection)
|
||||
) { outgoing.headers.connection = 'close'; }
|
||||
}
|
||||
|
||||
|
||||
// the final path is target path + relative path requested by user:
|
||||
var target = options[forward || 'target'];
|
||||
var targetPath = target && options.prependPath !== false
|
||||
? (target.path || '')
|
||||
: '';
|
||||
|
||||
//
|
||||
// Remark: Can we somehow not use url.parse as a perf optimization?
|
||||
//
|
||||
var outgoingPath = !options.toProxy
|
||||
? (url.parse(req.url).path || '')
|
||||
: req.url;
|
||||
|
||||
//
|
||||
// Remark: ignorePath will just straight up ignore whatever the request's
|
||||
// path is. This can be labeled as FOOT-GUN material if you do not know what
|
||||
// you are doing and are using conflicting options.
|
||||
//
|
||||
outgoingPath = !options.ignorePath ? outgoingPath : '';
|
||||
|
||||
outgoing.path = common.urlJoin(targetPath, outgoingPath);
|
||||
|
||||
if (options.changeOrigin) {
|
||||
outgoing.headers.host =
|
||||
required(outgoing.port, options[forward || 'target'].protocol) && !hasPort(outgoing.host)
|
||||
? outgoing.host + ':' + outgoing.port
|
||||
: outgoing.host;
|
||||
}
|
||||
return outgoing;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the proper configuration for sockets,
|
||||
* set no delay and set keep alive, also set
|
||||
* the timeout to 0.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* common.setupSocket(socket)
|
||||
* // => Socket
|
||||
*
|
||||
* @param {Socket} Socket instance to setup
|
||||
*
|
||||
* @return {Socket} Return the configured socket.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
common.setupSocket = function(socket) {
|
||||
socket.setTimeout(0);
|
||||
socket.setNoDelay(true);
|
||||
|
||||
socket.setKeepAlive(true, 0);
|
||||
|
||||
return socket;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the port number from the host. Or guess it based on the connection type.
|
||||
*
|
||||
* @param {Request} req Incoming HTTP request.
|
||||
*
|
||||
* @return {String} The port number.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
common.getPort = function(req) {
|
||||
var res = req.headers.host ? req.headers.host.match(/:(\d+)/) : '';
|
||||
|
||||
return res ?
|
||||
res[1] :
|
||||
common.hasEncryptedConnection(req) ? '443' : '80';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the request has an encrypted connection.
|
||||
*
|
||||
* @param {Request} req Incoming HTTP request.
|
||||
*
|
||||
* @return {Boolean} Whether the connection is encrypted or not.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
common.hasEncryptedConnection = function(req) {
|
||||
return Boolean(req.connection.encrypted || req.connection.pair);
|
||||
};
|
||||
|
||||
/**
|
||||
* OS-agnostic join (doesn't break on URLs like path.join does on Windows)>
|
||||
*
|
||||
* @return {String} The generated path.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
common.urlJoin = function() {
|
||||
//
|
||||
// We do not want to mess with the query string. All we want to touch is the path.
|
||||
//
|
||||
var args = Array.prototype.slice.call(arguments),
|
||||
lastIndex = args.length - 1,
|
||||
last = args[lastIndex],
|
||||
lastSegs = last.split('?'),
|
||||
retSegs;
|
||||
|
||||
args[lastIndex] = lastSegs.shift();
|
||||
|
||||
//
|
||||
// Join all strings, but remove empty strings so we don't get extra slashes from
|
||||
// joining e.g. ['', 'am']
|
||||
//
|
||||
retSegs = [
|
||||
args.filter(Boolean).join('/')
|
||||
.replace(/\/+/g, '/')
|
||||
.replace('http:/', 'http://')
|
||||
.replace('https:/', 'https://')
|
||||
];
|
||||
|
||||
// Only join the query string if it exists so we don't have trailing a '?'
|
||||
// on every request
|
||||
|
||||
// Handle case where there could be multiple ? in the URL.
|
||||
retSegs.push.apply(retSegs, lastSegs);
|
||||
|
||||
return retSegs.join('?')
|
||||
};
|
||||
|
||||
/**
|
||||
* Rewrites or removes the domain of a cookie header
|
||||
*
|
||||
* @param {String|Array} Header
|
||||
* @param {Object} Config, mapping of domain to rewritten domain.
|
||||
* '*' key to match any domain, null value to remove the domain.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
common.rewriteCookieProperty = function rewriteCookieProperty(header, config, property) {
|
||||
if (Array.isArray(header)) {
|
||||
return header.map(function (headerElement) {
|
||||
return rewriteCookieProperty(headerElement, config, property);
|
||||
});
|
||||
}
|
||||
return header.replace(new RegExp("(;\\s*" + property + "=)([^;]+)", 'i'), function(match, prefix, previousValue) {
|
||||
var newValue;
|
||||
if (previousValue in config) {
|
||||
newValue = config[previousValue];
|
||||
} else if ('*' in config) {
|
||||
newValue = config['*'];
|
||||
} else {
|
||||
//no match, return previous value
|
||||
return match;
|
||||
}
|
||||
if (newValue) {
|
||||
//replace value
|
||||
return prefix + newValue;
|
||||
} else {
|
||||
//remove value
|
||||
return '';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Check the host and see if it potentially has a port in it (keep it simple)
|
||||
*
|
||||
* @returns {Boolean} Whether we have one or not
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
function hasPort(host) {
|
||||
return !!~host.indexOf(':');
|
||||
};
|
||||
185
lib/http-proxy/index.js
Normal file
185
lib/http-proxy/index.js
Normal file
@ -0,0 +1,185 @@
|
||||
var httpProxy = module.exports,
|
||||
extend = require('util')._extend,
|
||||
parse_url = require('url').parse,
|
||||
EE3 = require('eventemitter3'),
|
||||
http = require('http'),
|
||||
https = require('https'),
|
||||
web = require('./passes/web-incoming'),
|
||||
ws = require('./passes/ws-incoming');
|
||||
|
||||
httpProxy.Server = ProxyServer;
|
||||
|
||||
/**
|
||||
* Returns a function that creates the loader for
|
||||
* either `ws` or `web`'s passes.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* httpProxy.createRightProxy('ws')
|
||||
* // => [Function]
|
||||
*
|
||||
* @param {String} Type Either 'ws' or 'web'
|
||||
*
|
||||
* @return {Function} Loader Function that when called returns an iterator for the right passes
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function createRightProxy(type) {
|
||||
|
||||
return function(options) {
|
||||
return function(req, res /*, [head], [opts] */) {
|
||||
var passes = (type === 'ws') ? this.wsPasses : this.webPasses,
|
||||
args = [].slice.call(arguments),
|
||||
cntr = args.length - 1,
|
||||
head, cbl;
|
||||
|
||||
/* optional args parse begin */
|
||||
if(typeof args[cntr] === 'function') {
|
||||
cbl = args[cntr];
|
||||
|
||||
cntr--;
|
||||
}
|
||||
|
||||
var requestOptions = options;
|
||||
if(
|
||||
!(args[cntr] instanceof Buffer) &&
|
||||
args[cntr] !== res
|
||||
) {
|
||||
//Copy global options
|
||||
requestOptions = extend({}, options);
|
||||
//Overwrite with request options
|
||||
extend(requestOptions, args[cntr]);
|
||||
|
||||
cntr--;
|
||||
}
|
||||
|
||||
if(args[cntr] instanceof Buffer) {
|
||||
head = args[cntr];
|
||||
}
|
||||
|
||||
/* optional args parse end */
|
||||
|
||||
['target', 'forward'].forEach(function(e) {
|
||||
if (typeof requestOptions[e] === 'string')
|
||||
requestOptions[e] = parse_url(requestOptions[e]);
|
||||
});
|
||||
|
||||
if (!requestOptions.target && !requestOptions.forward) {
|
||||
return this.emit('error', new Error('Must provide a proper URL as target'));
|
||||
}
|
||||
|
||||
for(var i=0; i < passes.length; i++) {
|
||||
/**
|
||||
* Call of passes functions
|
||||
* pass(req, res, options, head)
|
||||
*
|
||||
* In WebSockets case the `res` variable
|
||||
* refer to the connection socket
|
||||
* pass(req, socket, options, head)
|
||||
*/
|
||||
if(passes[i](req, res, requestOptions, head, this, cbl)) { // passes can return a truthy value to halt the loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
httpProxy.createRightProxy = createRightProxy;
|
||||
|
||||
function ProxyServer(options) {
|
||||
EE3.call(this);
|
||||
|
||||
options = options || {};
|
||||
options.prependPath = options.prependPath === false ? false : true;
|
||||
|
||||
this.web = this.proxyRequest = createRightProxy('web')(options);
|
||||
this.ws = this.proxyWebsocketRequest = createRightProxy('ws')(options);
|
||||
this.options = options;
|
||||
|
||||
this.webPasses = Object.keys(web).map(function(pass) {
|
||||
return web[pass];
|
||||
});
|
||||
|
||||
this.wsPasses = Object.keys(ws).map(function(pass) {
|
||||
return ws[pass];
|
||||
});
|
||||
|
||||
this.on('error', this.onError, this);
|
||||
|
||||
}
|
||||
|
||||
require('util').inherits(ProxyServer, EE3);
|
||||
|
||||
ProxyServer.prototype.onError = function (err) {
|
||||
//
|
||||
// Remark: Replicate node core behavior using EE3
|
||||
// so we force people to handle their own errors
|
||||
//
|
||||
if(this.listeners('error').length === 1) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
ProxyServer.prototype.listen = function(port, hostname) {
|
||||
var self = this,
|
||||
closure = function(req, res) { self.web(req, res); };
|
||||
|
||||
this._server = this.options.ssl ?
|
||||
https.createServer(this.options.ssl, closure) :
|
||||
http.createServer(closure);
|
||||
|
||||
if(this.options.ws) {
|
||||
this._server.on('upgrade', function(req, socket, head) { self.ws(req, socket, head); });
|
||||
}
|
||||
|
||||
this._server.listen(port, hostname);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
ProxyServer.prototype.close = function(callback) {
|
||||
var self = this;
|
||||
if (this._server) {
|
||||
this._server.close(done);
|
||||
}
|
||||
|
||||
// Wrap callback to nullify server after all open connections are closed.
|
||||
function done() {
|
||||
self._server = null;
|
||||
if (callback) {
|
||||
callback.apply(null, arguments);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
ProxyServer.prototype.before = function(type, passName, callback) {
|
||||
if (type !== 'ws' && type !== 'web') {
|
||||
throw new Error('type must be `web` or `ws`');
|
||||
}
|
||||
var passes = (type === 'ws') ? this.wsPasses : this.webPasses,
|
||||
i = false;
|
||||
|
||||
passes.forEach(function(v, idx) {
|
||||
if(v.name === passName) i = idx;
|
||||
})
|
||||
|
||||
if(i === false) throw new Error('No such pass');
|
||||
|
||||
passes.splice(i, 0, callback);
|
||||
};
|
||||
ProxyServer.prototype.after = function(type, passName, callback) {
|
||||
if (type !== 'ws' && type !== 'web') {
|
||||
throw new Error('type must be `web` or `ws`');
|
||||
}
|
||||
var passes = (type === 'ws') ? this.wsPasses : this.webPasses,
|
||||
i = false;
|
||||
|
||||
passes.forEach(function(v, idx) {
|
||||
if(v.name === passName) i = idx;
|
||||
})
|
||||
|
||||
if(i === false) throw new Error('No such pass');
|
||||
|
||||
passes.splice(i++, 0, callback);
|
||||
};
|
||||
194
lib/http-proxy/passes/web-incoming.js
Normal file
194
lib/http-proxy/passes/web-incoming.js
Normal file
@ -0,0 +1,194 @@
|
||||
var httpNative = require('http'),
|
||||
httpsNative = require('https'),
|
||||
web_o = require('./web-outgoing'),
|
||||
common = require('../common'),
|
||||
followRedirects = require('follow-redirects');
|
||||
|
||||
web_o = Object.keys(web_o).map(function(pass) {
|
||||
return web_o[pass];
|
||||
});
|
||||
|
||||
var nativeAgents = { http: httpNative, https: httpsNative };
|
||||
|
||||
/*!
|
||||
* Array of passes.
|
||||
*
|
||||
* A `pass` is just a function that is executed on `req, res, options`
|
||||
* so that you can easily add new checks while still keeping the base
|
||||
* flexible.
|
||||
*/
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* Sets `content-length` to '0' if request is of DELETE type.
|
||||
*
|
||||
* @param {ClientRequest} Req Request object
|
||||
* @param {IncomingMessage} Res Response object
|
||||
* @param {Object} Options Config object passed to the proxy
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
deleteLength: function deleteLength(req, res, options) {
|
||||
if((req.method === 'DELETE' || req.method === 'OPTIONS')
|
||||
&& !req.headers['content-length']) {
|
||||
req.headers['content-length'] = '0';
|
||||
delete req.headers['transfer-encoding'];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets timeout in request socket if it was specified in options.
|
||||
*
|
||||
* @param {ClientRequest} Req Request object
|
||||
* @param {IncomingMessage} Res Response object
|
||||
* @param {Object} Options Config object passed to the proxy
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
timeout: function timeout(req, res, options) {
|
||||
if(options.timeout) {
|
||||
req.socket.setTimeout(options.timeout);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets `x-forwarded-*` headers if specified in config.
|
||||
*
|
||||
* @param {ClientRequest} Req Request object
|
||||
* @param {IncomingMessage} Res Response object
|
||||
* @param {Object} Options Config object passed to the proxy
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
XHeaders: function XHeaders(req, res, options) {
|
||||
if(!options.xfwd) return;
|
||||
|
||||
var encrypted = req.isSpdy || common.hasEncryptedConnection(req);
|
||||
var values = {
|
||||
for : req.connection.remoteAddress || req.socket.remoteAddress,
|
||||
port : common.getPort(req),
|
||||
proto: encrypted ? 'https' : 'http'
|
||||
};
|
||||
|
||||
['for', 'port', 'proto'].forEach(function(header) {
|
||||
req.headers['x-forwarded-' + header] =
|
||||
(req.headers['x-forwarded-' + header] || '') +
|
||||
(req.headers['x-forwarded-' + header] ? ',' : '') +
|
||||
values[header];
|
||||
});
|
||||
|
||||
req.headers['x-forwarded-host'] = req.headers['x-forwarded-host'] || req.headers['host'] || '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Does the actual proxying. If `forward` is enabled fires up
|
||||
* a ForwardStream, same happens for ProxyStream. The request
|
||||
* just dies otherwise.
|
||||
*
|
||||
* @param {ClientRequest} Req Request object
|
||||
* @param {IncomingMessage} Res Response object
|
||||
* @param {Object} Options Config object passed to the proxy
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
stream: function stream(req, res, options, _, server, clb) {
|
||||
|
||||
// And we begin!
|
||||
server.emit('start', req, res, options.target || options.forward);
|
||||
|
||||
var agents = options.followRedirects ? followRedirects : nativeAgents;
|
||||
var http = agents.http;
|
||||
var https = agents.https;
|
||||
|
||||
if(options.forward) {
|
||||
// If forward enable, so just pipe the request
|
||||
var forwardReq = (options.forward.protocol === 'https:' ? https : http).request(
|
||||
common.setupOutgoing(options.ssl || {}, options, req, 'forward')
|
||||
);
|
||||
|
||||
// error handler (e.g. ECONNRESET, ECONNREFUSED)
|
||||
// Handle errors on incoming request as well as it makes sense to
|
||||
var forwardError = createErrorHandler(forwardReq, options.forward);
|
||||
req.on('error', forwardError);
|
||||
forwardReq.on('error', forwardError);
|
||||
|
||||
(options.buffer || req).pipe(forwardReq);
|
||||
if(!options.target) { return res.end(); }
|
||||
}
|
||||
|
||||
// Request initalization
|
||||
var proxyReq = (options.target.protocol === 'https:' ? https : http).request(
|
||||
common.setupOutgoing(options.ssl || {}, options, req)
|
||||
);
|
||||
|
||||
// Enable developers to modify the proxyReq before headers are sent
|
||||
proxyReq.on('socket', function(socket) {
|
||||
if(server && !proxyReq.getHeader('expect')) {
|
||||
server.emit('proxyReq', proxyReq, req, res, options);
|
||||
}
|
||||
});
|
||||
|
||||
// allow outgoing socket to timeout so that we could
|
||||
// show an error page at the initial request
|
||||
if(options.proxyTimeout) {
|
||||
proxyReq.setTimeout(options.proxyTimeout, function() {
|
||||
proxyReq.abort();
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure we abort proxy if request is aborted
|
||||
req.on('aborted', function () {
|
||||
proxyReq.abort();
|
||||
});
|
||||
|
||||
// handle errors in proxy and incoming request, just like for forward proxy
|
||||
var proxyError = createErrorHandler(proxyReq, options.target);
|
||||
req.on('error', proxyError);
|
||||
proxyReq.on('error', proxyError);
|
||||
|
||||
function createErrorHandler(proxyReq, url) {
|
||||
return function proxyError(err) {
|
||||
if (req.socket.destroyed && err.code === 'ECONNRESET') {
|
||||
server.emit('econnreset', err, req, res, url);
|
||||
return proxyReq.abort();
|
||||
}
|
||||
|
||||
if (clb) {
|
||||
clb(err, req, res, url);
|
||||
} else {
|
||||
server.emit('error', err, req, res, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(options.buffer || req).pipe(proxyReq);
|
||||
|
||||
proxyReq.on('response', function(proxyRes) {
|
||||
if(server) { server.emit('proxyRes', proxyRes, req, res); }
|
||||
|
||||
if(!res.headersSent && !options.selfHandleResponse) {
|
||||
for(var i=0; i < web_o.length; i++) {
|
||||
if(web_o[i](req, res, proxyRes, options)) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
if (!res.finished) {
|
||||
// Allow us to listen when the proxy has completed
|
||||
proxyRes.on('end', function () {
|
||||
if (server) server.emit('end', req, res, proxyRes);
|
||||
});
|
||||
// We pipe to the response unless its expected to be handled by the user
|
||||
if (!options.selfHandleResponse) proxyRes.pipe(res);
|
||||
} else {
|
||||
if (server) server.emit('end', req, res, proxyRes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
147
lib/http-proxy/passes/web-outgoing.js
Normal file
147
lib/http-proxy/passes/web-outgoing.js
Normal file
@ -0,0 +1,147 @@
|
||||
var url = require('url'),
|
||||
common = require('../common');
|
||||
|
||||
|
||||
var redirectRegex = /^201|30(1|2|7|8)$/;
|
||||
|
||||
/*!
|
||||
* Array of passes.
|
||||
*
|
||||
* A `pass` is just a function that is executed on `req, res, options`
|
||||
* so that you can easily add new checks while still keeping the base
|
||||
* flexible.
|
||||
*/
|
||||
|
||||
module.exports = { // <--
|
||||
|
||||
/**
|
||||
* If is a HTTP 1.0 request, remove chunk headers
|
||||
*
|
||||
* @param {ClientRequest} Req Request object
|
||||
* @param {IncomingMessage} Res Response object
|
||||
* @param {proxyResponse} Res Response object from the proxy request
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
removeChunked: function removeChunked(req, res, proxyRes) {
|
||||
if (req.httpVersion === '1.0') {
|
||||
delete proxyRes.headers['transfer-encoding'];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* If is a HTTP 1.0 request, set the correct connection header
|
||||
* or if connection header not present, then use `keep-alive`
|
||||
*
|
||||
* @param {ClientRequest} Req Request object
|
||||
* @param {IncomingMessage} Res Response object
|
||||
* @param {proxyResponse} Res Response object from the proxy request
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
setConnection: function setConnection(req, res, proxyRes) {
|
||||
if (req.httpVersion === '1.0') {
|
||||
proxyRes.headers.connection = req.headers.connection || 'close';
|
||||
} else if (req.httpVersion !== '2.0' && !proxyRes.headers.connection) {
|
||||
proxyRes.headers.connection = req.headers.connection || 'keep-alive';
|
||||
}
|
||||
},
|
||||
|
||||
setRedirectHostRewrite: function setRedirectHostRewrite(req, res, proxyRes, options) {
|
||||
if ((options.hostRewrite || options.autoRewrite || options.protocolRewrite)
|
||||
&& proxyRes.headers['location']
|
||||
&& redirectRegex.test(proxyRes.statusCode)) {
|
||||
var target = url.parse(options.target);
|
||||
var u = url.parse(proxyRes.headers['location']);
|
||||
|
||||
// make sure the redirected host matches the target host before rewriting
|
||||
if (target.host != u.host) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.hostRewrite) {
|
||||
u.host = options.hostRewrite;
|
||||
} else if (options.autoRewrite) {
|
||||
u.host = req.headers['host'];
|
||||
}
|
||||
if (options.protocolRewrite) {
|
||||
u.protocol = options.protocolRewrite;
|
||||
}
|
||||
|
||||
proxyRes.headers['location'] = u.format();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Copy headers from proxyResponse to response
|
||||
* set each header in response object.
|
||||
*
|
||||
* @param {ClientRequest} Req Request object
|
||||
* @param {IncomingMessage} Res Response object
|
||||
* @param {proxyResponse} Res Response object from the proxy request
|
||||
* @param {Object} Options options.cookieDomainRewrite: Config to rewrite cookie domain
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
writeHeaders: function writeHeaders(req, res, proxyRes, options) {
|
||||
var rewriteCookieDomainConfig = options.cookieDomainRewrite,
|
||||
rewriteCookiePathConfig = options.cookiePathRewrite,
|
||||
preserveHeaderKeyCase = options.preserveHeaderKeyCase,
|
||||
rawHeaderKeyMap,
|
||||
setHeader = function(key, header) {
|
||||
if (header == undefined) return;
|
||||
if (rewriteCookieDomainConfig && key.toLowerCase() === 'set-cookie') {
|
||||
header = common.rewriteCookieProperty(header, rewriteCookieDomainConfig, 'domain');
|
||||
}
|
||||
if (rewriteCookiePathConfig && key.toLowerCase() === 'set-cookie') {
|
||||
header = common.rewriteCookieProperty(header, rewriteCookiePathConfig, 'path');
|
||||
}
|
||||
res.setHeader(String(key).trim(), header);
|
||||
};
|
||||
|
||||
if (typeof rewriteCookieDomainConfig === 'string') { //also test for ''
|
||||
rewriteCookieDomainConfig = { '*': rewriteCookieDomainConfig };
|
||||
}
|
||||
|
||||
if (typeof rewriteCookiePathConfig === 'string') { //also test for ''
|
||||
rewriteCookiePathConfig = { '*': rewriteCookiePathConfig };
|
||||
}
|
||||
|
||||
// message.rawHeaders is added in: v0.11.6
|
||||
// https://nodejs.org/api/http.html#http_message_rawheaders
|
||||
if (preserveHeaderKeyCase && proxyRes.rawHeaders != undefined) {
|
||||
rawHeaderKeyMap = {};
|
||||
for (var i = 0; i < proxyRes.rawHeaders.length; i += 2) {
|
||||
var key = proxyRes.rawHeaders[i];
|
||||
rawHeaderKeyMap[key.toLowerCase()] = key;
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(proxyRes.headers).forEach(function(key) {
|
||||
var header = proxyRes.headers[key];
|
||||
if (preserveHeaderKeyCase && rawHeaderKeyMap) {
|
||||
key = rawHeaderKeyMap[key] || key;
|
||||
}
|
||||
setHeader(key, header);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the statusCode from the proxyResponse
|
||||
*
|
||||
* @param {ClientRequest} Req Request object
|
||||
* @param {IncomingMessage} Res Response object
|
||||
* @param {proxyResponse} Res Response object from the proxy request
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
writeStatusCode: function writeStatusCode(req, res, proxyRes) {
|
||||
// From Node.js docs: response.writeHead(statusCode[, statusMessage][, headers])
|
||||
if(proxyRes.statusMessage) {
|
||||
res.statusCode = proxyRes.statusCode;
|
||||
res.statusMessage = proxyRes.statusMessage;
|
||||
} else {
|
||||
res.statusCode = proxyRes.statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
162
lib/http-proxy/passes/ws-incoming.js
Normal file
162
lib/http-proxy/passes/ws-incoming.js
Normal file
@ -0,0 +1,162 @@
|
||||
var http = require('http'),
|
||||
https = require('https'),
|
||||
common = require('../common');
|
||||
|
||||
/*!
|
||||
* Array of passes.
|
||||
*
|
||||
* A `pass` is just a function that is executed on `req, socket, options`
|
||||
* so that you can easily add new checks while still keeping the base
|
||||
* flexible.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Websockets Passes
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* WebSocket requests must have the `GET` method and
|
||||
* the `upgrade:websocket` header
|
||||
*
|
||||
* @param {ClientRequest} Req Request object
|
||||
* @param {Socket} Websocket
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
checkMethodAndHeader : function checkMethodAndHeader(req, socket) {
|
||||
if (req.method !== 'GET' || !req.headers.upgrade) {
|
||||
socket.destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (req.headers.upgrade.toLowerCase() !== 'websocket') {
|
||||
socket.destroy();
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets `x-forwarded-*` headers if specified in config.
|
||||
*
|
||||
* @param {ClientRequest} Req Request object
|
||||
* @param {Socket} Websocket
|
||||
* @param {Object} Options Config object passed to the proxy
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
XHeaders : function XHeaders(req, socket, options) {
|
||||
if(!options.xfwd) return;
|
||||
|
||||
var values = {
|
||||
for : req.connection.remoteAddress || req.socket.remoteAddress,
|
||||
port : common.getPort(req),
|
||||
proto: common.hasEncryptedConnection(req) ? 'wss' : 'ws'
|
||||
};
|
||||
|
||||
['for', 'port', 'proto'].forEach(function(header) {
|
||||
req.headers['x-forwarded-' + header] =
|
||||
(req.headers['x-forwarded-' + header] || '') +
|
||||
(req.headers['x-forwarded-' + header] ? ',' : '') +
|
||||
values[header];
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Does the actual proxying. Make the request and upgrade it
|
||||
* send the Switching Protocols request and pipe the sockets.
|
||||
*
|
||||
* @param {ClientRequest} Req Request object
|
||||
* @param {Socket} Websocket
|
||||
* @param {Object} Options Config object passed to the proxy
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
stream : function stream(req, socket, options, head, server, clb) {
|
||||
|
||||
var createHttpHeader = function(line, headers) {
|
||||
return Object.keys(headers).reduce(function (head, key) {
|
||||
var value = headers[key];
|
||||
|
||||
if (!Array.isArray(value)) {
|
||||
head.push(key + ': ' + value);
|
||||
return head;
|
||||
}
|
||||
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
head.push(key + ': ' + value[i]);
|
||||
}
|
||||
return head;
|
||||
}, [line])
|
||||
.join('\r\n') + '\r\n\r\n';
|
||||
}
|
||||
|
||||
common.setupSocket(socket);
|
||||
|
||||
if (head && head.length) socket.unshift(head);
|
||||
|
||||
|
||||
var proxyReq = (common.isSSL.test(options.target.protocol) ? https : http).request(
|
||||
common.setupOutgoing(options.ssl || {}, options, req)
|
||||
);
|
||||
|
||||
// Enable developers to modify the proxyReq before headers are sent
|
||||
if (server) { server.emit('proxyReqWs', proxyReq, req, socket, options, head); }
|
||||
|
||||
// Error Handler
|
||||
proxyReq.on('error', onOutgoingError);
|
||||
proxyReq.on('response', function (res) {
|
||||
// if upgrade event isn't going to happen, close the socket
|
||||
if (!res.upgrade) {
|
||||
socket.write(createHttpHeader('HTTP/' + res.httpVersion + ' ' + res.statusCode + ' ' + res.statusMessage, res.headers));
|
||||
res.pipe(socket);
|
||||
}
|
||||
});
|
||||
|
||||
proxyReq.on('upgrade', function(proxyRes, proxySocket, proxyHead) {
|
||||
proxySocket.on('error', onOutgoingError);
|
||||
|
||||
// Allow us to listen when the websocket has completed
|
||||
proxySocket.on('end', function () {
|
||||
server.emit('close', proxyRes, proxySocket, proxyHead);
|
||||
});
|
||||
|
||||
// The pipe below will end proxySocket if socket closes cleanly, but not
|
||||
// if it errors (eg, vanishes from the net and starts returning
|
||||
// EHOSTUNREACH). We need to do that explicitly.
|
||||
socket.on('error', function () {
|
||||
proxySocket.end();
|
||||
});
|
||||
|
||||
common.setupSocket(proxySocket);
|
||||
|
||||
if (proxyHead && proxyHead.length) proxySocket.unshift(proxyHead);
|
||||
|
||||
//
|
||||
// Remark: Handle writing the headers to the socket when switching protocols
|
||||
// Also handles when a header is an array
|
||||
//
|
||||
socket.write(createHttpHeader('HTTP/1.1 101 Switching Protocols', proxyRes.headers));
|
||||
|
||||
proxySocket.pipe(socket).pipe(proxySocket);
|
||||
|
||||
server.emit('open', proxySocket);
|
||||
server.emit('proxySocket', proxySocket); //DEPRECATED.
|
||||
});
|
||||
|
||||
return proxyReq.end(); // XXX: CHECK IF THIS IS THIS CORRECT
|
||||
|
||||
function onOutgoingError(err) {
|
||||
if (clb) {
|
||||
clb(err, req, socket);
|
||||
} else {
|
||||
server.emit('error', err, req, socket);
|
||||
}
|
||||
socket.end();
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,397 +0,0 @@
|
||||
/*
|
||||
node-http-proxy.js: http proxy for node.js
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Marak Squires, Fedor Indutny
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
var util = require('util'),
|
||||
http = require('http'),
|
||||
https = require('https'),
|
||||
events = require('events'),
|
||||
maxSockets = 100;
|
||||
|
||||
//
|
||||
// Expose version information through `pkginfo`.
|
||||
//
|
||||
require('pkginfo')(module, 'version');
|
||||
|
||||
//
|
||||
// ### Export the relevant objects exposed by `node-http-proxy`
|
||||
//
|
||||
var HttpProxy = exports.HttpProxy = require('./node-http-proxy/http-proxy').HttpProxy,
|
||||
ProxyTable = exports.ProxyTable = require('./node-http-proxy/proxy-table').ProxyTable,
|
||||
RoutingProxy = exports.RoutingProxy = require('./node-http-proxy/routing-proxy').RoutingProxy;
|
||||
|
||||
//
|
||||
// ### function createServer ([port, host, options, handler])
|
||||
// #### @port {number} **Optional** Port to use on the proxy target host.
|
||||
// #### @host {string} **Optional** Host of the proxy target.
|
||||
// #### @options {Object} **Optional** Options for the HttpProxy instance used
|
||||
// #### @handler {function} **Optional** Request handler for the server
|
||||
// Returns a server that manages an instance of HttpProxy. Flexible arguments allow for:
|
||||
//
|
||||
// * `httpProxy.createServer(9000, 'localhost')`
|
||||
// * `httpProxy.createServer(9000, 'localhost', options)
|
||||
// * `httpPRoxy.createServer(function (req, res, proxy) { ... })`
|
||||
//
|
||||
exports.createServer = function () {
|
||||
var args = Array.prototype.slice.call(arguments),
|
||||
handlers = [],
|
||||
callback,
|
||||
options = {},
|
||||
message,
|
||||
handler,
|
||||
server,
|
||||
proxy,
|
||||
host,
|
||||
port;
|
||||
|
||||
//
|
||||
// Liberally parse arguments of the form:
|
||||
//
|
||||
// httpProxy.createServer('localhost', 9000, callback);
|
||||
// httpProxy.createServer({ host: 'localhost', port: 9000 }, callback);
|
||||
// **NEED MORE HERE!!!**
|
||||
//
|
||||
args.forEach(function (arg) {
|
||||
arg = Number(arg) || arg;
|
||||
switch (typeof arg) {
|
||||
case 'string': host = arg; break;
|
||||
case 'number': port = arg; break;
|
||||
case 'object': options = arg || {}; break;
|
||||
case 'function': callback = arg; handlers.push(callback); break;
|
||||
};
|
||||
});
|
||||
|
||||
//
|
||||
// Helper function to create intelligent error message(s)
|
||||
// for the very liberal arguments parsing performed by
|
||||
// `require('http-proxy').createServer()`.
|
||||
//
|
||||
function validArguments() {
|
||||
var conditions = {
|
||||
'port and host': function () {
|
||||
return port && host;
|
||||
},
|
||||
'options.target or options.router': function () {
|
||||
return options && (options.router ||
|
||||
(options.target && options.target.host && options.target.port));
|
||||
},
|
||||
'or proxy handlers': function () {
|
||||
return handlers && handlers.length;
|
||||
}
|
||||
}
|
||||
|
||||
var missing = Object.keys(conditions).filter(function (name) {
|
||||
return !conditions[name]();
|
||||
});
|
||||
|
||||
if (missing.length === 3) {
|
||||
message = 'Cannot proxy without ' + missing.join(', ');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!validArguments()) {
|
||||
//
|
||||
// If `host`, `port` and `options` are all not passed (with valid
|
||||
// options) then this server is improperly configured.
|
||||
//
|
||||
throw new Error(message);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Hoist up any explicit `host` or `port` arguments
|
||||
// that have been passed in to the options we will
|
||||
// pass to the `httpProxy.HttpProxy` constructor.
|
||||
//
|
||||
options.target = options.target || {};
|
||||
options.target.port = options.target.port || port;
|
||||
options.target.host = options.target.host || host;
|
||||
|
||||
if (options.target && options.target.host && options.target.port) {
|
||||
//
|
||||
// If an explicit `host` and `port` combination has been passed
|
||||
// to `.createServer()` then instantiate a hot-path optimized
|
||||
// `HttpProxy` object and add the "proxy" middleware layer.
|
||||
//
|
||||
proxy = new HttpProxy(options);
|
||||
handlers.push(function (req, res) {
|
||||
proxy.proxyRequest(req, res);
|
||||
});
|
||||
}
|
||||
else {
|
||||
//
|
||||
// If no explicit `host` or `port` combination has been passed then
|
||||
// we have to assume that this is a "go-anywhere" Proxy (i.e. a `RoutingProxy`).
|
||||
//
|
||||
proxy = new RoutingProxy(options);
|
||||
|
||||
if (options.router) {
|
||||
//
|
||||
// If a routing table has been supplied than we assume
|
||||
// the user intends us to add the "proxy" middleware layer
|
||||
// for them
|
||||
//
|
||||
handlers.push(function (req, res) {
|
||||
proxy.proxyRequest(req, res);
|
||||
});
|
||||
|
||||
proxy.on('routes', function (routes) {
|
||||
server.emit('routes', routes);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Create the `http[s].Server` instance which will use
|
||||
// an instance of `httpProxy.HttpProxy`.
|
||||
//
|
||||
handler = handlers.length > 1
|
||||
? exports.stack(handlers, proxy)
|
||||
: function (req, res) { handlers[0](req, res, proxy) };
|
||||
|
||||
server = options.https
|
||||
? https.createServer(options.https, handler)
|
||||
: http.createServer(handler);
|
||||
|
||||
server.on('close', function () {
|
||||
proxy.close();
|
||||
});
|
||||
|
||||
if (!callback) {
|
||||
//
|
||||
// If an explicit callback has not been supplied then
|
||||
// automagically proxy the request using the `HttpProxy`
|
||||
// instance we have created.
|
||||
//
|
||||
server.on('upgrade', function (req, socket, head) {
|
||||
proxy.proxyWebSocketRequest(req, socket, head);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Set the proxy on the server so it is available
|
||||
// to the consumer of the server
|
||||
//
|
||||
server.proxy = proxy;
|
||||
return server;
|
||||
};
|
||||
|
||||
//
|
||||
// ### function buffer (obj)
|
||||
// #### @obj {Object} Object to pause events from
|
||||
// Buffer `data` and `end` events from the given `obj`.
|
||||
// Consumers of HttpProxy performing async tasks
|
||||
// __must__ utilize this utility, to re-emit data once
|
||||
// the async operation has completed, otherwise these
|
||||
// __events will be lost.__
|
||||
//
|
||||
// var buffer = httpProxy.buffer(req);
|
||||
// fs.readFile(path, function () {
|
||||
// httpProxy.proxyRequest(req, res, host, port, buffer);
|
||||
// });
|
||||
//
|
||||
// __Attribution:__ This approach is based heavily on
|
||||
// [Connect](https://github.com/senchalabs/connect/blob/master/lib/utils.js#L157).
|
||||
// However, this is not a big leap from the implementation in node-http-proxy < 0.4.0.
|
||||
// This simply chooses to manage the scope of the events on a new Object literal as opposed to
|
||||
// [on the HttpProxy instance](https://github.com/nodejitsu/node-http-proxy/blob/v0.3.1/lib/node-http-proxy.js#L154).
|
||||
//
|
||||
exports.buffer = function (obj) {
|
||||
var events = [],
|
||||
onData,
|
||||
onEnd;
|
||||
|
||||
obj.on('data', onData = function (data, encoding) {
|
||||
events.push(['data', data, encoding]);
|
||||
});
|
||||
|
||||
obj.on('end', onEnd = function (data, encoding) {
|
||||
events.push(['end', data, encoding]);
|
||||
});
|
||||
|
||||
return {
|
||||
end: function () {
|
||||
obj.removeListener('data', onData);
|
||||
obj.removeListener('end', onEnd);
|
||||
},
|
||||
destroy: function () {
|
||||
this.end();
|
||||
this.resume = function () {
|
||||
console.error("Cannot resume buffer after destroying it.");
|
||||
};
|
||||
|
||||
onData = onEnd = events = obj = null;
|
||||
},
|
||||
resume: function () {
|
||||
this.end();
|
||||
for (var i = 0, len = events.length; i < len; ++i) {
|
||||
obj.emit.apply(obj, events[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//
|
||||
// ### function getMaxSockets ()
|
||||
// Returns the maximum number of sockets
|
||||
// allowed on __every__ outgoing request
|
||||
// made by __all__ instances of `HttpProxy`
|
||||
//
|
||||
exports.getMaxSockets = function () {
|
||||
return maxSockets;
|
||||
};
|
||||
|
||||
//
|
||||
// ### function setMaxSockets ()
|
||||
// Sets the maximum number of sockets
|
||||
// allowed on __every__ outgoing request
|
||||
// made by __all__ instances of `HttpProxy`
|
||||
//
|
||||
exports.setMaxSockets = function (value) {
|
||||
maxSockets = value;
|
||||
};
|
||||
|
||||
//
|
||||
// ### function stack (middlewares, proxy)
|
||||
// #### @middlewares {Array} Array of functions to stack.
|
||||
// #### @proxy {HttpProxy|RoutingProxy} Proxy instance to
|
||||
// Iteratively build up a single handler to the `http.Server`
|
||||
// `request` event (i.e. `function (req, res)`) by wrapping
|
||||
// each middleware `layer` into a `child` middleware which
|
||||
// is in invoked by the parent (i.e. predecessor in the Array).
|
||||
//
|
||||
// adapted from https://github.com/creationix/stack
|
||||
//
|
||||
exports.stack = function stack (middlewares, proxy) {
|
||||
var handle;
|
||||
middlewares.reverse().forEach(function (layer) {
|
||||
var child = handle;
|
||||
handle = function (req, res) {
|
||||
var next = function (err) {
|
||||
if (err) {
|
||||
if (! proxy.emit('middlewareError', err, req, res)) {
|
||||
console.error('Error in middleware(s): %s', err.stack);
|
||||
}
|
||||
|
||||
if (res._headerSent) {
|
||||
res.destroy();
|
||||
}
|
||||
else {
|
||||
res.statusCode = 500;
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.end('Internal Server Error');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (child) {
|
||||
child(req, res);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Set the prototype of the `next` function to the instance
|
||||
// of the `proxy` so that in can be used interchangably from
|
||||
// a `connect` style callback and a true `HttpProxy` object.
|
||||
//
|
||||
// e.g. `function (req, res, next)` vs. `function (req, res, proxy)`
|
||||
//
|
||||
next.__proto__ = proxy;
|
||||
layer(req, res, next);
|
||||
};
|
||||
});
|
||||
|
||||
return handle;
|
||||
};
|
||||
|
||||
//
|
||||
// ### function _getAgent (host, port, secure)
|
||||
// #### @options {Object} Options to use when creating the agent.
|
||||
//
|
||||
// {
|
||||
// host: 'localhost',
|
||||
// port: 9000,
|
||||
// https: true,
|
||||
// maxSockets: 100
|
||||
// }
|
||||
//
|
||||
// Createsan agent from the `http` or `https` module
|
||||
// and sets the `maxSockets` property appropriately.
|
||||
//
|
||||
exports._getAgent = function _getAgent (options) {
|
||||
if (!options || !options.host) {
|
||||
throw new Error('`options.host` is required to create an Agent.');
|
||||
}
|
||||
|
||||
if (!options.port) {
|
||||
options.port = options.https ? 443 : 80;
|
||||
}
|
||||
|
||||
var Agent = options.https ? https.Agent : http.Agent,
|
||||
agent;
|
||||
|
||||
// require('http-proxy').setMaxSockets() should override http's default
|
||||
// configuration value (which is pretty low).
|
||||
options.maxSockets = options.maxSockets || maxSockets;
|
||||
agent = new Agent(options);
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
//
|
||||
// ### function _getProtocol (options)
|
||||
// #### @options {Object} Options for the proxy target.
|
||||
// Returns the appropriate node.js core protocol module (i.e. `http` or `https`)
|
||||
// based on the `options` supplied.
|
||||
//
|
||||
exports._getProtocol = function _getProtocol (options) {
|
||||
return options.https ? https : http;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// ### function _getBase (options)
|
||||
// #### @options {Object} Options for the proxy target.
|
||||
// Returns the relevate base object to create on outgoing proxy request.
|
||||
// If `options.https` are supplied, this function respond with an object
|
||||
// containing the relevant `ca`, `key`, and `cert` properties.
|
||||
//
|
||||
exports._getBase = function _getBase (options) {
|
||||
var result = function () {};
|
||||
|
||||
if (options.https && typeof options.https === 'object') {
|
||||
['ca', 'cert', 'key'].forEach(function (key) {
|
||||
if (options.https[key]) {
|
||||
result.prototype[key] = options.https[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
@ -1,983 +0,0 @@
|
||||
/*
|
||||
node-http-proxy.js: http proxy for node.js
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Mikeal Rogers, Marak Squires, Fedor Indutny
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
var events = require('events'),
|
||||
http = require('http'),
|
||||
util = require('util'),
|
||||
url = require('url'),
|
||||
httpProxy = require('../node-http-proxy');
|
||||
|
||||
//
|
||||
// @private {RegExp} extractPort
|
||||
// Reusable regular expression for getting the
|
||||
// port from a host string.
|
||||
//
|
||||
var extractPort = /:(\d+)$/;
|
||||
|
||||
//
|
||||
// ### function HttpProxy (options)
|
||||
// #### @options {Object} Options for this instance.
|
||||
// Constructor function for new instances of HttpProxy responsible
|
||||
// for managing the life-cycle of streaming reverse proxyied HTTP requests.
|
||||
//
|
||||
// Example options:
|
||||
//
|
||||
// {
|
||||
// target: {
|
||||
// host: 'localhost',
|
||||
// port: 9000
|
||||
// },
|
||||
// forward: {
|
||||
// host: 'localhost',
|
||||
// port: 9001
|
||||
// }
|
||||
// }
|
||||
//
|
||||
var HttpProxy = exports.HttpProxy = function (options) {
|
||||
if (!options || !options.target) {
|
||||
throw new Error('Both `options` and `options.target` are required.');
|
||||
}
|
||||
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
var self = this;
|
||||
|
||||
//
|
||||
// Setup basic proxying options:
|
||||
//
|
||||
// * forward {Object} Options for a forward-proxy (if-any)
|
||||
// * target {Object} Options for the **sole** proxy target of this instance
|
||||
//
|
||||
this.forward = options.forward;
|
||||
this.target = options.target;
|
||||
this.timeout = options.timeout;
|
||||
|
||||
//
|
||||
// Setup the necessary instances instance variables for
|
||||
// the `target` and `forward` `host:port` combinations
|
||||
// used by this instance.
|
||||
//
|
||||
// * agent {http[s].Agent} Agent to be used by this instance.
|
||||
// * protocol {http|https} Core node.js module to make requests with.
|
||||
// * base {Object} Base object to create when proxying containing any https settings.
|
||||
//
|
||||
function setupProxy (key) {
|
||||
self[key].agent = httpProxy._getAgent(self[key]);
|
||||
self[key].protocol = httpProxy._getProtocol(self[key]);
|
||||
self[key].base = httpProxy._getBase(self[key]);
|
||||
}
|
||||
|
||||
setupProxy('target');
|
||||
if (this.forward) {
|
||||
setupProxy('forward');
|
||||
}
|
||||
|
||||
//
|
||||
// Setup opt-in features
|
||||
//
|
||||
this.enable = options.enable || {};
|
||||
this.enable.xforward = typeof this.enable.xforward === 'boolean'
|
||||
? this.enable.xforward
|
||||
: true;
|
||||
|
||||
// if event listener is set then use it else unlimited.
|
||||
this.eventListenerCount = typeof options.eventListenerCount === 'number'? options.eventListenerCount : 0 ;
|
||||
|
||||
//
|
||||
// Setup additional options for WebSocket proxying. When forcing
|
||||
// the WebSocket handshake to change the `sec-websocket-location`
|
||||
// and `sec-websocket-origin` headers `options.source` **MUST**
|
||||
// be provided or the operation will fail with an `origin mismatch`
|
||||
// by definition.
|
||||
//
|
||||
this.source = options.source || { host: 'localhost', port: 80 };
|
||||
this.source.https = this.source.https || options.https;
|
||||
this.changeOrigin = options.changeOrigin || false;
|
||||
};
|
||||
|
||||
// Inherit from events.EventEmitter
|
||||
util.inherits(HttpProxy, events.EventEmitter);
|
||||
|
||||
//
|
||||
// ### function proxyRequest (req, res, buffer)
|
||||
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
|
||||
// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
|
||||
// #### @buffer {Object} Result from `httpProxy.buffer(req)`
|
||||
//
|
||||
HttpProxy.prototype.proxyRequest = function (req, res, buffer) {
|
||||
var self = this,
|
||||
errState = false,
|
||||
outgoing = new(this.target.base),
|
||||
reverseProxy,
|
||||
location;
|
||||
|
||||
// If this is a DELETE request then set the "content-length"
|
||||
// header (if it is not already set)
|
||||
if (req.method === 'DELETE') {
|
||||
req.headers['content-length'] = req.headers['content-length'] || '0';
|
||||
}
|
||||
|
||||
//
|
||||
// Add common proxy headers to the request so that they can
|
||||
// be availible to the proxy target server. If the proxy is
|
||||
// part of proxy chain it will append the address:
|
||||
//
|
||||
// * `x-forwarded-for`: IP Address of the original request
|
||||
// * `x-forwarded-proto`: Protocol of the original request
|
||||
// * `x-forwarded-port`: Port of the original request.
|
||||
//
|
||||
if (this.enable.xforward && req.connection && req.socket) {
|
||||
if (req.headers['x-forwarded-for']) {
|
||||
var addressToAppend = "," + req.connection.remoteAddress || req.socket.remoteAddress;
|
||||
req.headers['x-forwarded-for'] += addressToAppend;
|
||||
}
|
||||
else {
|
||||
req.headers['x-forwarded-for'] = req.connection.remoteAddress || req.socket.remoteAddress;
|
||||
}
|
||||
|
||||
if (req.headers['x-forwarded-port']) {
|
||||
var portToAppend = "," + getPortFromHostHeader(req);
|
||||
req.headers['x-forwarded-port'] += portToAppend;
|
||||
}
|
||||
else {
|
||||
req.headers['x-forwarded-port'] = getPortFromHostHeader(req);
|
||||
}
|
||||
|
||||
if (req.headers['x-forwarded-proto']) {
|
||||
var protoToAppend = "," + getProto(req);
|
||||
req.headers['x-forwarded-proto'] += protoToAppend;
|
||||
}
|
||||
else {
|
||||
req.headers['x-forwarded-proto'] = getProto(req);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.timeout) {
|
||||
req.socket.setTimeout(this.timeout);
|
||||
}
|
||||
|
||||
//
|
||||
// Emit the `start` event indicating that we have begun the proxy operation.
|
||||
//
|
||||
this.emit('start', req, res, this.target);
|
||||
|
||||
//
|
||||
// If forwarding is enabled for this instance, foward proxy the
|
||||
// specified request to the address provided in `this.forward`
|
||||
//
|
||||
if (this.forward) {
|
||||
this.emit('forward', req, res, this.forward);
|
||||
this._forwardRequest(req);
|
||||
}
|
||||
|
||||
//
|
||||
// #### function proxyError (err)
|
||||
// #### @err {Error} Error contacting the proxy target
|
||||
// Short-circuits `res` in the event of any error when
|
||||
// contacting the proxy target at `host` / `port`.
|
||||
//
|
||||
function proxyError(err) {
|
||||
errState = true;
|
||||
|
||||
//
|
||||
// Emit an `error` event, allowing the application to use custom
|
||||
// error handling. The error handler should end the response.
|
||||
//
|
||||
if (self.emit('proxyError', err, req, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
||||
|
||||
if (req.method !== 'HEAD') {
|
||||
//
|
||||
// This NODE_ENV=production behavior is mimics Express and
|
||||
// Connect.
|
||||
//
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
res.write('Internal Server Error');
|
||||
}
|
||||
else {
|
||||
res.write('An error has occurred: ' + JSON.stringify(err));
|
||||
}
|
||||
}
|
||||
|
||||
try { res.end() }
|
||||
catch (ex) { console.error("res.end error: %s", ex.message) }
|
||||
}
|
||||
|
||||
//
|
||||
// Setup outgoing proxy with relevant properties.
|
||||
//
|
||||
outgoing.host = this.target.host;
|
||||
outgoing.hostname = this.target.hostname;
|
||||
outgoing.port = this.target.port;
|
||||
outgoing.socketPath = this.target.socketPath;
|
||||
outgoing.agent = this.target.agent;
|
||||
outgoing.method = req.method;
|
||||
outgoing.path = url.parse(req.url).path;
|
||||
outgoing.headers = req.headers;
|
||||
|
||||
//
|
||||
// If the changeOrigin option is specified, change the
|
||||
// origin of the host header to the target URL! Please
|
||||
// don't revert this without documenting it!
|
||||
//
|
||||
if (this.changeOrigin) {
|
||||
outgoing.headers.host = this.target.host;
|
||||
// Only add port information to the header if not default port
|
||||
// for this protocol.
|
||||
// See https://github.com/nodejitsu/node-http-proxy/issues/458
|
||||
if (this.target.port !== 443 && this.target.https ||
|
||||
this.target.port !== 80 && !this.target.https) {
|
||||
outgoing.headers.host += ':' + this.target.port;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Open new HTTP request to internal resource with will act
|
||||
// as a reverse proxy pass
|
||||
//
|
||||
reverseProxy = this.target.protocol.request(outgoing, function (response) {
|
||||
//
|
||||
// Process the `reverseProxy` `response` when it's received.
|
||||
//
|
||||
if (req.httpVersion === '1.0') {
|
||||
if (req.headers.connection) {
|
||||
response.headers.connection = req.headers.connection
|
||||
} else {
|
||||
response.headers.connection = 'close'
|
||||
}
|
||||
} else if (!response.headers.connection) {
|
||||
if (req.headers.connection) { response.headers.connection = req.headers.connection }
|
||||
else {
|
||||
response.headers.connection = 'keep-alive'
|
||||
}
|
||||
}
|
||||
|
||||
// Remove `Transfer-Encoding` header if client's protocol is HTTP/1.0
|
||||
// or if this is a DELETE request with no content-length header.
|
||||
// See: https://github.com/nodejitsu/node-http-proxy/pull/373
|
||||
if (req.httpVersion === '1.0' || (req.method === 'DELETE'
|
||||
&& !req.headers['content-length'])) {
|
||||
delete response.headers['transfer-encoding'];
|
||||
}
|
||||
|
||||
if ((response.statusCode === 301 || response.statusCode === 302)
|
||||
&& typeof response.headers.location !== 'undefined') {
|
||||
location = url.parse(response.headers.location);
|
||||
if (location.host === req.headers.host) {
|
||||
if (self.source.https && !self.target.https) {
|
||||
response.headers.location = response.headers.location.replace(/^http\:/, 'https:');
|
||||
}
|
||||
if (self.target.https && !self.source.https) {
|
||||
response.headers.location = response.headers.location.replace(/^https\:/, 'http:');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// When the `reverseProxy` `response` ends, end the
|
||||
// corresponding outgoing `res` unless we have entered
|
||||
// an error state. In which case, assume `res.end()` has
|
||||
// already been called and the 'error' event listener
|
||||
// removed.
|
||||
//
|
||||
var ended = false;
|
||||
response.on('close', function () {
|
||||
if (!ended) { response.emit('end') }
|
||||
});
|
||||
|
||||
//
|
||||
// After reading a chunked response, the underlying socket
|
||||
// will hit EOF and emit a 'end' event, which will abort
|
||||
// the request. If the socket was paused at that time,
|
||||
// pending data gets discarded, truncating the response.
|
||||
// This code makes sure that we flush pending data.
|
||||
//
|
||||
response.connection.on('end', function () {
|
||||
if (response.readable && response.resume) {
|
||||
response.resume();
|
||||
}
|
||||
});
|
||||
|
||||
response.on('end', function () {
|
||||
ended = true;
|
||||
if (!errState) {
|
||||
try { res.end() }
|
||||
catch (ex) { console.error("res.end error: %s", ex.message) }
|
||||
|
||||
// Emit the `end` event now that we have completed proxying
|
||||
self.emit('end', req, res, response);
|
||||
}
|
||||
});
|
||||
|
||||
// Allow observer to modify headers or abort response
|
||||
try { self.emit('proxyResponse', req, res, response) }
|
||||
catch (ex) {
|
||||
errState = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the headers of the client response
|
||||
if (res.sentHeaders !== true) {
|
||||
Object.keys(response.headers).forEach(function (key) {
|
||||
res.setHeader(key, response.headers[key]);
|
||||
});
|
||||
res.writeHead(response.statusCode);
|
||||
}
|
||||
|
||||
function ondata(chunk) {
|
||||
if (res.writable) {
|
||||
// Only pause if the underlying buffers are full,
|
||||
// *and* the connection is not in 'closing' state.
|
||||
// Otherwise, the pause will cause pending data to
|
||||
// be discarded and silently lost.
|
||||
if (false === res.write(chunk) && response.pause
|
||||
&& response.connection.readable) {
|
||||
response.pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.on('data', ondata);
|
||||
|
||||
function ondrain() {
|
||||
if (response.readable && response.resume) {
|
||||
response.resume();
|
||||
}
|
||||
}
|
||||
|
||||
res.on('drain', ondrain);
|
||||
});
|
||||
|
||||
// allow unlimited listeners ...
|
||||
reverseProxy.setMaxListeners(this.eventListenerCount);
|
||||
|
||||
//
|
||||
// Handle 'error' events from the `reverseProxy`. Setup timeout override if needed
|
||||
//
|
||||
reverseProxy.once('error', proxyError);
|
||||
|
||||
// Set a timeout on the socket if `this.timeout` is specified.
|
||||
reverseProxy.once('socket', function (socket) {
|
||||
if (self.timeout) {
|
||||
socket.setTimeout(self.timeout);
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Handle 'error' events from the `req` (e.g. `Parse Error`).
|
||||
//
|
||||
req.on('error', proxyError);
|
||||
|
||||
//
|
||||
// If `req` is aborted, we abort our `reverseProxy` request as well.
|
||||
//
|
||||
req.on('aborted', function () {
|
||||
reverseProxy.abort();
|
||||
});
|
||||
|
||||
//
|
||||
// For each data `chunk` received from the incoming
|
||||
// `req` write it to the `reverseProxy` request.
|
||||
//
|
||||
req.on('data', function (chunk) {
|
||||
if (!errState) {
|
||||
var flushed = reverseProxy.write(chunk);
|
||||
if (!flushed) {
|
||||
req.pause();
|
||||
reverseProxy.once('drain', function () {
|
||||
try { req.resume() }
|
||||
catch (er) { console.error("req.resume error: %s", er.message) }
|
||||
});
|
||||
|
||||
//
|
||||
// Force the `drain` event in 100ms if it hasn't
|
||||
// happened on its own.
|
||||
//
|
||||
setTimeout(function () {
|
||||
reverseProxy.emit('drain');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// When the incoming `req` ends, end the corresponding `reverseProxy`
|
||||
// request unless we have entered an error state.
|
||||
//
|
||||
req.on('end', function () {
|
||||
if (!errState) {
|
||||
reverseProxy.end();
|
||||
}
|
||||
});
|
||||
|
||||
//Aborts reverseProxy if client aborts the connection.
|
||||
req.on('close', function () {
|
||||
if (!errState) {
|
||||
reverseProxy.abort();
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// If we have been passed buffered data, resume it.
|
||||
//
|
||||
if (buffer) {
|
||||
return !errState
|
||||
? buffer.resume()
|
||||
: buffer.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// ### function proxyWebSocketRequest (req, socket, head, buffer)
|
||||
// #### @req {ServerRequest} Websocket request to proxy.
|
||||
// #### @socket {net.Socket} Socket for the underlying HTTP request
|
||||
// #### @head {string} Headers for the Websocket request.
|
||||
// #### @buffer {Object} Result from `httpProxy.buffer(req)`
|
||||
// Performs a WebSocket proxy operation to the location specified by
|
||||
// `this.target`.
|
||||
//
|
||||
HttpProxy.prototype.proxyWebSocketRequest = function (req, socket, upgradeHead, buffer) {
|
||||
var self = this,
|
||||
outgoing = new(this.target.base),
|
||||
listeners = {},
|
||||
errState = false,
|
||||
CRLF = '\r\n',
|
||||
//copy upgradeHead to avoid retention of large slab buffers used in node core
|
||||
head = new Buffer(upgradeHead.length);
|
||||
upgradeHead.copy(head);
|
||||
|
||||
//
|
||||
// WebSocket requests must have the `GET` method and
|
||||
// the `upgrade:websocket` header
|
||||
//
|
||||
if (req.method !== 'GET' || req.headers.upgrade.toLowerCase() !== 'websocket') {
|
||||
//
|
||||
// This request is not WebSocket request
|
||||
//
|
||||
return socket.destroy();
|
||||
}
|
||||
|
||||
//
|
||||
// Add common proxy headers to the request so that they can
|
||||
// be availible to the proxy target server. If the proxy is
|
||||
// part of proxy chain it will append the address:
|
||||
//
|
||||
// * `x-forwarded-for`: IP Address of the original request
|
||||
// * `x-forwarded-proto`: Protocol of the original request
|
||||
// * `x-forwarded-port`: Port of the original request.
|
||||
//
|
||||
if (this.enable.xforward && req.connection) {
|
||||
if (req.headers['x-forwarded-for']) {
|
||||
var addressToAppend = "," + req.connection.remoteAddress || socket.remoteAddress;
|
||||
req.headers['x-forwarded-for'] += addressToAppend;
|
||||
}
|
||||
else {
|
||||
req.headers['x-forwarded-for'] = req.connection.remoteAddress || socket.remoteAddress;
|
||||
}
|
||||
|
||||
if (req.headers['x-forwarded-port']) {
|
||||
var portToAppend = "," + getPortFromHostHeader(req);
|
||||
req.headers['x-forwarded-port'] += portToAppend;
|
||||
}
|
||||
else {
|
||||
req.headers['x-forwarded-port'] = getPortFromHostHeader(req);
|
||||
}
|
||||
|
||||
if (req.headers['x-forwarded-proto']) {
|
||||
var protoToAppend = "," + (req.connection.pair ? 'wss' : 'ws');
|
||||
req.headers['x-forwarded-proto'] += protoToAppend;
|
||||
}
|
||||
else {
|
||||
req.headers['x-forwarded-proto'] = req.connection.pair ? 'wss' : 'ws';
|
||||
}
|
||||
}
|
||||
|
||||
self.emit('websocket:start', req, socket, head, this.target);
|
||||
|
||||
//
|
||||
// Helper function for setting appropriate socket values:
|
||||
// 1. Turn of all bufferings
|
||||
// 2. For server set KeepAlive
|
||||
//
|
||||
function _socket(socket, keepAlive) {
|
||||
socket.setTimeout(0);
|
||||
socket.setNoDelay(true);
|
||||
|
||||
if (keepAlive) {
|
||||
if (socket.setKeepAlive) {
|
||||
socket.setKeepAlive(true, 0);
|
||||
}
|
||||
else if (socket.pair.cleartext.socket.setKeepAlive) {
|
||||
socket.pair.cleartext.socket.setKeepAlive(true, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Setup the incoming client socket.
|
||||
//
|
||||
_socket(socket, true);
|
||||
|
||||
//
|
||||
// On `upgrade` from the Agent socket, listen to
|
||||
// the appropriate events.
|
||||
//
|
||||
function onUpgrade (reverseProxy, proxySocket) {
|
||||
if (!reverseProxy) {
|
||||
proxySocket.end();
|
||||
socket.end();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Any incoming data on this WebSocket to the proxy target
|
||||
// will be written to the `reverseProxy` socket.
|
||||
//
|
||||
proxySocket.on('data', listeners.onIncoming = function (data) {
|
||||
if (reverseProxy.incoming.socket.writable) {
|
||||
try {
|
||||
self.emit('websocket:outgoing', req, socket, head, data);
|
||||
var flushed = reverseProxy.incoming.socket.write(data);
|
||||
if (!flushed) {
|
||||
proxySocket.pause();
|
||||
reverseProxy.incoming.socket.once('drain', function () {
|
||||
try { proxySocket.resume() }
|
||||
catch (er) { console.error("proxySocket.resume error: %s", er.message) }
|
||||
});
|
||||
|
||||
//
|
||||
// Force the `drain` event in 100ms if it hasn't
|
||||
// happened on its own.
|
||||
//
|
||||
setTimeout(function () {
|
||||
reverseProxy.incoming.socket.emit('drain');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
detach();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Any outgoing data on this Websocket from the proxy target
|
||||
// will be written to the `proxySocket` socket.
|
||||
//
|
||||
reverseProxy.incoming.socket.on('data', listeners.onOutgoing = function (data) {
|
||||
try {
|
||||
self.emit('websocket:incoming', reverseProxy, reverseProxy.incoming, head, data);
|
||||
var flushed = proxySocket.write(data);
|
||||
if (!flushed) {
|
||||
reverseProxy.incoming.socket.pause();
|
||||
proxySocket.once('drain', function () {
|
||||
try { reverseProxy.incoming.socket.resume() }
|
||||
catch (er) { console.error("reverseProxy.incoming.socket.resume error: %s", er.message) }
|
||||
});
|
||||
|
||||
//
|
||||
// Force the `drain` event in 100ms if it hasn't
|
||||
// happened on its own.
|
||||
//
|
||||
setTimeout(function () {
|
||||
proxySocket.emit('drain');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
detach();
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// Helper function to detach all event listeners
|
||||
// from `reverseProxy` and `proxySocket`.
|
||||
//
|
||||
function detach() {
|
||||
proxySocket.destroySoon();
|
||||
proxySocket.removeListener('end', listeners.onIncomingClose);
|
||||
proxySocket.removeListener('data', listeners.onIncoming);
|
||||
reverseProxy.incoming.socket.destroySoon();
|
||||
reverseProxy.incoming.socket.removeListener('end', listeners.onOutgoingClose);
|
||||
reverseProxy.incoming.socket.removeListener('data', listeners.onOutgoing);
|
||||
}
|
||||
|
||||
//
|
||||
// If the incoming `proxySocket` socket closes, then
|
||||
// detach all event listeners.
|
||||
//
|
||||
listeners.onIncomingClose = function () {
|
||||
reverseProxy.incoming.socket.destroy();
|
||||
detach();
|
||||
|
||||
// Emit the `end` event now that we have completed proxying
|
||||
self.emit('websocket:end', req, socket, head);
|
||||
}
|
||||
|
||||
//
|
||||
// If the `reverseProxy` socket closes, then detach all
|
||||
// event listeners.
|
||||
//
|
||||
listeners.onOutgoingClose = function () {
|
||||
proxySocket.destroy();
|
||||
detach();
|
||||
}
|
||||
|
||||
proxySocket.on('end', listeners.onIncomingClose);
|
||||
proxySocket.on('close', listeners.onIncomingClose);
|
||||
reverseProxy.incoming.socket.on('end', listeners.onOutgoingClose);
|
||||
reverseProxy.incoming.socket.on('close', listeners.onOutgoingClose);
|
||||
}
|
||||
|
||||
function getPort (port) {
|
||||
port = port || 80;
|
||||
return port - 80 === 0 ? '' : ':' + port;
|
||||
}
|
||||
|
||||
//
|
||||
// Get the protocol, and host for this request and create an instance
|
||||
// of `http.Agent` or `https.Agent` from the pool managed by `node-http-proxy`.
|
||||
//
|
||||
var agent = this.target.agent,
|
||||
protocolName = this.target.https ? 'https' : 'http',
|
||||
portUri = getPort(this.source.port),
|
||||
remoteHost = this.target.host + portUri;
|
||||
|
||||
//
|
||||
// Change headers (if requested).
|
||||
//
|
||||
if (this.changeOrigin) {
|
||||
req.headers.host = remoteHost;
|
||||
req.headers.origin = protocolName + '://' + remoteHost;
|
||||
}
|
||||
|
||||
//
|
||||
// Make the outgoing WebSocket request
|
||||
//
|
||||
outgoing.host = this.target.host;
|
||||
outgoing.port = this.target.port;
|
||||
outgoing.agent = agent;
|
||||
outgoing.method = 'GET';
|
||||
outgoing.path = req.url;
|
||||
outgoing.headers = req.headers;
|
||||
outgoing.agent = agent;
|
||||
|
||||
var reverseProxy = this.target.protocol.request(outgoing);
|
||||
|
||||
//
|
||||
// On any errors from the `reverseProxy` emit the
|
||||
// `webSocketProxyError` and close the appropriate
|
||||
// connections.
|
||||
//
|
||||
function proxyError (err) {
|
||||
reverseProxy.destroy();
|
||||
|
||||
process.nextTick(function () {
|
||||
//
|
||||
// Destroy the incoming socket in the next tick, in case the error handler
|
||||
// wants to write to it.
|
||||
//
|
||||
socket.destroy();
|
||||
});
|
||||
|
||||
self.emit('webSocketProxyError', err, req, socket, head);
|
||||
}
|
||||
|
||||
//
|
||||
// Here we set the incoming `req`, `socket` and `head` data to the outgoing
|
||||
// request so that we can reuse this data later on in the closure scope
|
||||
// available to the `upgrade` event. This bookkeeping is not tracked anywhere
|
||||
// in nodejs core and is **very** specific to proxying WebSockets.
|
||||
//
|
||||
reverseProxy.incoming = {
|
||||
request: req,
|
||||
socket: socket,
|
||||
head: head
|
||||
};
|
||||
|
||||
//
|
||||
// Here we set the handshake `headers` and `statusCode` data to the outgoing
|
||||
// request so that we can reuse this data later.
|
||||
//
|
||||
reverseProxy.handshake = {
|
||||
headers: {},
|
||||
statusCode: null,
|
||||
}
|
||||
|
||||
//
|
||||
// If the agent for this particular `host` and `port` combination
|
||||
// is not already listening for the `upgrade` event, then do so once.
|
||||
// This will force us not to disconnect.
|
||||
//
|
||||
// In addition, it's important to note the closure scope here. Since
|
||||
// there is no mapping of the socket to the request bound to it.
|
||||
//
|
||||
reverseProxy.on('upgrade', function (res, remoteSocket, head) {
|
||||
//
|
||||
// Prepare handshake response 'headers' and 'statusCode'.
|
||||
//
|
||||
reverseProxy.handshake = {
|
||||
headers: res.headers,
|
||||
statusCode: res.statusCode,
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare the socket for the reverseProxy request and begin to
|
||||
// stream data between the two sockets. Here it is important to
|
||||
// note that `remoteSocket._httpMessage === reverseProxy`.
|
||||
//
|
||||
_socket(remoteSocket, true);
|
||||
onUpgrade(remoteSocket._httpMessage, remoteSocket);
|
||||
});
|
||||
|
||||
//
|
||||
// If the reverseProxy connection has an underlying socket,
|
||||
// then execute the WebSocket handshake.
|
||||
//
|
||||
reverseProxy.once('socket', function (revSocket) {
|
||||
revSocket.on('data', function handshake (data) {
|
||||
// Set empty headers
|
||||
var headers = '';
|
||||
|
||||
//
|
||||
// If the handshake statusCode 101, concat headers.
|
||||
//
|
||||
if (reverseProxy.handshake.statusCode && reverseProxy.handshake.statusCode == 101) {
|
||||
headers = [
|
||||
'HTTP/1.1 101 Switching Protocols',
|
||||
'Upgrade: websocket',
|
||||
'Connection: Upgrade',
|
||||
'Sec-WebSocket-Accept: ' + reverseProxy.handshake.headers['sec-websocket-accept']
|
||||
];
|
||||
|
||||
headers = headers.concat('', '').join('\r\n');
|
||||
}
|
||||
|
||||
//
|
||||
// Ok, kind of harmfull part of code. Socket.IO sends a hash
|
||||
// at the end of handshake if protocol === 76, but we need
|
||||
// to replace 'host' and 'origin' in response so we split
|
||||
// data to printable data and to non-printable. (Non-printable
|
||||
// will come after double-CRLF).
|
||||
//
|
||||
var sdata = data.toString();
|
||||
|
||||
// Get the Printable data
|
||||
sdata = sdata.substr(0, sdata.search(CRLF + CRLF));
|
||||
|
||||
// Get the Non-Printable data
|
||||
data = data.slice(Buffer.byteLength(sdata), data.length);
|
||||
|
||||
if (self.source.https && !self.target.https) {
|
||||
//
|
||||
// If the proxy server is running HTTPS but the client is running
|
||||
// HTTP then replace `ws` with `wss` in the data sent back to the client.
|
||||
//
|
||||
sdata = sdata.replace('ws:', 'wss:');
|
||||
}
|
||||
|
||||
try {
|
||||
//
|
||||
// Write the printable and non-printable data to the socket
|
||||
// from the original incoming request.
|
||||
//
|
||||
self.emit('websocket:handshake', req, socket, head, sdata, data);
|
||||
// add headers to the socket
|
||||
socket.write(headers + sdata);
|
||||
var flushed = socket.write(data);
|
||||
if (!flushed) {
|
||||
revSocket.pause();
|
||||
socket.once('drain', function () {
|
||||
try { revSocket.resume() }
|
||||
catch (er) { console.error("reverseProxy.socket.resume error: %s", er.message) }
|
||||
});
|
||||
|
||||
//
|
||||
// Force the `drain` event in 100ms if it hasn't
|
||||
// happened on its own.
|
||||
//
|
||||
setTimeout(function () {
|
||||
socket.emit('drain');
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
//
|
||||
// Remove data listener on socket error because the
|
||||
// 'handshake' has failed.
|
||||
//
|
||||
revSocket.removeListener('data', handshake);
|
||||
return proxyError(ex);
|
||||
}
|
||||
|
||||
//
|
||||
// Remove data listener now that the 'handshake' is complete
|
||||
//
|
||||
revSocket.removeListener('data', handshake);
|
||||
});
|
||||
});
|
||||
|
||||
//
|
||||
// Handle 'error' events from the `reverseProxy`.
|
||||
//
|
||||
reverseProxy.on('error', proxyError);
|
||||
|
||||
//
|
||||
// Handle 'error' events from the `req` (e.g. `Parse Error`).
|
||||
//
|
||||
req.on('error', proxyError);
|
||||
|
||||
try {
|
||||
//
|
||||
// Attempt to write the upgrade-head to the reverseProxy
|
||||
// request. This is small, and there's only ever one of
|
||||
// it; no need for pause/resume.
|
||||
//
|
||||
// XXX This is very wrong and should be fixed in node's core
|
||||
//
|
||||
reverseProxy.write(head);
|
||||
if (head && head.length === 0) {
|
||||
reverseProxy._send('');
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
return proxyError(ex);
|
||||
}
|
||||
|
||||
//
|
||||
// If we have been passed buffered data, resume it.
|
||||
//
|
||||
if (buffer) {
|
||||
return !errState
|
||||
? buffer.resume()
|
||||
: buffer.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// ### function close()
|
||||
// Closes all sockets associated with the Agents
|
||||
// belonging to this instance.
|
||||
//
|
||||
HttpProxy.prototype.close = function () {
|
||||
[this.forward, this.target].forEach(function (proxy) {
|
||||
if (proxy && proxy.agent) {
|
||||
for (var host in proxy.agent.sockets) {
|
||||
proxy.agent.sockets[host].forEach(function (socket) {
|
||||
socket.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// ### @private function _forwardRequest (req)
|
||||
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
|
||||
// Forwards the specified `req` to the location specified
|
||||
// by `this.forward` ignoring errors and the subsequent response.
|
||||
//
|
||||
HttpProxy.prototype._forwardRequest = function (req) {
|
||||
var self = this,
|
||||
outgoing = new(this.forward.base),
|
||||
forwardProxy;
|
||||
|
||||
//
|
||||
// Setup outgoing proxy with relevant properties.
|
||||
//
|
||||
outgoing.host = this.forward.host;
|
||||
outgoing.port = this.forward.port,
|
||||
outgoing.agent = this.forward.agent;
|
||||
outgoing.method = req.method;
|
||||
outgoing.path = req.url;
|
||||
outgoing.headers = req.headers;
|
||||
|
||||
//
|
||||
// Open new HTTP request to internal resource with will
|
||||
// act as a reverse proxy pass.
|
||||
//
|
||||
forwardProxy = this.forward.protocol.request(outgoing, function (response) {
|
||||
//
|
||||
// Ignore the response from the forward proxy since this is a 'fire-and-forget' proxy.
|
||||
// Remark (indexzero): We will eventually emit a 'forward' event here for performance tuning.
|
||||
//
|
||||
});
|
||||
|
||||
//
|
||||
// Add a listener for the connection timeout event.
|
||||
//
|
||||
// Remark: Ignoring this error in the event
|
||||
// forward target doesn't exist.
|
||||
//
|
||||
forwardProxy.once('error', function (err) { });
|
||||
|
||||
//
|
||||
// Chunk the client request body as chunks from
|
||||
// the proxied request come in
|
||||
//
|
||||
req.on('data', function (chunk) {
|
||||
var flushed = forwardProxy.write(chunk);
|
||||
if (!flushed) {
|
||||
req.pause();
|
||||
forwardProxy.once('drain', function () {
|
||||
try { req.resume() }
|
||||
catch (er) { console.error("req.resume error: %s", er.message) }
|
||||
});
|
||||
|
||||
//
|
||||
// Force the `drain` event in 100ms if it hasn't
|
||||
// happened on its own.
|
||||
//
|
||||
setTimeout(function () {
|
||||
forwardProxy.emit('drain');
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// At the end of the client request, we are going to
|
||||
// stop the proxied request
|
||||
//
|
||||
req.on('end', function () {
|
||||
forwardProxy.end();
|
||||
});
|
||||
};
|
||||
|
||||
function getPortFromHostHeader(req) {
|
||||
var match;
|
||||
if ((match = extractPort.exec(req.headers.host))) {
|
||||
return parseInt(match[1]);
|
||||
}
|
||||
|
||||
return getProto(req) === 'https' ? 443 : 80;
|
||||
}
|
||||
|
||||
function getProto(req) {
|
||||
return req.isSpdy ? 'https' : (req.connection.pair ? 'https' : 'http');
|
||||
}
|
||||
@ -1,282 +0,0 @@
|
||||
/*
|
||||
node-http-proxy.js: Lookup table for proxy targets in node.js
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
var util = require('util'),
|
||||
events = require('events'),
|
||||
fs = require('fs'),
|
||||
url = require('url');
|
||||
|
||||
//
|
||||
// ### function ProxyTable (router, silent)
|
||||
// #### @router {Object} Object containing the host based routes
|
||||
// #### @silent {Boolean} Value indicating whether we should suppress logs
|
||||
// #### @hostnameOnly {Boolean} Value indicating if we should route based on __hostname string only__
|
||||
// #### @pathnameOnly {Boolean} Value indicating if we should route based on only the pathname. __This causes hostnames to be ignored.__. Using this along with hostnameOnly wont work at all.
|
||||
// Constructor function for the ProxyTable responsible for getting
|
||||
// locations of proxy targets based on ServerRequest headers; specifically
|
||||
// the HTTP host header.
|
||||
//
|
||||
var ProxyTable = exports.ProxyTable = function (options) {
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
this.silent = options.silent || options.silent !== true;
|
||||
this.target = options.target || {};
|
||||
this.pathnameOnly = options.pathnameOnly === true;
|
||||
this.hostnameOnly = options.hostnameOnly === true;
|
||||
|
||||
if (typeof options.router === 'object') {
|
||||
//
|
||||
// If we are passed an object literal setup
|
||||
// the routes with RegExps from the router
|
||||
//
|
||||
this.setRoutes(options.router);
|
||||
}
|
||||
else if (typeof options.router === 'string') {
|
||||
//
|
||||
// If we are passed a string then assume it is a
|
||||
// file path, parse that file and watch it for changes
|
||||
//
|
||||
var self = this;
|
||||
this.routeFile = options.router;
|
||||
this.setRoutes(JSON.parse(fs.readFileSync(options.router)).router);
|
||||
|
||||
fs.watchFile(this.routeFile, function () {
|
||||
fs.readFile(self.routeFile, function (err, data) {
|
||||
if (err) {
|
||||
self.emit('error', err);
|
||||
}
|
||||
|
||||
self.setRoutes(JSON.parse(data).router);
|
||||
self.emit('routes', self.hostnameOnly === false ? self.routes : self.router);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
throw new Error('Cannot parse router with unknown type: ' + typeof router);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Inherit from `events.EventEmitter`
|
||||
//
|
||||
util.inherits(ProxyTable, events.EventEmitter);
|
||||
|
||||
//
|
||||
// ### function addRoute (route, target)
|
||||
// #### @route {String} String containing route coming in
|
||||
// #### @target {String} String containing the target
|
||||
// Adds a host-based route to this instance.
|
||||
//
|
||||
ProxyTable.prototype.addRoute = function (route, target) {
|
||||
if (!this.router) {
|
||||
throw new Error('Cannot update ProxyTable routes without router.');
|
||||
}
|
||||
|
||||
this.router[route] = target;
|
||||
this.setRoutes(this.router);
|
||||
};
|
||||
|
||||
//
|
||||
// ### function removeRoute (route)
|
||||
// #### @route {String} String containing route to remove
|
||||
// Removes a host-based route from this instance.
|
||||
//
|
||||
ProxyTable.prototype.removeRoute = function (route) {
|
||||
if (!this.router) {
|
||||
throw new Error('Cannot update ProxyTable routes without router.');
|
||||
}
|
||||
|
||||
delete this.router[route];
|
||||
this.setRoutes(this.router);
|
||||
};
|
||||
|
||||
//
|
||||
// ### function setRoutes (router)
|
||||
// #### @router {Object} Object containing the host based routes
|
||||
// Sets the host-based routes to be used by this instance.
|
||||
//
|
||||
ProxyTable.prototype.setRoutes = function (router) {
|
||||
if (!router) {
|
||||
throw new Error('Cannot update ProxyTable routes without router.');
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.router = router;
|
||||
|
||||
if (this.hostnameOnly === false) {
|
||||
this.routes = [];
|
||||
|
||||
Object.keys(router).forEach(function (path) {
|
||||
if (!/http[s]?/.test(router[path])) {
|
||||
router[path] = (self.target.https ? 'https://' : 'http://')
|
||||
+ router[path];
|
||||
}
|
||||
|
||||
var target = url.parse(router[path]),
|
||||
defaultPort = self.target.https ? 443 : 80;
|
||||
|
||||
//
|
||||
// Setup a robust lookup table for the route:
|
||||
//
|
||||
// {
|
||||
// source: {
|
||||
// regexp: /^foo.com/i,
|
||||
// sref: 'foo.com',
|
||||
// url: {
|
||||
// protocol: 'http:',
|
||||
// slashes: true,
|
||||
// host: 'foo.com',
|
||||
// hostname: 'foo.com',
|
||||
// href: 'http://foo.com/',
|
||||
// pathname: '/',
|
||||
// path: '/'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// target: {
|
||||
// sref: '127.0.0.1:8000/',
|
||||
// url: {
|
||||
// protocol: 'http:',
|
||||
// slashes: true,
|
||||
// host: '127.0.0.1:8000',
|
||||
// hostname: '127.0.0.1',
|
||||
// href: 'http://127.0.0.1:8000/',
|
||||
// pathname: '/',
|
||||
// path: '/'
|
||||
// }
|
||||
// },
|
||||
//
|
||||
self.routes.push({
|
||||
source: {
|
||||
regexp: new RegExp('^' + path, 'i'),
|
||||
sref: path,
|
||||
url: url.parse('http://' + path)
|
||||
},
|
||||
target: {
|
||||
sref: target.hostname + ':' + (target.port || defaultPort) + target.path,
|
||||
url: target
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// ### function getProxyLocation (req)
|
||||
// #### @req {ServerRequest} The incoming server request to get proxy information about.
|
||||
// Returns the proxy location based on the HTTP Headers in the ServerRequest `req`
|
||||
// available to this instance.
|
||||
//
|
||||
ProxyTable.prototype.getProxyLocation = function (req) {
|
||||
if (!req || !req.headers || !req.headers.host) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var targetHost = req.headers.host.split(':')[0];
|
||||
if (this.hostnameOnly === true) {
|
||||
var target = targetHost;
|
||||
if (this.router.hasOwnProperty(target)) {
|
||||
var location = this.router[target].split(':'),
|
||||
host = location[0],
|
||||
port = location.length === 1 ? 80 : location[1];
|
||||
|
||||
return {
|
||||
port: port,
|
||||
host: host
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (this.pathnameOnly === true) {
|
||||
var target = req.url;
|
||||
for (var i in this.routes) {
|
||||
var route = this.routes[i];
|
||||
//
|
||||
// If we are matching pathname only, we remove the matched pattern.
|
||||
//
|
||||
// IE /wiki/heartbeat
|
||||
// is redirected to
|
||||
// /heartbeat
|
||||
//
|
||||
// for the route "/wiki" : "127.0.0.1:8020"
|
||||
//
|
||||
if (target.match(route.source.regexp)) {
|
||||
req.url = url.format(target.replace(route.source.regexp, ''));
|
||||
return {
|
||||
protocol: route.target.url.protocol.replace(':', ''),
|
||||
host: route.target.url.hostname,
|
||||
port: route.target.url.port
|
||||
|| (this.target.https ? 443 : 80)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
var target = targetHost + req.url;
|
||||
for (var i in this.routes) {
|
||||
var route = this.routes[i];
|
||||
if (target.match(route.source.regexp)) {
|
||||
//
|
||||
// Attempt to perform any path replacement for differences
|
||||
// between the source path and the target path. This replaces the
|
||||
// path's part of the URL to the target's part of the URL.
|
||||
//
|
||||
// 1. Parse the request URL
|
||||
// 2. Replace any portions of the source path with the target path
|
||||
// 3. Set the request URL to the formatted URL with replacements.
|
||||
//
|
||||
var parsed = url.parse(req.url);
|
||||
|
||||
parsed.pathname = parsed.pathname.replace(
|
||||
route.source.url.pathname,
|
||||
route.target.url.pathname
|
||||
);
|
||||
|
||||
req.url = url.format(parsed);
|
||||
|
||||
return {
|
||||
protocol: route.target.url.protocol.replace(':', ''),
|
||||
host: route.target.url.hostname,
|
||||
port: route.target.url.port
|
||||
|| (this.target.https ? 443 : 80)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
//
|
||||
// ### close function ()
|
||||
// Cleans up the event listeneners maintained
|
||||
// by this instance.
|
||||
//
|
||||
ProxyTable.prototype.close = function () {
|
||||
if (typeof this.routeFile === 'string') {
|
||||
fs.unwatchFile(this.routeFile);
|
||||
}
|
||||
};
|
||||
@ -1,322 +0,0 @@
|
||||
/*
|
||||
* routing-proxy.js: A routing proxy consuming a RoutingTable and multiple HttpProxy instances
|
||||
*
|
||||
* (C) 2011 Nodejitsu Inc.
|
||||
* MIT LICENCE
|
||||
*
|
||||
*/
|
||||
|
||||
var events = require('events'),
|
||||
utile = require('utile'),
|
||||
HttpProxy = require('./http-proxy').HttpProxy,
|
||||
ProxyTable = require('./proxy-table').ProxyTable;
|
||||
|
||||
//
|
||||
// ### function RoutingProxy (options)
|
||||
// #### @options {Object} Options for this instance
|
||||
// Constructor function for the RoutingProxy object, a higher level
|
||||
// reverse proxy Object which can proxy to multiple hosts and also interface
|
||||
// easily with a RoutingTable instance.
|
||||
//
|
||||
var RoutingProxy = exports.RoutingProxy = function (options) {
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
var self = this;
|
||||
options = options || {};
|
||||
|
||||
if (options.router) {
|
||||
this.proxyTable = new ProxyTable(options);
|
||||
this.proxyTable.on('routes', function (routes) {
|
||||
self.emit('routes', routes);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Create a set of `HttpProxy` objects to be used later on calls
|
||||
// to `.proxyRequest()` and `.proxyWebSocketRequest()`.
|
||||
//
|
||||
this.proxies = {};
|
||||
|
||||
//
|
||||
// Setup default target options (such as `https`).
|
||||
//
|
||||
this.target = {};
|
||||
this.target.https = options.target && options.target.https;
|
||||
this.target.maxSockets = options.target && options.target.maxSockets;
|
||||
|
||||
//
|
||||
// Setup other default options to be used for instances of
|
||||
// `HttpProxy` created by this `RoutingProxy` instance.
|
||||
//
|
||||
this.source = options.source || { host: 'localhost', port: 8000 };
|
||||
this.https = this.source.https || options.https;
|
||||
this.enable = options.enable;
|
||||
this.forward = options.forward;
|
||||
this.changeOrigin = options.changeOrigin || false;
|
||||
|
||||
//
|
||||
// Listen for 'newListener' events so that we can bind 'proxyError'
|
||||
// listeners to each HttpProxy's 'proxyError' event.
|
||||
//
|
||||
this.on('newListener', function (evt) {
|
||||
if (evt === 'proxyError' || evt === 'webSocketProxyError') {
|
||||
Object.keys(self.proxies).forEach(function (key) {
|
||||
self.proxies[key].on(evt, self.emit.bind(self, evt));
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Inherit from `events.EventEmitter`.
|
||||
//
|
||||
utile.inherits(RoutingProxy, events.EventEmitter);
|
||||
|
||||
//
|
||||
// ### function add (options)
|
||||
// #### @options {Object} Options for the `HttpProxy` to add.
|
||||
// Adds a new instance of `HttpProxy` to this `RoutingProxy` instance
|
||||
// for the specified `options.host` and `options.port`.
|
||||
//
|
||||
RoutingProxy.prototype.add = function (options) {
|
||||
var self = this,
|
||||
key = this._getKey(options);
|
||||
|
||||
//
|
||||
// TODO: Consume properties in `options` related to the `ProxyTable`.
|
||||
//
|
||||
options.target = options.target || {};
|
||||
options.target.host = options.target.host || options.host;
|
||||
options.target.port = options.target.port || options.port;
|
||||
options.target.socketPath = options.target.socketPath || options.socketPath;
|
||||
options.target.https = this.target && this.target.https ||
|
||||
options.target && options.target.https;
|
||||
options.target.maxSockets = this.target && this.target.maxSockets;
|
||||
|
||||
//
|
||||
// Setup options to pass-thru to the new `HttpProxy` instance
|
||||
// for the specified `options.host` and `options.port` pair.
|
||||
//
|
||||
['https', 'enable', 'forward', 'changeOrigin'].forEach(function (key) {
|
||||
if (options[key] !== false && self[key]) {
|
||||
options[key] = self[key];
|
||||
}
|
||||
});
|
||||
|
||||
this.proxies[key] = new HttpProxy(options);
|
||||
|
||||
if (this.listeners('proxyError').length > 0) {
|
||||
this.proxies[key].on('proxyError', this.emit.bind(this, 'proxyError'));
|
||||
}
|
||||
|
||||
if (this.listeners('webSocketProxyError').length > 0) {
|
||||
this.proxies[key].on('webSocketProxyError', this.emit.bind(this, 'webSocketProxyError'));
|
||||
}
|
||||
|
||||
[
|
||||
'start',
|
||||
'forward',
|
||||
'end',
|
||||
'proxyResponse',
|
||||
'websocket:start',
|
||||
'websocket:end',
|
||||
'websocket:incoming',
|
||||
'websocket:outgoing'
|
||||
].forEach(function (event) {
|
||||
this.proxies[key].on(event, this.emit.bind(this, event));
|
||||
}, this);
|
||||
};
|
||||
|
||||
//
|
||||
// ### function remove (options)
|
||||
// #### @options {Object} Options mapping to the `HttpProxy` to remove.
|
||||
// Removes an instance of `HttpProxy` from this `RoutingProxy` instance
|
||||
// for the specified `options.host` and `options.port` (if they exist).
|
||||
//
|
||||
RoutingProxy.prototype.remove = function (options) {
|
||||
var key = this._getKey(options),
|
||||
proxy = this.proxies[key];
|
||||
|
||||
delete this.proxies[key];
|
||||
return proxy;
|
||||
};
|
||||
|
||||
//
|
||||
// ### function close()
|
||||
// Cleans up any state left behind (sockets, timeouts, etc)
|
||||
// associated with this instance.
|
||||
//
|
||||
RoutingProxy.prototype.close = function () {
|
||||
var self = this;
|
||||
|
||||
if (this.proxyTable) {
|
||||
//
|
||||
// Close the `RoutingTable` associated with
|
||||
// this instance (if any).
|
||||
//
|
||||
this.proxyTable.close();
|
||||
}
|
||||
|
||||
//
|
||||
// Close all sockets for all `HttpProxy` object(s)
|
||||
// associated with this instance.
|
||||
//
|
||||
Object.keys(this.proxies).forEach(function (key) {
|
||||
self.proxies[key].close();
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// ### function proxyRequest (req, res, [port, host, paused])
|
||||
// #### @req {ServerRequest} Incoming HTTP Request to proxy.
|
||||
// #### @res {ServerResponse} Outgoing HTTP Request to write proxied data to.
|
||||
// #### @options {Object} Options for the outgoing proxy request.
|
||||
//
|
||||
// options.port {number} Port to use on the proxy target host.
|
||||
// options.host {string} Host of the proxy target.
|
||||
// options.buffer {Object} Result from `httpProxy.buffer(req)`
|
||||
// options.https {Object|boolean} Settings for https.
|
||||
//
|
||||
RoutingProxy.prototype.proxyRequest = function (req, res, options) {
|
||||
options = options || {};
|
||||
|
||||
var location;
|
||||
|
||||
//
|
||||
// Check the proxy table for this instance to see if we need
|
||||
// to get the proxy location for the request supplied. We will
|
||||
// always ignore the proxyTable if an explicit `port` and `host`
|
||||
// arguments are supplied to `proxyRequest`.
|
||||
//
|
||||
if (this.proxyTable && !options.host) {
|
||||
location = this.proxyTable.getProxyLocation(req);
|
||||
|
||||
//
|
||||
// If no location is returned from the ProxyTable instance
|
||||
// then respond with `404` since we do not have a valid proxy target.
|
||||
//
|
||||
if (!location) {
|
||||
try {
|
||||
if (!this.emit('notFound', req, res)) {
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
}
|
||||
}
|
||||
catch (er) {
|
||||
console.error("res.writeHead/res.end error: %s", er.message);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// When using the ProxyTable in conjunction with an HttpProxy instance
|
||||
// only the following arguments are valid:
|
||||
//
|
||||
// * `proxy.proxyRequest(req, res, { host: 'localhost' })`: This will be skipped
|
||||
// * `proxy.proxyRequest(req, res, { buffer: buffer })`: Buffer will get updated appropriately
|
||||
// * `proxy.proxyRequest(req, res)`: Options will be assigned appropriately.
|
||||
//
|
||||
options.port = location.port;
|
||||
options.host = location.host;
|
||||
}
|
||||
|
||||
var key = this._getKey(options),
|
||||
proxy;
|
||||
|
||||
if ((this.target && this.target.https)
|
||||
|| (location && location.protocol === 'https')) {
|
||||
options.target = options.target || {};
|
||||
options.target.https = true;
|
||||
}
|
||||
|
||||
if (!this.proxies[key]) {
|
||||
this.add(utile.clone(options));
|
||||
}
|
||||
|
||||
proxy = this.proxies[key];
|
||||
proxy.proxyRequest(req, res, options.buffer);
|
||||
};
|
||||
|
||||
//
|
||||
// ### function proxyWebSocketRequest (req, socket, head, options)
|
||||
// #### @req {ServerRequest} Websocket request to proxy.
|
||||
// #### @socket {net.Socket} Socket for the underlying HTTP request
|
||||
// #### @head {string} Headers for the Websocket request.
|
||||
// #### @options {Object} Options to use when proxying this request.
|
||||
//
|
||||
// options.port {number} Port to use on the proxy target host.
|
||||
// options.host {string} Host of the proxy target.
|
||||
// options.buffer {Object} Result from `httpProxy.buffer(req)`
|
||||
// options.https {Object|boolean} Settings for https.
|
||||
//
|
||||
RoutingProxy.prototype.proxyWebSocketRequest = function (req, socket, head, options) {
|
||||
options = options || {};
|
||||
|
||||
var location,
|
||||
proxy,
|
||||
key;
|
||||
|
||||
if (this.proxyTable && !options.host) {
|
||||
location = this.proxyTable.getProxyLocation(req);
|
||||
|
||||
if (!location) {
|
||||
return socket.destroy();
|
||||
}
|
||||
|
||||
options.port = location.port;
|
||||
options.host = location.host;
|
||||
}
|
||||
|
||||
key = this._getKey(options);
|
||||
|
||||
if (!this.proxies[key]) {
|
||||
this.add(utile.clone(options));
|
||||
}
|
||||
|
||||
proxy = this.proxies[key];
|
||||
proxy.proxyWebSocketRequest(req, socket, head, options.buffer);
|
||||
};
|
||||
|
||||
//
|
||||
// ### function addHost (host, target)
|
||||
// #### @host {String} Host to add to proxyTable
|
||||
// #### @target {String} Target to add to proxyTable
|
||||
// Adds a host to proxyTable
|
||||
//
|
||||
RoutingProxy.prototype.addHost = function (host, target) {
|
||||
if (this.proxyTable) {
|
||||
this.proxyTable.addRoute(host, target);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// ### function removeHost (host)
|
||||
// #### @host {String} Host to remove from proxyTable
|
||||
// Removes a host to proxyTable
|
||||
//
|
||||
RoutingProxy.prototype.removeHost = function (host) {
|
||||
if (this.proxyTable) {
|
||||
this.proxyTable.removeRoute(host);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// ### @private function _getKey (options)
|
||||
// #### @options {Object} Options to extract the key from
|
||||
// Ensures that the appropriate options are present in the `options`
|
||||
// provided and responds with a string key representing the `host`, `port`
|
||||
// combination contained within.
|
||||
//
|
||||
RoutingProxy.prototype._getKey = function (options) {
|
||||
if (!options || ((!options.host || !options.port)
|
||||
&& (!options.target || !options.target.host || !options.target.port))) {
|
||||
throw new Error('options.host and options.port or options.target are required.');
|
||||
}
|
||||
|
||||
return [
|
||||
options.host || options.target.host,
|
||||
options.port || options.target.port
|
||||
].join(':');
|
||||
};
|
||||
2028
package-lock.json
generated
Normal file
2028
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
60
package.json
60
package.json
@ -1,47 +1,41 @@
|
||||
{
|
||||
"name": "http-proxy",
|
||||
"version": "0.10.4",
|
||||
"description": "A full-featured http reverse proxy for node.js",
|
||||
"author": "Nodejitsu Inc. <info@nodejitsu.com>",
|
||||
"maintainers": [
|
||||
"indexzero <charlie@nodejitsu.com>",
|
||||
"AvianFlu <avianflu@nodejitsu.com>"
|
||||
],
|
||||
"version": "1.18.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/nodejitsu/node-http-proxy.git"
|
||||
"url": "https://github.com/http-party/node-http-proxy.git"
|
||||
},
|
||||
"keywords": [
|
||||
"reverse",
|
||||
"proxy",
|
||||
"http"
|
||||
"description": "HTTP proxying for the masses",
|
||||
"author": "Charlie Robbins <charlie.robbins@gmail.com>",
|
||||
"maintainers": [
|
||||
"jcrugzz <jcrugzz@gmail.com>"
|
||||
],
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"colors": "0.x.x",
|
||||
"optimist": "0.6.x",
|
||||
"pkginfo": "0.3.x",
|
||||
"utile": "~0.2.1"
|
||||
"eventemitter3": "^4.0.0",
|
||||
"requires-port": "^1.0.0",
|
||||
"follow-redirects": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"request": "2.14.x",
|
||||
"vows": "0.7.x",
|
||||
"async": "0.2.x",
|
||||
"socket.io": "0.9.11",
|
||||
"socket.io-client": "0.9.11",
|
||||
"ws": "0.4.23"
|
||||
},
|
||||
"main": "./lib/node-http-proxy",
|
||||
"bin": {
|
||||
"node-http-proxy": "./bin/node-http-proxy"
|
||||
"async": "^3.0.0",
|
||||
"auto-changelog": "^1.15.0",
|
||||
"concat-stream": "^2.0.0",
|
||||
"expect.js": "~0.3.1",
|
||||
"mocha": "^3.5.3",
|
||||
"nyc": "^14.0.0",
|
||||
"semver": "^5.0.3",
|
||||
"socket.io": "^2.1.0",
|
||||
"socket.io-client": "^2.1.0",
|
||||
"sse": "0.0.8",
|
||||
"ws": "^3.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "npm run-script test-http && npm run-script test-https && npm run-script test-core",
|
||||
"test-http": "vows --spec && vows --spec --target=https",
|
||||
"test-https": "vows --spec --proxy=https && vows --spec --proxy=https --target=https",
|
||||
"test-core": "test/core/run"
|
||||
"mocha": "mocha test/*-test.js",
|
||||
"test": "nyc --reporter=text --reporter=lcov npm run mocha",
|
||||
"version": "auto-changelog -p && git add CHANGELOG.md"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6.6"
|
||||
}
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
|
||||
19
renovate.json
Normal file
19
renovate.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"platform": "github",
|
||||
"autodiscover": false,
|
||||
"requireConfig": true,
|
||||
"ignoreNpmrcFile": true,
|
||||
"rangeStrategy": "replace",
|
||||
"packageRules": [
|
||||
{
|
||||
"packagePatterns": [
|
||||
"*"
|
||||
],
|
||||
"minor": {
|
||||
"groupName": "all non-major dependencies",
|
||||
"groupSlug": "all-minor-patch"
|
||||
}
|
||||
}
|
||||
],
|
||||
"commitMessagePrefix": "[dist]"
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
# `test/core`
|
||||
|
||||
`test/core` directory is a place where tests from node.js core go. They are
|
||||
here to ensure that node-http-proxy works just fine with all kinds of
|
||||
different situations, which are covered in core tests, but are not covered in
|
||||
our tests.
|
||||
|
||||
All these tests require little modifications to make them test node-http-proxy,
|
||||
but we try to keep them as vanilla as possible.
|
||||
|
||||
@ -1,190 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var path = require('path');
|
||||
var assert = require('assert');
|
||||
|
||||
exports.testDir = path.dirname(__filename);
|
||||
exports.fixturesDir = path.join(exports.testDir, 'fixtures');
|
||||
exports.libDir = path.join(exports.testDir, '../lib');
|
||||
exports.tmpDir = path.join(exports.testDir, 'tmp');
|
||||
exports.PORT = 12346;
|
||||
exports.PROXY_PORT = 1234567;
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
exports.PIPE = '\\\\.\\pipe\\libuv-test';
|
||||
} else {
|
||||
exports.PIPE = exports.tmpDir + '/test.sock';
|
||||
}
|
||||
|
||||
var util = require('util');
|
||||
for (var i in util) exports[i] = util[i];
|
||||
//for (var i in exports) global[i] = exports[i];
|
||||
|
||||
function protoCtrChain(o) {
|
||||
var result = [];
|
||||
for (; o; o = o.__proto__) { result.push(o.constructor); }
|
||||
return result.join();
|
||||
}
|
||||
|
||||
exports.indirectInstanceOf = function (obj, cls) {
|
||||
if (obj instanceof cls) { return true; }
|
||||
var clsChain = protoCtrChain(cls.prototype);
|
||||
var objChain = protoCtrChain(obj);
|
||||
return objChain.slice(-clsChain.length) === clsChain;
|
||||
};
|
||||
|
||||
|
||||
exports.ddCommand = function (filename, kilobytes) {
|
||||
if (process.platform === 'win32') {
|
||||
var p = path.resolve(exports.fixturesDir, 'create-file.js');
|
||||
return '"' + process.argv[0] + '" "' + p + '" "' +
|
||||
filename + '" ' + (kilobytes * 1024);
|
||||
} else {
|
||||
return 'dd if=/dev/zero of="' + filename + '" bs=1024 count=' + kilobytes;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
exports.spawnPwd = function (options) {
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
return spawn('cmd.exe', ['/c', 'cd'], options);
|
||||
} else {
|
||||
return spawn('pwd', [], options);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Turn this off if the test should not check for global leaks.
|
||||
exports.globalCheck = true;
|
||||
|
||||
process.on('exit', function () {
|
||||
if (!exports.globalCheck) return;
|
||||
var knownGlobals = [setTimeout,
|
||||
setInterval,
|
||||
clearTimeout,
|
||||
clearInterval,
|
||||
console,
|
||||
Buffer,
|
||||
process,
|
||||
global];
|
||||
|
||||
if (global.setImmediate) {
|
||||
knownGlobals.push(setImmediate);
|
||||
knownGlobals.push(clearImmediate);
|
||||
}
|
||||
|
||||
if (global.errno) {
|
||||
knownGlobals.push(errno);
|
||||
}
|
||||
|
||||
if (global.gc) {
|
||||
knownGlobals.push(gc);
|
||||
}
|
||||
|
||||
if (global.DTRACE_HTTP_SERVER_RESPONSE) {
|
||||
knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE);
|
||||
knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST);
|
||||
knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE);
|
||||
knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST);
|
||||
knownGlobals.push(DTRACE_NET_STREAM_END);
|
||||
knownGlobals.push(DTRACE_NET_SERVER_CONNECTION);
|
||||
knownGlobals.push(DTRACE_NET_SOCKET_READ);
|
||||
knownGlobals.push(DTRACE_NET_SOCKET_WRITE);
|
||||
}
|
||||
|
||||
if (global.ArrayBuffer) {
|
||||
knownGlobals.push(ArrayBuffer);
|
||||
knownGlobals.push(Int8Array);
|
||||
knownGlobals.push(Uint8Array);
|
||||
knownGlobals.push(Int16Array);
|
||||
knownGlobals.push(Uint16Array);
|
||||
knownGlobals.push(Int32Array);
|
||||
knownGlobals.push(Uint32Array);
|
||||
knownGlobals.push(Float32Array);
|
||||
knownGlobals.push(Float64Array);
|
||||
knownGlobals.push(DataView);
|
||||
|
||||
if (global.Uint8ClampedArray) {
|
||||
knownGlobals.push(Uint8ClampedArray);
|
||||
}
|
||||
}
|
||||
|
||||
for (var x in global) {
|
||||
var found = false;
|
||||
|
||||
for (var y in knownGlobals) {
|
||||
if (global[x] === knownGlobals[y]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
console.error('Unknown global: %s', x);
|
||||
assert.ok(false, 'Unknown global found');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var mustCallChecks = [];
|
||||
|
||||
|
||||
function runCallChecks() {
|
||||
var failed = mustCallChecks.filter(function (context) {
|
||||
return context.actual !== context.expected;
|
||||
});
|
||||
|
||||
failed.forEach(function (context) {
|
||||
console.log('Mismatched %s function calls. Expected %d, actual %d.',
|
||||
context.name,
|
||||
context.expected,
|
||||
context.actual);
|
||||
console.log(context.stack.split('\n').slice(2).join('\n'));
|
||||
});
|
||||
|
||||
if (failed.length) process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
exports.mustCall = function (fn, expected) {
|
||||
if (typeof expected !== 'number') expected = 1;
|
||||
|
||||
var context = {
|
||||
expected: expected,
|
||||
actual: 0,
|
||||
stack: (new Error).stack,
|
||||
name: fn.name || '<anonymous>'
|
||||
};
|
||||
|
||||
// add the exit listener only once to avoid listener leak warnings
|
||||
if (mustCallChecks.length === 0) process.on('exit', runCallChecks);
|
||||
|
||||
mustCallChecks.push(context);
|
||||
|
||||
return function () {
|
||||
context.actual++;
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
@ -1,69 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// This tests setTimeout() by having multiple clients connecting and sending
|
||||
// data in random intervals. Clients are also randomly disconnecting until there
|
||||
// are no more clients left. If no false timeout occurs, this test has passed.
|
||||
var common = require('../common'),
|
||||
assert = require('assert'),
|
||||
http = require('http'),
|
||||
server = http.createServer(),
|
||||
connections = 0;
|
||||
|
||||
server.on('request', function (req, res) {
|
||||
req.socket.setTimeout(1000);
|
||||
req.socket.on('timeout', function () {
|
||||
throw new Error('Unexpected timeout');
|
||||
});
|
||||
req.on('end', function () {
|
||||
connections--;
|
||||
res.writeHead(200);
|
||||
res.end('done\n');
|
||||
if (connections == 0) {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(common.PORT, '127.0.0.1', function () {
|
||||
for (var i = 0; i < 10; i++) {
|
||||
connections++;
|
||||
|
||||
setTimeout(function () {
|
||||
var request = http.request({
|
||||
port: common.PROXY_PORT,
|
||||
method: 'POST',
|
||||
path: '/'
|
||||
});
|
||||
|
||||
function ping() {
|
||||
var nextPing = (Math.random() * 900).toFixed();
|
||||
if (nextPing > 600) {
|
||||
request.end();
|
||||
return;
|
||||
}
|
||||
request.write('ping');
|
||||
setTimeout(ping, nextPing);
|
||||
}
|
||||
ping();
|
||||
}, i * 50);
|
||||
}
|
||||
});
|
||||
@ -1,90 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/*
|
||||
run.js: test runner for core tests
|
||||
|
||||
Copyright (c) 2011 Nodejitsu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
spawn = require('child_process').spawn,
|
||||
async = require('async'),
|
||||
colors = require('colors'),
|
||||
optimist = require('optimist');
|
||||
|
||||
optimist.argv.color && (colors.mode = optimist.argv.color);
|
||||
|
||||
var testTimeout = 15000;
|
||||
var results = {};
|
||||
|
||||
function runTest(test, callback) {
|
||||
var child = spawn(path.join(__dirname, 'run-single'), [ test ]);
|
||||
|
||||
var killTimeout = setTimeout(function () {
|
||||
child.kill();
|
||||
console.log(' ' + path.basename(test).yellow + ' timed out'.red);
|
||||
}, testTimeout);
|
||||
|
||||
child.on('exit', function (exitCode) {
|
||||
clearTimeout(killTimeout);
|
||||
|
||||
console.log(' ' + ((exitCode) ? '✘'.red : '✔'.green) + ' ' +
|
||||
path.basename(test) +
|
||||
(exitCode ? (' (exit code: ' + exitCode + ')') : ''));
|
||||
results[test] = { exitCode: exitCode };
|
||||
callback();
|
||||
//
|
||||
// We don't want tests to be stopped after first failure, and that's what
|
||||
// async does when it receives truthy value in callback.
|
||||
//
|
||||
});
|
||||
};
|
||||
|
||||
var tests = process.argv.slice(2).filter(function (test) {
|
||||
return test.substr(0, 2) != '--';
|
||||
});
|
||||
|
||||
if (!tests.length) {
|
||||
var pathPrefix = path.join(__dirname, 'simple');
|
||||
tests = fs.readdirSync(pathPrefix).map(function (test) {
|
||||
return path.join(pathPrefix, test);
|
||||
});
|
||||
//
|
||||
// We only run simple tests by default.
|
||||
//
|
||||
}
|
||||
|
||||
console.log('Running tests:'.bold);
|
||||
async.forEachSeries(tests, runTest, function () {
|
||||
var failed = [], ok = [];
|
||||
for (var test in results) {
|
||||
(results[test].exitCode != 0 ? failed : ok).push(test);
|
||||
}
|
||||
|
||||
console.log('\nSummary:'.bold);
|
||||
console.log((' ' + ok.length + '\tpassed tests').green);
|
||||
console.log((' ' + failed.length + '\tfailed tests').red);
|
||||
});
|
||||
|
||||
// vim:filetype=javascript
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/*
|
||||
run-single.js: test runner for core tests
|
||||
|
||||
Copyright (c) 2011 Nodejitsu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
//
|
||||
// Basic idea behind core test runner is to modify core tests as little as
|
||||
// possible. That's why we start up node-http-proxy here instead of embeeding
|
||||
// this code in tests.
|
||||
//
|
||||
// In most cases only modification to core tests you'll need is changing port
|
||||
// of http client to common.PROXY_PORT.
|
||||
//
|
||||
|
||||
var path = require('path'),
|
||||
spawn = require('child_process').spawn,
|
||||
httpProxy = require('../../'),
|
||||
common = require('./common');
|
||||
|
||||
var test = process.argv[2],
|
||||
testProcess;
|
||||
|
||||
if (!test) {
|
||||
return console.error('Need test to run');
|
||||
}
|
||||
|
||||
console.log('Running test ' + test);
|
||||
|
||||
var proxy = httpProxy.createServer(common.PORT, 'localhost');
|
||||
proxy.listen(common.PROXY_PORT);
|
||||
|
||||
proxy.on('listening', function () {
|
||||
console.log('Proxy server listening on ' + common.PROXY_PORT);
|
||||
testProcess = spawn(process.argv[0], [ process.argv[2] ]);
|
||||
testProcess.stdout.pipe(process.stdout);
|
||||
testProcess.stderr.pipe(process.stderr);
|
||||
|
||||
testProcess.on('exit', function (code) {
|
||||
process.exit(code);
|
||||
});
|
||||
});
|
||||
|
||||
process.on('SIGTERM', function () {
|
||||
testProcess.kill();
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// vim:filetype=javascript
|
||||
@ -1,63 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
|
||||
var UTF8_STRING = '南越国是前203年至前111年存在于岭南地区的一个国家,' +
|
||||
'国都位于番禺,疆域包括今天中国的广东、广西两省区的大部份地区,福建省、湖南、' +
|
||||
'贵州、云南的一小部份地区和越南的北部。南越国是秦朝灭亡后,' +
|
||||
'由南海郡尉赵佗于前203年起兵兼并桂林郡和象郡后建立。前196年和前179年,' +
|
||||
'南越国曾先后两次名义上臣属于西汉,成为西汉的“外臣”。前112年,' +
|
||||
'南越国末代君主赵建德与西汉发生战争,被汉武帝于前111年所灭。' +
|
||||
'南越国共存在93年,历经五代君主。南越国是岭南地区的第一个有记载的政权国家,' +
|
||||
'采用封建制和郡县制并存的制度,它的建立保证了秦末乱世岭南地区社会秩序的稳定,' +
|
||||
'有效的改善了岭南地区落后的政治、经济现状。';
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
res.writeHead(200, {'Content-Type': 'text/plain; charset=utf8'});
|
||||
res.end(UTF8_STRING, 'utf8');
|
||||
});
|
||||
server.listen(common.PORT, function () {
|
||||
var data = '';
|
||||
var get = http.get({
|
||||
path: '/',
|
||||
host: 'localhost',
|
||||
port: common.PROXY_PORT
|
||||
}, function (x) {
|
||||
x.setEncoding('utf8');
|
||||
x.on('data', function (c) {data += c});
|
||||
x.on('error', function (e) {
|
||||
throw e;
|
||||
});
|
||||
x.on('end', function () {
|
||||
assert.equal('string', typeof data);
|
||||
console.log('here is the response:');
|
||||
assert.equal(UTF8_STRING, data);
|
||||
console.log(data);
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
get.on('error', function (e) {throw e});
|
||||
get.end();
|
||||
|
||||
});
|
||||
@ -1,80 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
|
||||
var clientAborts = 0;
|
||||
|
||||
var server = http.Server(function (req, res) {
|
||||
console.log('Got connection');
|
||||
res.writeHead(200);
|
||||
res.write('Working on it...');
|
||||
|
||||
// I would expect an error event from req or res that the client aborted
|
||||
// before completing the HTTP request / response cycle, or maybe a new
|
||||
// event like "aborted" or something.
|
||||
req.on('aborted', function () {
|
||||
clientAborts++;
|
||||
console.log('Got abort ' + clientAborts);
|
||||
if (clientAborts === N) {
|
||||
console.log('All aborts detected, you win.');
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
|
||||
// since there is already clientError, maybe that would be appropriate,
|
||||
// since "error" is magical
|
||||
req.on('clientError', function () {
|
||||
console.log('Got clientError');
|
||||
});
|
||||
});
|
||||
|
||||
var responses = 0;
|
||||
var N = http.Agent.defaultMaxSockets - 1;
|
||||
var requests = [];
|
||||
|
||||
server.listen(common.PORT, function () {
|
||||
console.log('Server listening.');
|
||||
|
||||
for (var i = 0; i < N; i++) {
|
||||
console.log('Making client ' + i);
|
||||
var options = { port: common.PROXY_PORT, path: '/?id=' + i };
|
||||
var req = http.get(options, function (res) {
|
||||
console.log('Client response code ' + res.statusCode);
|
||||
|
||||
if (++responses == N) {
|
||||
console.log('All clients connected, destroying.');
|
||||
requests.forEach(function (outReq) {
|
||||
console.log('abort');
|
||||
outReq.abort();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
requests.push(req);
|
||||
}
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.equal(N, clientAborts);
|
||||
});
|
||||
@ -1,41 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// libuv-broken
|
||||
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
res.end('Hello');
|
||||
});
|
||||
|
||||
server.listen(common.PORT, function () {
|
||||
var req = http.get({port: common.PROXY_PORT}, function (res) {
|
||||
res.on('data', function (data) {
|
||||
req.abort();
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,74 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
|
||||
var N = 1024;
|
||||
var bytesRecieved = 0;
|
||||
var server_req_complete = false;
|
||||
var client_res_complete = false;
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
assert.equal('POST', req.method);
|
||||
|
||||
req.on('data', function (chunk) {
|
||||
bytesRecieved += chunk.length;
|
||||
});
|
||||
|
||||
req.on('end', function () {
|
||||
server_req_complete = true;
|
||||
console.log('request complete from server');
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.write('hello\n');
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
server.listen(common.PORT);
|
||||
|
||||
server.on('listening', function () {
|
||||
var req = http.request({
|
||||
port: common.PROXY_PORT,
|
||||
method: 'POST',
|
||||
path: '/'
|
||||
}, function (res) {
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) {
|
||||
console.log(chunk);
|
||||
});
|
||||
res.on('end', function () {
|
||||
client_res_complete = true;
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
|
||||
req.write(new Buffer(N));
|
||||
req.end();
|
||||
|
||||
common.error('client finished sending request');
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.equal(N, bytesRecieved);
|
||||
assert.equal(true, server_req_complete);
|
||||
assert.equal(true, client_res_complete);
|
||||
});
|
||||
@ -1,77 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
|
||||
var sent_body = '';
|
||||
var server_req_complete = false;
|
||||
var client_res_complete = false;
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
assert.equal('POST', req.method);
|
||||
req.setEncoding('utf8');
|
||||
|
||||
req.on('data', function (chunk) {
|
||||
console.log('server got: ' + JSON.stringify(chunk));
|
||||
sent_body += chunk;
|
||||
});
|
||||
|
||||
req.on('end', function () {
|
||||
server_req_complete = true;
|
||||
console.log('request complete from server');
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.write('hello\n');
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
server.listen(common.PORT);
|
||||
|
||||
server.on('listening', function () {
|
||||
var req = http.request({
|
||||
port: common.PROXY_PORT,
|
||||
method: 'POST',
|
||||
path: '/'
|
||||
}, function (res) {
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) {
|
||||
console.log(chunk);
|
||||
});
|
||||
res.on('end', function () {
|
||||
client_res_complete = true;
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
|
||||
req.write('1\n');
|
||||
req.write('2\n');
|
||||
req.write('3\n');
|
||||
req.end();
|
||||
|
||||
common.error('client finished sending request');
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.equal('1\n2\n3\n', sent_body);
|
||||
assert.equal(true, server_req_complete);
|
||||
assert.equal(true, client_res_complete);
|
||||
});
|
||||
@ -1,42 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var http = require('http');
|
||||
|
||||
// Simple test of Node's HTTP Client choking on a response
|
||||
// with a 'Content-Length: 0 ' response header.
|
||||
// I.E. a space character after the 'Content-Length' throws an `error` event.
|
||||
|
||||
|
||||
var s = http.createServer(function (req, res) {
|
||||
res.writeHead(200, {'Content-Length': '0 '});
|
||||
res.end();
|
||||
});
|
||||
s.listen(common.PORT, function () {
|
||||
|
||||
var request = http.request({ port: common.PROXY_PORT }, function (response) {
|
||||
console.log('STATUS: ' + response.statusCode);
|
||||
s.close();
|
||||
});
|
||||
|
||||
request.end();
|
||||
});
|
||||
@ -1,40 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var net = require('net');
|
||||
var http = require('http');
|
||||
|
||||
// This is a regression test for http://github.com/ry/node/issues/#issue/44
|
||||
// It is separate from test-http-malformed-request.js because it is only
|
||||
// reproduceable on the first packet on the first connection to a server.
|
||||
|
||||
var server = http.createServer(function (req, res) {});
|
||||
server.listen(common.PORT);
|
||||
|
||||
server.on('listening', function () {
|
||||
net.createConnection(common.PROXY_PORT).on('connect', function () {
|
||||
this.destroy();
|
||||
}).on('close', function () {
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
@ -1,86 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
var net = require('net');
|
||||
|
||||
// If an HTTP server is broken and sends data after the end of the response,
|
||||
// node should ignore it and drop the connection.
|
||||
// Demos this bug: https://github.com/ry/node/issues/680
|
||||
|
||||
var body = 'hello world\r\n';
|
||||
var fullResponse =
|
||||
'HTTP/1.1 500 Internal Server Error\r\n' +
|
||||
'Content-Length: ' + body.length + '\r\n' +
|
||||
'Content-Type: text/plain\r\n' +
|
||||
'Date: Fri + 18 Feb 2011 06:22:45 GMT\r\n' +
|
||||
'Host: 10.20.149.2\r\n' +
|
||||
'Access-Control-Allow-Credentials: true\r\n' +
|
||||
'Server: badly broken/0.1 (OS NAME)\r\n' +
|
||||
'\r\n' +
|
||||
body;
|
||||
|
||||
var gotResponse = false;
|
||||
|
||||
|
||||
var server = net.createServer(function (socket) {
|
||||
var postBody = '';
|
||||
|
||||
socket.setEncoding('utf8');
|
||||
|
||||
socket.on('data', function (chunk) {
|
||||
postBody += chunk;
|
||||
|
||||
if (postBody.indexOf('\r\n') > -1) {
|
||||
socket.write(fullResponse);
|
||||
// omg, I wrote the response twice, what a terrible HTTP server I am.
|
||||
socket.end(fullResponse);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
server.listen(common.PORT, function () {
|
||||
http.get({ port: common.PROXY_PORT }, function (res) {
|
||||
var buffer = '';
|
||||
console.log('Got res code: ' + res.statusCode);
|
||||
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) {
|
||||
buffer += chunk;
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
console.log('Response ended, read ' + buffer.length + ' bytes');
|
||||
assert.equal(body, buffer);
|
||||
server.close();
|
||||
gotResponse = true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.ok(gotResponse);
|
||||
});
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
var util = require('util');
|
||||
|
||||
|
||||
var body = 'hello world\n';
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
common.error('req: ' + req.method);
|
||||
res.writeHead(200, {'Content-Length': body.length});
|
||||
res.end();
|
||||
server.close();
|
||||
});
|
||||
|
||||
var gotEnd = false;
|
||||
|
||||
server.listen(common.PORT, function () {
|
||||
var request = http.request({
|
||||
port: common.PROXY_PORT,
|
||||
method: 'HEAD',
|
||||
path: '/'
|
||||
}, function (response) {
|
||||
common.error('response start');
|
||||
response.on('end', function () {
|
||||
common.error('response end');
|
||||
gotEnd = true;
|
||||
});
|
||||
});
|
||||
request.end();
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.ok(gotEnd);
|
||||
});
|
||||
@ -1,61 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// libuv-broken
|
||||
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
|
||||
var http = require('http');
|
||||
|
||||
// This test is to make sure that when the HTTP server
|
||||
// responds to a HEAD request with data to res.end,
|
||||
// it does not send any body.
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
res.writeHead(200);
|
||||
res.end('FAIL'); // broken: sends FAIL from hot path.
|
||||
});
|
||||
server.listen(common.PORT);
|
||||
|
||||
var responseComplete = false;
|
||||
|
||||
server.on('listening', function () {
|
||||
var req = http.request({
|
||||
port: common.PROXY_PORT,
|
||||
method: 'HEAD',
|
||||
path: '/'
|
||||
}, function (res) {
|
||||
common.error('response');
|
||||
res.on('end', function () {
|
||||
common.error('response end');
|
||||
server.close();
|
||||
responseComplete = true;
|
||||
});
|
||||
});
|
||||
common.error('req');
|
||||
req.end();
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.ok(responseComplete);
|
||||
});
|
||||
@ -1,58 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
|
||||
var http = require('http');
|
||||
|
||||
// This test is to make sure that when the HTTP server
|
||||
// responds to a HEAD request, it does not send any body.
|
||||
// In this case it was sending '0\r\n\r\n'
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
res.writeHead(200); // broken: defaults to TE chunked
|
||||
res.end();
|
||||
});
|
||||
server.listen(common.PORT);
|
||||
|
||||
var responseComplete = false;
|
||||
|
||||
server.on('listening', function () {
|
||||
var req = http.request({
|
||||
port: common.PROXY_PORT,
|
||||
method: 'HEAD',
|
||||
path: '/'
|
||||
}, function (res) {
|
||||
common.error('response');
|
||||
res.on('end', function () {
|
||||
common.error('response end');
|
||||
server.close();
|
||||
responseComplete = true;
|
||||
});
|
||||
});
|
||||
common.error('req');
|
||||
req.end();
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.ok(responseComplete);
|
||||
});
|
||||
@ -1,101 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// libuv-broken
|
||||
|
||||
|
||||
var http = require('http'),
|
||||
common = require('../common'),
|
||||
assert = require('assert'),
|
||||
httpServer = http.createServer(reqHandler);
|
||||
|
||||
function reqHandler(req, res) {
|
||||
console.log('Got request: ' + req.headers.host + ' ' + req.url);
|
||||
if (req.url === '/setHostFalse5') {
|
||||
assert.equal(req.headers.host, undefined);
|
||||
} else {
|
||||
assert.equal(req.headers.host, 'localhost:' + common.PROXY_PORT,
|
||||
'Wrong host header for req[' + req.url + ']: ' +
|
||||
req.headers.host);
|
||||
}
|
||||
res.writeHead(200, {});
|
||||
//process.nextTick(function () { res.end('ok'); });
|
||||
res.end('ok');
|
||||
}
|
||||
|
||||
function thrower(er) {
|
||||
throw er;
|
||||
}
|
||||
|
||||
testHttp();
|
||||
|
||||
function testHttp() {
|
||||
|
||||
console.log('testing http on port ' + common.PROXY_PORT + ' (proxied to ' +
|
||||
common.PORT + ')');
|
||||
|
||||
var counter = 0;
|
||||
|
||||
function cb() {
|
||||
counter--;
|
||||
console.log('back from http request. counter = ' + counter);
|
||||
if (counter === 0) {
|
||||
httpServer.close();
|
||||
}
|
||||
}
|
||||
|
||||
httpServer.listen(common.PORT, function (er) {
|
||||
console.error('listening on ' + common.PORT);
|
||||
|
||||
if (er) throw er;
|
||||
|
||||
http.get({ method: 'GET',
|
||||
path: '/' + (counter++),
|
||||
host: 'localhost',
|
||||
//agent: false,
|
||||
port: common.PROXY_PORT }, cb).on('error', thrower);
|
||||
|
||||
http.request({ method: 'GET',
|
||||
path: '/' + (counter++),
|
||||
host: 'localhost',
|
||||
//agent: false,
|
||||
port: common.PROXY_PORT }, cb).on('error', thrower).end();
|
||||
|
||||
http.request({ method: 'POST',
|
||||
path: '/' + (counter++),
|
||||
host: 'localhost',
|
||||
//agent: false,
|
||||
port: common.PROXY_PORT }, cb).on('error', thrower).end();
|
||||
|
||||
http.request({ method: 'PUT',
|
||||
path: '/' + (counter++),
|
||||
host: 'localhost',
|
||||
//agent: false,
|
||||
port: common.PROXY_PORT }, cb).on('error', thrower).end();
|
||||
|
||||
http.request({ method: 'DELETE',
|
||||
path: '/' + (counter++),
|
||||
host: 'localhost',
|
||||
//agent: false,
|
||||
port: common.PROXY_PORT }, cb).on('error', thrower).end();
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
|
||||
var expected = 10000;
|
||||
var responses = 0;
|
||||
var requests = 0;
|
||||
var connection;
|
||||
|
||||
var server = http.Server(function (req, res) {
|
||||
requests++;
|
||||
assert.equal(req.connection, connection);
|
||||
res.writeHead(200);
|
||||
res.end('hello world\n');
|
||||
});
|
||||
|
||||
server.once('connection', function (c) {
|
||||
connection = c;
|
||||
});
|
||||
|
||||
server.listen(common.PORT, function () {
|
||||
var callee = arguments.callee;
|
||||
var request = http.get({
|
||||
port: common.PROXY_PORT,
|
||||
path: '/',
|
||||
headers: {
|
||||
'Connection': 'Keep-alive'
|
||||
}
|
||||
}, function (res) {
|
||||
res.on('end', function () {
|
||||
if (++responses < expected) {
|
||||
callee();
|
||||
} else {
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
}).on('error', function (e) {
|
||||
console.log(e.message);
|
||||
process.exit(1);
|
||||
});
|
||||
request.agent.maxSockets = 1;
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.equal(expected, responses);
|
||||
assert.equal(expected, requests);
|
||||
});
|
||||
@ -1,59 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
|
||||
var http = require('http');
|
||||
var net = require('net');
|
||||
|
||||
var gotResponse = false;
|
||||
|
||||
var server = net.createServer(function (conn) {
|
||||
var body = 'Yet another node.js server.';
|
||||
|
||||
var response =
|
||||
'HTTP/1.1 200 OK\r\n' +
|
||||
'Connection: close\r\n' +
|
||||
'Content-Length: ' + body.length + '\r\n' +
|
||||
'Content-Type: text/plain;\r\n' +
|
||||
' x-unix-mode=0600;\r\n' +
|
||||
' name=\"hello.txt\"\r\n' +
|
||||
'\r\n' +
|
||||
body;
|
||||
|
||||
conn.write(response, function () {
|
||||
conn.destroy();
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(common.PORT, function () {
|
||||
http.get({host: '127.0.0.1', port: common.PROXY_PORT}, function (res) {
|
||||
assert.equal(res.headers['content-type'],
|
||||
'text/plain;x-unix-mode=0600;name="hello.txt"');
|
||||
gotResponse = true;
|
||||
});
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.ok(gotResponse);
|
||||
});
|
||||
@ -1,109 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
var url = require('url');
|
||||
|
||||
var PROXY_PORT = common.PORT;
|
||||
var BACKEND_PORT = common.PORT + 1;
|
||||
|
||||
var cookies = [
|
||||
'session_token=; path=/; expires=Sun, 15-Sep-2030 13:48:52 GMT',
|
||||
'prefers_open_id=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT'
|
||||
];
|
||||
|
||||
var headers = {'content-type': 'text/plain',
|
||||
'set-cookie': cookies,
|
||||
'hello': 'world' };
|
||||
|
||||
var backend = http.createServer(function (req, res) {
|
||||
common.debug('backend request');
|
||||
res.writeHead(200, headers);
|
||||
res.write('hello world\n');
|
||||
res.end();
|
||||
});
|
||||
|
||||
var proxy = http.createServer(function (req, res) {
|
||||
common.debug('proxy req headers: ' + JSON.stringify(req.headers));
|
||||
var proxy_req = http.get({
|
||||
port: BACKEND_PORT,
|
||||
path: url.parse(req.url).pathname
|
||||
}, function (proxy_res) {
|
||||
|
||||
common.debug('proxy res headers: ' + JSON.stringify(proxy_res.headers));
|
||||
|
||||
assert.equal('world', proxy_res.headers['hello']);
|
||||
assert.equal('text/plain', proxy_res.headers['content-type']);
|
||||
assert.deepEqual(cookies, proxy_res.headers['set-cookie']);
|
||||
|
||||
res.writeHead(proxy_res.statusCode, proxy_res.headers);
|
||||
|
||||
proxy_res.on('data', function (chunk) {
|
||||
res.write(chunk);
|
||||
});
|
||||
|
||||
proxy_res.on('end', function () {
|
||||
res.end();
|
||||
common.debug('proxy res');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var body = '';
|
||||
|
||||
var nlistening = 0;
|
||||
function startReq() {
|
||||
nlistening++;
|
||||
if (nlistening < 2) return;
|
||||
|
||||
var client = http.get({
|
||||
port: common.PROXY_PORT,
|
||||
path: '/test'
|
||||
}, function (res) {
|
||||
common.debug('got res');
|
||||
assert.equal(200, res.statusCode);
|
||||
|
||||
assert.equal('world', res.headers['hello']);
|
||||
assert.equal('text/plain', res.headers['content-type']);
|
||||
assert.deepEqual(cookies, res.headers['set-cookie']);
|
||||
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) { body += chunk; });
|
||||
res.on('end', function () {
|
||||
proxy.close();
|
||||
backend.close();
|
||||
common.debug('closed both');
|
||||
});
|
||||
});
|
||||
common.debug('client req');
|
||||
}
|
||||
|
||||
common.debug('listen proxy');
|
||||
proxy.listen(PROXY_PORT, startReq);
|
||||
|
||||
common.debug('listen backend');
|
||||
backend.listen(BACKEND_PORT, startReq);
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.equal(body, 'hello world\n');
|
||||
});
|
||||
@ -1,55 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
|
||||
var gotEnd = false;
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
res.writeHead(200);
|
||||
res.write('a');
|
||||
|
||||
req.on('close', function () {
|
||||
console.error('aborted');
|
||||
gotEnd = true;
|
||||
});
|
||||
});
|
||||
server.listen(common.PORT);
|
||||
|
||||
server.on('listening', function () {
|
||||
console.error('make req');
|
||||
http.get({
|
||||
port: common.PROXY_PORT
|
||||
}, function (res) {
|
||||
console.error('got res');
|
||||
res.on('data', function (data) {
|
||||
console.error('destroy res');
|
||||
res.destroy();
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.ok(gotEnd);
|
||||
});
|
||||
@ -1,59 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// Verify that the HTTP server implementation handles multiple instances
|
||||
// of the same header as per RFC2616: joining the handful of fields by ', '
|
||||
// that support it, and dropping duplicates for other fields.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
|
||||
var srv = http.createServer(function (req, res) {
|
||||
assert.equal(req.headers.accept, 'abc, def, ghijklmnopqrst');
|
||||
assert.equal(req.headers.host, 'foo');
|
||||
assert.equal(req.headers['x-foo'], 'bingo');
|
||||
assert.equal(req.headers['x-bar'], 'banjo, bango');
|
||||
|
||||
res.writeHead(200, {'Content-Type' : 'text/plain'});
|
||||
res.end('EOF');
|
||||
|
||||
srv.close();
|
||||
});
|
||||
|
||||
srv.listen(common.PORT, function () {
|
||||
http.get({
|
||||
host: 'localhost',
|
||||
port: common.PROXY_PORT,
|
||||
path: '/',
|
||||
headers: [
|
||||
['accept', 'abc'],
|
||||
['accept', 'def'],
|
||||
['Accept', 'ghijklmnopqrst'],
|
||||
['host', 'foo'],
|
||||
['Host', 'bar'],
|
||||
['hOst', 'baz'],
|
||||
['x-foo', 'bingo'],
|
||||
['x-bar', 'banjo'],
|
||||
['x-bar', 'bango']
|
||||
]
|
||||
});
|
||||
});
|
||||
@ -1,84 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
|
||||
var nresponses = 0;
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
if (req.url == '/one') {
|
||||
res.writeHead(200, [['set-cookie', 'A'],
|
||||
['content-type', 'text/plain']]);
|
||||
res.end('one\n');
|
||||
} else {
|
||||
res.writeHead(200, [['set-cookie', 'A'],
|
||||
['set-cookie', 'B'],
|
||||
['content-type', 'text/plain']]);
|
||||
res.end('two\n');
|
||||
}
|
||||
});
|
||||
server.listen(common.PORT);
|
||||
|
||||
server.on('listening', function () {
|
||||
//
|
||||
// one set-cookie header
|
||||
//
|
||||
http.get({ port: common.PROXY_PORT, path: '/one' }, function (res) {
|
||||
// set-cookie headers are always return in an array.
|
||||
// even if there is only one.
|
||||
assert.deepEqual(['A'], res.headers['set-cookie']);
|
||||
assert.equal('text/plain', res.headers['content-type']);
|
||||
|
||||
res.on('data', function (chunk) {
|
||||
console.log(chunk.toString());
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
if (++nresponses == 2) {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// two set-cookie headers
|
||||
|
||||
http.get({ port: common.PROXY_PORT, path: '/two' }, function (res) {
|
||||
assert.deepEqual(['A', 'B'], res.headers['set-cookie']);
|
||||
assert.equal('text/plain', res.headers['content-type']);
|
||||
|
||||
res.on('data', function (chunk) {
|
||||
console.log(chunk.toString());
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
if (++nresponses == 2) {
|
||||
server.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.equal(2, nresponses);
|
||||
});
|
||||
@ -1,69 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
// libuv-broken
|
||||
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
|
||||
// Simple test of Node's HTTP ServerResponse.statusCode
|
||||
// ServerResponse.prototype.statusCode
|
||||
|
||||
var testsComplete = 0;
|
||||
var tests = [200, 202, 300, 404, 500];
|
||||
var testIdx = 0;
|
||||
|
||||
var s = http.createServer(function (req, res) {
|
||||
var t = tests[testIdx];
|
||||
res.writeHead(t, {'Content-Type': 'text/plain'});
|
||||
console.log('--\nserver: statusCode after writeHead: ' + res.statusCode);
|
||||
assert.equal(res.statusCode, t);
|
||||
res.end('hello world\n');
|
||||
});
|
||||
|
||||
s.listen(common.PORT, nextTest);
|
||||
|
||||
|
||||
function nextTest() {
|
||||
if (testIdx + 1 === tests.length) {
|
||||
return s.close();
|
||||
}
|
||||
var test = tests[testIdx];
|
||||
|
||||
http.get({ port: common.PROXY_PORT }, function (response) {
|
||||
console.log('client: expected status: ' + test);
|
||||
console.log('client: statusCode: ' + response.statusCode);
|
||||
assert.equal(response.statusCode, test);
|
||||
response.on('end', function () {
|
||||
testsComplete++;
|
||||
testIdx += 1;
|
||||
nextTest();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.equal(4, testsComplete);
|
||||
});
|
||||
|
||||
@ -1,72 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
var net = require('net');
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
common.error('got req');
|
||||
throw new Error('This shouldn\'t happen.');
|
||||
});
|
||||
|
||||
server.on('upgrade', function (req, socket, upgradeHead) {
|
||||
common.error('got upgrade event');
|
||||
// test that throwing an error from upgrade gets
|
||||
// is uncaught
|
||||
throw new Error('upgrade error');
|
||||
});
|
||||
|
||||
var gotError = false;
|
||||
|
||||
process.on('uncaughtException', function (e) {
|
||||
common.error('got \'clientError\' event');
|
||||
assert.equal('upgrade error', e.message);
|
||||
gotError = true;
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
|
||||
server.listen(common.PORT, function () {
|
||||
var c = net.createConnection(common.PROXY_PORT);
|
||||
|
||||
c.on('connect', function () {
|
||||
common.error('client wrote message');
|
||||
c.write('GET /blah HTTP/1.1\r\n' +
|
||||
'Upgrade: WebSocket\r\n' +
|
||||
'Connection: Upgrade\r\n' +
|
||||
'\r\n\r\nhello world');
|
||||
});
|
||||
|
||||
c.on('end', function () {
|
||||
c.end();
|
||||
});
|
||||
|
||||
c.on('close', function () {
|
||||
common.error('client close');
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.ok(gotError);
|
||||
});
|
||||
@ -1,108 +0,0 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var http = require('http');
|
||||
var url = require('url');
|
||||
|
||||
function p(x) {
|
||||
common.error(common.inspect(x));
|
||||
}
|
||||
|
||||
var responses_sent = 0;
|
||||
var responses_recvd = 0;
|
||||
var body0 = '';
|
||||
var body1 = '';
|
||||
|
||||
var server = http.Server(function (req, res) {
|
||||
if (responses_sent == 0) {
|
||||
assert.equal('GET', req.method);
|
||||
assert.equal('/hello', url.parse(req.url).pathname);
|
||||
|
||||
console.dir(req.headers);
|
||||
assert.equal(true, 'accept' in req.headers);
|
||||
assert.equal('*/*', req.headers['accept']);
|
||||
|
||||
assert.equal(true, 'foo' in req.headers);
|
||||
assert.equal('bar', req.headers['foo']);
|
||||
}
|
||||
|
||||
if (responses_sent == 1) {
|
||||
assert.equal('POST', req.method);
|
||||
assert.equal('/world', url.parse(req.url).pathname);
|
||||
this.close();
|
||||
}
|
||||
|
||||
req.on('end', function () {
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.write('The path was ' + url.parse(req.url).pathname);
|
||||
res.end();
|
||||
responses_sent += 1;
|
||||
});
|
||||
|
||||
//assert.equal('127.0.0.1', res.connection.remoteAddress);
|
||||
});
|
||||
server.listen(common.PORT);
|
||||
|
||||
server.on('listening', function () {
|
||||
var agent = new http.Agent({ port: common.PROXY_PORT, maxSockets: 1 });
|
||||
http.get({
|
||||
port: common.PROXY_PORT,
|
||||
path: '/hello',
|
||||
headers: {'Accept': '*/*', 'Foo': 'bar'},
|
||||
agent: agent
|
||||
}, function (res) {
|
||||
assert.equal(200, res.statusCode);
|
||||
responses_recvd += 1;
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) { body0 += chunk; });
|
||||
common.debug('Got /hello response');
|
||||
});
|
||||
|
||||
setTimeout(function () {
|
||||
var req = http.request({
|
||||
port: common.PROXY_PORT,
|
||||
method: 'POST',
|
||||
path: '/world',
|
||||
agent: agent
|
||||
}, function (res) {
|
||||
assert.equal(200, res.statusCode);
|
||||
responses_recvd += 1;
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) { body1 += chunk; });
|
||||
common.debug('Got /world response');
|
||||
});
|
||||
req.end();
|
||||
}, 1);
|
||||
});
|
||||
|
||||
process.on('exit', function () {
|
||||
common.debug('responses_recvd: ' + responses_recvd);
|
||||
assert.equal(2, responses_recvd);
|
||||
|
||||
common.debug('responses_sent: ' + responses_sent);
|
||||
assert.equal(2, responses_sent);
|
||||
|
||||
assert.equal('The path was /hello', body0);
|
||||
assert.equal('The path was /world', body1);
|
||||
});
|
||||
|
||||
@ -1,26 +1,71 @@
|
||||
/*
|
||||
* examples.js: Tests which ensure all examples do not throw errors.
|
||||
*
|
||||
* (C) 2010, Charlie Robbins
|
||||
*
|
||||
*/
|
||||
examples-test.js: Test to run all the examples
|
||||
|
||||
var vows = require('vows')
|
||||
macros = require('./macros'),
|
||||
examples = macros.examples;
|
||||
Copyright (c) 2013 - 2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
||||
|
||||
//
|
||||
// Suppress `EADDRINUSE` errors since
|
||||
// we are just checking for require-time errors
|
||||
//
|
||||
process.on('uncaughtException', function (err) {
|
||||
if (err.code !== 'EADDRINUSE') {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
*/
|
||||
var path = require('path'),
|
||||
fs = require('fs'),
|
||||
spawn = require('child_process').spawn,
|
||||
expect = require('expect.js'),
|
||||
async = require('async');
|
||||
|
||||
vows.describe('node-http-proxy/examples').addBatch(
|
||||
examples.shouldHaveDeps()
|
||||
).addBatch(
|
||||
examples.shouldHaveNoErrors()
|
||||
).export(module);
|
||||
var rootDir = path.join(__dirname, '..'),
|
||||
examplesDir = path.join(rootDir, 'examples');
|
||||
|
||||
describe.skip('http-proxy examples', function () {
|
||||
describe('Before testing examples', function () {
|
||||
// Set a timeout to avoid this error
|
||||
this.timeout(30 * 1000);
|
||||
it('should have installed dependencies', function (done) {
|
||||
async.waterfall([
|
||||
//
|
||||
// 1. Read files in examples dir
|
||||
//
|
||||
async.apply(fs.readdir, examplesDir),
|
||||
//
|
||||
// 2. If node_modules exists, continue. Otherwise
|
||||
// exec `npm` to install them
|
||||
//
|
||||
function checkNodeModules(files, next) {
|
||||
if (files.indexOf('node_modules') !== -1) {
|
||||
return next();
|
||||
}
|
||||
|
||||
console.log('Warning: installing dependencies, this operation could take a while');
|
||||
|
||||
var child = spawn('npm', ['install', '-f'], {
|
||||
cwd: examplesDir
|
||||
});
|
||||
|
||||
child.on('exit', function (code) {
|
||||
return code
|
||||
? next(new Error('npm install exited with non-zero exit code'))
|
||||
: next();
|
||||
});
|
||||
},
|
||||
//
|
||||
// 3. Read files in examples dir again to ensure the install
|
||||
// worked as expected.
|
||||
//
|
||||
async.apply(fs.readdir, examplesDir),
|
||||
], done);
|
||||
})
|
||||
});
|
||||
|
||||
describe('Requiring all the examples', function () {
|
||||
it('should have no errors', function (done) {
|
||||
async.each(['balancer', 'http', 'middleware', 'websocket'], function (dir, cb) {
|
||||
var name = 'examples/' + dir,
|
||||
files = fs.readdirSync(path.join(rootDir, 'examples', dir));
|
||||
|
||||
async.each(files, function (file, callback) {
|
||||
var example;
|
||||
expect(function () { example = require(path.join(examplesDir, dir, file)); }).to.not.throwException();
|
||||
expect(example).to.be.an('object');
|
||||
callback();
|
||||
}, cb);
|
||||
}, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
34
test/fixtures/agent2-cert.pem
vendored
34
test/fixtures/agent2-cert.pem
vendored
@ -1,13 +1,25 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB7DCCAZYCCQC7gs0MDNn6MTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJV
|
||||
UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO
|
||||
BgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEgMB4GCSqGSIb3DQEJARYR
|
||||
cnlAdGlueWNsb3Vkcy5vcmcwHhcNMTEwMzE0MTgyOTEyWhcNMzgwNzI5MTgyOTEy
|
||||
WjB9MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYD
|
||||
VQQKEwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEg
|
||||
MB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwXDANBgkqhkiG9w0BAQEF
|
||||
AANLADBIAkEAyXb8FrRdKbhrKLgLSsn61i1C7w7fVVVd7OQsmV/7p9WB2lWFiDlC
|
||||
WKGU9SiIz/A6wNZDUAuc2E+VwtpCT561AQIDAQABMA0GCSqGSIb3DQEBBQUAA0EA
|
||||
C8HzpuNhFLCI3A5KkBS5zHAQax6TFUOhbpBCR0aTDbJ6F1liDTK1lmU/BjvPoj+9
|
||||
1LHwrmh29rK8kBPEjmymCQ==
|
||||
MIIEIDCCAggCCQChRDh/XiBF+zANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJ1
|
||||
czETMBEGA1UECAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEeMBwGA1UE
|
||||
AwwVRHVtbXkgSW50ZXJtZWRpYXRlIENBMB4XDTE4MDYyMjIwMzEwNFoXDTMyMDIy
|
||||
OTIwMzEwNFowUDELMAkGA1UEBhMCdXMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAO
|
||||
BgNVBAcMB1NlYXR0bGUxGjAYBgNVBAMMEWR1bW15LmV4YW1wbGUuY29tMIIBIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvSQq3d8AeZMTvtqZ13jWCckikyXJ
|
||||
SACvkGCQUCJqOceESbg6IHdRzQdoccE4P3sbvNsf9BlbdJKM+neCxabqKaU1PPje
|
||||
4P0tHT57t6yJrMuUh9NxEz3Bgh1srNHVS7saKvwHmcKm79jc+wxlioPmEQvQagjn
|
||||
y7oTkyLt0sn4LGxBjrcv2JoHOC9f1pxX7l47MaiN0/ctRau7Nr3PFn+pkB4Yf6Z0
|
||||
VyicVJbaUSz39Qo4HQWl1L2hiBP3CS1oKs2Yk0O1aOCMExWrhZQan+ZgHqL1rhgm
|
||||
kPpw2/zwwPt5Vf9CSakvHwg198EXuTTXtkzYduuIJAm8yp969iEIiG2xTwIDAQAB
|
||||
MA0GCSqGSIb3DQEBCwUAA4ICAQBnMSIo+kujkeXPh+iErFBmNtu/7EA+i/QnFPbN
|
||||
lSLngclYYBJAGQI+DhirJI8ghDi6vmlHB2THewDaOJXEKvC1czE8064wioIcA9HJ
|
||||
l3QJ3YYOFRctYdSHBU4TWdJbPgkLWDzYP5smjOfw8nDdr4WO/5jh9qRFcFpTFmQf
|
||||
DyU3xgWLsQnNK3qXLdJjWG75pEhHR+7TGo+Ob/RUho/1RX/P89Ux7/oVbzdKqqFu
|
||||
SErXAsjEIEFzWOM2uDOt6hrxDF6q+8/zudwQNEo422poEcTT9tDEFxMQ391CzZRi
|
||||
nozBm4igRn1f5S3YZzLI6VEUns0s76BNy2CzvFWn40DziTqNBExAMfFFj76wiMsX
|
||||
6fTIdcvkaTBa0S9SZB0vN99qahBdcG17rt4RssMHVRH1Wn7NXMwe476L0yXZ6gO7
|
||||
Z4uNAPxgaI3BRP75EPfslLutCLZ+BC4Zzu6MY0Salbpfl0Go462EhsKCxvYhE2Dg
|
||||
T477pICLfETZfA499Fd1tOaIsoLCrILAia/+Yd76uf94MuXUIqykDng/4H7xCc47
|
||||
BZhNFJiPC6XHaXzN7NYSEUNX9VOwY8ncxKwtP6TXga96PdMUy/p98KIM8RZlDoxB
|
||||
Xy9dcZBFNn/zrqjW7R0CCWCUriDIFSmEP0wDZ91YYa6BVuJMb5uL/USkTLpjZS4/
|
||||
HNGvug==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
10
test/fixtures/agent2-csr.pem
vendored
10
test/fixtures/agent2-csr.pem
vendored
@ -1,10 +0,0 @@
|
||||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIBXTCCAQcCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQH
|
||||
EwJTRjEPMA0GA1UEChMGSm95ZW50MRAwDgYDVQQLEwdOb2RlLmpzMQ8wDQYDVQQD
|
||||
EwZhZ2VudDIxIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMFwwDQYJ
|
||||
KoZIhvcNAQEBBQADSwAwSAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf
|
||||
+6fVgdpVhYg5QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAaAlMCMGCSqG
|
||||
SIb3DQEJBzEWExRBIGNoYWxsZW5nZSBwYXNzd29yZDANBgkqhkiG9w0BAQUFAANB
|
||||
AJnll2pt5l0pzskQSpjjLVTlFDFmJr/AZ3UK8v0WxBjYjCe5Jx4YehkChpxIyDUm
|
||||
U3J9q9MDUf0+Y2+EGkssFfk=
|
||||
-----END CERTIFICATE REQUEST-----
|
||||
32
test/fixtures/agent2-key.pem
vendored
32
test/fixtures/agent2-key.pem
vendored
@ -1,9 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBOgIBAAJBAMl2/Ba0XSm4ayi4C0rJ+tYtQu8O31VVXezkLJlf+6fVgdpVhYg5
|
||||
QlihlPUoiM/wOsDWQ1ALnNhPlcLaQk+etQECAwEAAQJBAMT6Bf34+UHKY1ObpsbH
|
||||
9u2jsVblFq1rWvs8GPMY6oertzvwm3DpuSUp7PTgOB1nLTLYtCERbQ4ovtN8tn3p
|
||||
OHUCIQDzIEGsoCr5vlxXvy2zJwu+fxYuhTZWMVuo1397L0VyhwIhANQh+yzqUgaf
|
||||
WRtSB4T2W7ADtJI35ET61jKBty3CqJY3AiAIwju7dVW3A5WeD6Qc1SZGKZvp9yCb
|
||||
AFI2BfVwwaY11wIgXF3PeGcvACMyMWsuSv7aPXHfliswAbkWuzcwA4TW01ECIGWa
|
||||
cgsDvVFxmfM5NPSuT/UDTa6R5BFISB5ea0N0AR3I
|
||||
MIIEpQIBAAKCAQEAvSQq3d8AeZMTvtqZ13jWCckikyXJSACvkGCQUCJqOceESbg6
|
||||
IHdRzQdoccE4P3sbvNsf9BlbdJKM+neCxabqKaU1PPje4P0tHT57t6yJrMuUh9Nx
|
||||
Ez3Bgh1srNHVS7saKvwHmcKm79jc+wxlioPmEQvQagjny7oTkyLt0sn4LGxBjrcv
|
||||
2JoHOC9f1pxX7l47MaiN0/ctRau7Nr3PFn+pkB4Yf6Z0VyicVJbaUSz39Qo4HQWl
|
||||
1L2hiBP3CS1oKs2Yk0O1aOCMExWrhZQan+ZgHqL1rhgmkPpw2/zwwPt5Vf9CSakv
|
||||
Hwg198EXuTTXtkzYduuIJAm8yp969iEIiG2xTwIDAQABAoIBAGPIw/C/qJF7HYyv
|
||||
6T+7GTiaa2o0IiehbP3/Y8NTFLWc49a8obXlHTvMr7Zr2I/tE+ojtIzkH9K1SjkN
|
||||
eelqsNj9tsOPDI6oIvftsflpxkxqLtclnt8m0oMhoObf4OaONDT/N8dP4SBiSdsM
|
||||
ZDmacnMFx5NZVWiup4sVf2CYexx7qks9FhyN2K5PArCQ4S9LHjFhSJVH4DSEpv7E
|
||||
Ykbp30rhpqV7wSwjgUsm8ZYvI2NOlmffzLSiPdt3vy2K5Q25S/MVEAicg83rfDgK
|
||||
6EluHjeygRI1xU6DJ0hU7tnU7zE9KURoHPUycO3BKzZnzUH26AA36I58Pu4fXWw/
|
||||
Cgmbv2ECgYEA+og9E4ziKCEi3p8gqjIfwTRgWZxDLjEzooB/K0UhEearn/xiX29A
|
||||
FiSzEHKfCB4uSrw5OENg2ckDs8uy08Qmxx7xFXL7AtufAl5fIYaWa0sNSqCaIk7p
|
||||
ebbUmPcaYhKiLzIEd1EYEL38sXVZ62wvSVMRSWvEMq44g1qnoRlDa/8CgYEAwUTt
|
||||
talYNwVmR9ZdkVEWm9ZxirdzoM6NaM6u4Tf34ygptpapdmIFSUhfq4iOiEnRGNg/
|
||||
tuNqhNCIb3LNpJbhRPEzqN7E7qiF/mp7AcJgbuxLZBm12QuLuJdG3nrisKPFXcY1
|
||||
lA4A7CFmNgH3E4THFfgwzyDXsBOxVLXleTqn+rECgYEA9up1P6J3dtOJuV2d5P/3
|
||||
ugRz/X173LfTSxJXw36jZDAy8D/feG19/RT4gnplcKvGNhQiVOhbOOnbw0U8n2fQ
|
||||
TCmbs+cZqyxnH/+AxNsPvvk+RVHZ93xMsY/XIldP4l65B8jFDA+Zp06IESI2mEeM
|
||||
pzi+bd1Phh+dRSCA2865W2MCgYEAlxYsgmQ1WyX0dFpHYU+zzfXRYzDQyrhOYc2Z
|
||||
duVK+yCto1iad7pfCY/zgmRJkI+sT7DV9kJIRjXDQuTLkEyHJF8vFGe6KhxCS8aw
|
||||
DIsI2g4NTd6vg1J8UryoIUqNpqsQoqNNxUVBQVdG0ReuMGsPO8R/W50AIFz0txVP
|
||||
o/rP0LECgYEA7e/mOwCnR+ovmS/CAksmos3oIqvkRkXNKpKe513FVmp3TpTU38ex
|
||||
cBkFNU3hEO31FyrX1hGIKp3N5mHYSQ1lyODHM6teHW0OLWWTwIe8rIGvR2jfRLe0
|
||||
bbkdj40atYVkfeFmpz9uHHG24CUYxJdPc360jbXTVp4i3q8zqgL5aMY=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
||||
19
test/fixtures/agent2.cnf
vendored
19
test/fixtures/agent2.cnf
vendored
@ -1,19 +0,0 @@
|
||||
[ req ]
|
||||
default_bits = 1024
|
||||
days = 999
|
||||
distinguished_name = req_distinguished_name
|
||||
attributes = req_attributes
|
||||
prompt = no
|
||||
|
||||
[ req_distinguished_name ]
|
||||
C = US
|
||||
ST = CA
|
||||
L = SF
|
||||
O = Joyent
|
||||
OU = Node.js
|
||||
CN = agent2
|
||||
emailAddress = ry@tinyclouds.org
|
||||
|
||||
[ req_attributes ]
|
||||
challengePassword = A challenge password
|
||||
|
||||
@ -1,182 +0,0 @@
|
||||
/*
|
||||
* http.js: Top level include for node-http-proxy http helpers
|
||||
*
|
||||
* (C) 2010 Nodejitsu Inc.
|
||||
* MIT LICENCE
|
||||
*
|
||||
*/
|
||||
|
||||
var assert = require('assert'),
|
||||
http = require('http'),
|
||||
https = require('https'),
|
||||
url = require('url'),
|
||||
async = require('async'),
|
||||
helpers = require('./index'),
|
||||
protocols = helpers.protocols,
|
||||
httpProxy = require('../../lib/node-http-proxy');
|
||||
|
||||
//
|
||||
// ### function createServerPair (options, callback)
|
||||
// #### @options {Object} Options to create target and proxy server.
|
||||
// #### @callback {function} Continuation to respond to when complete.
|
||||
//
|
||||
// Creates http target and proxy servers
|
||||
//
|
||||
exports.createServerPair = function (options, callback) {
|
||||
async.series([
|
||||
//
|
||||
// 1. Create the target server
|
||||
//
|
||||
function createTarget(next) {
|
||||
exports.createServer(options.target, next);
|
||||
},
|
||||
//
|
||||
// 2. Create the proxy server
|
||||
//
|
||||
function createTarget(next) {
|
||||
exports.createProxyServer(options.proxy, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
//
|
||||
// ### function createServer (options, callback)
|
||||
// #### @options {Object} Options for creatig an http server.
|
||||
// #### @port {number} Port to listen on
|
||||
// #### @output {string} String to write to each HTTP response
|
||||
// #### @headers {Object} Headers to assert are sent by `node-http-proxy`.
|
||||
// #### @callback {function} Continuation to respond to when complete.
|
||||
//
|
||||
// Creates a target server that the tests will proxy to.
|
||||
//
|
||||
exports.createServer = function (options, callback) {
|
||||
//
|
||||
// Request handler to use in either `http`
|
||||
// or `https` server.
|
||||
//
|
||||
function requestHandler(req, res) {
|
||||
if (options.headers) {
|
||||
Object.keys(options.headers).forEach(function (key) {
|
||||
assert.equal(req.headers[key], options.headers[key]);
|
||||
});
|
||||
}
|
||||
|
||||
if (options.outputHeaders) {
|
||||
Object.keys(options.outputHeaders).forEach(function (header) {
|
||||
res.setHeader(header, options.outputHeaders[header]);
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.write(options.output || 'hello proxy');
|
||||
res.end();
|
||||
}, options.latency || 1);
|
||||
}
|
||||
|
||||
var server = protocols.target === 'https'
|
||||
? https.createServer(helpers.https, requestHandler)
|
||||
: http.createServer(requestHandler);
|
||||
|
||||
server.listen(options.port, function () {
|
||||
callback(null, this);
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// ### function createProxyServer (options, callback)
|
||||
// #### @options {Object} Options for creatig an http server.
|
||||
// #### @port {number} Port to listen on
|
||||
// #### @latency {number} Latency of this server in milliseconds
|
||||
// #### @proxy {Object} Options to pass to the HttpProxy.
|
||||
// #### @routing {boolean} Enables `httpProxy.RoutingProxy`
|
||||
// #### @callback {function} Continuation to respond to when complete.
|
||||
//
|
||||
// Creates a proxy server that the tests will request against.
|
||||
//
|
||||
exports.createProxyServer = function (options, callback) {
|
||||
if (!options.latency) {
|
||||
if (protocols.proxy === 'https') {
|
||||
options.proxy.https = helpers.https;
|
||||
}
|
||||
options.proxy.rejectUnauthorized = false;
|
||||
|
||||
return httpProxy
|
||||
.createServer(options.proxy)
|
||||
.listen(options.port, function () {
|
||||
callback(null, this);
|
||||
});
|
||||
}
|
||||
|
||||
var server,
|
||||
proxy;
|
||||
|
||||
proxy = options.routing
|
||||
? new httpProxy.RoutingProxy(options.proxy)
|
||||
: new httpProxy.HttpProxy(options.proxy);
|
||||
|
||||
//
|
||||
// Request handler to use in either `http`
|
||||
// or `https` server.
|
||||
//
|
||||
function requestHandler(req, res) {
|
||||
var buffer = httpProxy.buffer(req);
|
||||
|
||||
if (options.outputHeaders) {
|
||||
Object.keys(options.outputHeaders).forEach(function (header) {
|
||||
res.setHeader(header, options.outputHeaders[header]);
|
||||
});
|
||||
}
|
||||
setTimeout(function () {
|
||||
//
|
||||
// Setup options dynamically for `RoutingProxy.prototype.proxyRequest`
|
||||
// or `HttpProxy.prototype.proxyRequest`.
|
||||
//
|
||||
buffer = options.routing ? { buffer: buffer } : buffer;
|
||||
proxy.proxyRequest(req, res, buffer);
|
||||
}, options.latency);
|
||||
}
|
||||
|
||||
server = protocols.proxy === 'https'
|
||||
? https.createServer(helpers.https, requestHandler)
|
||||
: http.createServer(requestHandler);
|
||||
|
||||
server.listen(options.port, function () {
|
||||
callback(null, this);
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// ### function assignPortsToRoutes (routes)
|
||||
// #### @routes {Object} Routing table to assign ports to
|
||||
//
|
||||
// Assigns dynamic ports to the `routes` for runtime testing.
|
||||
//
|
||||
exports.assignPortsToRoutes = function (routes) {
|
||||
Object.keys(routes).forEach(function (source) {
|
||||
routes[source] = routes[source].replace('{PORT}', helpers.nextPort);
|
||||
});
|
||||
|
||||
return routes;
|
||||
};
|
||||
|
||||
//
|
||||
// ### function parseRoutes (options)
|
||||
// #### @options {Object} Options to use when parsing routes
|
||||
// #### @protocol {string} Protocol to use in the routes
|
||||
// #### @routes {Object} Routes to parse.
|
||||
//
|
||||
// Returns an Array of fully-parsed URLs for the source and
|
||||
// target of `options.routes`.
|
||||
//
|
||||
exports.parseRoutes = function (options) {
|
||||
var protocol = options.protocol || 'http',
|
||||
routes = options.routes;
|
||||
|
||||
return Object.keys(routes).map(function (source) {
|
||||
return {
|
||||
source: url.parse(protocol + '://' + source),
|
||||
target: url.parse(protocol + '://' + routes[source])
|
||||
};
|
||||
});
|
||||
};
|
||||
@ -1,105 +0,0 @@
|
||||
/*
|
||||
* index.js: Top level include for node-http-proxy helpers
|
||||
*
|
||||
* (C) 2010 Nodejitsu Inc.
|
||||
* MIT LICENCE
|
||||
*
|
||||
*/
|
||||
|
||||
var fs = require('fs'),
|
||||
path = require('path');
|
||||
|
||||
var fixturesDir = path.join(__dirname, '..', 'fixtures');
|
||||
|
||||
//
|
||||
// @https {Object}
|
||||
// Returns the necessary `https` credentials.
|
||||
//
|
||||
Object.defineProperty(exports, 'https', {
|
||||
get: function () {
|
||||
delete this.https;
|
||||
return this.https = {
|
||||
key: fs.readFileSync(path.join(fixturesDir, 'agent2-key.pem'), 'utf8'),
|
||||
cert: fs.readFileSync(path.join(fixturesDir, 'agent2-cert.pem'), 'utf8')
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// @protocols {Object}
|
||||
// Returns an object representing the desired protocols
|
||||
// for the `proxy` and `target` server.
|
||||
//
|
||||
Object.defineProperty(exports, 'protocols', {
|
||||
get: function () {
|
||||
delete this.protocols;
|
||||
return this.protocols = {
|
||||
target: exports.argv.target || 'http',
|
||||
proxy: exports.argv.proxy || 'http'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// @nextPort {number}
|
||||
// Returns an auto-incrementing port for tests.
|
||||
//
|
||||
Object.defineProperty(exports, 'nextPort', {
|
||||
get: function () {
|
||||
var current = this.port || 9050;
|
||||
this.port = current + 1;
|
||||
return current;
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// @nextPortPair {Object}
|
||||
// Returns an auto-incrementing pair of ports for tests.
|
||||
//
|
||||
Object.defineProperty(exports, 'nextPortPair', {
|
||||
get: function () {
|
||||
return {
|
||||
target: this.nextPort,
|
||||
proxy: this.nextPort
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// ### function describe(prefix)
|
||||
// #### @prefix {string} Prefix to use before the description
|
||||
//
|
||||
// Returns a string representing the protocols that this suite
|
||||
// is testing based on CLI arguments.
|
||||
//
|
||||
exports.describe = function (prefix, base) {
|
||||
prefix = prefix || '';
|
||||
base = base || 'http';
|
||||
|
||||
function protocol(endpoint) {
|
||||
return exports.protocols[endpoint] === 'https'
|
||||
? base + 's'
|
||||
: base;
|
||||
}
|
||||
|
||||
return [
|
||||
'node-http-proxy',
|
||||
prefix,
|
||||
[
|
||||
protocol('proxy'),
|
||||
'-to-',
|
||||
protocol('target')
|
||||
].join('')
|
||||
].filter(Boolean).join('/');
|
||||
};
|
||||
|
||||
//
|
||||
// Expose the CLI arguments
|
||||
//
|
||||
exports.argv = require('optimist').argv;
|
||||
|
||||
//
|
||||
// Export additional helpers for `http` and `websockets`.
|
||||
//
|
||||
exports.http = require('./http');
|
||||
exports.ws = require('./ws');
|
||||
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* ws.js: Top level include for node-http-proxy websocket helpers
|
||||
*
|
||||
* (C) 2010 Nodejitsu Inc.
|
||||
* MIT LICENCE
|
||||
*
|
||||
*/
|
||||
|
||||
var assert = require('assert'),
|
||||
https = require('https'),
|
||||
async = require('async'),
|
||||
io = require('socket.io'),
|
||||
ws = require('ws'),
|
||||
helpers = require('./index'),
|
||||
protocols = helpers.protocols,
|
||||
http = require('./http');
|
||||
|
||||
//
|
||||
// ### function createServerPair (options, callback)
|
||||
// #### @options {Object} Options to create target and proxy server.
|
||||
// #### @target {Object} Options for the target server.
|
||||
// #### @proxy {Object} Options for the proxy server.
|
||||
// #### @callback {function} Continuation to respond to when complete.
|
||||
//
|
||||
// Creates http target and proxy servers
|
||||
//
|
||||
exports.createServerPair = function (options, callback) {
|
||||
async.series([
|
||||
//
|
||||
// 1. Create the target server
|
||||
//
|
||||
function createTarget(next) {
|
||||
exports.createServer(options.target, next);
|
||||
},
|
||||
//
|
||||
// 2. Create the proxy server
|
||||
//
|
||||
function createTarget(next) {
|
||||
http.createProxyServer(options.proxy, next);
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
//
|
||||
// ### function createServer (options, callback)
|
||||
// #### @options {Object} Options for creating the socket.io or ws server.
|
||||
// #### @raw {boolean} Enables ws.Websocket server.
|
||||
//
|
||||
// Creates a socket.io or ws server using the specified `options`.
|
||||
//
|
||||
exports.createServer = function (options, callback) {
|
||||
return options.raw
|
||||
? exports.createWsServer(options, callback)
|
||||
: exports.createSocketIoServer(options, callback);
|
||||
};
|
||||
|
||||
//
|
||||
// ### function createSocketIoServer (options, callback)
|
||||
// #### @options {Object} Options for creating the socket.io server
|
||||
// #### @port {number} Port to listen on
|
||||
// #### @input {string} Input to expect from the only socket
|
||||
// #### @output {string} Output to send the only socket
|
||||
//
|
||||
// Creates a socket.io server on the specified `options.port` which
|
||||
// will expect `options.input` and then send `options.output`.
|
||||
//
|
||||
exports.createSocketIoServer = function (options, callback) {
|
||||
var server = protocols.target === 'https'
|
||||
? io.listen(options.port, helpers.https, callback)
|
||||
: io.listen(options.port, callback);
|
||||
|
||||
server.sockets.on('connection', function (socket) {
|
||||
socket.on('incoming', function (data) {
|
||||
assert.equal(data, options.input);
|
||||
socket.emit('outgoing', options.output);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//
|
||||
// ### function createWsServer (options, callback)
|
||||
// #### @options {Object} Options for creating the ws.Server instance
|
||||
// #### @port {number} Port to listen on
|
||||
// #### @input {string} Input to expect from the only socket
|
||||
// #### @output {string} Output to send the only socket
|
||||
//
|
||||
// Creates a ws.Server instance on the specified `options.port` which
|
||||
// will expect `options.input` and then send `options.output`.
|
||||
//
|
||||
exports.createWsServer = function (options, callback) {
|
||||
var server,
|
||||
wss;
|
||||
|
||||
if (protocols.target === 'https') {
|
||||
server = https.createServer(helpers.https, function (req, res) {
|
||||
req.writeHead(200);
|
||||
req.end();
|
||||
}).listen(options.port, callback);
|
||||
|
||||
wss = new ws.Server({ server: server });
|
||||
}
|
||||
else {
|
||||
wss = new ws.Server({ port: options.port }, callback);
|
||||
}
|
||||
|
||||
wss.on('connection', function (socket) {
|
||||
socket.on('message', function (data) {
|
||||
assert.equal(data, options.input);
|
||||
socket.send(options.output);
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -1,102 +0,0 @@
|
||||
/*
|
||||
node-http-proxy-test.js: http proxy for node.js
|
||||
|
||||
Copyright (c) 2010 Charlie Robbins, Marak Squires and Fedor Indutny
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
var assert = require('assert'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
async = require('async'),
|
||||
request = require('request'),
|
||||
vows = require('vows'),
|
||||
macros = require('../macros'),
|
||||
helpers = require('../helpers');
|
||||
|
||||
var routeFile = path.join(__dirname, 'config.json');
|
||||
|
||||
vows.describe(helpers.describe()).addBatch({
|
||||
"With a valid target server": {
|
||||
"and no latency": {
|
||||
"and no headers": macros.http.assertProxied(),
|
||||
"and headers": macros.http.assertProxied({
|
||||
request: { headers: { host: 'unknown.com' } }
|
||||
}),
|
||||
"and request close connection header": macros.http.assertProxied({
|
||||
request: { headers: { connection: "close" } },
|
||||
outputHeaders: { connection: "close" }
|
||||
}),
|
||||
"and request keep alive connection header": macros.http.assertProxied({
|
||||
request: { headers: { connection: "keep-alive" } },
|
||||
outputHeaders: { connection: "keep-alive" }
|
||||
}),
|
||||
"and response close connection header": macros.http.assertProxied({
|
||||
request: { headers: { connection: "" } }, // Must explicitly set to "" because otherwise node will automatically add a "connection: keep-alive" header
|
||||
targetHeaders: { connection: "close" },
|
||||
outputHeaders: { connection: "close" }
|
||||
}),
|
||||
"and response keep-alive connection header": macros.http.assertProxied({
|
||||
request: { headers: { connection: "" } }, // Must explicitly set to "" because otherwise node will automatically add a "connection: keep-alive" header
|
||||
targetHeaders: { connection: "keep-alive" },
|
||||
outputHeaders: { connection: "keep-alive" }
|
||||
}),
|
||||
"and response keep-alive connection header from http 1.0 client": macros.http.assertRawHttpProxied({
|
||||
rawRequest: "GET / HTTP/1.0\r\n\r\n",
|
||||
targetHeaders: { connection: "keep-alive" },
|
||||
match: /connection: close/i
|
||||
}),
|
||||
"and request keep alive from http 1.0 client": macros.http.assertRawHttpProxied({
|
||||
rawRequest: "GET / HTTP/1.0\r\nConnection: Keep-Alive\r\n\r\n",
|
||||
targetHeaders: { connection: "keep-alive" },
|
||||
match: /connection: keep-alive/i
|
||||
}),
|
||||
"and no connection header": macros.http.assertProxied({
|
||||
request: { headers: { connection: "" } }, // Must explicitly set to "" because otherwise node will automatically add a "connection: keep-alive" header
|
||||
outputHeaders: { connection: "keep-alive" }
|
||||
}),
|
||||
"and forwarding enabled": macros.http.assertForwardProxied()
|
||||
},
|
||||
"and latency": {
|
||||
"and no headers": macros.http.assertProxied({
|
||||
latency: 2000
|
||||
}),
|
||||
"and response headers": macros.http.assertProxied({
|
||||
targetHeaders: { "x-testheader": "target" },
|
||||
proxyHeaders: { "X-TestHeader": "proxy" },
|
||||
outputHeaders: { "x-testheader": "target" },
|
||||
latency: 1000
|
||||
})
|
||||
},
|
||||
"and timeout set": macros.http.assertProxied({
|
||||
shouldFail: true,
|
||||
timeout: 2000,
|
||||
requestLatency: 4000
|
||||
})
|
||||
},
|
||||
"With a no valid target server": {
|
||||
"and no latency": macros.http.assertInvalidProxy(),
|
||||
"and latency": macros.http.assertInvalidProxy({
|
||||
latency: 2000
|
||||
})
|
||||
}
|
||||
}).export(module);
|
||||
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* routing-table-test.js: Tests for the proxying using the ProxyTable object.
|
||||
*
|
||||
* (C) 2010, Charlie Robbins
|
||||
*
|
||||
*/
|
||||
|
||||
var assert = require('assert'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
async = require('async'),
|
||||
request = require('request'),
|
||||
vows = require('vows'),
|
||||
macros = require('../macros'),
|
||||
helpers = require('../helpers');
|
||||
|
||||
var routeFile = path.join(__dirname, 'config.json');
|
||||
|
||||
vows.describe(helpers.describe('routing-table')).addBatch({
|
||||
"With a routing table": {
|
||||
"with latency": macros.http.assertProxiedToRoutes({
|
||||
latency: 2000,
|
||||
routes: {
|
||||
"icanhaz.com": "127.0.0.1:{PORT}",
|
||||
"latency.com": "127.0.0.1:{PORT}"
|
||||
}
|
||||
}),
|
||||
"addHost() / removeHost()": macros.http.assertDynamicProxy({
|
||||
hostnameOnly: true,
|
||||
routes: {
|
||||
"static.com": "127.0.0.1:{PORT}",
|
||||
"removed.com": "127.0.0.1:{PORT}"
|
||||
}
|
||||
}, {
|
||||
add: [{ host: 'dynamic1.com', target: '127.0.0.1:' }],
|
||||
drop: ['removed.com']
|
||||
}),
|
||||
"using RegExp": macros.http.assertProxiedToRoutes({
|
||||
routes: {
|
||||
"foo.com": "127.0.0.1:{PORT}",
|
||||
"bar.com": "127.0.0.1:{PORT}",
|
||||
"baz.com/taco": "127.0.0.1:{PORT}",
|
||||
"pizza.com/taco/muffins": "127.0.0.1:{PORT}",
|
||||
"blah.com/me": "127.0.0.1:{PORT}/remapped",
|
||||
"bleh.com/remap/this": "127.0.0.1:{PORT}/remap/remapped",
|
||||
"test.com/double/tap": "127.0.0.1:{PORT}/remap"
|
||||
}
|
||||
}),
|
||||
"using hostnameOnly": macros.http.assertProxiedToRoutes({
|
||||
hostnameOnly: true,
|
||||
routes: {
|
||||
"foo.com": "127.0.0.1:{PORT}",
|
||||
"bar.com": "127.0.0.1:{PORT}"
|
||||
}
|
||||
}),
|
||||
"using pathnameOnly": macros.http.assertProxiedToRoutes({
|
||||
pathnameOnly: true,
|
||||
routes: {
|
||||
"/foo": "127.0.0.1:{PORT}",
|
||||
"/bar": "127.0.0.1:{PORT}",
|
||||
"/pizza": "127.0.0.1:{PORT}"
|
||||
}
|
||||
}),
|
||||
"using a routing file": macros.http.assertProxiedToRoutes({
|
||||
filename: routeFile,
|
||||
routes: {
|
||||
"foo.com": "127.0.0.1:{PORT}",
|
||||
"bar.com": "127.0.0.1:{PORT}"
|
||||
}
|
||||
}, {
|
||||
"after the file has been modified": {
|
||||
topic: function () {
|
||||
var config = JSON.parse(fs.readFileSync(routeFile, 'utf8')),
|
||||
protocol = helpers.protocols.proxy,
|
||||
port = helpers.nextPort,
|
||||
that = this;
|
||||
|
||||
config.router['dynamic.com'] = "127.0.0.1:" + port;
|
||||
fs.writeFileSync(routeFile, JSON.stringify(config));
|
||||
|
||||
async.parallel([
|
||||
function waitForRoutes(next) {
|
||||
that.proxyServer.on('routes', next);
|
||||
},
|
||||
async.apply(
|
||||
helpers.http.createServer,
|
||||
{
|
||||
port: port,
|
||||
output: 'hello from dynamic.com'
|
||||
}
|
||||
)
|
||||
], function () {
|
||||
request({
|
||||
uri: protocol + '://127.0.0.1:' + that.port,
|
||||
headers: {
|
||||
host: 'dynamic.com'
|
||||
}
|
||||
}, that.callback);
|
||||
});
|
||||
},
|
||||
"should receive 'hello from dynamic.com'": function (err, res, body) {
|
||||
assert.equal(body, 'hello from dynamic.com');
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}).export(module);
|
||||
397
test/lib-http-proxy-common-test.js
Normal file
397
test/lib-http-proxy-common-test.js
Normal file
@ -0,0 +1,397 @@
|
||||
var common = require('../lib/http-proxy/common'),
|
||||
url = require('url'),
|
||||
expect = require('expect.js');
|
||||
|
||||
describe('lib/http-proxy/common.js', function () {
|
||||
describe('#setupOutgoing', function () {
|
||||
it('should setup the correct headers', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing,
|
||||
{
|
||||
agent : '?',
|
||||
target: {
|
||||
host : 'hey',
|
||||
hostname : 'how',
|
||||
socketPath: 'are',
|
||||
port : 'you',
|
||||
},
|
||||
headers: {'fizz': 'bang', 'overwritten':true},
|
||||
localAddress: 'local.address',
|
||||
auth:'username:pass'
|
||||
},
|
||||
{
|
||||
method : 'i',
|
||||
url : 'am',
|
||||
headers : {'pro':'xy','overwritten':false}
|
||||
});
|
||||
|
||||
expect(outgoing.host).to.eql('hey');
|
||||
expect(outgoing.hostname).to.eql('how');
|
||||
expect(outgoing.socketPath).to.eql('are');
|
||||
expect(outgoing.port).to.eql('you');
|
||||
expect(outgoing.agent).to.eql('?');
|
||||
|
||||
expect(outgoing.method).to.eql('i');
|
||||
expect(outgoing.path).to.eql('am');
|
||||
|
||||
expect(outgoing.headers.pro).to.eql('xy');
|
||||
expect(outgoing.headers.fizz).to.eql('bang');
|
||||
expect(outgoing.headers.overwritten).to.eql(true);
|
||||
expect(outgoing.localAddress).to.eql('local.address');
|
||||
expect(outgoing.auth).to.eql('username:pass');
|
||||
});
|
||||
|
||||
it('should not override agentless upgrade header', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing,
|
||||
{
|
||||
agent: undefined,
|
||||
target: {
|
||||
host : 'hey',
|
||||
hostname : 'how',
|
||||
socketPath: 'are',
|
||||
port : 'you',
|
||||
},
|
||||
headers: {'connection': 'upgrade'},
|
||||
},
|
||||
{
|
||||
method : 'i',
|
||||
url : 'am',
|
||||
headers : {'pro':'xy','overwritten':false}
|
||||
});
|
||||
expect(outgoing.headers.connection).to.eql('upgrade');
|
||||
});
|
||||
|
||||
it('should not override agentless connection: contains upgrade', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing,
|
||||
{
|
||||
agent: undefined,
|
||||
target: {
|
||||
host : 'hey',
|
||||
hostname : 'how',
|
||||
socketPath: 'are',
|
||||
port : 'you',
|
||||
},
|
||||
headers: {'connection': 'keep-alive, upgrade'}, // this is what Firefox sets
|
||||
},
|
||||
{
|
||||
method : 'i',
|
||||
url : 'am',
|
||||
headers : {'pro':'xy','overwritten':false}
|
||||
});
|
||||
expect(outgoing.headers.connection).to.eql('keep-alive, upgrade');
|
||||
});
|
||||
|
||||
it('should override agentless connection: contains improper upgrade', function () {
|
||||
// sanity check on upgrade regex
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing,
|
||||
{
|
||||
agent: undefined,
|
||||
target: {
|
||||
host : 'hey',
|
||||
hostname : 'how',
|
||||
socketPath: 'are',
|
||||
port : 'you',
|
||||
},
|
||||
headers: {'connection': 'keep-alive, not upgrade'},
|
||||
},
|
||||
{
|
||||
method : 'i',
|
||||
url : 'am',
|
||||
headers : {'pro':'xy','overwritten':false}
|
||||
});
|
||||
expect(outgoing.headers.connection).to.eql('close');
|
||||
});
|
||||
|
||||
it('should override agentless non-upgrade header to close', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing,
|
||||
{
|
||||
agent: undefined,
|
||||
target: {
|
||||
host : 'hey',
|
||||
hostname : 'how',
|
||||
socketPath: 'are',
|
||||
port : 'you',
|
||||
},
|
||||
headers: {'connection': 'xyz'},
|
||||
},
|
||||
{
|
||||
method : 'i',
|
||||
url : 'am',
|
||||
headers : {'pro':'xy','overwritten':false}
|
||||
});
|
||||
expect(outgoing.headers.connection).to.eql('close');
|
||||
});
|
||||
|
||||
it('should set the agent to false if none is given', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing, {target:
|
||||
'http://localhost'
|
||||
}, { url: '/' });
|
||||
expect(outgoing.agent).to.eql(false);
|
||||
});
|
||||
|
||||
it('set the port according to the protocol', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing,
|
||||
{
|
||||
agent : '?',
|
||||
target: {
|
||||
host : 'how',
|
||||
hostname : 'are',
|
||||
socketPath: 'you',
|
||||
protocol: 'https:'
|
||||
}
|
||||
},
|
||||
{
|
||||
method : 'i',
|
||||
url : 'am',
|
||||
headers : {pro:'xy'}
|
||||
});
|
||||
|
||||
expect(outgoing.host).to.eql('how');
|
||||
expect(outgoing.hostname).to.eql('are');
|
||||
expect(outgoing.socketPath).to.eql('you');
|
||||
expect(outgoing.agent).to.eql('?');
|
||||
|
||||
expect(outgoing.method).to.eql('i');
|
||||
expect(outgoing.path).to.eql('am');
|
||||
expect(outgoing.headers.pro).to.eql('xy');
|
||||
|
||||
expect(outgoing.port).to.eql(443);
|
||||
});
|
||||
|
||||
it('should keep the original target path in the outgoing path', function(){
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing, {target:
|
||||
{ path: 'some-path' }
|
||||
}, { url : 'am' });
|
||||
|
||||
expect(outgoing.path).to.eql('some-path/am');
|
||||
});
|
||||
|
||||
it('should keep the original forward path in the outgoing path', function(){
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: {},
|
||||
forward: {
|
||||
path: 'some-path'
|
||||
}
|
||||
}, {
|
||||
url : 'am'
|
||||
}, 'forward');
|
||||
|
||||
expect(outgoing.path).to.eql('some-path/am');
|
||||
});
|
||||
|
||||
it('should properly detect https/wss protocol without the colon', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: {
|
||||
protocol: 'https',
|
||||
host: 'whatever.com'
|
||||
}
|
||||
}, { url: '/' });
|
||||
|
||||
expect(outgoing.port).to.eql(443);
|
||||
});
|
||||
|
||||
it('should not prepend the target path to the outgoing path with prependPath = false', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: { path: 'hellothere' },
|
||||
prependPath: false
|
||||
}, { url: 'hi' });
|
||||
|
||||
expect(outgoing.path).to.eql('hi');
|
||||
})
|
||||
|
||||
it('should properly join paths', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: { path: '/forward' },
|
||||
}, { url: '/static/path' });
|
||||
|
||||
expect(outgoing.path).to.eql('/forward/static/path');
|
||||
})
|
||||
|
||||
it('should not modify the query string', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: { path: '/forward' },
|
||||
}, { url: '/?foo=bar//&target=http://foobar.com/?a=1%26b=2&other=2' });
|
||||
|
||||
expect(outgoing.path).to.eql('/forward/?foo=bar//&target=http://foobar.com/?a=1%26b=2&other=2');
|
||||
})
|
||||
|
||||
//
|
||||
// This is the proper failing test case for the common.join problem
|
||||
//
|
||||
it('should correctly format the toProxy URL', function () {
|
||||
var outgoing = {};
|
||||
var google = 'https://google.com'
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: url.parse('http://sometarget.com:80'),
|
||||
toProxy: true,
|
||||
}, { url: google });
|
||||
|
||||
expect(outgoing.path).to.eql('/' + google);
|
||||
});
|
||||
|
||||
it('should not replace :\ to :\\ when no https word before', function () {
|
||||
var outgoing = {};
|
||||
var google = 'https://google.com:/join/join.js'
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: url.parse('http://sometarget.com:80'),
|
||||
toProxy: true,
|
||||
}, { url: google });
|
||||
|
||||
expect(outgoing.path).to.eql('/' + google);
|
||||
});
|
||||
|
||||
it('should not replace :\ to :\\ when no http word before', function () {
|
||||
var outgoing = {};
|
||||
var google = 'http://google.com:/join/join.js'
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: url.parse('http://sometarget.com:80'),
|
||||
toProxy: true,
|
||||
}, { url: google });
|
||||
|
||||
expect(outgoing.path).to.eql('/' + google);
|
||||
});
|
||||
|
||||
describe('when using ignorePath', function () {
|
||||
it('should ignore the path of the `req.url` passed in but use the target path', function () {
|
||||
var outgoing = {};
|
||||
var myEndpoint = 'https://whatever.com/some/crazy/path/whoooo';
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: url.parse(myEndpoint),
|
||||
ignorePath: true
|
||||
}, { url: '/more/crazy/pathness' });
|
||||
|
||||
expect(outgoing.path).to.eql('/some/crazy/path/whoooo');
|
||||
});
|
||||
|
||||
it('and prependPath: false, it should ignore path of target and incoming request', function () {
|
||||
var outgoing = {};
|
||||
var myEndpoint = 'https://whatever.com/some/crazy/path/whoooo';
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: url.parse(myEndpoint),
|
||||
ignorePath: true,
|
||||
prependPath: false
|
||||
}, { url: '/more/crazy/pathness' });
|
||||
|
||||
expect(outgoing.path).to.eql('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when using changeOrigin', function () {
|
||||
it('should correctly set the port to the host when it is a non-standard port using url.parse', function () {
|
||||
var outgoing = {};
|
||||
var myEndpoint = 'https://myCouch.com:6984';
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: url.parse(myEndpoint),
|
||||
changeOrigin: true
|
||||
}, { url: '/' });
|
||||
|
||||
expect(outgoing.headers.host).to.eql('mycouch.com:6984');
|
||||
});
|
||||
|
||||
it('should correctly set the port to the host when it is a non-standard port when setting host and port manually (which ignores port)', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: {
|
||||
protocol: 'https:',
|
||||
host: 'mycouch.com',
|
||||
port: 6984
|
||||
},
|
||||
changeOrigin: true
|
||||
}, { url: '/' });
|
||||
expect(outgoing.headers.host).to.eql('mycouch.com:6984');
|
||||
})
|
||||
});
|
||||
|
||||
it('should pass through https client parameters', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing,
|
||||
{
|
||||
agent : '?',
|
||||
target: {
|
||||
host : 'how',
|
||||
hostname : 'are',
|
||||
socketPath: 'you',
|
||||
protocol: 'https:',
|
||||
pfx: 'my-pfx',
|
||||
key: 'my-key',
|
||||
passphrase: 'my-passphrase',
|
||||
cert: 'my-cert',
|
||||
ca: 'my-ca',
|
||||
ciphers: 'my-ciphers',
|
||||
secureProtocol: 'my-secure-protocol'
|
||||
}
|
||||
},
|
||||
{
|
||||
method : 'i',
|
||||
url : 'am'
|
||||
});
|
||||
|
||||
expect(outgoing.pfx).eql('my-pfx');
|
||||
expect(outgoing.key).eql('my-key');
|
||||
expect(outgoing.passphrase).eql('my-passphrase');
|
||||
expect(outgoing.cert).eql('my-cert');
|
||||
expect(outgoing.ca).eql('my-ca');
|
||||
expect(outgoing.ciphers).eql('my-ciphers');
|
||||
expect(outgoing.secureProtocol).eql('my-secure-protocol');
|
||||
});
|
||||
|
||||
it('should handle overriding the `method` of the http request', function () {
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing, {
|
||||
target: url.parse('https://whooooo.com'),
|
||||
method: 'POST' ,
|
||||
}, { method: 'GET', url: '' });
|
||||
|
||||
expect(outgoing.method).eql('POST');
|
||||
});
|
||||
|
||||
// url.parse('').path => null
|
||||
it('should not pass null as last arg to #urlJoin', function(){
|
||||
var outgoing = {};
|
||||
common.setupOutgoing(outgoing, {target:
|
||||
{ path: '' }
|
||||
}, { url : '' });
|
||||
|
||||
expect(outgoing.path).to.be('');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#setupSocket', function () {
|
||||
it('should setup a socket', function () {
|
||||
var socketConfig = {
|
||||
timeout: null,
|
||||
nodelay: false,
|
||||
keepalive: false
|
||||
},
|
||||
stubSocket = {
|
||||
setTimeout: function (num) {
|
||||
socketConfig.timeout = num;
|
||||
},
|
||||
setNoDelay: function (bol) {
|
||||
socketConfig.nodelay = bol;
|
||||
},
|
||||
setKeepAlive: function (bol) {
|
||||
socketConfig.keepalive = bol;
|
||||
}
|
||||
}
|
||||
returnValue = common.setupSocket(stubSocket);
|
||||
|
||||
expect(socketConfig.timeout).to.eql(0);
|
||||
expect(socketConfig.nodelay).to.eql(true);
|
||||
expect(socketConfig.keepalive).to.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
536
test/lib-http-proxy-passes-web-incoming-test.js
Normal file
536
test/lib-http-proxy-passes-web-incoming-test.js
Normal file
@ -0,0 +1,536 @@
|
||||
var webPasses = require('../lib/http-proxy/passes/web-incoming'),
|
||||
httpProxy = require('../lib/http-proxy'),
|
||||
expect = require('expect.js'),
|
||||
concat = require('concat-stream'),
|
||||
async = require('async'),
|
||||
url = require('url'),
|
||||
http = require('http');
|
||||
|
||||
describe('lib/http-proxy/passes/web.js', function() {
|
||||
describe('#deleteLength', function() {
|
||||
it('should change `content-length` for DELETE requests', function() {
|
||||
var stubRequest = {
|
||||
method: 'DELETE',
|
||||
headers: {}
|
||||
};
|
||||
webPasses.deleteLength(stubRequest, {}, {});
|
||||
expect(stubRequest.headers['content-length']).to.eql('0');
|
||||
});
|
||||
|
||||
it('should change `content-length` for OPTIONS requests', function() {
|
||||
var stubRequest = {
|
||||
method: 'OPTIONS',
|
||||
headers: {}
|
||||
};
|
||||
webPasses.deleteLength(stubRequest, {}, {});
|
||||
expect(stubRequest.headers['content-length']).to.eql('0');
|
||||
});
|
||||
|
||||
it('should remove `transfer-encoding` from empty DELETE requests', function() {
|
||||
var stubRequest = {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'transfer-encoding': 'chunked'
|
||||
}
|
||||
};
|
||||
webPasses.deleteLength(stubRequest, {}, {});
|
||||
expect(stubRequest.headers['content-length']).to.eql('0');
|
||||
expect(stubRequest.headers).to.not.have.key('transfer-encoding');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#timeout', function() {
|
||||
it('should set timeout on the socket', function() {
|
||||
var done = false, stubRequest = {
|
||||
socket: {
|
||||
setTimeout: function(value) { done = value; }
|
||||
}
|
||||
}
|
||||
|
||||
webPasses.timeout(stubRequest, {}, { timeout: 5000});
|
||||
expect(done).to.eql(5000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#XHeaders', function () {
|
||||
var stubRequest = {
|
||||
connection: {
|
||||
remoteAddress: '192.168.1.2',
|
||||
remotePort: '8080'
|
||||
},
|
||||
headers: {
|
||||
host: '192.168.1.2:8080'
|
||||
}
|
||||
}
|
||||
|
||||
it('set the correct x-forwarded-* headers', function () {
|
||||
webPasses.XHeaders(stubRequest, {}, { xfwd: true });
|
||||
expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2');
|
||||
expect(stubRequest.headers['x-forwarded-port']).to.be('8080');
|
||||
expect(stubRequest.headers['x-forwarded-proto']).to.be('http');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createProxyServer.web() using own http server', function () {
|
||||
it('should proxy the request using the web proxy handler', function (done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:8080'
|
||||
});
|
||||
|
||||
function requestHandler(req, res) {
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
source.close();
|
||||
proxyServer.close();
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(req.headers.host.split(':')[1]).to.eql('8081');
|
||||
done();
|
||||
});
|
||||
|
||||
proxyServer.listen('8081');
|
||||
source.listen('8080');
|
||||
|
||||
http.request('http://127.0.0.1:8081', function() {}).end();
|
||||
});
|
||||
|
||||
it('should detect a proxyReq event and modify headers', function (done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:8080',
|
||||
});
|
||||
|
||||
proxy.on('proxyReq', function(proxyReq, req, res, options) {
|
||||
proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');
|
||||
});
|
||||
|
||||
function requestHandler(req, res) {
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
source.close();
|
||||
proxyServer.close();
|
||||
expect(req.headers['x-special-proxy-header']).to.eql('foobar');
|
||||
done();
|
||||
});
|
||||
|
||||
proxyServer.listen('8081');
|
||||
source.listen('8080');
|
||||
|
||||
http.request('http://127.0.0.1:8081', function() {}).end();
|
||||
});
|
||||
|
||||
it('should skip proxyReq event when handling a request with header "expect: 100-continue" [https://www.npmjs.com/advisories/1486]', function (done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:8080',
|
||||
});
|
||||
|
||||
proxy.on('proxyReq', function(proxyReq, req, res, options) {
|
||||
proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');
|
||||
});
|
||||
|
||||
function requestHandler(req, res) {
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
source.close();
|
||||
proxyServer.close();
|
||||
expect(req.headers['x-special-proxy-header']).to.not.eql('foobar');
|
||||
done();
|
||||
});
|
||||
|
||||
proxyServer.listen('8081');
|
||||
source.listen('8080');
|
||||
|
||||
const postData = ''.padStart(1025, 'x');
|
||||
|
||||
const postOptions = {
|
||||
hostname: '127.0.0.1',
|
||||
port: 8081,
|
||||
path: '/',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': Buffer.byteLength(postData),
|
||||
'expect': '100-continue'
|
||||
}
|
||||
};
|
||||
|
||||
const req = http.request(postOptions, function() {});
|
||||
req.write(postData);
|
||||
req.end();
|
||||
});
|
||||
|
||||
it('should proxy the request and handle error via callback', function(done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:8080'
|
||||
});
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
function requestHandler(req, res) {
|
||||
proxy.web(req, res, function (err) {
|
||||
proxyServer.close();
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.be('ECONNREFUSED');
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
proxyServer.listen('8082');
|
||||
|
||||
http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: '8082',
|
||||
method: 'GET',
|
||||
}, function() {}).end();
|
||||
});
|
||||
|
||||
it('should proxy the request and handle error via event listener', function(done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:8080'
|
||||
});
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
function requestHandler(req, res) {
|
||||
proxy.once('error', function (err, errReq, errRes) {
|
||||
proxyServer.close();
|
||||
expect(err).to.be.an(Error);
|
||||
expect(errReq).to.be.equal(req);
|
||||
expect(errRes).to.be.equal(res);
|
||||
expect(err.code).to.be('ECONNREFUSED');
|
||||
done();
|
||||
});
|
||||
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
proxyServer.listen('8083');
|
||||
|
||||
http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: '8083',
|
||||
method: 'GET',
|
||||
}, function() {}).end();
|
||||
});
|
||||
|
||||
it('should forward the request and handle error via event listener', function(done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
forward: 'http://127.0.0.1:8080'
|
||||
});
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
function requestHandler(req, res) {
|
||||
proxy.once('error', function (err, errReq, errRes) {
|
||||
proxyServer.close();
|
||||
expect(err).to.be.an(Error);
|
||||
expect(errReq).to.be.equal(req);
|
||||
expect(errRes).to.be.equal(res);
|
||||
expect(err.code).to.be('ECONNREFUSED');
|
||||
done();
|
||||
});
|
||||
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
proxyServer.listen('8083');
|
||||
|
||||
http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: '8083',
|
||||
method: 'GET',
|
||||
}, function() {}).end();
|
||||
});
|
||||
|
||||
it('should proxy the request and handle timeout error (proxyTimeout)', function(done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:45000',
|
||||
proxyTimeout: 100
|
||||
});
|
||||
|
||||
require('net').createServer().listen(45000);
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
var started = new Date().getTime();
|
||||
function requestHandler(req, res) {
|
||||
proxy.once('error', function (err, errReq, errRes) {
|
||||
proxyServer.close();
|
||||
expect(err).to.be.an(Error);
|
||||
expect(errReq).to.be.equal(req);
|
||||
expect(errRes).to.be.equal(res);
|
||||
expect(new Date().getTime() - started).to.be.greaterThan(99);
|
||||
expect(err.code).to.be('ECONNRESET');
|
||||
done();
|
||||
});
|
||||
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
proxyServer.listen('8084');
|
||||
|
||||
http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: '8084',
|
||||
method: 'GET',
|
||||
}, function() {}).end();
|
||||
});
|
||||
|
||||
it('should proxy the request and handle timeout error', function(done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:45001',
|
||||
timeout: 100
|
||||
});
|
||||
|
||||
require('net').createServer().listen(45001);
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
var cnt = 0;
|
||||
var doneOne = function() {
|
||||
cnt += 1;
|
||||
if(cnt === 2) done();
|
||||
}
|
||||
|
||||
var started = new Date().getTime();
|
||||
function requestHandler(req, res) {
|
||||
proxy.once('econnreset', function (err, errReq, errRes) {
|
||||
proxyServer.close();
|
||||
expect(err).to.be.an(Error);
|
||||
expect(errReq).to.be.equal(req);
|
||||
expect(errRes).to.be.equal(res);
|
||||
expect(err.code).to.be('ECONNRESET');
|
||||
doneOne();
|
||||
});
|
||||
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
proxyServer.listen('8085');
|
||||
|
||||
var req = http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: '8085',
|
||||
method: 'GET',
|
||||
}, function() {});
|
||||
|
||||
req.on('error', function(err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.be('ECONNRESET');
|
||||
expect(new Date().getTime() - started).to.be.greaterThan(99);
|
||||
doneOne();
|
||||
});
|
||||
req.end();
|
||||
});
|
||||
|
||||
it('should proxy the request and provide a proxyRes event with the request and response parameters', function(done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:8080'
|
||||
});
|
||||
|
||||
function requestHandler(req, res) {
|
||||
proxy.once('proxyRes', function (proxyRes, pReq, pRes) {
|
||||
source.close();
|
||||
proxyServer.close();
|
||||
expect(pReq).to.be.equal(req);
|
||||
expect(pRes).to.be.equal(res);
|
||||
done();
|
||||
});
|
||||
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
res.end('Response');
|
||||
});
|
||||
|
||||
proxyServer.listen('8086');
|
||||
source.listen('8080');
|
||||
http.request('http://127.0.0.1:8086', function() {}).end();
|
||||
});
|
||||
|
||||
it('should proxy the request and provide and respond to manual user response when using modifyResponse', function(done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:8080',
|
||||
selfHandleResponse: true
|
||||
});
|
||||
|
||||
function requestHandler(req, res) {
|
||||
proxy.once('proxyRes', function (proxyRes, pReq, pRes) {
|
||||
proxyRes.pipe(concat(function (body) {
|
||||
expect(body.toString('utf8')).eql('Response');
|
||||
pRes.end(Buffer.from('my-custom-response'));
|
||||
}))
|
||||
});
|
||||
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
res.end('Response');
|
||||
});
|
||||
|
||||
async.parallel([
|
||||
next => proxyServer.listen(8086, next),
|
||||
next => source.listen(8080, next)
|
||||
], function (err) {
|
||||
http.get('http://127.0.0.1:8086', function(res) {
|
||||
res.pipe(concat(function(body) {
|
||||
expect(body.toString('utf8')).eql('my-custom-response');
|
||||
source.close();
|
||||
proxyServer.close();
|
||||
done();
|
||||
}));
|
||||
}).once('error', done);
|
||||
})
|
||||
});
|
||||
|
||||
it('should proxy the request and handle changeOrigin option', function (done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:8080',
|
||||
changeOrigin: true
|
||||
});
|
||||
|
||||
function requestHandler(req, res) {
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
source.close();
|
||||
proxyServer.close();
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(req.headers.host.split(':')[1]).to.eql('8080');
|
||||
done();
|
||||
});
|
||||
|
||||
proxyServer.listen('8081');
|
||||
source.listen('8080');
|
||||
|
||||
http.request('http://127.0.0.1:8081', function() {}).end();
|
||||
});
|
||||
|
||||
it('should proxy the request with the Authorization header set', function (done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:8080',
|
||||
auth: 'user:pass'
|
||||
});
|
||||
|
||||
function requestHandler(req, res) {
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
source.close();
|
||||
proxyServer.close();
|
||||
var auth = new Buffer(req.headers.authorization.split(' ')[1], 'base64');
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(auth.toString()).to.eql('user:pass');
|
||||
done();
|
||||
});
|
||||
|
||||
proxyServer.listen('8081');
|
||||
source.listen('8080');
|
||||
|
||||
http.request('http://127.0.0.1:8081', function() {}).end();
|
||||
});
|
||||
|
||||
it('should proxy requests to multiple servers with different options', function (done) {
|
||||
var proxy = httpProxy.createProxyServer();
|
||||
|
||||
// proxies to two servers depending on url, rewriting the url as well
|
||||
// http://127.0.0.1:8080/s1/ -> http://127.0.0.1:8081/
|
||||
// http://127.0.0.1:8080/ -> http://127.0.0.1:8082/
|
||||
function requestHandler(req, res) {
|
||||
if (req.url.indexOf('/s1/') === 0) {
|
||||
proxy.web(req, res, {
|
||||
ignorePath: true,
|
||||
target: 'http://127.0.0.1:8081' + req.url.substring(3)
|
||||
});
|
||||
} else {
|
||||
proxy.web(req, res, {
|
||||
target: 'http://127.0.0.1:8082'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
var source1 = http.createServer(function(req, res) {
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(req.headers.host.split(':')[1]).to.eql('8080');
|
||||
expect(req.url).to.eql('/test1');
|
||||
});
|
||||
|
||||
var source2 = http.createServer(function(req, res) {
|
||||
source1.close();
|
||||
source2.close();
|
||||
proxyServer.close();
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(req.headers.host.split(':')[1]).to.eql('8080');
|
||||
expect(req.url).to.eql('/test2');
|
||||
done();
|
||||
});
|
||||
|
||||
proxyServer.listen('8080');
|
||||
source1.listen('8081');
|
||||
source2.listen('8082');
|
||||
|
||||
http.request('http://127.0.0.1:8080/s1/test1', function() {}).end();
|
||||
http.request('http://127.0.0.1:8080/test2', function() {}).end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#followRedirects', function () {
|
||||
it('should proxy the request follow redirects', function (done) {
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:8080',
|
||||
followRedirects: true
|
||||
});
|
||||
|
||||
function requestHandler(req, res) {
|
||||
proxy.web(req, res);
|
||||
}
|
||||
|
||||
var proxyServer = http.createServer(requestHandler);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
|
||||
if (url.parse(req.url).pathname === '/redirect') {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end('ok');
|
||||
}
|
||||
|
||||
res.writeHead(301, { 'Location': '/redirect' });
|
||||
res.end();
|
||||
});
|
||||
|
||||
proxyServer.listen('8081');
|
||||
source.listen('8080');
|
||||
|
||||
http.request('http://127.0.0.1:8081', function(res) {
|
||||
source.close();
|
||||
proxyServer.close();
|
||||
expect(res.statusCode).to.eql(200);
|
||||
done();
|
||||
}).end();
|
||||
});
|
||||
});
|
||||
423
test/lib-http-proxy-passes-web-outgoing-test.js
Normal file
423
test/lib-http-proxy-passes-web-outgoing-test.js
Normal file
@ -0,0 +1,423 @@
|
||||
var httpProxy = require('../lib/http-proxy/passes/web-outgoing'),
|
||||
expect = require('expect.js');
|
||||
|
||||
describe('lib/http-proxy/passes/web-outgoing.js', function () {
|
||||
describe('#setRedirectHostRewrite', function () {
|
||||
beforeEach(function() {
|
||||
this.req = {
|
||||
headers: {
|
||||
host: 'ext-auto.com'
|
||||
}
|
||||
};
|
||||
this.proxyRes = {
|
||||
statusCode: 301,
|
||||
headers: {
|
||||
location: 'http://backend.com/'
|
||||
}
|
||||
};
|
||||
this.options = {
|
||||
target: 'http://backend.com'
|
||||
};
|
||||
});
|
||||
|
||||
context('rewrites location host with hostRewrite', function() {
|
||||
beforeEach(function() {
|
||||
this.options.hostRewrite = 'ext-manual.com';
|
||||
});
|
||||
[201, 301, 302, 307, 308].forEach(function(code) {
|
||||
it('on ' + code, function() {
|
||||
this.proxyRes.statusCode = code;
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://ext-manual.com/');
|
||||
});
|
||||
});
|
||||
|
||||
it('not on 200', function() {
|
||||
this.proxyRes.statusCode = 200;
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://backend.com/');
|
||||
});
|
||||
|
||||
it('not when hostRewrite is unset', function() {
|
||||
delete this.options.hostRewrite;
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://backend.com/');
|
||||
});
|
||||
|
||||
it('takes precedence over autoRewrite', function() {
|
||||
this.options.autoRewrite = true;
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://ext-manual.com/');
|
||||
});
|
||||
|
||||
it('not when the redirected location does not match target host', function() {
|
||||
this.proxyRes.statusCode = 302;
|
||||
this.proxyRes.headers.location = 'http://some-other/';
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://some-other/');
|
||||
});
|
||||
|
||||
it('not when the redirected location does not match target port', function() {
|
||||
this.proxyRes.statusCode = 302;
|
||||
this.proxyRes.headers.location = 'http://backend.com:8080/';
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://backend.com:8080/');
|
||||
});
|
||||
});
|
||||
|
||||
context('rewrites location host with autoRewrite', function() {
|
||||
beforeEach(function() {
|
||||
this.options.autoRewrite = true;
|
||||
});
|
||||
[201, 301, 302, 307, 308].forEach(function(code) {
|
||||
it('on ' + code, function() {
|
||||
this.proxyRes.statusCode = code;
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://ext-auto.com/');
|
||||
});
|
||||
});
|
||||
|
||||
it('not on 200', function() {
|
||||
this.proxyRes.statusCode = 200;
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://backend.com/');
|
||||
});
|
||||
|
||||
it('not when autoRewrite is unset', function() {
|
||||
delete this.options.autoRewrite;
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://backend.com/');
|
||||
});
|
||||
|
||||
it('not when the redirected location does not match target host', function() {
|
||||
this.proxyRes.statusCode = 302;
|
||||
this.proxyRes.headers.location = 'http://some-other/';
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://some-other/');
|
||||
});
|
||||
|
||||
it('not when the redirected location does not match target port', function() {
|
||||
this.proxyRes.statusCode = 302;
|
||||
this.proxyRes.headers.location = 'http://backend.com:8080/';
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://backend.com:8080/');
|
||||
});
|
||||
});
|
||||
|
||||
context('rewrites location protocol with protocolRewrite', function() {
|
||||
beforeEach(function() {
|
||||
this.options.protocolRewrite = 'https';
|
||||
});
|
||||
[201, 301, 302, 307, 308].forEach(function(code) {
|
||||
it('on ' + code, function() {
|
||||
this.proxyRes.statusCode = code;
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('https://backend.com/');
|
||||
});
|
||||
});
|
||||
|
||||
it('not on 200', function() {
|
||||
this.proxyRes.statusCode = 200;
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://backend.com/');
|
||||
});
|
||||
|
||||
it('not when protocolRewrite is unset', function() {
|
||||
delete this.options.protocolRewrite;
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('http://backend.com/');
|
||||
});
|
||||
|
||||
it('works together with hostRewrite', function() {
|
||||
this.options.hostRewrite = 'ext-manual.com';
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('https://ext-manual.com/');
|
||||
});
|
||||
|
||||
it('works together with autoRewrite', function() {
|
||||
this.options.autoRewrite = true;
|
||||
httpProxy.setRedirectHostRewrite(this.req, {}, this.proxyRes, this.options);
|
||||
expect(this.proxyRes.headers.location).to.eql('https://ext-auto.com/');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setConnection', function () {
|
||||
it('set the right connection with 1.0 - `close`', function() {
|
||||
var proxyRes = { headers: {} };
|
||||
httpProxy.setConnection({
|
||||
httpVersion: '1.0',
|
||||
headers: {
|
||||
connection: null
|
||||
}
|
||||
}, {}, proxyRes);
|
||||
|
||||
expect(proxyRes.headers.connection).to.eql('close');
|
||||
});
|
||||
|
||||
it('set the right connection with 1.0 - req.connection', function() {
|
||||
var proxyRes = { headers: {} };
|
||||
httpProxy.setConnection({
|
||||
httpVersion: '1.0',
|
||||
headers: {
|
||||
connection: 'hey'
|
||||
}
|
||||
}, {}, proxyRes);
|
||||
|
||||
expect(proxyRes.headers.connection).to.eql('hey');
|
||||
});
|
||||
|
||||
it('set the right connection - req.connection', function() {
|
||||
var proxyRes = { headers: {} };
|
||||
httpProxy.setConnection({
|
||||
httpVersion: null,
|
||||
headers: {
|
||||
connection: 'hola'
|
||||
}
|
||||
}, {}, proxyRes);
|
||||
|
||||
expect(proxyRes.headers.connection).to.eql('hola');
|
||||
});
|
||||
|
||||
it('set the right connection - `keep-alive`', function() {
|
||||
var proxyRes = { headers: {} };
|
||||
httpProxy.setConnection({
|
||||
httpVersion: null,
|
||||
headers: {
|
||||
connection: null
|
||||
}
|
||||
}, {}, proxyRes);
|
||||
|
||||
expect(proxyRes.headers.connection).to.eql('keep-alive');
|
||||
});
|
||||
|
||||
it('don`t set connection with 2.0 if exist', function() {
|
||||
var proxyRes = { headers: {} };
|
||||
httpProxy.setConnection({
|
||||
httpVersion: '2.0',
|
||||
headers: {
|
||||
connection: 'namstey'
|
||||
}
|
||||
}, {}, proxyRes);
|
||||
|
||||
expect(proxyRes.headers.connection).to.eql(undefined);
|
||||
});
|
||||
|
||||
it('don`t set connection with 2.0 if doesn`t exist', function() {
|
||||
var proxyRes = { headers: {} };
|
||||
httpProxy.setConnection({
|
||||
httpVersion: '2.0',
|
||||
headers: {}
|
||||
}, {}, proxyRes);
|
||||
|
||||
expect(proxyRes.headers.connection).to.eql(undefined);
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('#writeStatusCode', function () {
|
||||
it('should write status code', function() {
|
||||
var res = {
|
||||
writeHead: function(n) {
|
||||
expect(n).to.eql(200);
|
||||
}
|
||||
};
|
||||
|
||||
httpProxy.writeStatusCode({}, res, { statusCode: 200 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('#writeHeaders', function() {
|
||||
beforeEach(function() {
|
||||
this.proxyRes = {
|
||||
headers: {
|
||||
hey: 'hello',
|
||||
how: 'are you?',
|
||||
'set-cookie': [
|
||||
'hello; domain=my.domain; path=/',
|
||||
'there; domain=my.domain; path=/'
|
||||
]
|
||||
}
|
||||
};
|
||||
this.rawProxyRes = {
|
||||
headers: {
|
||||
hey: 'hello',
|
||||
how: 'are you?',
|
||||
'set-cookie': [
|
||||
'hello; domain=my.domain; path=/',
|
||||
'there; domain=my.domain; path=/'
|
||||
]
|
||||
},
|
||||
rawHeaders: [
|
||||
'Hey', 'hello',
|
||||
'How', 'are you?',
|
||||
'Set-Cookie', 'hello; domain=my.domain; path=/',
|
||||
'Set-Cookie', 'there; domain=my.domain; path=/'
|
||||
]
|
||||
};
|
||||
this.res = {
|
||||
setHeader: function(k, v) {
|
||||
// https://nodejs.org/api/http.html#http_message_headers
|
||||
// Header names are lower-cased
|
||||
this.headers[k.toLowerCase()] = v;
|
||||
},
|
||||
headers: {}
|
||||
};
|
||||
});
|
||||
|
||||
it('writes headers', function() {
|
||||
var options = {};
|
||||
httpProxy.writeHeaders({}, this.res, this.proxyRes, options);
|
||||
|
||||
expect(this.res.headers.hey).to.eql('hello');
|
||||
expect(this.res.headers.how).to.eql('are you?');
|
||||
|
||||
expect(this.res.headers).to.have.key('set-cookie');
|
||||
expect(this.res.headers['set-cookie']).to.be.an(Array);
|
||||
expect(this.res.headers['set-cookie']).to.have.length(2);
|
||||
});
|
||||
|
||||
it('writes raw headers', function() {
|
||||
var options = {};
|
||||
httpProxy.writeHeaders({}, this.res, this.rawProxyRes, options);
|
||||
|
||||
expect(this.res.headers.hey).to.eql('hello');
|
||||
expect(this.res.headers.how).to.eql('are you?');
|
||||
|
||||
expect(this.res.headers).to.have.key('set-cookie');
|
||||
expect(this.res.headers['set-cookie']).to.be.an(Array);
|
||||
expect(this.res.headers['set-cookie']).to.have.length(2);
|
||||
});
|
||||
|
||||
it('rewrites path', function() {
|
||||
var options = {
|
||||
cookiePathRewrite: '/dummyPath'
|
||||
};
|
||||
|
||||
httpProxy.writeHeaders({}, this.res, this.proxyRes, options);
|
||||
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello; domain=my.domain; path=/dummyPath');
|
||||
});
|
||||
|
||||
it('does not rewrite path', function() {
|
||||
var options = {};
|
||||
|
||||
httpProxy.writeHeaders({}, this.res, this.proxyRes, options);
|
||||
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello; domain=my.domain; path=/');
|
||||
});
|
||||
|
||||
it('removes path', function() {
|
||||
var options = {
|
||||
cookiePathRewrite: ''
|
||||
};
|
||||
|
||||
httpProxy.writeHeaders({}, this.res, this.proxyRes, options);
|
||||
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello; domain=my.domain');
|
||||
});
|
||||
|
||||
it('does not rewrite domain', function() {
|
||||
var options = {};
|
||||
|
||||
httpProxy.writeHeaders({}, this.res, this.proxyRes, options);
|
||||
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello; domain=my.domain; path=/');
|
||||
});
|
||||
|
||||
it('rewrites domain', function() {
|
||||
var options = {
|
||||
cookieDomainRewrite: 'my.new.domain'
|
||||
};
|
||||
|
||||
httpProxy.writeHeaders({}, this.res, this.proxyRes, options);
|
||||
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello; domain=my.new.domain; path=/');
|
||||
});
|
||||
|
||||
it('removes domain', function() {
|
||||
var options = {
|
||||
cookieDomainRewrite: ''
|
||||
};
|
||||
|
||||
httpProxy.writeHeaders({}, this.res, this.proxyRes, options);
|
||||
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello; path=/');
|
||||
});
|
||||
|
||||
it('rewrites headers with advanced configuration', function() {
|
||||
var options = {
|
||||
cookieDomainRewrite: {
|
||||
'*': '',
|
||||
'my.old.domain': 'my.new.domain',
|
||||
'my.special.domain': 'my.special.domain'
|
||||
}
|
||||
};
|
||||
this.proxyRes.headers['set-cookie'] = [
|
||||
'hello-on-my.domain; domain=my.domain; path=/',
|
||||
'hello-on-my.old.domain; domain=my.old.domain; path=/',
|
||||
'hello-on-my.special.domain; domain=my.special.domain; path=/'
|
||||
];
|
||||
httpProxy.writeHeaders({}, this.res, this.proxyRes, options);
|
||||
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello-on-my.domain; path=/');
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello-on-my.old.domain; domain=my.new.domain; path=/');
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello-on-my.special.domain; domain=my.special.domain; path=/');
|
||||
});
|
||||
|
||||
it('rewrites raw headers with advanced configuration', function() {
|
||||
var options = {
|
||||
cookieDomainRewrite: {
|
||||
'*': '',
|
||||
'my.old.domain': 'my.new.domain',
|
||||
'my.special.domain': 'my.special.domain'
|
||||
}
|
||||
};
|
||||
this.rawProxyRes.headers['set-cookie'] = [
|
||||
'hello-on-my.domain; domain=my.domain; path=/',
|
||||
'hello-on-my.old.domain; domain=my.old.domain; path=/',
|
||||
'hello-on-my.special.domain; domain=my.special.domain; path=/'
|
||||
];
|
||||
this.rawProxyRes.rawHeaders = this.rawProxyRes.rawHeaders.concat([
|
||||
'Set-Cookie',
|
||||
'hello-on-my.domain; domain=my.domain; path=/',
|
||||
'Set-Cookie',
|
||||
'hello-on-my.old.domain; domain=my.old.domain; path=/',
|
||||
'Set-Cookie',
|
||||
'hello-on-my.special.domain; domain=my.special.domain; path=/'
|
||||
]);
|
||||
httpProxy.writeHeaders({}, this.res, this.rawProxyRes, options);
|
||||
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello-on-my.domain; path=/');
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello-on-my.old.domain; domain=my.new.domain; path=/');
|
||||
expect(this.res.headers['set-cookie'])
|
||||
.to.contain('hello-on-my.special.domain; domain=my.special.domain; path=/');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#removeChunked', function() {
|
||||
var proxyRes = {
|
||||
headers: {
|
||||
'transfer-encoding': 'hello'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
httpProxy.removeChunked({ httpVersion: '1.0' }, {}, proxyRes);
|
||||
|
||||
expect(proxyRes.headers['transfer-encoding']).to.eql(undefined);
|
||||
});
|
||||
|
||||
});
|
||||
120
test/lib-http-proxy-passes-ws-incoming-test.js
Normal file
120
test/lib-http-proxy-passes-ws-incoming-test.js
Normal file
@ -0,0 +1,120 @@
|
||||
var httpProxy = require('../lib/http-proxy/passes/ws-incoming'),
|
||||
expect = require('expect.js');
|
||||
|
||||
describe('lib/http-proxy/passes/ws-incoming.js', function () {
|
||||
describe('#checkMethodAndHeader', function () {
|
||||
it('should drop non-GET connections', function () {
|
||||
var destroyCalled = false,
|
||||
stubRequest = {
|
||||
method: 'DELETE',
|
||||
headers: {}
|
||||
},
|
||||
stubSocket = {
|
||||
destroy: function () {
|
||||
// Simulate Socket.destroy() method when call
|
||||
destroyCalled = true;
|
||||
}
|
||||
}
|
||||
returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket);
|
||||
expect(returnValue).to.be(true);
|
||||
expect(destroyCalled).to.be(true);
|
||||
})
|
||||
|
||||
it('should drop connections when no upgrade header', function () {
|
||||
var destroyCalled = false,
|
||||
stubRequest = {
|
||||
method: 'GET',
|
||||
headers: {}
|
||||
},
|
||||
stubSocket = {
|
||||
destroy: function () {
|
||||
// Simulate Socket.destroy() method when call
|
||||
destroyCalled = true;
|
||||
}
|
||||
}
|
||||
returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket);
|
||||
expect(returnValue).to.be(true);
|
||||
expect(destroyCalled).to.be(true);
|
||||
})
|
||||
|
||||
it('should drop connections when upgrade header is different of `websocket`', function () {
|
||||
var destroyCalled = false,
|
||||
stubRequest = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
upgrade: 'anotherprotocol'
|
||||
}
|
||||
},
|
||||
stubSocket = {
|
||||
destroy: function () {
|
||||
// Simulate Socket.destroy() method when call
|
||||
destroyCalled = true;
|
||||
}
|
||||
}
|
||||
returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket);
|
||||
expect(returnValue).to.be(true);
|
||||
expect(destroyCalled).to.be(true);
|
||||
})
|
||||
|
||||
it('should return nothing when all is ok', function () {
|
||||
var destroyCalled = false,
|
||||
stubRequest = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
upgrade: 'websocket'
|
||||
}
|
||||
},
|
||||
stubSocket = {
|
||||
destroy: function () {
|
||||
// Simulate Socket.destroy() method when call
|
||||
destroyCalled = true;
|
||||
}
|
||||
}
|
||||
returnValue = httpProxy.checkMethodAndHeader(stubRequest, stubSocket);
|
||||
expect(returnValue).to.be(undefined);
|
||||
expect(destroyCalled).to.be(false);
|
||||
})
|
||||
});
|
||||
|
||||
describe('#XHeaders', function () {
|
||||
it('return if no forward request', function () {
|
||||
var returnValue = httpProxy.XHeaders({}, {}, {});
|
||||
expect(returnValue).to.be(undefined);
|
||||
});
|
||||
|
||||
it('set the correct x-forwarded-* headers from req.connection', function () {
|
||||
var stubRequest = {
|
||||
connection: {
|
||||
remoteAddress: '192.168.1.2',
|
||||
remotePort: '8080'
|
||||
},
|
||||
headers: {
|
||||
host: '192.168.1.2:8080'
|
||||
}
|
||||
}
|
||||
httpProxy.XHeaders(stubRequest, {}, { xfwd: true });
|
||||
expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.2');
|
||||
expect(stubRequest.headers['x-forwarded-port']).to.be('8080');
|
||||
expect(stubRequest.headers['x-forwarded-proto']).to.be('ws');
|
||||
});
|
||||
|
||||
it('set the correct x-forwarded-* headers from req.socket', function () {
|
||||
var stubRequest = {
|
||||
socket: {
|
||||
remoteAddress: '192.168.1.3',
|
||||
remotePort: '8181'
|
||||
},
|
||||
connection: {
|
||||
pair: true
|
||||
},
|
||||
headers: {
|
||||
host: '192.168.1.3:8181'
|
||||
}
|
||||
};
|
||||
httpProxy.XHeaders(stubRequest, {}, { xfwd: true });
|
||||
expect(stubRequest.headers['x-forwarded-for']).to.be('192.168.1.3');
|
||||
expect(stubRequest.headers['x-forwarded-port']).to.be('8181');
|
||||
expect(stubRequest.headers['x-forwarded-proto']).to.be('wss');
|
||||
});
|
||||
});
|
||||
});
|
||||
639
test/lib-http-proxy-test.js
Normal file
639
test/lib-http-proxy-test.js
Normal file
@ -0,0 +1,639 @@
|
||||
var httpProxy = require('../lib/http-proxy'),
|
||||
expect = require('expect.js'),
|
||||
http = require('http'),
|
||||
net = require('net'),
|
||||
ws = require('ws'),
|
||||
io = require('socket.io'),
|
||||
SSE = require('sse'),
|
||||
ioClient = require('socket.io-client');
|
||||
|
||||
//
|
||||
// Expose a port number generator.
|
||||
// thanks to @3rd-Eden
|
||||
//
|
||||
var initialPort = 1024, gen = {};
|
||||
Object.defineProperty(gen, 'port', {
|
||||
get: function get() {
|
||||
return initialPort++;
|
||||
}
|
||||
});
|
||||
|
||||
describe('lib/http-proxy.js', function() {
|
||||
describe('#createProxyServer', function() {
|
||||
it.skip('should throw without options', function() {
|
||||
var error;
|
||||
try {
|
||||
httpProxy.createProxyServer();
|
||||
} catch(e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
expect(error).to.be.an(Error);
|
||||
})
|
||||
|
||||
it('should return an object otherwise', function() {
|
||||
var obj = httpProxy.createProxyServer({
|
||||
target: 'http://www.google.com:80'
|
||||
});
|
||||
|
||||
expect(obj.web).to.be.a(Function);
|
||||
expect(obj.ws).to.be.a(Function);
|
||||
expect(obj.listen).to.be.a(Function);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createProxyServer with forward options and using web-incoming passes', function () {
|
||||
it('should pipe the request using web-incoming#stream method', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
forward: 'http://127.0.0.1:' + ports.source
|
||||
}).listen(ports.proxy);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
|
||||
source.close();
|
||||
proxy.close();
|
||||
done();
|
||||
});
|
||||
|
||||
source.listen(ports.source);
|
||||
http.request('http://127.0.0.1:' + ports.proxy, function() {}).end();
|
||||
})
|
||||
});
|
||||
|
||||
describe('#createProxyServer using the web-incoming passes', function () {
|
||||
it('should proxy sse', function(done){
|
||||
var ports = { source: gen.port, proxy: gen.port },
|
||||
proxy = httpProxy.createProxyServer({
|
||||
target: 'http://localhost:' + ports.source,
|
||||
}),
|
||||
proxyServer = proxy.listen(ports.proxy),
|
||||
source = http.createServer(),
|
||||
sse = new SSE(source, {path: '/'});
|
||||
|
||||
sse.on('connection', function(client) {
|
||||
client.send('Hello over SSE');
|
||||
client.close();
|
||||
});
|
||||
|
||||
source.listen(ports.source);
|
||||
|
||||
var options = {
|
||||
hostname: 'localhost',
|
||||
port: ports.proxy,
|
||||
};
|
||||
|
||||
var req = http.request(options, function(res) {
|
||||
var streamData = '';
|
||||
res.on('data', function (chunk) {
|
||||
streamData += chunk.toString('utf8');
|
||||
});
|
||||
res.on('end', function (chunk) {
|
||||
expect(streamData).to.equal(':ok\n\ndata: Hello over SSE\n\n');
|
||||
source.close();
|
||||
proxy.close();
|
||||
done();
|
||||
});
|
||||
}).end();
|
||||
});
|
||||
|
||||
it('should make the request on pipe and finish it', function(done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:' + ports.source
|
||||
}).listen(ports.proxy);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
expect(req.method).to.eql('POST');
|
||||
expect(req.headers['x-forwarded-for']).to.eql('127.0.0.1');
|
||||
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
|
||||
source.close();
|
||||
proxy.close();
|
||||
done();
|
||||
});
|
||||
|
||||
source.listen(ports.source);
|
||||
|
||||
http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: ports.proxy,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-forwarded-for': '127.0.0.1'
|
||||
}
|
||||
}, function() {}).end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createProxyServer using the web-incoming passes', function () {
|
||||
it('should make the request, handle response and finish it', function(done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:' + ports.source,
|
||||
preserveHeaderKeyCase: true
|
||||
}).listen(ports.proxy);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'})
|
||||
res.end('Hello from ' + source.address().port);
|
||||
});
|
||||
|
||||
source.listen(ports.source);
|
||||
|
||||
http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: ports.proxy,
|
||||
method: 'GET'
|
||||
}, function(res) {
|
||||
expect(res.statusCode).to.eql(200);
|
||||
expect(res.headers['content-type']).to.eql('text/plain');
|
||||
if (res.rawHeaders != undefined) {
|
||||
expect(res.rawHeaders.indexOf('Content-Type')).not.to.eql(-1);
|
||||
expect(res.rawHeaders.indexOf('text/plain')).not.to.eql(-1);
|
||||
}
|
||||
|
||||
res.on('data', function (data) {
|
||||
expect(data.toString()).to.eql('Hello from ' + ports.source);
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
source.close();
|
||||
proxy.close();
|
||||
done();
|
||||
});
|
||||
}).end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createProxyServer() method with error response', function () {
|
||||
it('should make the request and emit the error event', function(done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:' + ports.source
|
||||
});
|
||||
|
||||
proxy.on('error', function (err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.be('ECONNREFUSED');
|
||||
proxy.close();
|
||||
done();
|
||||
})
|
||||
|
||||
proxy.listen(ports.proxy);
|
||||
|
||||
http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: ports.proxy,
|
||||
method: 'GET',
|
||||
}, function() {}).end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createProxyServer setting the correct timeout value', function () {
|
||||
it('should hang up the socket at the timeout', function (done) {
|
||||
this.timeout(30);
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:' + ports.source,
|
||||
timeout: 3
|
||||
}).listen(ports.proxy);
|
||||
|
||||
proxy.on('error', function (e) {
|
||||
expect(e).to.be.an(Error);
|
||||
expect(e.code).to.be.eql('ECONNRESET');
|
||||
});
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
setTimeout(function () {
|
||||
res.end('At this point the socket should be closed');
|
||||
}, 5)
|
||||
});
|
||||
|
||||
source.listen(ports.source);
|
||||
|
||||
var testReq = http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: ports.proxy,
|
||||
method: 'GET',
|
||||
}, function() {});
|
||||
|
||||
testReq.on('error', function (e) {
|
||||
expect(e).to.be.an(Error);
|
||||
expect(e.code).to.be.eql('ECONNRESET');
|
||||
proxy.close();
|
||||
source.close();
|
||||
done();
|
||||
});
|
||||
|
||||
testReq.end();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createProxyServer with xfwd option', function () {
|
||||
it('should not throw on empty http host header', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
forward: 'http://127.0.0.1:' + ports.source,
|
||||
xfwd: true
|
||||
}).listen(ports.proxy);
|
||||
|
||||
var source = http.createServer(function(req, res) {
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(req.headers.host.split(':')[1]).to.eql(ports.source);
|
||||
source.close();
|
||||
proxy.close();
|
||||
done();
|
||||
});
|
||||
|
||||
source.listen(ports.source);
|
||||
|
||||
var socket = net.connect({port: ports.proxy}, function()
|
||||
{
|
||||
socket.write('GET / HTTP/1.0\r\n\r\n');
|
||||
});
|
||||
|
||||
// handle errors
|
||||
socket.on('error', function()
|
||||
{
|
||||
expect.fail('Unexpected socket error');
|
||||
});
|
||||
|
||||
socket.on('data', function(data)
|
||||
{
|
||||
socket.end();
|
||||
});
|
||||
|
||||
socket.on('end', function()
|
||||
{
|
||||
expect('Socket to finish').to.be.ok();
|
||||
});
|
||||
|
||||
// http.request('http://127.0.0.1:' + ports.proxy, function() {}).end();
|
||||
})
|
||||
});
|
||||
|
||||
// describe('#createProxyServer using the web-incoming passes', function () {
|
||||
// it('should emit events correctly', function(done) {
|
||||
// var proxy = httpProxy.createProxyServer({
|
||||
// target: 'http://127.0.0.1:8080'
|
||||
// }),
|
||||
|
||||
// proxyServer = proxy.listen('8081'),
|
||||
|
||||
// source = http.createServer(function(req, res) {
|
||||
// expect(req.method).to.eql('GET');
|
||||
// expect(req.headers.host.split(':')[1]).to.eql('8081');
|
||||
// res.writeHead(200, {'Content-Type': 'text/plain'})
|
||||
// res.end('Hello from ' + source.address().port);
|
||||
// }),
|
||||
|
||||
// events = [];
|
||||
|
||||
// source.listen('8080');
|
||||
|
||||
// proxy.ee.on('http-proxy:**', function (uno, dos, tres) {
|
||||
// events.push(this.event);
|
||||
// })
|
||||
|
||||
// http.request({
|
||||
// hostname: '127.0.0.1',
|
||||
// port: '8081',
|
||||
// method: 'GET',
|
||||
// }, function(res) {
|
||||
// expect(res.statusCode).to.eql(200);
|
||||
|
||||
// res.on('data', function (data) {
|
||||
// expect(data.toString()).to.eql('Hello from 8080');
|
||||
// });
|
||||
|
||||
// res.on('end', function () {
|
||||
// expect(events).to.contain('http-proxy:outgoing:web:begin');
|
||||
// expect(events).to.contain('http-proxy:outgoing:web:end');
|
||||
// source.close();
|
||||
// proxyServer.close();
|
||||
// done();
|
||||
// });
|
||||
// }).end();
|
||||
// });
|
||||
// });
|
||||
|
||||
describe('#createProxyServer using the ws-incoming passes', function () {
|
||||
it('should proxy the websockets stream', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'ws://127.0.0.1:' + ports.source,
|
||||
ws: true
|
||||
}),
|
||||
proxyServer = proxy.listen(ports.proxy),
|
||||
destiny = new ws.Server({ port: ports.source }, function () {
|
||||
var client = new ws('ws://127.0.0.1:' + ports.proxy);
|
||||
|
||||
client.on('open', function () {
|
||||
client.send('hello there');
|
||||
});
|
||||
|
||||
client.on('message', function (msg) {
|
||||
expect(msg).to.be('Hello over websockets');
|
||||
client.close();
|
||||
proxyServer.close();
|
||||
destiny.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
destiny.on('connection', function (socket) {
|
||||
socket.on('message', function (msg) {
|
||||
expect(msg).to.be('hello there');
|
||||
socket.send('Hello over websockets');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit error on proxy error', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
// note: we don't ever listen on this port
|
||||
target: 'ws://127.0.0.1:' + ports.source,
|
||||
ws: true
|
||||
}),
|
||||
proxyServer = proxy.listen(ports.proxy),
|
||||
client = new ws('ws://127.0.0.1:' + ports.proxy);
|
||||
|
||||
client.on('open', function () {
|
||||
client.send('hello there');
|
||||
});
|
||||
|
||||
var count = 0;
|
||||
function maybe_done () {
|
||||
count += 1;
|
||||
if (count === 2) done();
|
||||
}
|
||||
|
||||
client.on('error', function (err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.be('ECONNRESET');
|
||||
maybe_done();
|
||||
});
|
||||
|
||||
proxy.on('error', function (err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.be('ECONNREFUSED');
|
||||
proxyServer.close();
|
||||
maybe_done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should close client socket if upstream is closed before upgrade', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var server = http.createServer();
|
||||
server.on('upgrade', function (req, socket, head) {
|
||||
var response = [
|
||||
'HTTP/1.1 404 Not Found',
|
||||
'Content-type: text/html',
|
||||
'',
|
||||
''
|
||||
];
|
||||
socket.write(response.join('\r\n'));
|
||||
socket.end();
|
||||
});
|
||||
server.listen(ports.source);
|
||||
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
// note: we don't ever listen on this port
|
||||
target: 'ws://127.0.0.1:' + ports.source,
|
||||
ws: true
|
||||
}),
|
||||
proxyServer = proxy.listen(ports.proxy),
|
||||
client = new ws('ws://127.0.0.1:' + ports.proxy);
|
||||
|
||||
client.on('open', function () {
|
||||
client.send('hello there');
|
||||
});
|
||||
|
||||
client.on('error', function (err) {
|
||||
expect(err).to.be.an(Error);
|
||||
proxyServer.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should proxy a socket.io stream', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port },
|
||||
proxy = httpProxy.createProxyServer({
|
||||
target: 'ws://127.0.0.1:' + ports.source,
|
||||
ws: true
|
||||
}),
|
||||
proxyServer = proxy.listen(ports.proxy),
|
||||
server = http.createServer(),
|
||||
destiny = io.listen(server);
|
||||
|
||||
function startSocketIo() {
|
||||
var client = ioClient.connect('ws://127.0.0.1:' + ports.proxy);
|
||||
|
||||
client.on('connect', function () {
|
||||
client.emit('incoming', 'hello there');
|
||||
});
|
||||
|
||||
client.on('outgoing', function (data) {
|
||||
expect(data).to.be('Hello over websockets');
|
||||
proxyServer.close();
|
||||
server.close();
|
||||
done();
|
||||
});
|
||||
}
|
||||
server.listen(ports.source);
|
||||
server.on('listening', startSocketIo);
|
||||
|
||||
destiny.sockets.on('connection', function (socket) {
|
||||
socket.on('incoming', function (msg) {
|
||||
expect(msg).to.be('hello there');
|
||||
socket.emit('outgoing', 'Hello over websockets');
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
it('should emit open and close events when socket.io client connects and disconnects', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'ws://127.0.0.1:' + ports.source,
|
||||
ws: true
|
||||
});
|
||||
var proxyServer = proxy.listen(ports.proxy);
|
||||
var server = http.createServer();
|
||||
var destiny = io.listen(server);
|
||||
|
||||
function startSocketIo() {
|
||||
var client = ioClient.connect('ws://127.0.0.1:' + ports.proxy, {rejectUnauthorized: null});
|
||||
client.on('connect', function () {
|
||||
client.disconnect();
|
||||
});
|
||||
}
|
||||
var count = 0;
|
||||
|
||||
proxyServer.on('open', function() {
|
||||
count += 1;
|
||||
|
||||
});
|
||||
|
||||
proxyServer.on('close', function() {
|
||||
proxyServer.close();
|
||||
server.close();
|
||||
destiny.close();
|
||||
if (count == 1) { done(); }
|
||||
});
|
||||
|
||||
server.listen(ports.source);
|
||||
server.on('listening', startSocketIo);
|
||||
|
||||
});
|
||||
|
||||
it('should pass all set-cookie headers to client', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'ws://127.0.0.1:' + ports.source,
|
||||
ws: true
|
||||
}),
|
||||
proxyServer = proxy.listen(ports.proxy),
|
||||
destiny = new ws.Server({ port: ports.source }, function () {
|
||||
var key = new Buffer(Math.random().toString()).toString('base64');
|
||||
|
||||
var requestOptions = {
|
||||
port: ports.proxy,
|
||||
host: '127.0.0.1',
|
||||
headers: {
|
||||
'Connection': 'Upgrade',
|
||||
'Upgrade': 'websocket',
|
||||
'Host': 'ws://127.0.0.1',
|
||||
'Sec-WebSocket-Version': 13,
|
||||
'Sec-WebSocket-Key': key
|
||||
}
|
||||
};
|
||||
|
||||
var req = http.request(requestOptions);
|
||||
|
||||
req.on('upgrade', function (res, socket, upgradeHead) {
|
||||
expect(res.headers['set-cookie'].length).to.be(2);
|
||||
done();
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
|
||||
destiny.on('headers', function (headers) {
|
||||
headers.push('Set-Cookie: test1=test1');
|
||||
headers.push('Set-Cookie: test2=test2');
|
||||
});
|
||||
});
|
||||
|
||||
it('should detect a proxyReq event and modify headers', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port },
|
||||
proxy,
|
||||
proxyServer,
|
||||
destiny;
|
||||
|
||||
proxy = httpProxy.createProxyServer({
|
||||
target: 'ws://127.0.0.1:' + ports.source,
|
||||
ws: true
|
||||
});
|
||||
|
||||
proxy.on('proxyReqWs', function(proxyReq, req, socket, options, head) {
|
||||
proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');
|
||||
});
|
||||
|
||||
proxyServer = proxy.listen(ports.proxy);
|
||||
|
||||
destiny = new ws.Server({ port: ports.source }, function () {
|
||||
var client = new ws('ws://127.0.0.1:' + ports.proxy);
|
||||
|
||||
client.on('open', function () {
|
||||
client.send('hello there');
|
||||
});
|
||||
|
||||
client.on('message', function (msg) {
|
||||
expect(msg).to.be('Hello over websockets');
|
||||
client.close();
|
||||
proxyServer.close();
|
||||
destiny.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
destiny.on('connection', function (socket, upgradeReq) {
|
||||
expect(upgradeReq.headers['x-special-proxy-header']).to.eql('foobar');
|
||||
|
||||
socket.on('message', function (msg) {
|
||||
expect(msg).to.be('hello there');
|
||||
socket.send('Hello over websockets');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should forward frames with single frame payload (including on node 4.x)', function (done) {
|
||||
var payload = Array(65529).join('0');
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'ws://127.0.0.1:' + ports.source,
|
||||
ws: true
|
||||
}),
|
||||
proxyServer = proxy.listen(ports.proxy),
|
||||
destiny = new ws.Server({ port: ports.source }, function () {
|
||||
var client = new ws('ws://127.0.0.1:' + ports.proxy);
|
||||
|
||||
client.on('open', function () {
|
||||
client.send(payload);
|
||||
});
|
||||
|
||||
client.on('message', function (msg) {
|
||||
expect(msg).to.be('Hello over websockets');
|
||||
client.close();
|
||||
proxyServer.close();
|
||||
destiny.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
destiny.on('connection', function (socket) {
|
||||
socket.on('message', function (msg) {
|
||||
expect(msg).to.be(payload);
|
||||
socket.send('Hello over websockets');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should forward continuation frames with big payload (including on node 4.x)', function (done) {
|
||||
var payload = Array(65530).join('0');
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'ws://127.0.0.1:' + ports.source,
|
||||
ws: true
|
||||
}),
|
||||
proxyServer = proxy.listen(ports.proxy),
|
||||
destiny = new ws.Server({ port: ports.source }, function () {
|
||||
var client = new ws('ws://127.0.0.1:' + ports.proxy);
|
||||
|
||||
client.on('open', function () {
|
||||
client.send(payload);
|
||||
});
|
||||
|
||||
client.on('message', function (msg) {
|
||||
expect(msg).to.be('Hello over websockets');
|
||||
client.close();
|
||||
proxyServer.close();
|
||||
destiny.close();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
destiny.on('connection', function (socket) {
|
||||
socket.on('message', function (msg) {
|
||||
expect(msg).to.be(payload);
|
||||
socket.send('Hello over websockets');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
233
test/lib-https-proxy-test.js
Normal file
233
test/lib-https-proxy-test.js
Normal file
@ -0,0 +1,233 @@
|
||||
var httpProxy = require('../lib/http-proxy'),
|
||||
semver = require('semver'),
|
||||
expect = require('expect.js'),
|
||||
http = require('http')
|
||||
https = require('https'),
|
||||
path = require('path'),
|
||||
fs = require('fs');
|
||||
|
||||
//
|
||||
// Expose a port number generator.
|
||||
// thanks to @3rd-Eden
|
||||
//
|
||||
var initialPort = 1024, gen = {};
|
||||
Object.defineProperty(gen, 'port', {
|
||||
get: function get() {
|
||||
return initialPort++;
|
||||
}
|
||||
});
|
||||
|
||||
describe('lib/http-proxy.js', function() {
|
||||
describe('HTTPS #createProxyServer', function() {
|
||||
describe('HTTPS to HTTP', function () {
|
||||
it('should proxy the request en send back the response', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var source = http.createServer(function(req, res) {
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end('Hello from ' + ports.source);
|
||||
});
|
||||
|
||||
source.listen(ports.source);
|
||||
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'http://127.0.0.1:' + ports.source,
|
||||
ssl: {
|
||||
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
|
||||
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
|
||||
ciphers: 'AES128-GCM-SHA256',
|
||||
}
|
||||
}).listen(ports.proxy);
|
||||
|
||||
https.request({
|
||||
host: 'localhost',
|
||||
port: ports.proxy,
|
||||
path: '/',
|
||||
method: 'GET',
|
||||
rejectUnauthorized: false
|
||||
}, function(res) {
|
||||
expect(res.statusCode).to.eql(200);
|
||||
|
||||
res.on('data', function (data) {
|
||||
expect(data.toString()).to.eql('Hello from ' + ports.source);
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
source.close();
|
||||
proxy.close();
|
||||
done();
|
||||
})
|
||||
}).end();
|
||||
})
|
||||
});
|
||||
describe('HTTP to HTTPS', function () {
|
||||
it('should proxy the request en send back the response', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var source = https.createServer({
|
||||
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
|
||||
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
|
||||
ciphers: 'AES128-GCM-SHA256',
|
||||
}, function (req, res) {
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end('Hello from ' + ports.source);
|
||||
});
|
||||
|
||||
source.listen(ports.source);
|
||||
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'https://127.0.0.1:' + ports.source,
|
||||
// Allow to use SSL self signed
|
||||
secure: false
|
||||
}).listen(ports.proxy);
|
||||
|
||||
http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: ports.proxy,
|
||||
method: 'GET'
|
||||
}, function(res) {
|
||||
expect(res.statusCode).to.eql(200);
|
||||
|
||||
res.on('data', function (data) {
|
||||
expect(data.toString()).to.eql('Hello from ' + ports.source);
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
source.close();
|
||||
proxy.close();
|
||||
done();
|
||||
});
|
||||
}).end();
|
||||
})
|
||||
})
|
||||
describe('HTTPS to HTTPS', function () {
|
||||
it('should proxy the request en send back the response', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var source = https.createServer({
|
||||
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
|
||||
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
|
||||
ciphers: 'AES128-GCM-SHA256',
|
||||
}, function(req, res) {
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end('Hello from ' + ports.source);
|
||||
});
|
||||
|
||||
source.listen(ports.source);
|
||||
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'https://127.0.0.1:' + ports.source,
|
||||
ssl: {
|
||||
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
|
||||
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
|
||||
ciphers: 'AES128-GCM-SHA256',
|
||||
},
|
||||
secure: false
|
||||
}).listen(ports.proxy);
|
||||
|
||||
https.request({
|
||||
host: 'localhost',
|
||||
port: ports.proxy,
|
||||
path: '/',
|
||||
method: 'GET',
|
||||
rejectUnauthorized: false
|
||||
}, function(res) {
|
||||
expect(res.statusCode).to.eql(200);
|
||||
|
||||
res.on('data', function (data) {
|
||||
expect(data.toString()).to.eql('Hello from ' + ports.source);
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
source.close();
|
||||
proxy.close();
|
||||
done();
|
||||
})
|
||||
}).end();
|
||||
})
|
||||
});
|
||||
describe('HTTPS not allow SSL self signed', function () {
|
||||
it('should fail with error', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var source = https.createServer({
|
||||
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
|
||||
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
|
||||
ciphers: 'AES128-GCM-SHA256',
|
||||
}).listen(ports.source);
|
||||
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
target: 'https://127.0.0.1:' + ports.source,
|
||||
secure: true
|
||||
});
|
||||
|
||||
proxy.listen(ports.proxy);
|
||||
|
||||
proxy.on('error', function (err, req, res) {
|
||||
expect(err).to.be.an(Error);
|
||||
if (semver.gt(process.versions.node, '0.12.0')) {
|
||||
expect(err.toString()).to.be('Error: unable to verify the first certificate')
|
||||
} else {
|
||||
expect(err.toString()).to.be('Error: DEPTH_ZERO_SELF_SIGNED_CERT')
|
||||
}
|
||||
done();
|
||||
})
|
||||
|
||||
http.request({
|
||||
hostname: '127.0.0.1',
|
||||
port: ports.proxy,
|
||||
method: 'GET'
|
||||
}).end();
|
||||
})
|
||||
})
|
||||
describe('HTTPS to HTTP using own server', function () {
|
||||
it('should proxy the request en send back the response', function (done) {
|
||||
var ports = { source: gen.port, proxy: gen.port };
|
||||
var source = http.createServer(function(req, res) {
|
||||
expect(req.method).to.eql('GET');
|
||||
expect(req.headers.host.split(':')[1]).to.eql(ports.proxy);
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end('Hello from ' + ports.source);
|
||||
});
|
||||
|
||||
source.listen(ports.source);
|
||||
|
||||
var proxy = httpProxy.createServer({
|
||||
agent: new http.Agent({ maxSockets: 2 })
|
||||
});
|
||||
|
||||
var ownServer = https.createServer({
|
||||
key: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-key.pem')),
|
||||
cert: fs.readFileSync(path.join(__dirname, 'fixtures', 'agent2-cert.pem')),
|
||||
ciphers: 'AES128-GCM-SHA256',
|
||||
}, function (req, res) {
|
||||
proxy.web(req, res, {
|
||||
target: 'http://127.0.0.1:' + ports.source
|
||||
})
|
||||
}).listen(ports.proxy);
|
||||
|
||||
https.request({
|
||||
host: 'localhost',
|
||||
port: ports.proxy,
|
||||
path: '/',
|
||||
method: 'GET',
|
||||
rejectUnauthorized: false
|
||||
}, function(res) {
|
||||
expect(res.statusCode).to.eql(200);
|
||||
|
||||
res.on('data', function (data) {
|
||||
expect(data.toString()).to.eql('Hello from ' + ports.source);
|
||||
});
|
||||
|
||||
res.on('end', function () {
|
||||
source.close();
|
||||
ownServer.close();
|
||||
done();
|
||||
})
|
||||
}).end();
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user