proxy_parser_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. package upstream
  2. import (
  3. "testing"
  4. )
  5. func TestParseProxyTargetsFromRawContent(t *testing.T) {
  6. config := `map $http_upgrade $connection_upgrade {
  7. default upgrade;
  8. '' close;
  9. }
  10. upstream api-1 {
  11. server 127.0.0.1:9000;
  12. server 127.0.0.1:443;
  13. }
  14. upstream api-2 {
  15. server 127.0.0.1:9003;
  16. server 127.0.0.1:9005;
  17. }
  18. server {
  19. listen 80;
  20. listen [::]:80;
  21. server_name test.jackyu.cn;
  22. location / {
  23. # First attempt to serve request as file, then
  24. # as directory, then fall back to displaying a 404.
  25. index index.html;
  26. try_files $uri $uri/ /index.html;
  27. }
  28. location /admin {
  29. index admin.html;
  30. try_files $uri $uri/ /admin.html;
  31. }
  32. location /user {
  33. index user.html;
  34. try_files $uri $uri/ /user.html;
  35. }
  36. location /api/ {
  37. proxy_http_version 1.1;
  38. proxy_set_header Upgrade $http_upgrade;
  39. proxy_set_header Connection $connection_upgrade;
  40. proxy_pass http://api-1/;
  41. proxy_redirect off;
  42. proxy_set_header Host $host;
  43. proxy_set_header X-Real-IP $remote_addr;
  44. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  45. proxy_set_header X-Forwarded-Proto $scheme;
  46. client_max_body_size 1000m;
  47. }
  48. }
  49. server {
  50. listen 443 ssl;
  51. listen [::]:443 ssl;
  52. server_name test.jackyu.cn;
  53. ssl_certificate /etc/nginx/ssl/test.jackyu.cn_P256/fullchain.cer;
  54. ssl_certificate_key /etc/nginx/ssl/test.jackyu.cn_P256/private.key;
  55. root /var/www/ibeta/html;
  56. index index.html;
  57. http2 on;
  58. access_log /var/log/nginx/test.jackyu.cn.log main;
  59. location / {
  60. # First attempt to serve request as file, then
  61. # as directory, then fall back to displaying a 404.
  62. index index.html;
  63. try_files $uri $uri/ /index.html;
  64. }
  65. location /admin {
  66. index admin.html;
  67. try_files $uri $uri/ /admin.html;
  68. }
  69. location /user {
  70. index user.html;
  71. try_files $uri $uri/ /user.html;
  72. }
  73. location /api/ {
  74. proxy_http_version 1.1;
  75. proxy_set_header Upgrade $http_upgrade;
  76. proxy_set_header Connection $connection_upgrade;
  77. proxy_pass http://api-1/;
  78. proxy_redirect off;
  79. proxy_set_header Host $host;
  80. proxy_set_header X-Real-IP $remote_addr;
  81. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  82. proxy_set_header X-Forwarded-Proto $scheme;
  83. client_max_body_size 100m;
  84. }
  85. }`
  86. targets := ParseProxyTargetsFromRawContent(config)
  87. // Expected targets: 4 upstream servers (2 from api-1, 2 from api-2)
  88. // proxy_pass http://api-1/ should be ignored since it references an upstream
  89. expectedTargets := []ProxyTarget{
  90. {Host: "127.0.0.1", Port: "9000", Type: "upstream"},
  91. {Host: "127.0.0.1", Port: "443", Type: "upstream"},
  92. {Host: "127.0.0.1", Port: "9003", Type: "upstream"},
  93. {Host: "127.0.0.1", Port: "9005", Type: "upstream"},
  94. }
  95. if len(targets) != len(expectedTargets) {
  96. t.Errorf("Expected %d targets, got %d", len(expectedTargets), len(targets))
  97. for i, target := range targets {
  98. t.Logf("Target %d: %+v", i, target)
  99. }
  100. return
  101. }
  102. // Create a map for easier comparison
  103. targetMap := make(map[string]ProxyTarget)
  104. for _, target := range targets {
  105. key := target.Host + ":" + target.Port + ":" + target.Type
  106. targetMap[key] = target
  107. }
  108. for _, expected := range expectedTargets {
  109. key := expected.Host + ":" + expected.Port + ":" + expected.Type
  110. if _, found := targetMap[key]; !found {
  111. t.Errorf("Expected target not found: %+v", expected)
  112. }
  113. }
  114. }
  115. func TestIsUpstreamReference(t *testing.T) {
  116. upstreamNames := map[string]bool{
  117. "api-1": true,
  118. "api-2": true,
  119. "backend": true,
  120. }
  121. tests := []struct {
  122. proxyPass string
  123. expected bool
  124. }{
  125. {"http://api-1/", true},
  126. {"http://api-1", true},
  127. {"https://api-2/path", true},
  128. {"http://backend", true},
  129. {"http://127.0.0.1:8080", false},
  130. {"https://example.com", false},
  131. {"http://unknown-upstream", false},
  132. }
  133. for _, test := range tests {
  134. result := isUpstreamReference(test.proxyPass, upstreamNames)
  135. if result != test.expected {
  136. t.Errorf("isUpstreamReference(%q) = %v, expected %v", test.proxyPass, result, test.expected)
  137. }
  138. }
  139. }
  140. func TestParseProxyTargetsWithDirectProxyPass(t *testing.T) {
  141. config := `upstream api-1 {
  142. server 127.0.0.1:9000;
  143. server 127.0.0.1:443;
  144. }
  145. server {
  146. listen 80;
  147. server_name test.jackyu.cn;
  148. location /api/ {
  149. proxy_pass http://api-1/;
  150. }
  151. location /external/ {
  152. proxy_pass http://external.example.com:8080/;
  153. }
  154. location /another/ {
  155. proxy_pass https://another.example.com/;
  156. }
  157. }`
  158. targets := ParseProxyTargetsFromRawContent(config)
  159. // Expected targets:
  160. // - 2 upstream servers from api-1
  161. // - 2 direct proxy_pass targets (external.example.com:8080, another.example.com:443)
  162. // - proxy_pass http://api-1/ should be ignored since it references an upstream
  163. expectedTargets := []ProxyTarget{
  164. {Host: "127.0.0.1", Port: "9000", Type: "upstream"},
  165. {Host: "127.0.0.1", Port: "443", Type: "upstream"},
  166. {Host: "external.example.com", Port: "8080", Type: "proxy_pass"},
  167. {Host: "another.example.com", Port: "443", Type: "proxy_pass"},
  168. }
  169. if len(targets) != len(expectedTargets) {
  170. t.Errorf("Expected %d targets, got %d", len(expectedTargets), len(targets))
  171. for i, target := range targets {
  172. t.Logf("Target %d: %+v", i, target)
  173. }
  174. return
  175. }
  176. // Create a map for easier comparison
  177. targetMap := make(map[string]ProxyTarget)
  178. for _, target := range targets {
  179. key := target.Host + ":" + target.Port + ":" + target.Type
  180. targetMap[key] = target
  181. }
  182. for _, expected := range expectedTargets {
  183. key := expected.Host + ":" + expected.Port + ":" + expected.Type
  184. if _, found := targetMap[key]; !found {
  185. t.Errorf("Expected target not found: %+v", expected)
  186. }
  187. }
  188. }
  189. func TestParseProxyTargetsFromStreamConfig(t *testing.T) {
  190. config := `upstream backend {
  191. server 127.0.0.1:9000;
  192. server 127.0.0.1:9001;
  193. }
  194. server {
  195. listen 12345;
  196. proxy_pass backend;
  197. }
  198. server {
  199. listen 12346;
  200. proxy_pass 192.168.1.100:8080;
  201. }
  202. server {
  203. listen 12347;
  204. proxy_pass example.com:3306;
  205. }`
  206. targets := ParseProxyTargetsFromRawContent(config)
  207. // Expected targets:
  208. // - 2 upstream servers from backend
  209. // - 2 direct proxy_pass targets (192.168.1.100:8080, example.com:3306)
  210. // - proxy_pass backend should be ignored since it references an upstream
  211. expectedTargets := []ProxyTarget{
  212. {Host: "127.0.0.1", Port: "9000", Type: "upstream"},
  213. {Host: "127.0.0.1", Port: "9001", Type: "upstream"},
  214. {Host: "192.168.1.100", Port: "8080", Type: "proxy_pass"},
  215. {Host: "example.com", Port: "3306", Type: "proxy_pass"},
  216. }
  217. if len(targets) != len(expectedTargets) {
  218. t.Errorf("Expected %d targets, got %d", len(expectedTargets), len(targets))
  219. for i, target := range targets {
  220. t.Logf("Target %d: %+v", i, target)
  221. }
  222. return
  223. }
  224. // Create a map for easier comparison
  225. targetMap := make(map[string]ProxyTarget)
  226. for _, target := range targets {
  227. key := target.Host + ":" + target.Port + ":" + target.Type
  228. targetMap[key] = target
  229. }
  230. for _, expected := range expectedTargets {
  231. key := expected.Host + ":" + expected.Port + ":" + expected.Type
  232. if _, found := targetMap[key]; !found {
  233. t.Errorf("Expected target not found: %+v", expected)
  234. }
  235. }
  236. }
  237. func TestParseProxyTargetsFromMixedConfig(t *testing.T) {
  238. config := `upstream web_backend {
  239. server web1.example.com:80;
  240. server web2.example.com:80;
  241. }
  242. upstream stream_backend {
  243. server stream1.example.com:12345;
  244. server stream2.example.com:12345;
  245. }
  246. # HTTP server block
  247. server {
  248. listen 80;
  249. server_name example.com;
  250. location / {
  251. proxy_pass http://web_backend/;
  252. }
  253. location /api {
  254. proxy_pass http://api.example.com:8080/;
  255. }
  256. }
  257. # Stream server blocks
  258. server {
  259. listen 12345;
  260. proxy_pass stream_backend;
  261. }
  262. server {
  263. listen 3306;
  264. proxy_pass mysql.example.com:3306;
  265. }`
  266. targets := ParseProxyTargetsFromRawContent(config)
  267. // Expected targets:
  268. // - 2 upstream servers from web_backend
  269. // - 2 upstream servers from stream_backend
  270. // - 1 direct HTTP proxy_pass (api.example.com:8080)
  271. // - 1 direct stream proxy_pass (mysql.example.com:3306)
  272. // - proxy_pass http://web_backend/ and proxy_pass stream_backend should be ignored
  273. expectedTargets := []ProxyTarget{
  274. {Host: "web1.example.com", Port: "80", Type: "upstream"},
  275. {Host: "web2.example.com", Port: "80", Type: "upstream"},
  276. {Host: "stream1.example.com", Port: "12345", Type: "upstream"},
  277. {Host: "stream2.example.com", Port: "12345", Type: "upstream"},
  278. {Host: "api.example.com", Port: "8080", Type: "proxy_pass"},
  279. {Host: "mysql.example.com", Port: "3306", Type: "proxy_pass"},
  280. }
  281. if len(targets) != len(expectedTargets) {
  282. t.Errorf("Expected %d targets, got %d", len(expectedTargets), len(targets))
  283. for i, target := range targets {
  284. t.Logf("Target %d: %+v", i, target)
  285. }
  286. return
  287. }
  288. // Create a map for easier comparison
  289. targetMap := make(map[string]ProxyTarget)
  290. for _, target := range targets {
  291. key := target.Host + ":" + target.Port + ":" + target.Type
  292. targetMap[key] = target
  293. }
  294. for _, expected := range expectedTargets {
  295. key := expected.Host + ":" + expected.Port + ":" + expected.Type
  296. if _, found := targetMap[key]; !found {
  297. t.Errorf("Expected target not found: %+v", expected)
  298. }
  299. }
  300. }
  301. func TestParseProxyTargetsFromUserConfig(t *testing.T) {
  302. config := `upstream my-tcp {
  303. server 127.0.0.1:9000;
  304. }
  305. server {
  306. listen 1234-1236;
  307. resolver 8.8.8.8 valid=1s;
  308. proxy_pass example.com:$server_port;
  309. }`
  310. targets := ParseProxyTargetsFromRawContent(config)
  311. // Print actual results for debugging
  312. t.Logf("Found %d targets:", len(targets))
  313. for i, target := range targets {
  314. t.Logf("Target %d: Host=%s, Port=%s, Type=%s", i+1, target.Host, target.Port, target.Type)
  315. }
  316. // Expected targets:
  317. // - 1 upstream server from my-tcp
  318. // - 1 proxy_pass target (example.com with variable port should still be parsed)
  319. expectedTargets := []ProxyTarget{
  320. {Host: "127.0.0.1", Port: "9000", Type: "upstream"},
  321. {Host: "example.com", Port: "$server_port", Type: "proxy_pass"},
  322. }
  323. if len(targets) != len(expectedTargets) {
  324. t.Errorf("Expected %d targets, got %d", len(expectedTargets), len(targets))
  325. return
  326. }
  327. // Create a map for easier comparison
  328. targetMap := make(map[string]ProxyTarget)
  329. for _, target := range targets {
  330. key := target.Host + ":" + target.Port + ":" + target.Type
  331. targetMap[key] = target
  332. }
  333. for _, expected := range expectedTargets {
  334. key := expected.Host + ":" + expected.Port + ":" + expected.Type
  335. if _, found := targetMap[key]; !found {
  336. t.Errorf("Expected target not found: %+v", expected)
  337. }
  338. }
  339. }