Initial QSfera import
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
// Package acme abstracts away various ACME libraries
|
||||
package acme
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrProviderNotImplemented can be returned when attempting to
|
||||
// instantiate an unimplemented provider.
|
||||
ErrProviderNotImplemented = errors.New("Provider not implemented")
|
||||
)
|
||||
|
||||
// Provider is a ACME provider interface.
|
||||
type Provider interface {
|
||||
// Listen returns a new listener
|
||||
Listen(...string) (net.Listener, error)
|
||||
// TLSConfig returns a tls config
|
||||
TLSConfig(...string) (*tls.Config, error)
|
||||
}
|
||||
|
||||
// The Let's Encrypt ACME endpoints.
|
||||
const (
|
||||
LetsEncryptStagingCA = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
)
|
||||
@@ -0,0 +1,86 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"go-micro.dev/v4/logger"
|
||||
)
|
||||
|
||||
// Option (or Options) are passed to New() to configure providers.
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options represents various options you can present to ACME providers.
|
||||
type Options struct {
|
||||
// ChallengeProvider is a go-acme/lego challenge provider. Set this if you
|
||||
// want to use DNS Challenges. Otherwise, tls-alpn-01 will be used
|
||||
ChallengeProvider challenge.Provider
|
||||
// Cache is a storage interface. Most ACME libraries have an cache, but
|
||||
// there's no defined interface, so if you consume this option
|
||||
// sanity check it before using.
|
||||
Cache interface{}
|
||||
|
||||
// Logger is the underling logging framework
|
||||
Logger logger.Logger
|
||||
// CA is the CA to use
|
||||
CA string
|
||||
// AcceptTLS must be set to true to indicate that you have read your
|
||||
// provider's terms of service.
|
||||
AcceptToS bool
|
||||
// Issue certificates for domains on demand. Otherwise, certs will be
|
||||
// retrieved / issued on start-up.
|
||||
OnDemand bool
|
||||
}
|
||||
|
||||
// AcceptToS indicates whether you accept your CA's terms of service.
|
||||
func AcceptToS(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.AcceptToS = b
|
||||
}
|
||||
}
|
||||
|
||||
// CA sets the CA of an acme.Options.
|
||||
func CA(CA string) Option {
|
||||
return func(o *Options) {
|
||||
o.CA = CA
|
||||
}
|
||||
}
|
||||
|
||||
// ChallengeProvider sets the Challenge provider of an acme.Options
|
||||
// if set, it enables the DNS challenge, otherwise tls-alpn-01 will be used.
|
||||
func ChallengeProvider(p challenge.Provider) Option {
|
||||
return func(o *Options) {
|
||||
o.ChallengeProvider = p
|
||||
}
|
||||
}
|
||||
|
||||
// OnDemand enables on-demand certificate issuance. Not recommended for use
|
||||
// with the DNS challenge, as the first connection may be very slow.
|
||||
func OnDemand(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.OnDemand = b
|
||||
}
|
||||
}
|
||||
|
||||
// Cache provides a cache / storage interface to the underlying ACME library
|
||||
// as there is no standard, this needs to be validated by the underlying
|
||||
// implementation.
|
||||
func Cache(c interface{}) Option {
|
||||
return func(o *Options) {
|
||||
o.Cache = c
|
||||
}
|
||||
}
|
||||
|
||||
// Logger sets the underline logger.
|
||||
func Logger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultOptions uses the Let's Encrypt Production CA, with DNS Challenge disabled.
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
AcceptToS: true,
|
||||
CA: LetsEncryptProductionCA,
|
||||
OnDemand: true,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package cors
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
AllowOrigin string
|
||||
AllowMethods string
|
||||
AllowHeaders string
|
||||
AllowCredentials bool
|
||||
}
|
||||
|
||||
// CombinedCORSHandler wraps a server and provides CORS headers.
|
||||
func CombinedCORSHandler(h http.Handler, config *Config) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if config != nil {
|
||||
SetHeaders(w, r, config)
|
||||
}
|
||||
if r.Method == "OPTIONS" {
|
||||
return
|
||||
}
|
||||
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// SetHeaders sets the CORS headers.
|
||||
func SetHeaders(w http.ResponseWriter, _ *http.Request, config *Config) {
|
||||
set := func(w http.ResponseWriter, k, v string) {
|
||||
if v := w.Header().Get(k); len(v) > 0 {
|
||||
return
|
||||
}
|
||||
w.Header().Set(k, v)
|
||||
}
|
||||
// For forward-compatible code, default values may not be provided in the future
|
||||
if config.AllowCredentials {
|
||||
set(w, "Access-Control-Allow-Credentials", "true")
|
||||
} else {
|
||||
set(w, "Access-Control-Allow-Credentials", "false")
|
||||
}
|
||||
if config.AllowOrigin == "" {
|
||||
set(w, "Access-Control-Allow-Origin", "*")
|
||||
} else {
|
||||
set(w, "Access-Control-Allow-Origin", config.AllowOrigin)
|
||||
}
|
||||
if config.AllowMethods == "" {
|
||||
set(w, "Access-Control-Allow-Methods", "POST, PATCH, GET, OPTIONS, PUT, DELETE")
|
||||
} else {
|
||||
set(w, "Access-Control-Allow-Methods", config.AllowMethods)
|
||||
}
|
||||
if config.AllowHeaders == "" {
|
||||
set(w, "Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
} else {
|
||||
set(w, "Access-Control-Allow-Headers", config.AllowHeaders)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// Package http provides a http server with features; acme, cors, etc
|
||||
package http
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
"go-micro.dev/v4/api/server"
|
||||
"go-micro.dev/v4/api/server/cors"
|
||||
log "go-micro.dev/v4/logger"
|
||||
)
|
||||
|
||||
type httpServer struct {
|
||||
opts server.Options
|
||||
|
||||
mux *http.ServeMux
|
||||
exit chan chan error
|
||||
address string
|
||||
|
||||
mtx sync.RWMutex
|
||||
}
|
||||
|
||||
func NewServer(address string, opts ...server.Option) server.Server {
|
||||
options := server.NewOptions(opts...)
|
||||
|
||||
return &httpServer{
|
||||
opts: options,
|
||||
mux: http.NewServeMux(),
|
||||
address: address,
|
||||
exit: make(chan chan error),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *httpServer) Address() string {
|
||||
s.mtx.RLock()
|
||||
defer s.mtx.RUnlock()
|
||||
return s.address
|
||||
}
|
||||
|
||||
func (s *httpServer) Init(opts ...server.Option) error {
|
||||
for _, o := range opts {
|
||||
o(&s.opts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *httpServer) Handle(path string, handler http.Handler) {
|
||||
// TODO: move this stuff out to one place with ServeHTTP
|
||||
|
||||
// apply the wrappers, e.g. auth
|
||||
for _, wrapper := range s.opts.Wrappers {
|
||||
handler = wrapper(handler)
|
||||
}
|
||||
|
||||
// wrap with cors
|
||||
if s.opts.EnableCORS {
|
||||
handler = cors.CombinedCORSHandler(handler, s.opts.CORSConfig)
|
||||
}
|
||||
|
||||
// wrap with logger
|
||||
handler = handlers.CombinedLoggingHandler(os.Stdout, handler)
|
||||
|
||||
s.mux.Handle(path, handler)
|
||||
}
|
||||
|
||||
func (s *httpServer) Start() error {
|
||||
logger := s.opts.Logger
|
||||
var l net.Listener
|
||||
var err error
|
||||
|
||||
if s.opts.EnableACME && s.opts.ACMEProvider != nil {
|
||||
// should we check the address to make sure its using :443?
|
||||
l, err = s.opts.ACMEProvider.Listen(s.opts.ACMEHosts...)
|
||||
} else if s.opts.EnableTLS && s.opts.TLSConfig != nil {
|
||||
l, err = tls.Listen("tcp", s.address, s.opts.TLSConfig)
|
||||
} else {
|
||||
// otherwise plain listen
|
||||
l, err = net.Listen("tcp", s.address)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Logf(log.InfoLevel, "HTTP API Listening on %s", l.Addr().String())
|
||||
|
||||
s.mtx.Lock()
|
||||
s.address = l.Addr().String()
|
||||
s.mtx.Unlock()
|
||||
|
||||
go func() {
|
||||
if err := http.Serve(l, s.mux); err != nil {
|
||||
// temporary fix
|
||||
// logger.Log(log.FatalLevel, err)
|
||||
logger.Log(log.ErrorLevel, err)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
ch := <-s.exit
|
||||
ch <- l.Close()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *httpServer) Stop() error {
|
||||
ch := make(chan error)
|
||||
s.exit <- ch
|
||||
return <-ch
|
||||
}
|
||||
|
||||
func (s *httpServer) String() string {
|
||||
return "http"
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
|
||||
"go-micro.dev/v4/api/resolver"
|
||||
"go-micro.dev/v4/api/server/acme"
|
||||
"go-micro.dev/v4/api/server/cors"
|
||||
"go-micro.dev/v4/logger"
|
||||
)
|
||||
|
||||
type Option func(o *Options)
|
||||
|
||||
type Options struct {
|
||||
ACMEProvider acme.Provider
|
||||
Resolver resolver.Resolver
|
||||
Logger logger.Logger
|
||||
CORSConfig *cors.Config
|
||||
TLSConfig *tls.Config
|
||||
ACMEHosts []string
|
||||
Wrappers []Wrapper
|
||||
EnableACME bool
|
||||
EnableCORS bool
|
||||
EnableTLS bool
|
||||
}
|
||||
|
||||
type Wrapper func(h http.Handler) http.Handler
|
||||
|
||||
func NewOptions(opts ...Option) Options {
|
||||
options := Options{
|
||||
Logger: logger.DefaultLogger,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
func WrapHandler(w Wrapper) Option {
|
||||
return func(o *Options) {
|
||||
o.Wrappers = append(o.Wrappers, w)
|
||||
}
|
||||
}
|
||||
|
||||
func EnableCORS(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.EnableCORS = b
|
||||
}
|
||||
}
|
||||
|
||||
func CORSConfig(c *cors.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.CORSConfig = c
|
||||
}
|
||||
}
|
||||
|
||||
func EnableACME(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.EnableACME = b
|
||||
}
|
||||
}
|
||||
|
||||
func ACMEHosts(hosts ...string) Option {
|
||||
return func(o *Options) {
|
||||
o.ACMEHosts = hosts
|
||||
}
|
||||
}
|
||||
|
||||
func ACMEProvider(p acme.Provider) Option {
|
||||
return func(o *Options) {
|
||||
o.ACMEProvider = p
|
||||
}
|
||||
}
|
||||
|
||||
func EnableTLS(b bool) Option {
|
||||
return func(o *Options) {
|
||||
o.EnableTLS = b
|
||||
}
|
||||
}
|
||||
|
||||
func TLSConfig(t *tls.Config) Option {
|
||||
return func(o *Options) {
|
||||
o.TLSConfig = t
|
||||
}
|
||||
}
|
||||
|
||||
func Resolver(r resolver.Resolver) Option {
|
||||
return func(o *Options) {
|
||||
o.Resolver = r
|
||||
}
|
||||
}
|
||||
|
||||
// Logger sets the underline logging framework.
|
||||
func Logger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Package server provides an API gateway server which handles inbound requests
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Server serves api requests.
|
||||
type Server interface {
|
||||
Address() string
|
||||
Init(opts ...Option) error
|
||||
Handle(path string, handler http.Handler)
|
||||
Start() error
|
||||
Stop() error
|
||||
}
|
||||
Reference in New Issue
Block a user