From 8da67a78f41d639a6402a36a87f38dcb661e4b42 Mon Sep 17 00:00:00 2001 From: Ian Kent Date: Sun, 23 Nov 2014 15:05:11 +0000 Subject: [PATCH] Refactor web/api/smtp servers --- MailHog-Server/config/config.go | 70 ++++++++++++++ {http => MailHog-Server/http}/api/v1.go | 2 +- MailHog-Server/main.go | 42 +++++++++ MailHog-Server/smtp/smtp.go | 36 ++++++++ MailHog-UI/assets/.gitignore | 1 + MailHog-UI/http/web/web.go | 44 +++++++++ MailHog-UI/main.go | 117 +++-------------------- MailHog/main.go | 118 +++--------------------- Makefile | 2 +- http/server.go | 21 +++-- 10 files changed, 234 insertions(+), 219 deletions(-) create mode 100644 MailHog-Server/config/config.go rename {http => MailHog-Server/http}/api/v1.go (99%) create mode 100644 MailHog-Server/main.go create mode 100644 MailHog-Server/smtp/smtp.go create mode 100644 MailHog-UI/assets/.gitignore create mode 100644 MailHog-UI/http/web/web.go diff --git a/MailHog-Server/config/config.go b/MailHog-Server/config/config.go new file mode 100644 index 0000000..95ed5f4 --- /dev/null +++ b/MailHog-Server/config/config.go @@ -0,0 +1,70 @@ +package config + +import ( + "flag" + "log" + + "github.com/ian-kent/Go-MailHog/data" + "github.com/ian-kent/Go-MailHog/storage" + "github.com/ian-kent/envconf" +) + +func DefaultConfig() *Config { + return &Config{ + SMTPBindAddr: "0.0.0.0:1025", + HTTPBindAddr: "0.0.0.0:8025", + Hostname: "mailhog.example", + MongoUri: "127.0.0.1:27017", + MongoDb: "mailhog", + MongoColl: "messages", + StorageType: "memory", + MessageChan: make(chan *data.Message), + } +} + +type Config struct { + SMTPBindAddr string + HTTPBindAddr string + Hostname string + MongoUri string + MongoDb string + MongoColl string + StorageType string + Storage storage.Storage + MessageChan chan *data.Message + Assets func(asset string) ([]byte, error) +} + +var cfg = DefaultConfig() + +func Configure() *Config { + switch cfg.StorageType { + case "memory": + log.Println("Using in-memory storage") + cfg.Storage = storage.CreateInMemory() + case "mongodb": + log.Println("Using MongoDB message storage") + s := storage.CreateMongoDB(cfg.MongoUri, cfg.MongoDb, cfg.MongoColl) + if s == nil { + log.Println("MongoDB storage unavailable, reverting to in-memory storage") + cfg.Storage = storage.CreateInMemory() + } else { + log.Println("Connected to MongoDB") + cfg.Storage = s + } + default: + log.Fatalf("Invalid storage type %s", cfg.StorageType) + } + + return cfg +} + +func RegisterFlags() { + flag.StringVar(&cfg.SMTPBindAddr, "smtpbindaddr", envconf.FromEnvP("MH_SMTP_BIND_ADDR", "0.0.0.0:1025").(string), "SMTP bind interface and port, e.g. 0.0.0.0:1025 or just :1025") + flag.StringVar(&cfg.HTTPBindAddr, "httpbindaddr", envconf.FromEnvP("MH_HTTP_BIND_ADDR", "0.0.0.0:8025").(string), "HTTP bind interface and port, e.g. 0.0.0.0:8025 or just :8025") + flag.StringVar(&cfg.Hostname, "hostname", envconf.FromEnvP("MH_HOSTNAME", "mailhog.example").(string), "Hostname for EHLO/HELO response, e.g. mailhog.example") + flag.StringVar(&cfg.StorageType, "storage", envconf.FromEnvP("MH_STORAGE", "memory").(string), "Message storage: memory (default) or mongodb") + flag.StringVar(&cfg.MongoUri, "mongouri", envconf.FromEnvP("MH_MONGO_URI", "127.0.0.1:27017").(string), "MongoDB URI, e.g. 127.0.0.1:27017") + flag.StringVar(&cfg.MongoDb, "mongodb", envconf.FromEnvP("MH_MONGO_DB", "mailhog").(string), "MongoDB database, e.g. mailhog") + flag.StringVar(&cfg.MongoColl, "mongocoll", envconf.FromEnvP("MH_MONGO_COLLECTION", "messages").(string), "MongoDB collection, e.g. messages") +} diff --git a/http/api/v1.go b/MailHog-Server/http/api/v1.go similarity index 99% rename from http/api/v1.go rename to MailHog-Server/http/api/v1.go index 7f90361..f9c379f 100644 --- a/http/api/v1.go +++ b/MailHog-Server/http/api/v1.go @@ -5,7 +5,7 @@ import ( "net/smtp" "strconv" - "github.com/ian-kent/Go-MailHog/config" + "github.com/ian-kent/Go-MailHog/MailHog-Server/config" "github.com/ian-kent/Go-MailHog/data" "github.com/ian-kent/Go-MailHog/storage" "github.com/ian-kent/go-log/log" diff --git a/MailHog-Server/main.go b/MailHog-Server/main.go new file mode 100644 index 0000000..76a1956 --- /dev/null +++ b/MailHog-Server/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "flag" + "os" + + "github.com/ian-kent/Go-MailHog/MailHog-Server/config" + "github.com/ian-kent/Go-MailHog/MailHog-Server/http/api" + "github.com/ian-kent/Go-MailHog/MailHog-Server/smtp" + "github.com/ian-kent/Go-MailHog/MailHog-UI/assets" + "github.com/ian-kent/Go-MailHog/http" + "github.com/ian-kent/go-log/log" + gotcha "github.com/ian-kent/gotcha/app" +) + +var conf *config.Config +var exitCh chan int + +func configure() { + config.RegisterFlags() + flag.Parse() + conf = config.Configure() +} + +func main() { + configure() + + exitCh = make(chan int) + cb := func(app *gotcha.App) { + api.CreateAPIv1(conf, app) + } + go http.Listen(conf, assets.Asset, exitCh, cb) + go smtp.Listen(conf, exitCh) + + for { + select { + case <-exitCh: + log.Printf("Received exit signal") + os.Exit(0) + } + } +} diff --git a/MailHog-Server/smtp/smtp.go b/MailHog-Server/smtp/smtp.go new file mode 100644 index 0000000..4b13938 --- /dev/null +++ b/MailHog-Server/smtp/smtp.go @@ -0,0 +1,36 @@ +package smtp + +import ( + "io" + "log" + "net" + + "github.com/ian-kent/Go-MailHog/MailHog-Server/config" + "github.com/ian-kent/Go-MailHog/smtp/server" +) + +func Listen(cfg *config.Config, exitCh chan int) *net.TCPListener { + log.Printf("[SMTP] Binding to address: %s\n", cfg.SMTPBindAddr) + ln, err := net.Listen("tcp", cfg.SMTPBindAddr) + if err != nil { + log.Fatalf("[SMTP] Error listening on socket: %s\n", err) + } + defer ln.Close() + + for { + conn, err := ln.Accept() + if err != nil { + log.Printf("[SMTP] Error accepting connection: %s\n", err) + continue + } + defer conn.Close() + + go server.Accept( + conn.(*net.TCPConn).RemoteAddr().String(), + io.ReadWriteCloser(conn), + cfg.Storage, + cfg.MessageChan, + cfg.Hostname, + ) + } +} diff --git a/MailHog-UI/assets/.gitignore b/MailHog-UI/assets/.gitignore new file mode 100644 index 0000000..60ce659 --- /dev/null +++ b/MailHog-UI/assets/.gitignore @@ -0,0 +1 @@ +assets.go diff --git a/MailHog-UI/http/web/web.go b/MailHog-UI/http/web/web.go new file mode 100644 index 0000000..dad7af0 --- /dev/null +++ b/MailHog-UI/http/web/web.go @@ -0,0 +1,44 @@ +package web + +import ( + "html/template" + + "github.com/ian-kent/Go-MailHog/MailHog-Server/config" + gotcha "github.com/ian-kent/gotcha/app" + "github.com/ian-kent/gotcha/events" + "github.com/ian-kent/gotcha/http" +) + +type Web struct { + config *config.Config + app *gotcha.App +} + +func CreateWeb(cfg *config.Config, app *gotcha.App) *Web { + app.On(events.BeforeHandler, func(session *http.Session, next func()) { + session.Stash["config"] = cfg + next() + }) + + r := app.Router + + r.Get("/images/(?P.*)", r.Static("assets/images/{{file}}")) + r.Get("/js/(?P.*)", r.Static("assets/js/{{file}}")) + r.Get("/", Index) + + app.Config.LeftDelim = "[:" + app.Config.RightDelim = ":]" + + return &Web{ + config: cfg, + app: app, + } +} + +func Index(session *http.Session) { + html, _ := session.RenderTemplate("index.html") + + session.Stash["Page"] = "Browse" + session.Stash["Content"] = template.HTML(html) + session.Render("layout.html") +} diff --git a/MailHog-UI/main.go b/MailHog-UI/main.go index aaf275a..8c3cebc 100644 --- a/MailHog-UI/main.go +++ b/MailHog-UI/main.go @@ -2,74 +2,35 @@ package main import ( "flag" - "io" - "net" "os" - "github.com/ian-kent/Go-MailHog/config" - "github.com/ian-kent/Go-MailHog/data" - mhhttp "github.com/ian-kent/Go-MailHog/http" - "github.com/ian-kent/Go-MailHog/http/api" - smtp "github.com/ian-kent/Go-MailHog/smtp/server" - "github.com/ian-kent/Go-MailHog/storage" - "github.com/ian-kent/envconf" + "github.com/ian-kent/Go-MailHog/MailHog-Server/config" + "github.com/ian-kent/Go-MailHog/MailHog-UI/assets" + "github.com/ian-kent/Go-MailHog/MailHog-UI/http/web" + "github.com/ian-kent/Go-MailHog/http" "github.com/ian-kent/go-log/log" gotcha "github.com/ian-kent/gotcha/app" - "github.com/ian-kent/gotcha/events" - "github.com/ian-kent/gotcha/http" ) var conf *config.Config var exitCh chan int func configure() { - var smtpbindaddr, httpbindaddr, hostname, storage_type, mongouri, mongodb, mongocoll string - - flag.StringVar(&smtpbindaddr, "smtpbindaddr", envconf.FromEnvP("MH_SMTP_BIND_ADDR", "0.0.0.0:1025").(string), "SMTP bind interface and port, e.g. 0.0.0.0:1025 or just :1025") - flag.StringVar(&httpbindaddr, "httpbindaddr", envconf.FromEnvP("MH_HTTP_BIND_ADDR", "0.0.0.0:8025").(string), "HTTP bind interface and port, e.g. 0.0.0.0:8025 or just :8025") - flag.StringVar(&hostname, "hostname", envconf.FromEnvP("MH_HOSTNAME", "mailhog.example").(string), "Hostname for EHLO/HELO response, e.g. mailhog.example") - flag.StringVar(&storage_type, "storage", envconf.FromEnvP("MH_STORAGE", "memory").(string), "Message storage: memory (default) or mongodb") - flag.StringVar(&mongouri, "mongouri", envconf.FromEnvP("MH_MONGO_URI", "127.0.0.1:27017").(string), "MongoDB URI, e.g. 127.0.0.1:27017") - flag.StringVar(&mongodb, "mongodb", envconf.FromEnvP("MH_MONGO_DB", "mailhog").(string), "MongoDB database, e.g. mailhog") - flag.StringVar(&mongocoll, "mongocoll", envconf.FromEnvP("MH_MONGO_COLLECTION", "messages").(string), "MongoDB collection, e.g. messages") - + config.RegisterFlags() flag.Parse() - - conf = &config.Config{ - SMTPBindAddr: smtpbindaddr, - HTTPBindAddr: httpbindaddr, - Hostname: hostname, - MongoUri: mongouri, - MongoDb: mongodb, - MongoColl: mongocoll, - Assets: Asset, - MessageChan: make(chan *data.Message), - } - - if storage_type == "mongodb" { - log.Println("Using MongoDB message storage") - s := storage.CreateMongoDB(conf.MongoUri, conf.MongoDb, conf.MongoColl) - if s == nil { - log.Println("MongoDB storage unavailable, reverting to in-memory storage") - conf.Storage = storage.CreateInMemory() - } else { - log.Println("Connected to MongoDB") - conf.Storage = s - } - } else if storage_type == "memory" { - log.Println("Using in-memory message storage") - conf.Storage = storage.CreateInMemory() - } else { - log.Fatalf("Invalid storage type %s", storage_type) - } + conf = config.Configure() } func main() { configure() + // FIXME need to make API URL configurable + exitCh = make(chan int) - go web_listen() - go smtp_listen() + cb := func(app *gotcha.App) { + web.CreateWeb(conf, app) + } + go http.Listen(conf, assets.Asset, exitCh, cb) for { select { @@ -79,57 +40,3 @@ func main() { } } } - -func web_listen() { - log.Info("[HTTP] Binding to address: %s", conf.HTTPBindAddr) - - var app = gotcha.Create(Asset) - app.Config.Listen = conf.HTTPBindAddr - - app.On(events.BeforeHandler, func(session *http.Session, next func()) { - session.Stash["config"] = conf - next() - }) - - r := app.Router - - r.Get("/images/(?P.*)", r.Static("assets/images/{{file}}")) - r.Get("/js/(?P.*)", r.Static("assets/js/{{file}}")) - r.Get("/", mhhttp.Index) - - api.CreateAPIv1(conf, app) - - app.Config.LeftDelim = "[:" - app.Config.RightDelim = ":]" - - app.Start() - - <-make(chan int) - exitCh <- 1 -} - -func smtp_listen() *net.TCPListener { - log.Printf("[SMTP] Binding to address: %s\n", conf.SMTPBindAddr) - ln, err := net.Listen("tcp", conf.SMTPBindAddr) - if err != nil { - log.Fatalf("[SMTP] Error listening on socket: %s\n", err) - } - defer ln.Close() - - for { - conn, err := ln.Accept() - if err != nil { - log.Printf("[SMTP] Error accepting connection: %s\n", err) - continue - } - defer conn.Close() - - go smtp.Accept( - conn.(*net.TCPConn).RemoteAddr().String(), - io.ReadWriteCloser(conn), - conf.Storage, - conf.MessageChan, - conf.Hostname, - ) - } -} diff --git a/MailHog/main.go b/MailHog/main.go index dea975e..91a7eb6 100644 --- a/MailHog/main.go +++ b/MailHog/main.go @@ -2,75 +2,37 @@ package main import ( "flag" - "io" - "net" "os" + "github.com/ian-kent/Go-MailHog/MailHog-Server/config" + "github.com/ian-kent/Go-MailHog/MailHog-Server/http/api" + "github.com/ian-kent/Go-MailHog/MailHog-Server/smtp" "github.com/ian-kent/Go-MailHog/MailHog-UI/assets" - "github.com/ian-kent/Go-MailHog/config" - "github.com/ian-kent/Go-MailHog/data" - mhhttp "github.com/ian-kent/Go-MailHog/http" - "github.com/ian-kent/Go-MailHog/http/api" - smtp "github.com/ian-kent/Go-MailHog/smtp/server" - "github.com/ian-kent/Go-MailHog/storage" - "github.com/ian-kent/envconf" + "github.com/ian-kent/Go-MailHog/MailHog-UI/http/web" + "github.com/ian-kent/Go-MailHog/http" "github.com/ian-kent/go-log/log" gotcha "github.com/ian-kent/gotcha/app" - "github.com/ian-kent/gotcha/events" - "github.com/ian-kent/gotcha/http" ) var conf *config.Config var exitCh chan int func configure() { - var smtpbindaddr, httpbindaddr, hostname, storage_type, mongouri, mongodb, mongocoll string - - flag.StringVar(&smtpbindaddr, "smtpbindaddr", envconf.FromEnvP("MH_SMTP_BIND_ADDR", "0.0.0.0:1025").(string), "SMTP bind interface and port, e.g. 0.0.0.0:1025 or just :1025") - flag.StringVar(&httpbindaddr, "httpbindaddr", envconf.FromEnvP("MH_HTTP_BIND_ADDR", "0.0.0.0:8025").(string), "HTTP bind interface and port, e.g. 0.0.0.0:8025 or just :8025") - flag.StringVar(&hostname, "hostname", envconf.FromEnvP("MH_HOSTNAME", "mailhog.example").(string), "Hostname for EHLO/HELO response, e.g. mailhog.example") - flag.StringVar(&storage_type, "storage", envconf.FromEnvP("MH_STORAGE", "memory").(string), "Message storage: memory (default) or mongodb") - flag.StringVar(&mongouri, "mongouri", envconf.FromEnvP("MH_MONGO_URI", "127.0.0.1:27017").(string), "MongoDB URI, e.g. 127.0.0.1:27017") - flag.StringVar(&mongodb, "mongodb", envconf.FromEnvP("MH_MONGO_DB", "mailhog").(string), "MongoDB database, e.g. mailhog") - flag.StringVar(&mongocoll, "mongocoll", envconf.FromEnvP("MH_MONGO_COLLECTION", "messages").(string), "MongoDB collection, e.g. messages") - + config.RegisterFlags() flag.Parse() - - conf = &config.Config{ - SMTPBindAddr: smtpbindaddr, - HTTPBindAddr: httpbindaddr, - Hostname: hostname, - MongoUri: mongouri, - MongoDb: mongodb, - MongoColl: mongocoll, - Assets: assets.Asset, - MessageChan: make(chan *data.Message), - } - - if storage_type == "mongodb" { - log.Println("Using MongoDB message storage") - s := storage.CreateMongoDB(conf.MongoUri, conf.MongoDb, conf.MongoColl) - if s == nil { - log.Println("MongoDB storage unavailable, reverting to in-memory storage") - conf.Storage = storage.CreateInMemory() - } else { - log.Println("Connected to MongoDB") - conf.Storage = s - } - } else if storage_type == "memory" { - log.Println("Using in-memory message storage") - conf.Storage = storage.CreateInMemory() - } else { - log.Fatalf("Invalid storage type %s", storage_type) - } + conf = config.Configure() } func main() { configure() exitCh = make(chan int) - go web_listen() - go smtp_listen() + cb := func(app *gotcha.App) { + web.CreateWeb(conf, app) + api.CreateAPIv1(conf, app) + } + go http.Listen(conf, assets.Asset, exitCh, cb) + go smtp.Listen(conf, exitCh) for { select { @@ -80,57 +42,3 @@ func main() { } } } - -func web_listen() { - log.Info("[HTTP] Binding to address: %s", conf.HTTPBindAddr) - - var app = gotcha.Create(assets.Asset) - app.Config.Listen = conf.HTTPBindAddr - - app.On(events.BeforeHandler, func(session *http.Session, next func()) { - session.Stash["config"] = conf - next() - }) - - r := app.Router - - r.Get("/images/(?P.*)", r.Static("assets/images/{{file}}")) - r.Get("/js/(?P.*)", r.Static("assets/js/{{file}}")) - r.Get("/", mhhttp.Index) - - api.CreateAPIv1(conf, app) - - app.Config.LeftDelim = "[:" - app.Config.RightDelim = ":]" - - app.Start() - - <-make(chan int) - exitCh <- 1 -} - -func smtp_listen() *net.TCPListener { - log.Printf("[SMTP] Binding to address: %s\n", conf.SMTPBindAddr) - ln, err := net.Listen("tcp", conf.SMTPBindAddr) - if err != nil { - log.Fatalf("[SMTP] Error listening on socket: %s\n", err) - } - defer ln.Close() - - for { - conn, err := ln.Accept() - if err != nil { - log.Printf("[SMTP] Error accepting connection: %s\n", err) - continue - } - defer conn.Close() - - go smtp.Accept( - conn.(*net.TCPConn).RemoteAddr().String(), - io.ReadWriteCloser(conn), - conf.Storage, - conf.MessageChan, - conf.Hostname, - ) - } -} diff --git a/Makefile b/Makefile index 0dae9ec..236f7ed 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ all: deps bindata fmt go install ./MailHog bindata: - go-bindata -o MailHog-UI/assets/assets.go -pkg assets MailHog-UI/assets/... + go-bindata -o MailHog-UI/assets/assets.go -pkg assets -prefix MailHog-UI/ MailHog-UI/assets/... release: release-deps gox -output="build/{{.Dir}}_{{.OS}}_{{.Arch}}" diff --git a/http/server.go b/http/server.go index 42fe721..47e3881 100644 --- a/http/server.go +++ b/http/server.go @@ -1,14 +1,21 @@ package http import ( - "github.com/ian-kent/gotcha/http" - "html/template" + "github.com/ian-kent/Go-MailHog/MailHog-Server/config" + "github.com/ian-kent/go-log/log" + gotcha "github.com/ian-kent/gotcha/app" ) -func Index(session *http.Session) { - html, _ := session.RenderTemplate("index.html") +func Listen(cfg *config.Config, Asset func(string) ([]byte, error), exitCh chan int, registerCallback func(*gotcha.App)) { + log.Info("[HTTP] Binding to address: %s", cfg.HTTPBindAddr) - session.Stash["Page"] = "Browse" - session.Stash["Content"] = template.HTML(html) - session.Render("layout.html") + var app = gotcha.Create(Asset) + app.Config.Listen = cfg.HTTPBindAddr + + registerCallback(app) + + app.Start() + + <-make(chan int) + exitCh <- 1 }