rewrite api package

This commit is contained in:
Asim Aslam
2020-10-17 15:33:56 +01:00
parent f2728a7fee
commit 975da990a9
13 changed files with 44 additions and 164 deletions

View File

@@ -7,7 +7,7 @@ import (
"net" "net"
"os" "os"
"github.com/micro/go-micro/v3/api/server/acme" "github.com/micro/go-micro/v3/api/acme"
"github.com/micro/go-micro/v3/logger" "github.com/micro/go-micro/v3/logger"
"golang.org/x/crypto/acme/autocert" "golang.org/x/crypto/acme/autocert"
) )

View File

@@ -8,7 +8,7 @@ import (
"time" "time"
"github.com/caddyserver/certmagic" "github.com/caddyserver/certmagic"
"github.com/micro/go-micro/v3/api/server/acme" "github.com/micro/go-micro/v3/api/acme"
"github.com/micro/go-micro/v3/logger" "github.com/micro/go-micro/v3/logger"
) )

View File

@@ -2,6 +2,7 @@ package api
import ( import (
"errors" "errors"
"net/http"
"regexp" "regexp"
"strings" "strings"
@@ -9,23 +10,24 @@ import (
"github.com/micro/go-micro/v3/server" "github.com/micro/go-micro/v3/server"
) )
type Api interface { // Gateway is an api gateway interface
type Gateway interface {
// Initialise options // Initialise options
Init(...Option) error Init(...Option) error
// Get the options // Get the options
Options() Options Options() Options
// Register a http handler // Register an endpoint
Register(*Endpoint) error Register(*Endpoint) error
// Register a route // Deregister a route
Deregister(*Endpoint) error Deregister(*Endpoint) error
// Register http handler
Handle(path string, hd http.Handler)
// Start serving requests
Serve() error
// Implementation of api // Implementation of api
String() string String() string
} }
type Options struct{}
type Option func(*Options) error
// Endpoint is a mapping between an RPC method and HTTP endpoint // Endpoint is a mapping between an RPC method and HTTP endpoint
type Endpoint struct { type Endpoint struct {
// RPC Method e.g. Greeter.Hello // RPC Method e.g. Greeter.Hello

View File

@@ -5,70 +5,62 @@ import (
"crypto/tls" "crypto/tls"
"net" "net"
"net/http" "net/http"
"os"
"sync" "sync"
"github.com/gorilla/handlers" "github.com/micro/go-micro/v3/api"
"github.com/micro/go-micro/v3/api/server"
"github.com/micro/go-micro/v3/api/server/cors"
"github.com/micro/go-micro/v3/logger" "github.com/micro/go-micro/v3/logger"
) )
type httpServer struct { type httpServer struct {
mux *http.ServeMux mux *http.ServeMux
opts server.Options opts api.Options
mtx sync.RWMutex mtx sync.RWMutex
address string address string
exit chan chan error exit chan chan error
} }
func NewServer(address string, opts ...server.Option) server.Server { // NewGateway returns a new HTTP api gateway
var options server.Options func NewGateway(opts ...api.Option) api.Gateway {
var options api.Options
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
} }
return &httpServer{ return &httpServer{
opts: options, opts: options,
mux: http.NewServeMux(), mux: http.NewServeMux(),
address: address, exit: make(chan chan error),
exit: make(chan chan error),
} }
} }
func (s *httpServer) Address() string { func (s *httpServer) Init(opts ...api.Option) error {
s.mtx.RLock()
defer s.mtx.RUnlock()
return s.address
}
func (s *httpServer) Init(opts ...server.Option) error {
for _, o := range opts { for _, o := range opts {
o(&s.opts) o(&s.opts)
} }
return nil return nil
} }
func (s *httpServer) Options() api.Options {
return s.opts
}
func (s *httpServer) Register(ep *api.Endpoint) error { return nil }
func (s *httpServer) Deregister(ep *api.Endpoint) error { return nil }
func (s *httpServer) Handle(path string, handler http.Handler) { 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)
}
// wrap with logger
handler = handlers.CombinedLoggingHandler(os.Stdout, handler)
s.mux.Handle(path, handler) s.mux.Handle(path, handler)
} }
func (s *httpServer) Serve() error {
if err := s.Start(); err != nil {
return err
}
<-s.exit
return nil
}
func (s *httpServer) Start() error { func (s *httpServer) Start() error {
var l net.Listener var l net.Listener
var err error var err error
@@ -77,10 +69,10 @@ func (s *httpServer) Start() error {
// should we check the address to make sure its using :443? // should we check the address to make sure its using :443?
l, err = s.opts.ACMEProvider.Listen(s.opts.ACMEHosts...) l, err = s.opts.ACMEProvider.Listen(s.opts.ACMEHosts...)
} else if s.opts.EnableTLS && s.opts.TLSConfig != nil { } else if s.opts.EnableTLS && s.opts.TLSConfig != nil {
l, err = tls.Listen("tcp", s.address, s.opts.TLSConfig) l, err = tls.Listen("tcp", s.opts.Address, s.opts.TLSConfig)
} else { } else {
// otherwise plain listen // otherwise plain listen
l, err = net.Listen("tcp", s.address) l, err = net.Listen("tcp", s.opts.Address)
} }
if err != nil { if err != nil {
return err return err
@@ -90,10 +82,6 @@ func (s *httpServer) Start() error {
logger.Infof("HTTP API Listening on %s", l.Addr().String()) logger.Infof("HTTP API Listening on %s", l.Addr().String())
} }
s.mtx.Lock()
s.address = l.Addr().String()
s.mtx.Unlock()
go func() { go func() {
if err := http.Serve(l, s.mux); err != nil { if err := http.Serve(l, s.mux); err != nil {
// temporary fix // temporary fix

View File

@@ -1,37 +1,27 @@
package server package api
import ( import (
"crypto/tls" "crypto/tls"
"net/http"
"github.com/micro/go-micro/v3/api/acme"
"github.com/micro/go-micro/v3/api/resolver" "github.com/micro/go-micro/v3/api/resolver"
"github.com/micro/go-micro/v3/api/server/acme"
) )
type Option func(o *Options)
type Options struct { type Options struct {
Address string
EnableACME bool EnableACME bool
EnableCORS bool
ACMEProvider acme.Provider ACMEProvider acme.Provider
EnableTLS bool EnableTLS bool
ACMEHosts []string ACMEHosts []string
TLSConfig *tls.Config TLSConfig *tls.Config
Resolver resolver.Resolver Resolver resolver.Resolver
Wrappers []Wrapper
} }
type Wrapper func(h http.Handler) http.Handler type Option func(o *Options)
func WrapHandler(w ...Wrapper) Option { func Address(a string) Option {
return func(o *Options) { return func(o *Options) {
o.Wrappers = append(o.Wrappers, w...) o.Address = a
}
}
func EnableCORS(b bool) Option {
return func(o *Options) {
o.EnableCORS = b
} }
} }

View File

@@ -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, Namespace")
}

View File

@@ -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)
}
}

View File

@@ -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
}