1
0

basic.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. package basic
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "strings"
  6. "time"
  7. "github.com/onsi/ginkgo/v2"
  8. "github.com/fatedier/frp/pkg/transport"
  9. "github.com/fatedier/frp/test/e2e/framework"
  10. "github.com/fatedier/frp/test/e2e/framework/consts"
  11. "github.com/fatedier/frp/test/e2e/mock/server/httpserver"
  12. "github.com/fatedier/frp/test/e2e/mock/server/streamserver"
  13. "github.com/fatedier/frp/test/e2e/pkg/port"
  14. "github.com/fatedier/frp/test/e2e/pkg/request"
  15. )
  16. var _ = ginkgo.Describe("[Feature: Basic]", func() {
  17. f := framework.NewDefaultFramework()
  18. ginkgo.Describe("TCP && UDP", func() {
  19. types := []string{"tcp", "udp"}
  20. for _, t := range types {
  21. proxyType := t
  22. ginkgo.It(fmt.Sprintf("Expose a %s echo server", strings.ToUpper(proxyType)), func() {
  23. serverConf := consts.LegacyDefaultServerConfig
  24. clientConf := consts.LegacyDefaultClientConfig
  25. localPortName := ""
  26. protocol := "tcp"
  27. switch proxyType {
  28. case "tcp":
  29. localPortName = framework.TCPEchoServerPort
  30. protocol = "tcp"
  31. case "udp":
  32. localPortName = framework.UDPEchoServerPort
  33. protocol = "udp"
  34. }
  35. getProxyConf := func(proxyName string, portName string, extra string) string {
  36. return fmt.Sprintf(`
  37. [%s]
  38. type = %s
  39. local_port = {{ .%s }}
  40. remote_port = {{ .%s }}
  41. `+extra, proxyName, proxyType, localPortName, portName)
  42. }
  43. tests := []struct {
  44. proxyName string
  45. portName string
  46. extraConfig string
  47. }{
  48. {
  49. proxyName: "normal",
  50. portName: port.GenName("Normal"),
  51. },
  52. {
  53. proxyName: "with-encryption",
  54. portName: port.GenName("WithEncryption"),
  55. extraConfig: "use_encryption = true",
  56. },
  57. {
  58. proxyName: "with-compression",
  59. portName: port.GenName("WithCompression"),
  60. extraConfig: "use_compression = true",
  61. },
  62. {
  63. proxyName: "with-encryption-and-compression",
  64. portName: port.GenName("WithEncryptionAndCompression"),
  65. extraConfig: `
  66. use_encryption = true
  67. use_compression = true
  68. `,
  69. },
  70. }
  71. // build all client config
  72. for _, test := range tests {
  73. clientConf += getProxyConf(test.proxyName, test.portName, test.extraConfig) + "\n"
  74. }
  75. // run frps and frpc
  76. f.RunProcesses([]string{serverConf}, []string{clientConf})
  77. for _, test := range tests {
  78. framework.NewRequestExpect(f).
  79. Protocol(protocol).
  80. PortName(test.portName).
  81. Explain(test.proxyName).
  82. Ensure()
  83. }
  84. })
  85. }
  86. })
  87. ginkgo.Describe("HTTP", func() {
  88. ginkgo.It("proxy to HTTP server", func() {
  89. serverConf := consts.LegacyDefaultServerConfig
  90. vhostHTTPPort := f.AllocPort()
  91. serverConf += fmt.Sprintf(`
  92. vhost_http_port = %d
  93. `, vhostHTTPPort)
  94. clientConf := consts.LegacyDefaultClientConfig
  95. getProxyConf := func(proxyName string, customDomains string, extra string) string {
  96. return fmt.Sprintf(`
  97. [%s]
  98. type = http
  99. local_port = {{ .%s }}
  100. custom_domains = %s
  101. `+extra, proxyName, framework.HTTPSimpleServerPort, customDomains)
  102. }
  103. tests := []struct {
  104. proxyName string
  105. customDomains string
  106. extraConfig string
  107. }{
  108. {
  109. proxyName: "normal",
  110. },
  111. {
  112. proxyName: "with-encryption",
  113. extraConfig: "use_encryption = true",
  114. },
  115. {
  116. proxyName: "with-compression",
  117. extraConfig: "use_compression = true",
  118. },
  119. {
  120. proxyName: "with-encryption-and-compression",
  121. extraConfig: `
  122. use_encryption = true
  123. use_compression = true
  124. `,
  125. },
  126. {
  127. proxyName: "multiple-custom-domains",
  128. customDomains: "a.example.com, b.example.com",
  129. },
  130. }
  131. // build all client config
  132. for i, test := range tests {
  133. if tests[i].customDomains == "" {
  134. tests[i].customDomains = test.proxyName + ".example.com"
  135. }
  136. clientConf += getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n"
  137. }
  138. // run frps and frpc
  139. f.RunProcesses([]string{serverConf}, []string{clientConf})
  140. for _, test := range tests {
  141. for _, domain := range strings.Split(test.customDomains, ",") {
  142. domain = strings.TrimSpace(domain)
  143. framework.NewRequestExpect(f).
  144. Explain(test.proxyName + "-" + domain).
  145. Port(vhostHTTPPort).
  146. RequestModify(func(r *request.Request) {
  147. r.HTTP().HTTPHost(domain)
  148. }).
  149. Ensure()
  150. }
  151. }
  152. // not exist host
  153. framework.NewRequestExpect(f).
  154. Explain("not exist host").
  155. Port(vhostHTTPPort).
  156. RequestModify(func(r *request.Request) {
  157. r.HTTP().HTTPHost("not-exist.example.com")
  158. }).
  159. Ensure(framework.ExpectResponseCode(404))
  160. })
  161. })
  162. ginkgo.Describe("HTTPS", func() {
  163. ginkgo.It("proxy to HTTPS server", func() {
  164. serverConf := consts.LegacyDefaultServerConfig
  165. vhostHTTPSPort := f.AllocPort()
  166. serverConf += fmt.Sprintf(`
  167. vhost_https_port = %d
  168. `, vhostHTTPSPort)
  169. localPort := f.AllocPort()
  170. clientConf := consts.LegacyDefaultClientConfig
  171. getProxyConf := func(proxyName string, customDomains string, extra string) string {
  172. return fmt.Sprintf(`
  173. [%s]
  174. type = https
  175. local_port = %d
  176. custom_domains = %s
  177. `+extra, proxyName, localPort, customDomains)
  178. }
  179. tests := []struct {
  180. proxyName string
  181. customDomains string
  182. extraConfig string
  183. }{
  184. {
  185. proxyName: "normal",
  186. },
  187. {
  188. proxyName: "with-encryption",
  189. extraConfig: "use_encryption = true",
  190. },
  191. {
  192. proxyName: "with-compression",
  193. extraConfig: "use_compression = true",
  194. },
  195. {
  196. proxyName: "with-encryption-and-compression",
  197. extraConfig: `
  198. use_encryption = true
  199. use_compression = true
  200. `,
  201. },
  202. {
  203. proxyName: "multiple-custom-domains",
  204. customDomains: "a.example.com, b.example.com",
  205. },
  206. }
  207. // build all client config
  208. for i, test := range tests {
  209. if tests[i].customDomains == "" {
  210. tests[i].customDomains = test.proxyName + ".example.com"
  211. }
  212. clientConf += getProxyConf(test.proxyName, tests[i].customDomains, test.extraConfig) + "\n"
  213. }
  214. // run frps and frpc
  215. f.RunProcesses([]string{serverConf}, []string{clientConf})
  216. tlsConfig, err := transport.NewServerTLSConfig("", "", "")
  217. framework.ExpectNoError(err)
  218. localServer := httpserver.New(
  219. httpserver.WithBindPort(localPort),
  220. httpserver.WithTLSConfig(tlsConfig),
  221. httpserver.WithResponse([]byte("test")),
  222. )
  223. f.RunServer("", localServer)
  224. for _, test := range tests {
  225. for _, domain := range strings.Split(test.customDomains, ",") {
  226. domain = strings.TrimSpace(domain)
  227. framework.NewRequestExpect(f).
  228. Explain(test.proxyName + "-" + domain).
  229. Port(vhostHTTPSPort).
  230. RequestModify(func(r *request.Request) {
  231. r.HTTPS().HTTPHost(domain).TLSConfig(&tls.Config{
  232. ServerName: domain,
  233. InsecureSkipVerify: true,
  234. })
  235. }).
  236. ExpectResp([]byte("test")).
  237. Ensure()
  238. }
  239. }
  240. // not exist host
  241. notExistDomain := "not-exist.example.com"
  242. framework.NewRequestExpect(f).
  243. Explain("not exist host").
  244. Port(vhostHTTPSPort).
  245. RequestModify(func(r *request.Request) {
  246. r.HTTPS().HTTPHost(notExistDomain).TLSConfig(&tls.Config{
  247. ServerName: notExistDomain,
  248. InsecureSkipVerify: true,
  249. })
  250. }).
  251. ExpectError(true).
  252. Ensure()
  253. })
  254. })
  255. ginkgo.Describe("STCP && SUDP && XTCP", func() {
  256. types := []string{"stcp", "sudp", "xtcp"}
  257. for _, t := range types {
  258. proxyType := t
  259. ginkgo.It(fmt.Sprintf("Expose echo server with %s", strings.ToUpper(proxyType)), func() {
  260. serverConf := consts.LegacyDefaultServerConfig
  261. clientServerConf := consts.LegacyDefaultClientConfig + "\nuser = user1"
  262. clientVisitorConf := consts.LegacyDefaultClientConfig + "\nuser = user1"
  263. clientUser2VisitorConf := consts.LegacyDefaultClientConfig + "\nuser = user2"
  264. localPortName := ""
  265. protocol := "tcp"
  266. switch proxyType {
  267. case "stcp":
  268. localPortName = framework.TCPEchoServerPort
  269. protocol = "tcp"
  270. case "sudp":
  271. localPortName = framework.UDPEchoServerPort
  272. protocol = "udp"
  273. case "xtcp":
  274. localPortName = framework.TCPEchoServerPort
  275. protocol = "tcp"
  276. ginkgo.Skip("stun server is not stable")
  277. }
  278. correctSK := "abc"
  279. wrongSK := "123"
  280. getProxyServerConf := func(proxyName string, extra string) string {
  281. return fmt.Sprintf(`
  282. [%s]
  283. type = %s
  284. role = server
  285. sk = %s
  286. local_port = {{ .%s }}
  287. `+extra, proxyName, proxyType, correctSK, localPortName)
  288. }
  289. getProxyVisitorConf := func(proxyName string, portName, visitorSK, extra string) string {
  290. return fmt.Sprintf(`
  291. [%s]
  292. type = %s
  293. role = visitor
  294. server_name = %s
  295. sk = %s
  296. bind_port = {{ .%s }}
  297. `+extra, proxyName, proxyType, proxyName, visitorSK, portName)
  298. }
  299. tests := []struct {
  300. proxyName string
  301. bindPortName string
  302. visitorSK string
  303. commonExtraConfig string
  304. proxyExtraConfig string
  305. visitorExtraConfig string
  306. expectError bool
  307. deployUser2Client bool
  308. // skipXTCP is used to skip xtcp test case
  309. skipXTCP bool
  310. }{
  311. {
  312. proxyName: "normal",
  313. bindPortName: port.GenName("Normal"),
  314. visitorSK: correctSK,
  315. skipXTCP: true,
  316. },
  317. {
  318. proxyName: "with-encryption",
  319. bindPortName: port.GenName("WithEncryption"),
  320. visitorSK: correctSK,
  321. commonExtraConfig: "use_encryption = true",
  322. skipXTCP: true,
  323. },
  324. {
  325. proxyName: "with-compression",
  326. bindPortName: port.GenName("WithCompression"),
  327. visitorSK: correctSK,
  328. commonExtraConfig: "use_compression = true",
  329. skipXTCP: true,
  330. },
  331. {
  332. proxyName: "with-encryption-and-compression",
  333. bindPortName: port.GenName("WithEncryptionAndCompression"),
  334. visitorSK: correctSK,
  335. commonExtraConfig: `
  336. use_encryption = true
  337. use_compression = true
  338. `,
  339. skipXTCP: true,
  340. },
  341. {
  342. proxyName: "with-error-sk",
  343. bindPortName: port.GenName("WithErrorSK"),
  344. visitorSK: wrongSK,
  345. expectError: true,
  346. },
  347. {
  348. proxyName: "allowed-user",
  349. bindPortName: port.GenName("AllowedUser"),
  350. visitorSK: correctSK,
  351. proxyExtraConfig: "allow_users = another, user2",
  352. visitorExtraConfig: "server_user = user1",
  353. deployUser2Client: true,
  354. },
  355. {
  356. proxyName: "not-allowed-user",
  357. bindPortName: port.GenName("NotAllowedUser"),
  358. visitorSK: correctSK,
  359. proxyExtraConfig: "allow_users = invalid",
  360. visitorExtraConfig: "server_user = user1",
  361. expectError: true,
  362. },
  363. {
  364. proxyName: "allow-all",
  365. bindPortName: port.GenName("AllowAll"),
  366. visitorSK: correctSK,
  367. proxyExtraConfig: "allow_users = *",
  368. visitorExtraConfig: "server_user = user1",
  369. deployUser2Client: true,
  370. },
  371. }
  372. // build all client config
  373. for _, test := range tests {
  374. clientServerConf += getProxyServerConf(test.proxyName, test.commonExtraConfig+"\n"+test.proxyExtraConfig) + "\n"
  375. }
  376. for _, test := range tests {
  377. config := getProxyVisitorConf(
  378. test.proxyName, test.bindPortName, test.visitorSK, test.commonExtraConfig+"\n"+test.visitorExtraConfig,
  379. ) + "\n"
  380. if test.deployUser2Client {
  381. clientUser2VisitorConf += config
  382. } else {
  383. clientVisitorConf += config
  384. }
  385. }
  386. // run frps and frpc
  387. f.RunProcesses([]string{serverConf}, []string{clientServerConf, clientVisitorConf, clientUser2VisitorConf})
  388. for _, test := range tests {
  389. timeout := time.Second
  390. if t == "xtcp" {
  391. if test.skipXTCP {
  392. continue
  393. }
  394. timeout = 10 * time.Second
  395. }
  396. framework.NewRequestExpect(f).
  397. RequestModify(func(r *request.Request) {
  398. r.Timeout(timeout)
  399. }).
  400. Protocol(protocol).
  401. PortName(test.bindPortName).
  402. Explain(test.proxyName).
  403. ExpectError(test.expectError).
  404. Ensure()
  405. }
  406. })
  407. }
  408. })
  409. ginkgo.Describe("TCPMUX", func() {
  410. ginkgo.It("Type tcpmux", func() {
  411. serverConf := consts.LegacyDefaultServerConfig
  412. clientConf := consts.LegacyDefaultClientConfig
  413. tcpmuxHTTPConnectPortName := port.GenName("TCPMUX")
  414. serverConf += fmt.Sprintf(`
  415. tcpmux_httpconnect_port = {{ .%s }}
  416. `, tcpmuxHTTPConnectPortName)
  417. getProxyConf := func(proxyName string, extra string) string {
  418. return fmt.Sprintf(`
  419. [%s]
  420. type = tcpmux
  421. multiplexer = httpconnect
  422. local_port = {{ .%s }}
  423. custom_domains = %s
  424. `+extra, proxyName, port.GenName(proxyName), proxyName)
  425. }
  426. tests := []struct {
  427. proxyName string
  428. extraConfig string
  429. }{
  430. {
  431. proxyName: "normal",
  432. },
  433. {
  434. proxyName: "with-encryption",
  435. extraConfig: "use_encryption = true",
  436. },
  437. {
  438. proxyName: "with-compression",
  439. extraConfig: "use_compression = true",
  440. },
  441. {
  442. proxyName: "with-encryption-and-compression",
  443. extraConfig: `
  444. use_encryption = true
  445. use_compression = true
  446. `,
  447. },
  448. }
  449. // build all client config
  450. for _, test := range tests {
  451. clientConf += getProxyConf(test.proxyName, test.extraConfig) + "\n"
  452. localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(f.AllocPort()), streamserver.WithRespContent([]byte(test.proxyName)))
  453. f.RunServer(port.GenName(test.proxyName), localServer)
  454. }
  455. // run frps and frpc
  456. f.RunProcesses([]string{serverConf}, []string{clientConf})
  457. // Request without HTTP connect should get error
  458. framework.NewRequestExpect(f).
  459. PortName(tcpmuxHTTPConnectPortName).
  460. ExpectError(true).
  461. Explain("request without HTTP connect expect error").
  462. Ensure()
  463. proxyURL := fmt.Sprintf("http://127.0.0.1:%d", f.PortByName(tcpmuxHTTPConnectPortName))
  464. // Request with incorrect connect hostname
  465. framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
  466. r.Addr("invalid").Proxy(proxyURL)
  467. }).ExpectError(true).Explain("request without HTTP connect expect error").Ensure()
  468. // Request with correct connect hostname
  469. for _, test := range tests {
  470. framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
  471. r.Addr(test.proxyName).Proxy(proxyURL)
  472. }).ExpectResp([]byte(test.proxyName)).Explain(test.proxyName).Ensure()
  473. }
  474. })
  475. })
  476. })