Files
golib/httpserver/config.go
Nicolas JUHEL 1249f319bc Bump dependancies
- AWS SDK to release v1.0.0
  - other dependancies
Fix Packages :
  - AWS : fix validator function, rules, config model
  - Certificates : fix func NewFrom
  - HTTPServer: fix IsRunning
Fix other :
  - Fix CI/CD job to prevent alert on files modified
  - Fix missing licence comment header
2021-01-25 08:33:18 +01:00

346 lines
10 KiB
Go

/*
* MIT License
*
* Copyright (c) 2020 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*
*/
package httpserver
import (
"context"
"fmt"
"net"
"net/url"
"time"
"github.com/go-playground/validator/v10"
libtls "github.com/nabbar/golib/certificates"
liberr "github.com/nabbar/golib/errors"
)
type MapUpdPoolServerConfig func(cfg ServerConfig) ServerConfig
type MapRunPoolServerConfig func(cfg ServerConfig)
type PoolServerConfig []ServerConfig
func (p PoolServerConfig) PoolServer() (PoolServer, liberr.Error) {
var (
r = NewPool()
e = ErrorPoolAdd.Error(nil)
)
p.MapRun(func(cfg ServerConfig) {
var err liberr.Error
if r, err = r.Add(cfg.Server()); err != nil {
e.AddParentError(err)
}
})
if !e.HasParent() {
e = nil
}
return r, e
}
func (p PoolServerConfig) UpdatePoolServer(pSrv PoolServer) (PoolServer, liberr.Error) {
var e = ErrorPoolAdd.Error(nil)
p.MapRun(func(cfg ServerConfig) {
var err liberr.Error
if pSrv, err = pSrv.Add(cfg.Server()); err != nil {
e.AddParentError(err)
}
})
if !e.HasParent() {
e = nil
}
return pSrv, e
}
func (p PoolServerConfig) Validate() liberr.Error {
var e = ErrorPoolValidate.Error(nil)
p.MapRun(func(cfg ServerConfig) {
var err liberr.Error
if err = cfg.Validate(); err != nil {
e.AddParentError(err)
}
})
if !e.HasParent() {
e = nil
}
return e
}
func (p PoolServerConfig) MapUpdate(f MapUpdPoolServerConfig) PoolServerConfig {
var r = make(PoolServerConfig, 0)
if p != nil {
r = p
}
for i, c := range r {
r[i] = f(c)
}
return r
}
func (p PoolServerConfig) MapRun(f MapRunPoolServerConfig) PoolServerConfig {
var r = make(PoolServerConfig, 0)
if p != nil {
r = p
}
for _, c := range r {
f(c)
}
return r
}
type ServerConfig struct {
getTLSDefault func() libtls.TLSConfig
getParentContext func() context.Context
/*** http options ***/
// ReadTimeout is the maximum duration for reading the entire
// request, including the body.
//
// Because ReadTimeout does not let Handlers make per-request
// decisions on each request body's acceptable deadline or
// upload rate, most users will prefer to use
// ReadHeaderTimeout. It is valid to use them both.
ReadTimeout time.Duration `mapstructure:"read_timeout" json:"read_timeout" yaml:"read_timeout" toml:"read_timeout"`
// ReadHeaderTimeout is the amount of time allowed to read
// request headers. The connection's read deadline is reset
// after reading the headers and the Handler can decide what
// is considered too slow for the body. If ReadHeaderTimeout
// is zero, the value of ReadTimeout is used. If both are
// zero, there is no timeout.
ReadHeaderTimeout time.Duration `mapstructure:"read_header_timeout" json:"read_header_timeout" yaml:"read_header_timeout" toml:"read_header_timeout"`
// WriteTimeout is the maximum duration before timing out
// writes of the response. It is reset whenever a new
// request's header is read. Like ReadTimeout, it does not
// let Handlers make decisions on a per-request basis.
WriteTimeout time.Duration `mapstructure:"write_timeout" json:"write_timeout" yaml:"write_timeout" toml:"write_timeout"`
// MaxHeaderBytes controls the maximum number of bytes the
// server will read parsing the request header's keys and
// values, including the request line. It does not limit the
// size of the request body.
// If zero, DefaultMaxHeaderBytes is used.
MaxHeaderBytes int `mapstructure:"max_header_bytes" json:"max_header_bytes" yaml:"max_header_bytes" toml:"max_header_bytes"`
/*** http2 options ***/
// MaxHandlers limits the number of http.Handler ServeHTTP goroutines
// which may run at a time over all connections.
// Negative or zero no limit.
MaxHandlers int `mapstructure:"max_handlers" json:"max_handlers" yaml:"max_handlers" toml:"max_handlers"`
// MaxConcurrentStreams optionally specifies the number of
// concurrent streams that each client may have open at a
// time. This is unrelated to the number of http.Handler goroutines
// which may be active globally, which is MaxHandlers.
// If zero, MaxConcurrentStreams defaults to at least 100, per
// the HTTP/2 spec's recommendations.
MaxConcurrentStreams uint32 `json:"max_concurrent_streams" json:"max_concurrent_streams" yaml:"max_concurrent_streams" toml:"max_concurrent_streams"`
// MaxReadFrameSize optionally specifies the largest frame
// this server is willing to read. A valid value is between
// 16k and 16M, inclusive. If zero or otherwise invalid, a
// default value is used.
MaxReadFrameSize uint32 `json:"max_read_frame_size" json:"max_read_frame_size" yaml:"max_read_frame_size" toml:"max_read_frame_size"`
// PermitProhibitedCipherSuites, if true, permits the use of
// cipher suites prohibited by the HTTP/2 spec.
PermitProhibitedCipherSuites bool `json:"permit_prohibited_cipher_suites" json:"permit_prohibited_cipher_suites" yaml:"permit_prohibited_cipher_suites" toml:"permit_prohibited_cipher_suites"`
// IdleTimeout specifies how long until idle clients should be
// closed with a GOAWAY frame. PING frames are not considered
// activity for the purposes of IdleTimeout.
IdleTimeout time.Duration `json:"idle_timeout" json:"idle_timeout" yaml:"idle_timeout" toml:"idle_timeout"`
// MaxUploadBufferPerConnection is the size of the initial flow
// control window for each connections. The HTTP/2 spec does not
// allow this to be smaller than 65535 or larger than 2^32-1.
// If the value is outside this range, a default value will be
// used instead.
MaxUploadBufferPerConnection int32 `json:"max_upload_buffer_per_connection" json:"max_upload_buffer_per_connection" yaml:"max_upload_buffer_per_connection" toml:"max_upload_buffer_per_connection"`
// MaxUploadBufferPerStream is the size of the initial flow control
// window for each stream. The HTTP/2 spec does not allow this to
// be larger than 2^32-1. If the value is zero or larger than the
// maximum, a default value will be used instead.
MaxUploadBufferPerStream int32 `json:"max_upload_buffer_per_stream" json:"max_upload_buffer_per_stream" yaml:"max_upload_buffer_per_stream" toml:"max_upload_buffer_per_stream"`
// Name is the name of the current server
// the configuration allow multipke server, which each one must be identify by a name
// If not defined, will use the listen address
Name string `mapstructure:"name" json:"name" yaml:"name" toml:"name" validate:"required"`
// Listen is the local address (ip, hostname, unix socket, ...) with a port
// The server will bind with this address only and listen for the port defined
Listen string `mapstructure:"listen" json:"listen" yaml:"listen" toml:"listen" validate:"required,hostname_port"`
// Expose is the address use to call this server. This can be allow to use a single fqdn to multiple server"
Expose string `mapstructure:"expose" json:"expose" yaml:"expose" toml:"expose" validate:"required,url"`
// TLS is the tls configuration for this server.
// To allow tls on this server, at least the TLS Config option InheritDefault must be at true and the default TLS config must be set.
// If you don't want any tls config, just omit or set an empty struct.
TLS libtls.Config `mapstructure:"tls" json:"tls" yaml:"tls" toml:"tls"`
}
func (c *ServerConfig) SetDefaultTLS(f func() libtls.TLSConfig) {
c.getTLSDefault = f
}
func (c *ServerConfig) SetParentContext(f func() context.Context) {
c.getParentContext = f
}
func (c ServerConfig) GetTLS() (libtls.TLSConfig, liberr.Error) {
var def libtls.TLSConfig
if c.getTLSDefault != nil {
def = c.getTLSDefault()
}
return c.TLS.NewFrom(def)
}
func (c ServerConfig) IsTLS() bool {
if ssl, err := c.GetTLS(); err == nil && ssl != nil && ssl.LenCertificatePair() > 0 {
return true
}
return false
}
func (c ServerConfig) getContext() context.Context {
var ctx context.Context
if c.getParentContext != nil {
ctx = c.getParentContext()
}
if ctx == nil {
return context.Background()
}
return ctx
}
func (c ServerConfig) GetListen() *url.URL {
var (
err error
add *url.URL
)
if c.Listen != "" {
if add, err = url.Parse(c.Listen); err != nil {
if host, prt, err := net.SplitHostPort(c.Listen); err == nil {
add = &url.URL{
Host: fmt.Sprintf("%s:%s", host, prt),
}
} else {
add = nil
}
}
}
if add == nil && c.Expose != "" {
if add, err = url.Parse(c.Expose); err != nil {
add = nil
}
}
return add
}
func (c ServerConfig) GetExpose() *url.URL {
var (
err error
add *url.URL
)
if c.Expose != "" {
if add, err = url.Parse(c.Expose); err != nil {
add = nil
}
}
if add == nil {
if add = c.GetListen(); add != nil {
if c.IsTLS() {
add.Scheme = "https"
} else {
add.Scheme = "http"
}
}
}
return add
}
func (c ServerConfig) Validate() liberr.Error {
val := validator.New()
err := val.Struct(c)
if e, ok := err.(*validator.InvalidValidationError); ok {
return ErrorServerValidate.ErrorParent(e)
}
out := ErrorServerValidate.Error(nil)
for _, e := range err.(validator.ValidationErrors) {
//nolint goerr113
out.AddParent(fmt.Errorf("config field '%s' is not validated by constraint '%s'", e.Field(), e.ActualTag()))
}
if out.HasParent() {
return out
}
return nil
}
func (c ServerConfig) Server() Server {
return NewServer(&c)
}