Browse Source

Breaking change: Top-level config is now required.

multi-app
Benjamin Shelton 4 months ago
parent
commit
4af24fedd4
12 changed files with 93 additions and 81 deletions
  1. +2
    -2
      api.go
  2. +17
    -17
      application.go
  3. +5
    -3
      cmd/demos/main.go
  4. +1
    -1
      extension/csrf.go
  5. +18
    -18
      router.go
  6. +12
    -12
      router_listen.go
  7. +1
    -1
      router_private.go
  8. +4
    -2
      tests/configuration_test.go
  9. +7
    -5
      tests/hostname_test.go
  10. +5
    -3
      tests/multiapp_test.go
  11. +4
    -2
      tests/rebind_multiapp_test.go
  12. +17
    -15
      tests/server.go

+ 2
- 2
api.go View File

@@ -27,12 +27,12 @@ type Application interface {
RegisterShutdownHook(hook ShutdownHook)
Listen() error
Stop()
Config() *config.ServerConfig
Config() *config.Config
Dependencies() *mappers.DependencyMapper
Logger() *logging.Log
Router() *Router
Extensions() *ExtensionManager
Reload(config *config.ServerConfig)
Reload(config *config.Config)
}

type Controller interface {


+ 17
- 17
application.go View File

@@ -18,7 +18,7 @@ import (
type application struct {
router *Router
logger *logging.Log
config *config.ServerConfig
config *config.Config
dependencies *mappers.DependencyMapper
extensions *ExtensionManager
subapps map[string]Application
@@ -27,7 +27,7 @@ type application struct {
sync.RWMutex
}

func New(conf ...*config.ServerConfig) *application {
func New(conf ...*config.Config) *application {
dependencies := mappers.NewDependencyMapper()

app := &application{
@@ -40,23 +40,23 @@ func New(conf ...*config.ServerConfig) *application {
if len(conf) == 1 {
app.config = conf[0]
} else {
app.config = &config.ServerConfig{}
app.config = &config.Config{}
}

// ListenAddress wasn't set, so we'll pick something reasonable.
if app.config.ListenAddress == "" {
app.config.ListenAddress = "localhost:8080"
if app.config.Server.ListenAddress == "" {
app.config.Server.ListenAddress = "localhost:8080"
}

// Init session cookies.
if app.config.Session == nil {
app.config.Session = &config.SessionConfig{
if app.config.Server.Session == nil {
app.config.Server.Session = &config.SessionConfig{
CookieOptions: &http.Cookie{},
}
}

if app.config.Session.CookieOptions.Domain == "" {
app.config.Session.CookieOptions.Domain = app.config.Hostname
if app.config.Server.Session.CookieOptions.Domain == "" {
app.config.Server.Session.CookieOptions.Domain = app.config.Server.Hostname
}

app.router = NewRouter(app)
@@ -83,15 +83,15 @@ func (a *application) AttachApplication(app Application) {
// port, and/or basepath. This would allow the multiapp application handler
// to be slightly more forgiving when deciding what to load, particularly on
// developer installs.
p.Loader().AddApplicationHandler(app.Config().CalculatedHostname(), app.Router().proxy)
p.Loader().AddApplicationHandler(app.Config().Server.CalculatedHostname(), app.Router().proxy)
//p.Loader().AddApplicationHandler(app.Config().CalculatedPath(), app.Router().proxy)

app.Router().SetupMuxer()
}

func (a *application) LoadSession() {
if a.config.Session != nil {
session.New(a.config.Session)
if a.config.Server.Session != nil {
session.New(a.config.Server.Session)
}
}

@@ -136,13 +136,13 @@ func (a *application) DefaultMiddleware() {
}
}

if a.config.RealIP {
if a.config.Server.RealIP {
a.router.Middleware(middleware.RealIP)
}

logger := a.logger
if a.config.RequestLog != "" {
logger = logging.MustGetLogger(a.config.RequestLog)
if a.config.Server.RequestLog != "" {
logger = logging.MustGetLogger(a.config.Server.RequestLog)
//logger.SetLevel(a.config.LogLevel)
}

@@ -195,7 +195,7 @@ func (a *application) Stop() {
a.router.Close()
}

func (a *application) Config() *config.ServerConfig {
func (a *application) Config() *config.Config {
return a.config
}

@@ -217,6 +217,6 @@ func (a *application) Extensions() *ExtensionManager {

// Reload the Capstan server. This reconfigures all dependent services and
// rebinds all endpoints.
func (a *application) Reload(config *config.ServerConfig) {
func (a *application) Reload(config *config.Config) {
a.router.Rebind()
}

+ 5
- 3
cmd/demos/main.go View File

@@ -307,7 +307,9 @@ func main() {
},
}

app := capstan.New(serverConfig)
app := capstan.New(&capstan.Config{
Server: serverConfig,
})
//app.DefaultMiddleware()
app.Dependencies().Register("dep1", &Dependency{"yes", true})
app.Dependencies().Register("logger", logging.MustGetLogger("main"))
@@ -315,8 +317,8 @@ func main() {
app.Dependencies().Register("dup2", &DuplicateDependency{"dup2", true})

csrf, err := extension.NewCSRF(&extension.CSRFOptions{
Key: app.Config().Session.Key,
HMAC: app.Config().Session.HMACKey,
Key: app.Config().Server.Session.Key,
HMAC: app.Config().Server.Session.HMACKey,
})
if err != nil {
fmt.Println("couldn't initialize CSRF extensions:", err)


+ 1
- 1
extension/csrf.go View File

@@ -463,7 +463,7 @@ func (c *CSRF) Requires() []string {
}

func (c *CSRF) Init(server capstan.Application) {
c.hostname = server.Config().CalculatedHostname()
c.hostname = server.Config().Server.CalculatedHostname()
server.Router().BeforeResponseFunc(c.beforeResponse)
server.Dependencies().Register(c.options.InjectedName, c)
}


+ 18
- 18
router.go View File

@@ -185,11 +185,11 @@ func NewRouter(app Application) *Router {
router := NewEmptyRouter()
router.app = app
router.mux = chi.NewRouter()
router.urls = NewURLMapper(app.Config())
router.urls = NewURLMapper(app.Config().Server)
router.dependencies = app.Dependencies()

if app.Config().BasePath != "" {
router.basePath = app.Config().BasePath
if app.Config().Server.BasePath != "" {
router.basePath = app.Config().Server.BasePath
if !strings.HasPrefix(router.basePath, "/") {
router.basePath = "/" + router.basePath
}
@@ -198,32 +198,32 @@ func NewRouter(app Application) *Router {
}
}

if app.Config().IdleConnectionTimeout == 0 {
app.Config().IdleConnectionTimeout = 10
if app.Config().Server.IdleConnectionTimeout == 0 {
app.Config().Server.IdleConnectionTimeout = 10
}

if app.Config().HeaderTimeout == 0 {
app.Config().HeaderTimeout = 60
if app.Config().Server.HeaderTimeout == 0 {
app.Config().Server.HeaderTimeout = 60
}

if app.Config().ShutdownTimeout == 0 {
app.Config().ShutdownTimeout = 10
if app.Config().Server.ShutdownTimeout == 0 {
app.Config().Server.ShutdownTimeout = 10
}

if app.Config().MaxRequestDuration == 0 {
app.Config().MaxRequestDuration = 30
if app.Config().Server.MaxRequestDuration == 0 {
app.Config().Server.MaxRequestDuration = 30
}

if app.Config().MaxHeaderLength == 0 {
app.Config().MaxHeaderLength = 1 << 20
if app.Config().Server.MaxHeaderLength == 0 {
app.Config().Server.MaxHeaderLength = 1 << 20
}

router.proxy = newProxy(&proxyOptions{
ListenAddr: app.Config().ListenAddress,
Hostname: app.Config().CalculatedHostname(),
EnforcePort: app.Config().EnforcePortInHost,
EnforceHost: app.Config().EnforceHostname,
MaxRequestDuration: app.Config().MaxRequestDuration,
ListenAddr: app.Config().Server.ListenAddress,
Hostname: app.Config().Server.CalculatedHostname(),
EnforcePort: app.Config().Server.EnforcePortInHost,
EnforceHost: app.Config().Server.EnforceHostname,
MaxRequestDuration: app.Config().Server.MaxRequestDuration,
})

router.mux.NotFound(func(w http.ResponseWriter, r *http.Request) {


+ 12
- 12
router_listen.go View File

@@ -36,28 +36,28 @@ func (r *Router) listen(errc chan error) {
// availability configurations that expect graceful restart capabilities.
func (r *Router) setupListeners() error {
var requiresPriv bool
config := r.app.Config()
config := r.app.Config().Server

r.proxy.Switch(r.mux)

if len(r.listeners) == 0 {
requiresPriv = isPrivPort(r.app.Config().ListenAddress)
if listener, err := r.ListenHTTP(r.app.Config().ListenAddress); err != nil {
requiresPriv = isPrivPort(r.app.Config().Server.ListenAddress)
if listener, err := r.ListenHTTP(r.app.Config().Server.ListenAddress); err != nil {
return err
} else {
r.namedListeners.tcp = listener
r.AddListener(listener)
}

requiresPriv = isPrivPort(r.app.Config().ListenAddressTLS)
if listener, err := r.ListenTLS(r.app.Config().ListenAddressTLS); err != nil {
requiresPriv = isPrivPort(r.app.Config().Server.ListenAddressTLS)
if listener, err := r.ListenTLS(r.app.Config().Server.ListenAddressTLS); err != nil {
return err
} else {
r.namedListeners.tls = listener
r.AddListener(listener)
}

if listener, err := r.ListenSocket(r.app.Config().ListenSocket); err != nil {
if listener, err := r.ListenSocket(r.app.Config().Server.ListenSocket); err != nil {
return err
} else {
r.namedListeners.sock = listener
@@ -98,9 +98,9 @@ func (r *Router) AddListener(listener net.Listener) {
func (r *Router) AttachListener(listener net.Listener) {
var server HTTPServer
server.Handler = r.proxy
server.ReadHeaderTimeout = time.Second * time.Duration(r.app.Config().HeaderTimeout)
server.IdleTimeout = time.Second * time.Duration(r.app.Config().IdleConnectionTimeout)
server.MaxHeaderBytes = r.app.Config().MaxHeaderLength
server.ReadHeaderTimeout = time.Second * time.Duration(r.app.Config().Server.HeaderTimeout)
server.IdleTimeout = time.Second * time.Duration(r.app.Config().Server.IdleConnectionTimeout)
server.MaxHeaderBytes = r.app.Config().Server.MaxHeaderLength
r.servers = append(r.servers, &server)
r.wg.Add(1)
go func(l net.Listener) {
@@ -130,10 +130,10 @@ func (r *Router) AttachNamedListener(name string, listener net.Listener) {
// Close all configured listeners gracefully. This calls Shutdown on each
// http.Server and waits until all connections have closed before terminating.
func (r *Router) Close() {
r.app.Logger().Noticef("Shutting down (waiting max %d second(s) for connections to close)...", r.app.Config().ShutdownTimeout)
r.app.Logger().Noticef("Shutting down (waiting max %d second(s) for connections to close)...", r.app.Config().Server.ShutdownTimeout)
for _, server := range r.servers {
ctx, cancel := context.WithTimeout(r.shutdownCtx,
time.Second*time.Duration(r.app.Config().ShutdownTimeout))
time.Second*time.Duration(r.app.Config().Server.ShutdownTimeout))
defer cancel()

err := server.Shutdown(ctx)
@@ -291,7 +291,7 @@ func (r *Router) ListenTLS(addr string) (net.Listener, error) {
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
}

config := r.app.Config()
config := r.app.Config().Server
if config.TLSConfig != nil {
tc = config.TLSConfig
} else {


+ 1
- 1
router_private.go View File

@@ -16,7 +16,7 @@ import (

// Housekeeping initialization such as PID file generation (if enabled).
func (r *Router) init() {
pidfile := r.app.Config().PIDFile
pidfile := r.app.Config().Server.PIDFile
if pidfile == "" {
return
}


+ 4
- 2
tests/configuration_test.go View File

@@ -32,8 +32,10 @@ func (c *ConfigurationController) Get(ctx capstan.Context) error {
}

func Test_ConfigurationProperties_BasePath(t *testing.T) {
config := NewServerConfig(&capstan.ServerConfig{
BasePath: "/root",
config := NewServerConfig(&capstan.Config{
Server: &capstan.ServerConfig{
BasePath: "/root",
},
})
server := config(func(app capstan.Application) {
app.Bind(&ConfigurationController{


+ 7
- 5
tests/hostname_test.go View File

@@ -12,9 +12,11 @@ import (
)

func Test_Hostname(t *testing.T) {
setup := NewServerConfig(&capstan.ServerConfig{
Hostname: "test.example.com",
EnforceHostname: true,
setup := NewServerConfig(&capstan.Config{
Server: &capstan.ServerConfig{
Hostname: "test.example.com",
EnforceHostname: true,
},
})
server := setup(func(app capstan.Application) {
app.Bind(&MethodEndpointController{
@@ -23,8 +25,8 @@ func Test_Hostname(t *testing.T) {
},
})

app.Config().Hostname = "test.example.com"
app.Config().EnforceHostname = true
app.Config().Server.Hostname = "test.example.com"
app.Config().Server.EnforceHostname = true
})
defer server.Close()



+ 5
- 3
tests/multiapp_test.go View File

@@ -33,10 +33,12 @@ func Test_MultiApp(t *testing.T) {
Path: "/main",
},
})
app.Config().Hostname = "example.com"
app.Config().Server.Hostname = "example.com"

subapp := capstan.New(&config.ServerConfig{
Hostname: "subapp.example.com",
subapp := capstan.New(&config.Config{
Server: &config.ServerConfig{
Hostname: "subapp.example.com",
},
})

subapp.Bind(&MultiAppController{


+ 4
- 2
tests/rebind_multiapp_test.go View File

@@ -15,8 +15,10 @@ import (
// validates that the path replacement has taken effect.
func Test_MultiApp_ReplacePath(t *testing.T) {
server := NewServer(func(app capstan.Application) {
a := capstan.New(&capstan.ServerConfig{
Hostname: "example.com",
a := capstan.New(&capstan.Config{
Server: &capstan.ServerConfig{
Hostname: "example.com",
},
})
a.Bind(&ReplacePathController{
BaseController: capstan.BaseController{


+ 17
- 15
tests/server.go View File

@@ -17,10 +17,10 @@ type ReturnTypesController struct {
capstan.BaseController
}

func NewServerConfig(conf *capstan.ServerConfig) func(func(capstan.Application)) *httptest.Server {
func NewServerConfig(conf *capstan.Config) func(func(capstan.Application)) *httptest.Server {
return func(bind func(capstan.Application)) *httptest.Server {
if conf.Session == nil {
conf.Session = &capstan.SessionConfig{
if conf.Server.Session == nil {
conf.Server.Session = &capstan.SessionConfig{
Backend: config.SessionCookieBackend,
PlainText: true,
CookieOptions: &http.Cookie{
@@ -41,36 +41,38 @@ func NewServerConfig(conf *capstan.ServerConfig) func(func(capstan.Application))
app.Router().SetupMuxer()

parsed, _ := url.Parse(server.URL)
app.Config().FullHost = parsed.Host
app.Config().Server.FullHost = parsed.Host
return server
}
}

func NewServer(bind func(capstan.Application)) *httptest.Server {
app := capstan.New(&capstan.ServerConfig{
Session: &capstan.SessionConfig{
Backend: config.SessionCookieBackend,
PlainText: true,
CookieOptions: &http.Cookie{
Name: "test_session",
app := capstan.New(&capstan.Config{
Server: &capstan.ServerConfig{
Session: &capstan.SessionConfig{
Backend: config.SessionCookieBackend,
PlainText: true,
CookieOptions: &http.Cookie{
Name: "test_session",
},
Key: testKey,
HMACKey: testHMAC,
},
Key: testKey,
HMACKey: testHMAC,
},
})

bind(app)

server := httptest.NewServer(app.Router())
app.Config().Hostname = server.URL[7:]
app.Config().ListenAddress = server.URL[7:]
app.Config().Server.Hostname = server.URL[7:]
app.Config().Server.ListenAddress = server.URL[7:]

// Since we're not calling app.Listen(), the default muxer will never be
// configured unless we do so manually.
app.Router().SetupMuxer()

parsed, _ := url.Parse(server.URL)
app.Config().FullHost = parsed.Host
app.Config().Server.FullHost = parsed.Host
return server
}



Loading…
Cancel
Save