rule_test.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. package ingress
  2. import (
  3. "encoding/json"
  4. "io"
  5. "net/http/httptest"
  6. "net/url"
  7. "regexp"
  8. "testing"
  9. "github.com/stretchr/testify/require"
  10. "github.com/cloudflare/cloudflared/config"
  11. )
  12. func Test_rule_matches(t *testing.T) {
  13. type args struct {
  14. requestURL *url.URL
  15. }
  16. tests := []struct {
  17. name string
  18. rule Rule
  19. args args
  20. want bool
  21. }{
  22. {
  23. name: "Just hostname, pass",
  24. rule: Rule{
  25. Hostname: "example.com",
  26. },
  27. args: args{
  28. requestURL: MustParseURL(t, "https://example.com"),
  29. },
  30. want: true,
  31. },
  32. {
  33. name: "Unicode hostname with unicode request, pass",
  34. rule: Rule{
  35. Hostname: "môô.cloudflare.com",
  36. punycodeHostname: "xn--m-xgaa.cloudflare.com",
  37. },
  38. args: args{
  39. requestURL: MustParseURL(t, "https://môô.cloudflare.com"),
  40. },
  41. want: true,
  42. },
  43. {
  44. name: "Unicode hostname with punycode request, pass",
  45. rule: Rule{
  46. Hostname: "môô.cloudflare.com",
  47. punycodeHostname: "xn--m-xgaa.cloudflare.com",
  48. },
  49. args: args{
  50. requestURL: MustParseURL(t, "https://xn--m-xgaa.cloudflare.com"),
  51. },
  52. want: true,
  53. },
  54. {
  55. name: "Entire hostname is wildcard, should match everything",
  56. rule: Rule{
  57. Hostname: "*",
  58. },
  59. args: args{
  60. requestURL: MustParseURL(t, "https://example.com"),
  61. },
  62. want: true,
  63. },
  64. {
  65. name: "Just hostname, fail",
  66. rule: Rule{
  67. Hostname: "example.com",
  68. },
  69. args: args{
  70. requestURL: MustParseURL(t, "https://foo.bar"),
  71. },
  72. want: false,
  73. },
  74. {
  75. name: "Just wildcard hostname, pass",
  76. rule: Rule{
  77. Hostname: "*.example.com",
  78. },
  79. args: args{
  80. requestURL: MustParseURL(t, "https://adam.example.com"),
  81. },
  82. want: true,
  83. },
  84. {
  85. name: "Just wildcard hostname, fail",
  86. rule: Rule{
  87. Hostname: "*.example.com",
  88. },
  89. args: args{
  90. requestURL: MustParseURL(t, "https://tunnel.com"),
  91. },
  92. want: false,
  93. },
  94. {
  95. name: "Just wildcard outside of subdomain in hostname, fail",
  96. rule: Rule{
  97. Hostname: "*example.com",
  98. },
  99. args: args{
  100. requestURL: MustParseURL(t, "https://www.example.com"),
  101. },
  102. want: false,
  103. },
  104. {
  105. name: "Wildcard over multiple subdomains",
  106. rule: Rule{
  107. Hostname: "*.example.com",
  108. },
  109. args: args{
  110. requestURL: MustParseURL(t, "https://adam.chalmers.example.com"),
  111. },
  112. want: true,
  113. },
  114. {
  115. name: "Hostname and path",
  116. rule: Rule{
  117. Hostname: "*.example.com",
  118. Path: &Regexp{Regexp: regexp.MustCompile("/static/.*\\.html")},
  119. },
  120. args: args{
  121. requestURL: MustParseURL(t, "https://www.example.com/static/index.html"),
  122. },
  123. want: true,
  124. },
  125. {
  126. name: "Hostname and empty Regex",
  127. rule: Rule{
  128. Hostname: "example.com",
  129. Path: &Regexp{},
  130. },
  131. args: args{
  132. requestURL: MustParseURL(t, "https://example.com/"),
  133. },
  134. want: true,
  135. },
  136. {
  137. name: "Hostname and nil path",
  138. rule: Rule{
  139. Hostname: "example.com",
  140. Path: nil,
  141. },
  142. args: args{
  143. requestURL: MustParseURL(t, "https://example.com/"),
  144. },
  145. want: true,
  146. },
  147. {
  148. name: "Hostname with wildcard should not match if no dot present",
  149. rule: Rule{
  150. Hostname: "*.api.abc.cloud",
  151. },
  152. args: args{
  153. requestURL: MustParseURL(t, "https://testing-api.abc.cloud"),
  154. },
  155. want: false,
  156. },
  157. }
  158. for _, tt := range tests {
  159. t.Run(tt.name, func(t *testing.T) {
  160. u := tt.args.requestURL
  161. if got := tt.rule.Matches(u.Hostname(), u.Path); got != tt.want {
  162. t.Errorf("rule.matches() = %v, want %v", got, tt.want)
  163. }
  164. })
  165. }
  166. }
  167. func TestStaticHTTPStatus(t *testing.T) {
  168. o := newStatusCode(404)
  169. buf := make([]byte, 100)
  170. sendReq := func() {
  171. resp, err := o.RoundTrip(nil)
  172. require.NoError(t, err)
  173. _, err = resp.Body.Read(buf)
  174. require.Equal(t, io.EOF, err)
  175. require.NoError(t, resp.Body.Close())
  176. require.Equal(t, 404, resp.StatusCode)
  177. resp, err = o.RoundTrip(nil)
  178. require.NoError(t, err)
  179. w := httptest.NewRecorder()
  180. n, err := io.Copy(w, resp.Body)
  181. require.NoError(t, err)
  182. require.Equal(t, int64(0), n)
  183. }
  184. sendReq()
  185. sendReq()
  186. }
  187. func TestMarshalJSON(t *testing.T) {
  188. localhost8000 := MustParseURL(t, "https://localhost:8000")
  189. defaultConfig := setConfig(originRequestFromConfig(config.OriginRequestConfig{}), config.OriginRequestConfig{})
  190. tests := []struct {
  191. name string
  192. path *Regexp
  193. expected string
  194. want bool
  195. }{
  196. {
  197. name: "Nil",
  198. path: nil,
  199. expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","matchSNItoHost":false,"caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`,
  200. want: true,
  201. },
  202. {
  203. name: "Nil regex",
  204. path: &Regexp{Regexp: nil},
  205. expected: `{"hostname":"example.com","path":null,"service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","matchSNItoHost":false,"caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`,
  206. want: true,
  207. },
  208. {
  209. name: "Empty",
  210. path: &Regexp{Regexp: regexp.MustCompile("")},
  211. expected: `{"hostname":"example.com","path":"","service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","matchSNItoHost":false,"caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`,
  212. want: true,
  213. },
  214. {
  215. name: "Basic",
  216. path: &Regexp{Regexp: regexp.MustCompile("/echo")},
  217. expected: `{"hostname":"example.com","path":"/echo","service":"https://localhost:8000","Handlers":null,"originRequest":{"connectTimeout":30,"tlsTimeout":10,"tcpKeepAlive":30,"noHappyEyeballs":false,"keepAliveTimeout":90,"keepAliveConnections":100,"httpHostHeader":"","originServerName":"","matchSNItoHost":false,"caPool":"","noTLSVerify":false,"disableChunkedEncoding":false,"bastionMode":false,"proxyAddress":"127.0.0.1","proxyPort":0,"proxyType":"","ipRules":null,"http2Origin":false,"access":{"teamName":"","audTag":null}}}`,
  218. want: true,
  219. },
  220. }
  221. for _, tt := range tests {
  222. t.Run(tt.name, func(t *testing.T) {
  223. r := Rule{
  224. Hostname: "example.com",
  225. Service: &httpService{url: localhost8000},
  226. Path: tt.path,
  227. Config: defaultConfig,
  228. }
  229. bytes, err := json.Marshal(r)
  230. require.NoError(t, err)
  231. require.Equal(t, tt.expected, string(bytes))
  232. })
  233. }
  234. }