api/server: move to dedicated repo
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
// 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 {
|
||||
Init(...Option) error
|
||||
// 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"
|
||||
)
|
@@ -1,53 +0,0 @@
|
||||
// Package autocert is the ACME provider from golang.org/x/crypto/acme/autocert
|
||||
// This provider does not take any config.
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/unistack-org/micro/v3/api/server"
|
||||
"github.com/unistack-org/micro/v3/api/server/acme"
|
||||
"github.com/unistack-org/micro/v3/logger"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
// autoCertACME is the ACME provider from golang.org/x/crypto/acme/autocert
|
||||
type autocertProvider struct {
|
||||
opts server.Options
|
||||
}
|
||||
|
||||
func (a *autocertProvider) Init(opts ...acme.Option) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen implements acme.Provider
|
||||
func (a *autocertProvider) Listen(hosts ...string) (net.Listener, error) {
|
||||
return autocert.NewListener(hosts...), nil
|
||||
}
|
||||
|
||||
// TLSConfig returns a new tls config
|
||||
func (a *autocertProvider) TLSConfig(hosts ...string) (*tls.Config, error) {
|
||||
// create a new manager
|
||||
m := &autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
}
|
||||
if len(hosts) > 0 {
|
||||
m.HostPolicy = autocert.HostWhitelist(hosts...)
|
||||
}
|
||||
dir := cacheDir()
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
if logger.V(logger.InfoLevel) {
|
||||
logger.Info(a.opts.Context, "warning: autocert not using a cache: %v", err)
|
||||
}
|
||||
} else {
|
||||
m.Cache = autocert.DirCache(dir)
|
||||
}
|
||||
return m.TLSConfig(), nil
|
||||
}
|
||||
|
||||
// New returns an autocert acme.Provider
|
||||
func NewProvider() acme.Provider {
|
||||
return &autocertProvider{}
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAutocert(t *testing.T) {
|
||||
l := NewProvider()
|
||||
if _, ok := l.(*autocertProvider); !ok {
|
||||
t.Error("NewProvider() didn't return an autocertProvider")
|
||||
}
|
||||
// TODO: Travis CI doesn't let us bind :443
|
||||
// if _, err := l.NewListener(); err != nil {
|
||||
// t.Error(err.Error())
|
||||
// }
|
||||
}
|
@@ -1,37 +0,0 @@
|
||||
package autocert
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func homeDir() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
|
||||
}
|
||||
if h := os.Getenv("HOME"); h != "" {
|
||||
return h
|
||||
}
|
||||
return "/"
|
||||
}
|
||||
|
||||
func cacheDir() string {
|
||||
const base = "golang-autocert"
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
return filepath.Join(homeDir(), "Library", "Caches", base)
|
||||
case "windows":
|
||||
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
|
||||
if v := os.Getenv(ev); v != "" {
|
||||
return filepath.Join(v, base)
|
||||
}
|
||||
}
|
||||
// Worst case:
|
||||
return filepath.Join(homeDir(), base)
|
||||
}
|
||||
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
|
||||
return filepath.Join(xdg, base)
|
||||
}
|
||||
return filepath.Join(homeDir(), ".cache", base)
|
||||
}
|
@@ -1,71 +0,0 @@
|
||||
// Package certmagic is the ACME provider from github.com/caddyserver/certmagic
|
||||
package certmagic
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/unistack-org/micro/v3/api/server/acme"
|
||||
)
|
||||
|
||||
type certmagicProvider struct {
|
||||
opts acme.Options
|
||||
}
|
||||
|
||||
// TODO: set self-contained options
|
||||
func (c *certmagicProvider) setup() {
|
||||
certmagic.DefaultACME.CA = c.opts.CA
|
||||
if c.opts.ChallengeProvider != nil {
|
||||
// Enabling DNS Challenge disables the other challenges
|
||||
certmagic.DefaultACME.DNSProvider = c.opts.ChallengeProvider
|
||||
}
|
||||
if c.opts.OnDemand {
|
||||
certmagic.Default.OnDemand = new(certmagic.OnDemandConfig)
|
||||
}
|
||||
if c.opts.Cache != nil {
|
||||
// already validated by new()
|
||||
certmagic.Default.Storage = c.opts.Cache.(certmagic.Storage)
|
||||
}
|
||||
// If multiple instances of the provider are running, inject some
|
||||
// randomness so they don't collide
|
||||
// RenewalWindowRatio [0.33 - 0.50)
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
randomRatio := float64(rand.Intn(17)+33) * 0.01
|
||||
certmagic.Default.RenewalWindowRatio = randomRatio
|
||||
}
|
||||
|
||||
func (c *certmagicProvider) Listen(hosts ...string) (net.Listener, error) {
|
||||
c.setup()
|
||||
return certmagic.Listen(hosts)
|
||||
}
|
||||
|
||||
func (c *certmagicProvider) TLSConfig(hosts ...string) (*tls.Config, error) {
|
||||
c.setup()
|
||||
return certmagic.TLS(hosts)
|
||||
}
|
||||
|
||||
func (p *certmagicProvider) Init(opts ...acme.Option) error {
|
||||
if p.opts.Cache != nil {
|
||||
if _, ok := p.opts.Cache.(certmagic.Storage); !ok {
|
||||
return fmt.Errorf("ACME: cache provided doesn't implement certmagic's Storage interface")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewProvider returns a certmagic provider
|
||||
func NewProvider(options ...acme.Option) acme.Provider {
|
||||
opts := acme.DefaultOptions()
|
||||
|
||||
for _, o := range options {
|
||||
o(&opts)
|
||||
}
|
||||
|
||||
return &certmagicProvider{
|
||||
opts: opts,
|
||||
}
|
||||
}
|
@@ -1,138 +0,0 @@
|
||||
package certmagic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/unistack-org/micro/v3/store"
|
||||
"github.com/unistack-org/micro/v3/sync"
|
||||
)
|
||||
|
||||
// File represents a "File" that will be stored in store.Store - the contents and last modified time
|
||||
type File struct {
|
||||
// last modified time
|
||||
LastModified time.Time
|
||||
// Contents
|
||||
Contents []byte
|
||||
}
|
||||
|
||||
// storage is an implementation of certmagic.Storage using micro's sync.Map and store.Store interfaces.
|
||||
// As certmagic storage expects a filesystem (with stat() abilities) we have to implement
|
||||
// the bare minimum of metadata.
|
||||
type storage struct {
|
||||
lock sync.Sync
|
||||
store store.Store
|
||||
}
|
||||
|
||||
func (s *storage) Lock(key string) error {
|
||||
return s.lock.Lock(key, sync.LockTTL(10*time.Minute))
|
||||
}
|
||||
|
||||
func (s *storage) Unlock(key string) error {
|
||||
return s.lock.Unlock(key)
|
||||
}
|
||||
|
||||
func (s *storage) Store(key string, value []byte) error {
|
||||
f := File{
|
||||
LastModified: time.Now(),
|
||||
Contents: value,
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
e := gob.NewEncoder(buf)
|
||||
if err := e.Encode(f); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.store.Write(s.store.Options().Context, key, buf.Bytes())
|
||||
}
|
||||
|
||||
func (s *storage) Load(key string) ([]byte, error) {
|
||||
if !s.Exists(key) {
|
||||
return nil, certmagic.ErrNotExist(errors.New(key + " doesn't exist"))
|
||||
}
|
||||
var val []byte
|
||||
err := s.store.Read(s.store.Options().Context, key, val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := bytes.NewBuffer(val)
|
||||
d := gob.NewDecoder(b)
|
||||
var f File
|
||||
err = d.Decode(&f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f.Contents, nil
|
||||
}
|
||||
|
||||
func (s *storage) Delete(key string) error {
|
||||
return s.store.Delete(s.store.Options().Context, key)
|
||||
}
|
||||
|
||||
func (s *storage) Exists(key string) bool {
|
||||
if err := s.store.Read(s.store.Options().Context, key, nil); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *storage) List(prefix string, recursive bool) ([]string, error) {
|
||||
keys, err := s.store.List(s.store.Options().Context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//nolint:prealloc
|
||||
var results []string
|
||||
for _, k := range keys {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
results = append(results, k)
|
||||
}
|
||||
}
|
||||
if recursive {
|
||||
return results, nil
|
||||
}
|
||||
keysMap := make(map[string]bool)
|
||||
for _, key := range results {
|
||||
dir := strings.Split(strings.TrimPrefix(key, prefix+"/"), "/")
|
||||
keysMap[dir[0]] = true
|
||||
}
|
||||
results = make([]string, 0)
|
||||
for k := range keysMap {
|
||||
results = append(results, path.Join(prefix, k))
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (s *storage) Stat(key string) (certmagic.KeyInfo, error) {
|
||||
var val []byte
|
||||
err := s.store.Read(s.store.Options().Context, key, val)
|
||||
if err != nil {
|
||||
return certmagic.KeyInfo{}, err
|
||||
}
|
||||
b := bytes.NewBuffer(val)
|
||||
d := gob.NewDecoder(b)
|
||||
var f File
|
||||
err = d.Decode(&f)
|
||||
if err != nil {
|
||||
return certmagic.KeyInfo{}, err
|
||||
}
|
||||
return certmagic.KeyInfo{
|
||||
Key: key,
|
||||
Modified: f.LastModified,
|
||||
Size: int64(len(f.Contents)),
|
||||
IsTerminal: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewStorage returns a certmagic.Storage backed by a micro/lock and micro/store
|
||||
func NewStorage(lock sync.Sync, store store.Store) certmagic.Storage {
|
||||
return &storage{
|
||||
lock: lock,
|
||||
store: store,
|
||||
}
|
||||
}
|
@@ -1,73 +0,0 @@
|
||||
package acme
|
||||
|
||||
import "github.com/go-acme/lego/v3/challenge"
|
||||
|
||||
// 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 {
|
||||
// AcceptTLS must be set to true to indicate that you have read your
|
||||
// provider's terms of service.
|
||||
AcceptToS bool
|
||||
// CA is the CA to use
|
||||
CA string
|
||||
// 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
|
||||
// Issue certificates for domains on demand. Otherwise, certs will be
|
||||
// retrieved / issued on start-up.
|
||||
OnDemand bool
|
||||
// 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{}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultOptions uses the Let's Encrypt Production CA, with DNS Challenge disabled.
|
||||
func DefaultOptions() Options {
|
||||
return Options{
|
||||
AcceptToS: true,
|
||||
CA: LetsEncryptProductionCA,
|
||||
OnDemand: true,
|
||||
}
|
||||
}
|
@@ -1,44 +0,0 @@
|
||||
package cors
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// CombinedCORSHandler wraps a server and provides CORS headers
|
||||
func CombinedCORSHandler(h http.Handler) http.Handler {
|
||||
return corsHandler{h}
|
||||
}
|
||||
|
||||
type corsHandler struct {
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
func (c corsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
SetHeaders(w, r)
|
||||
|
||||
if r.Method == "OPTIONS" {
|
||||
return
|
||||
}
|
||||
|
||||
c.handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// SetHeaders sets the CORS headers
|
||||
func SetHeaders(w http.ResponseWriter, r *http.Request) {
|
||||
set := func(w http.ResponseWriter, k, v string) {
|
||||
if v := w.Header().Get(k); len(v) > 0 {
|
||||
return
|
||||
}
|
||||
w.Header().Set(k, v)
|
||||
}
|
||||
|
||||
if origin := r.Header.Get("Origin"); len(origin) > 0 {
|
||||
set(w, "Access-Control-Allow-Origin", origin)
|
||||
} else {
|
||||
set(w, "Access-Control-Allow-Origin", "*")
|
||||
}
|
||||
|
||||
set(w, "Access-Control-Allow-Credentials", "true")
|
||||
set(w, "Access-Control-Allow-Methods", "POST, PATCH, GET, OPTIONS, PUT, DELETE")
|
||||
set(w, "Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
}
|
@@ -1,110 +0,0 @@
|
||||
// Package http provides a http server with features; acme, cors, etc
|
||||
package http
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/unistack-org/micro/v3/api/server"
|
||||
"github.com/unistack-org/micro/v3/logger"
|
||||
)
|
||||
|
||||
type httpServer struct {
|
||||
mux *http.ServeMux
|
||||
opts server.Options
|
||||
|
||||
sync.RWMutex
|
||||
address string
|
||||
exit chan chan error
|
||||
}
|
||||
|
||||
func NewServer(address string, opts ...server.Option) server.Server {
|
||||
return &httpServer{
|
||||
opts: server.NewOptions(opts...),
|
||||
mux: http.NewServeMux(),
|
||||
address: address,
|
||||
exit: make(chan chan error),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *httpServer) Address() string {
|
||||
s.RLock()
|
||||
defer s.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)
|
||||
}
|
||||
|
||||
s.mux.Handle(path, handler)
|
||||
}
|
||||
|
||||
func (s *httpServer) Start() error {
|
||||
var l net.Listener
|
||||
var err error
|
||||
|
||||
s.RLock()
|
||||
config := s.opts
|
||||
s.RUnlock()
|
||||
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
|
||||
}
|
||||
|
||||
if config.Logger.V(logger.InfoLevel) {
|
||||
config.Logger.Infof(s.opts.Context, "HTTP API Listening on %s", l.Addr().String())
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
s.address = l.Addr().String()
|
||||
s.Unlock()
|
||||
|
||||
go func() {
|
||||
if err := http.Serve(l, s.mux); err != nil {
|
||||
// temporary fix
|
||||
if config.Logger.V(logger.ErrorLevel) {
|
||||
config.Logger.Errorf(s.opts.Context, "serve err: %v", err)
|
||||
}
|
||||
s.Stop()
|
||||
}
|
||||
}()
|
||||
|
||||
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"
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHTTPServer(t *testing.T) {
|
||||
testResponse := "hello world"
|
||||
|
||||
s := NewServer("localhost:0")
|
||||
|
||||
s.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, testResponse)
|
||||
}))
|
||||
|
||||
if err := s.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rsp, err := http.Get(fmt.Sprintf("http://%s/", s.Address()))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer rsp.Body.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(rsp.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if string(b) != testResponse {
|
||||
t.Fatalf("Unexpected response, got %s, expected %s", string(b), testResponse)
|
||||
}
|
||||
|
||||
if err := s.Stop(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
@@ -1,96 +0,0 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
|
||||
"github.com/unistack-org/micro/v3/api/resolver"
|
||||
"github.com/unistack-org/micro/v3/api/server/acme"
|
||||
"github.com/unistack-org/micro/v3/logger"
|
||||
)
|
||||
|
||||
// Option func
|
||||
type Option func(o *Options)
|
||||
|
||||
// Options for api server
|
||||
type Options struct {
|
||||
EnableACME bool
|
||||
EnableCORS bool
|
||||
ACMEProvider acme.Provider
|
||||
EnableTLS bool
|
||||
ACMEHosts []string
|
||||
TLSConfig *tls.Config
|
||||
Resolver resolver.Resolver
|
||||
Wrappers []Wrapper
|
||||
Logger logger.Logger
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// NewOptions returns new Options
|
||||
func NewOptions(opts ...Option) Options {
|
||||
options := Options{
|
||||
Logger: logger.DefaultLogger,
|
||||
Context: context.Background(),
|
||||
}
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
type Wrapper func(h http.Handler) http.Handler
|
||||
|
||||
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 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
|
||||
}
|
||||
}
|
||||
|
||||
func Logger(l logger.Logger) Option {
|
||||
return func(o *Options) {
|
||||
o.Logger = l
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
// 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