server_test.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. package guerrilla
  2. import (
  3. "testing"
  4. "bufio"
  5. "net/textproto"
  6. "strings"
  7. "sync"
  8. "fmt"
  9. "github.com/flashmob/go-guerrilla/backends"
  10. "github.com/flashmob/go-guerrilla/log"
  11. "github.com/flashmob/go-guerrilla/mail"
  12. "github.com/flashmob/go-guerrilla/mocks"
  13. "net"
  14. )
  15. // getMockServerConfig gets a mock ServerConfig struct used for creating a new server
  16. func getMockServerConfig() *ServerConfig {
  17. sc := &ServerConfig{
  18. IsEnabled: true, // not tested here
  19. Hostname: "saggydimes.test.com",
  20. MaxSize: 1024, // smtp message max size
  21. PrivateKeyFile: "./tests/mail.guerrillamail.com.key.pem",
  22. PublicKeyFile: "./tests/mail.guerrillamail.com.cert.pem",
  23. Timeout: 5,
  24. ListenInterface: "127.0.0.1:2529",
  25. StartTLSOn: true,
  26. TLSAlwaysOn: false,
  27. MaxClients: 30, // not tested here
  28. LogFile: "./tests/testlog",
  29. }
  30. return sc
  31. }
  32. // getMockServerConn gets a new server using sc. Server will be using a mocked TCP connection
  33. // using the dummy backend
  34. // RCP TO command only allows test.com host
  35. func getMockServerConn(sc *ServerConfig, t *testing.T) (*mocks.Conn, *server) {
  36. var logOpenError error
  37. var mainlog log.Logger
  38. mainlog, logOpenError = log.GetLogger(sc.LogFile, "debug")
  39. if logOpenError != nil {
  40. mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface)
  41. }
  42. backend, err := backends.New(
  43. backends.BackendConfig{"log_received_mails": true, "save_workers_size": 1},
  44. mainlog)
  45. if err != nil {
  46. t.Error("new dummy backend failed because:", err)
  47. }
  48. server, err := newServer(sc, backend, mainlog)
  49. if err != nil {
  50. //t.Error("new server failed because:", err)
  51. } else {
  52. server.setAllowedHosts([]string{"test.com"})
  53. }
  54. conn := mocks.NewConn()
  55. return conn, server
  56. }
  57. func TestHandleClient(t *testing.T) {
  58. var mainlog log.Logger
  59. var logOpenError error
  60. sc := getMockServerConfig()
  61. mainlog, logOpenError = log.GetLogger(sc.LogFile, "debug")
  62. if logOpenError != nil {
  63. mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface)
  64. }
  65. conn, server := getMockServerConn(sc, t)
  66. // call the serve.handleClient() func in a goroutine.
  67. client := NewClient(conn.Server, 1, mainlog, mail.NewPool(5))
  68. var wg sync.WaitGroup
  69. wg.Add(1)
  70. go func() {
  71. server.handleClient(client)
  72. wg.Done()
  73. }()
  74. // Wait for the greeting from the server
  75. r := textproto.NewReader(bufio.NewReader(conn.Client))
  76. line, _ := r.ReadLine()
  77. // fmt.Println(line)
  78. w := textproto.NewWriter(bufio.NewWriter(conn.Client))
  79. w.PrintfLine("HELO test.test.com")
  80. line, _ = r.ReadLine()
  81. //fmt.Println(line)
  82. w.PrintfLine("QUIT")
  83. line, _ = r.ReadLine()
  84. //fmt.Println("line is:", line)
  85. expected := "221 2.0.0 Bye"
  86. if strings.Index(line, expected) != 0 {
  87. t.Error("expected", expected, "but got:", line)
  88. }
  89. wg.Wait() // wait for handleClient to exit
  90. }
  91. func TestXClient(t *testing.T) {
  92. var mainlog log.Logger
  93. var logOpenError error
  94. sc := getMockServerConfig()
  95. sc.XClientOn = true
  96. mainlog, logOpenError = log.GetLogger(sc.LogFile, "debug")
  97. if logOpenError != nil {
  98. mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface)
  99. }
  100. conn, server := getMockServerConn(sc, t)
  101. // call the serve.handleClient() func in a goroutine.
  102. client := NewClient(conn.Server, 1, mainlog, mail.NewPool(5))
  103. var wg sync.WaitGroup
  104. wg.Add(1)
  105. go func() {
  106. server.handleClient(client)
  107. wg.Done()
  108. }()
  109. // Wait for the greeting from the server
  110. r := textproto.NewReader(bufio.NewReader(conn.Client))
  111. line, _ := r.ReadLine()
  112. // fmt.Println(line)
  113. w := textproto.NewWriter(bufio.NewWriter(conn.Client))
  114. w.PrintfLine("HELO test.test.com")
  115. line, _ = r.ReadLine()
  116. //fmt.Println(line)
  117. w.PrintfLine("XCLIENT ADDR=212.96.64.216 NAME=[UNAVAILABLE]")
  118. line, _ = r.ReadLine()
  119. if client.RemoteIP != "212.96.64.216" {
  120. t.Error("client.RemoteIP should be 212.96.64.216, but got:", client.RemoteIP)
  121. }
  122. expected := "250 2.1.0 OK"
  123. if strings.Index(line, expected) != 0 {
  124. t.Error("expected", expected, "but got:", line)
  125. }
  126. // try malformed input
  127. w.PrintfLine("XCLIENT c")
  128. line, _ = r.ReadLine()
  129. expected = "250 2.1.0 OK"
  130. if strings.Index(line, expected) != 0 {
  131. t.Error("expected", expected, "but got:", line)
  132. }
  133. w.PrintfLine("QUIT")
  134. line, _ = r.ReadLine()
  135. wg.Wait() // wait for handleClient to exit
  136. }
  137. // The backend gateway should time out after 1 second because it sleeps for 2 sec.
  138. // The transaction should wait until finished, and then test to see if we can do
  139. // a second transaction
  140. func TestGatewayTimeout(t *testing.T) {
  141. bcfg := backends.BackendConfig{
  142. "save_workers_size": 1,
  143. "save_process": "HeadersParser|Debugger",
  144. "log_received_mails": true,
  145. "primary_mail_host": "example.com",
  146. "gw_save_timeout": "1s",
  147. "gw_val_rcpt_timeout": "1s",
  148. "sleep_seconds": 2,
  149. }
  150. cfg := &AppConfig{
  151. LogFile: log.OutputOff.String(),
  152. AllowedHosts: []string{"grr.la"},
  153. }
  154. cfg.BackendConfig = bcfg
  155. d := Daemon{Config: cfg}
  156. err := d.Start()
  157. if err != nil {
  158. t.Error("server didn't start")
  159. } else {
  160. conn, err := net.Dial("tcp", "127.0.0.1:2525")
  161. if err != nil {
  162. return
  163. }
  164. in := bufio.NewReader(conn)
  165. str, err := in.ReadString('\n')
  166. fmt.Fprint(conn, "HELO host\r\n")
  167. str, err = in.ReadString('\n')
  168. // perform 2 transactions
  169. // both should panic.
  170. for i := 0; i < 2; i++ {
  171. fmt.Fprint(conn, "MAIL FROM:<[email protected]>r\r\n")
  172. str, err = in.ReadString('\n')
  173. fmt.Fprint(conn, "RCPT TO:[email protected]\r\n")
  174. str, err = in.ReadString('\n')
  175. fmt.Fprint(conn, "DATA\r\n")
  176. str, err = in.ReadString('\n')
  177. fmt.Fprint(conn, "Subject: Test subject\r\n")
  178. fmt.Fprint(conn, "\r\n")
  179. fmt.Fprint(conn, "A an email body\r\n")
  180. fmt.Fprint(conn, ".\r\n")
  181. str, err = in.ReadString('\n')
  182. expect := "transaction timeout"
  183. if strings.Index(str, expect) == -1 {
  184. t.Error("Expected the reply to have'", expect, "'but got", str)
  185. }
  186. }
  187. _ = str
  188. d.Shutdown()
  189. }
  190. }
  191. // The processor will panic and gateway should recover from it
  192. func TestGatewayPanic(t *testing.T) {
  193. bcfg := backends.BackendConfig{
  194. "save_workers_size": 1,
  195. "save_process": "HeadersParser|Debugger",
  196. "log_received_mails": true,
  197. "primary_mail_host": "example.com",
  198. "gw_save_timeout": "2s",
  199. "gw_val_rcpt_timeout": "2s",
  200. "sleep_seconds": 1,
  201. }
  202. cfg := &AppConfig{
  203. LogFile: log.OutputOff.String(),
  204. AllowedHosts: []string{"grr.la"},
  205. }
  206. cfg.BackendConfig = bcfg
  207. d := Daemon{Config: cfg}
  208. err := d.Start()
  209. if err != nil {
  210. t.Error("server didn't start")
  211. } else {
  212. conn, err := net.Dial("tcp", "127.0.0.1:2525")
  213. if err != nil {
  214. return
  215. }
  216. in := bufio.NewReader(conn)
  217. str, err := in.ReadString('\n')
  218. fmt.Fprint(conn, "HELO host\r\n")
  219. str, err = in.ReadString('\n')
  220. // perform 2 transactions
  221. // both should timeout. The reason why 2 is because we want to make
  222. // sure that the client waits until processing finishes, and the
  223. // timeout event is captured.
  224. for i := 0; i < 2; i++ {
  225. fmt.Fprint(conn, "MAIL FROM:<[email protected]>r\r\n")
  226. str, err = in.ReadString('\n')
  227. fmt.Fprint(conn, "RCPT TO:[email protected]\r\n")
  228. str, err = in.ReadString('\n')
  229. fmt.Fprint(conn, "DATA\r\n")
  230. str, err = in.ReadString('\n')
  231. fmt.Fprint(conn, "Subject: Test subject\r\n")
  232. fmt.Fprint(conn, "\r\n")
  233. fmt.Fprint(conn, "A an email body\r\n")
  234. fmt.Fprint(conn, ".\r\n")
  235. str, err = in.ReadString('\n')
  236. expect := "storage failed"
  237. if strings.Index(str, expect) == -1 {
  238. t.Error("Expected the reply to have'", expect, "'but got", str)
  239. }
  240. }
  241. _ = str
  242. d.Shutdown()
  243. }
  244. }
  245. // TODO
  246. // - test github issue #44 and #42