server_test.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package server
  4. import (
  5. "net/http"
  6. "os"
  7. "runtime"
  8. "testing"
  9. "time"
  10. "miniflux.app/v2/internal/config"
  11. )
  12. func TestDetermineListenTargets(t *testing.T) {
  13. tests := []struct {
  14. name string
  15. addresses []string
  16. certDomain string
  17. certFile string
  18. keyFile string
  19. expected []listenTarget
  20. }{
  21. {
  22. name: "single HTTP listener",
  23. addresses: []string{":8080"},
  24. expected: []listenTarget{
  25. {address: ":8080", mode: modeHTTP},
  26. },
  27. },
  28. {
  29. name: "multiple HTTP listeners",
  30. addresses: []string{":8080", ":9090"},
  31. expected: []listenTarget{
  32. {address: ":8080", mode: modeHTTP},
  33. {address: ":9090", mode: modeHTTP},
  34. },
  35. },
  36. {
  37. name: "TLS with cert files",
  38. addresses: []string{":443"},
  39. certFile: "/path/to/cert.pem",
  40. keyFile: "/path/to/key.pem",
  41. expected: []listenTarget{
  42. {address: ":443", mode: modeTLS},
  43. },
  44. },
  45. {
  46. name: "cert file without key file falls back to HTTP",
  47. addresses: []string{":8080"},
  48. certFile: "/path/to/cert.pem",
  49. expected: []listenTarget{
  50. {address: ":8080", mode: modeHTTP},
  51. },
  52. },
  53. {
  54. name: "key file without cert file falls back to HTTP",
  55. addresses: []string{":8080"},
  56. keyFile: "/path/to/key.pem",
  57. expected: []listenTarget{
  58. {address: ":8080", mode: modeHTTP},
  59. },
  60. },
  61. {
  62. name: "autocert with :https address",
  63. addresses: []string{":https"},
  64. certDomain: "example.com",
  65. expected: []listenTarget{
  66. {address: ":https", mode: modeAutocertTLS},
  67. },
  68. },
  69. {
  70. name: "autocert with first address containing colon",
  71. addresses: []string{":443"},
  72. certDomain: "example.com",
  73. expected: []listenTarget{
  74. {address: ":443", mode: modeAutocertTLS},
  75. },
  76. },
  77. {
  78. name: "autocert does not apply to second non-https address",
  79. addresses: []string{":https", ":8080"},
  80. certDomain: "example.com",
  81. expected: []listenTarget{
  82. {address: ":https", mode: modeAutocertTLS},
  83. {address: ":8080", mode: modeHTTP},
  84. },
  85. },
  86. {
  87. name: "unix socket",
  88. addresses: []string{"/var/run/miniflux.sock"},
  89. expected: []listenTarget{
  90. {address: "/var/run/miniflux.sock", mode: modeUnixSocket},
  91. },
  92. },
  93. {
  94. name: "unix socket with TLS",
  95. addresses: []string{"/var/run/miniflux.sock"},
  96. certFile: "/path/to/cert.pem",
  97. keyFile: "/path/to/key.pem",
  98. expected: []listenTarget{
  99. {address: "/var/run/miniflux.sock", mode: modeUnixSocketTLS},
  100. },
  101. },
  102. {
  103. name: "mixed unix socket and TCP",
  104. addresses: []string{"/var/run/miniflux.sock", ":8080"},
  105. certFile: "/path/to/cert.pem",
  106. keyFile: "/path/to/key.pem",
  107. expected: []listenTarget{
  108. {address: "/var/run/miniflux.sock", mode: modeUnixSocketTLS},
  109. {address: ":8080", mode: modeTLS},
  110. },
  111. },
  112. {
  113. name: "empty address list",
  114. addresses: []string{},
  115. expected: nil,
  116. },
  117. }
  118. for _, tc := range tests {
  119. t.Run(tc.name, func(t *testing.T) {
  120. got := determineListenTargets(tc.addresses, tc.certDomain, tc.certFile, tc.keyFile)
  121. if len(got) != len(tc.expected) {
  122. t.Fatalf("got %d targets, want %d", len(got), len(tc.expected))
  123. }
  124. for i := range got {
  125. if got[i] != tc.expected[i] {
  126. t.Errorf("target[%d] = %+v, want %+v", i, got[i], tc.expected[i])
  127. }
  128. }
  129. })
  130. }
  131. }
  132. func TestAnyTLS(t *testing.T) {
  133. tests := []struct {
  134. name string
  135. targets []listenTarget
  136. expected bool
  137. }{
  138. {
  139. name: "empty list",
  140. targets: nil,
  141. expected: false,
  142. },
  143. {
  144. name: "HTTP only",
  145. targets: []listenTarget{{mode: modeHTTP}},
  146. expected: false,
  147. },
  148. {
  149. name: "systemd only",
  150. targets: []listenTarget{{mode: modeSystemd}},
  151. expected: false,
  152. },
  153. {
  154. name: "unix socket without TLS",
  155. targets: []listenTarget{{mode: modeUnixSocket}},
  156. expected: false,
  157. },
  158. {
  159. name: "TLS mode",
  160. targets: []listenTarget{{mode: modeTLS}},
  161. expected: true,
  162. },
  163. {
  164. name: "autocert TLS mode",
  165. targets: []listenTarget{{mode: modeAutocertTLS}},
  166. expected: true,
  167. },
  168. {
  169. name: "unix socket TLS mode",
  170. targets: []listenTarget{{mode: modeUnixSocketTLS}},
  171. expected: true,
  172. },
  173. {
  174. name: "mixed with one TLS",
  175. targets: []listenTarget{{mode: modeHTTP}, {mode: modeTLS}, {mode: modeUnixSocket}},
  176. expected: true,
  177. },
  178. }
  179. for _, tc := range tests {
  180. t.Run(tc.name, func(t *testing.T) {
  181. if got := anyTLS(tc.targets); got != tc.expected {
  182. t.Errorf("anyTLS() = %v, want %v", got, tc.expected)
  183. }
  184. })
  185. }
  186. }
  187. func TestNewHTTPServerConfiguresTimeouts(t *testing.T) {
  188. previousOptions := config.Opts
  189. config.Opts = config.NewConfigOptions()
  190. t.Cleanup(func() { config.Opts = previousOptions })
  191. handler := http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
  192. server := newHTTPServer(":8080", handler)
  193. timeout := config.Opts.HTTPServerTimeout()
  194. if server.Addr != ":8080" {
  195. t.Errorf("server address = %q, want %q", server.Addr, ":8080")
  196. }
  197. if server.Handler == nil {
  198. t.Error("server handler must not be nil")
  199. }
  200. for name, actual := range map[string]time.Duration{
  201. "ReadTimeout": server.ReadTimeout,
  202. "WriteTimeout": server.WriteTimeout,
  203. "IdleTimeout": server.IdleTimeout,
  204. "ReadHeaderTimeout": server.ReadHeaderTimeout,
  205. } {
  206. if actual != timeout {
  207. t.Errorf("%s = %v, want %v", name, actual, timeout)
  208. }
  209. }
  210. }
  211. func TestCreateUnixSocketListenerPermissions(t *testing.T) {
  212. if runtime.GOOS == "windows" {
  213. t.Skip("Unix sockets are not supported on Windows")
  214. }
  215. tempFile, err := os.CreateTemp("/tmp", "miniflux-*.sock")
  216. if err != nil {
  217. t.Fatalf("Unable to allocate Unix socket path: %v", err)
  218. }
  219. socketFile := tempFile.Name()
  220. if err := tempFile.Close(); err != nil {
  221. t.Fatalf("Unable to close temporary file: %v", err)
  222. }
  223. if err := os.Remove(socketFile); err != nil {
  224. t.Fatalf("Unable to prepare Unix socket path: %v", err)
  225. }
  226. t.Cleanup(func() { os.Remove(socketFile) })
  227. listener := createUnixSocketListener(socketFile)
  228. t.Cleanup(func() { listener.Close() })
  229. fileInfo, err := os.Stat(socketFile)
  230. if err != nil {
  231. t.Fatalf("Unable to stat Unix socket: %v", err)
  232. }
  233. if got, want := fileInfo.Mode().Perm(), os.FileMode(0660); got != want {
  234. t.Errorf("Unix socket permissions = %04o, want %04o", got, want)
  235. }
  236. }