Add http server which implements go-micro.Server
This commit is contained in:
commit
a067b0b2e8
65
README.md
Normal file
65
README.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# HTTP Server
|
||||||
|
|
||||||
|
The HTTP Server is a go-micro.Server. It's a partial implementation which strips out codecs, transports, etc but enables you
|
||||||
|
to create a HTTP Server that could potentially be used for REST based API services.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/micro/go-micro/server"
|
||||||
|
httpServer "github.com/micro/go-plugins/server/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
srv := httpServer.NewServer(
|
||||||
|
server.Name("helloworld"),
|
||||||
|
)
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte(`hello world`))
|
||||||
|
})
|
||||||
|
|
||||||
|
hd := srv.NewHandler(mux)
|
||||||
|
|
||||||
|
srv.Handle(hd)
|
||||||
|
srv.Start()
|
||||||
|
srv.Register()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or as part of a service
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/micro/go-micro"
|
||||||
|
"github.com/micro/go-micro/server"
|
||||||
|
httpServer "github.com/micro/go-plugins/server/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
srv := httpServer.NewServer(
|
||||||
|
server.Name("helloworld"),
|
||||||
|
)
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte(`hello world`))
|
||||||
|
})
|
||||||
|
|
||||||
|
hd := srv.NewHandler(mux)
|
||||||
|
|
||||||
|
srv.Handle(hd)
|
||||||
|
|
||||||
|
service := micro.NewService(
|
||||||
|
micro.Server(srv),
|
||||||
|
)
|
||||||
|
service.Init()
|
||||||
|
service.Run()
|
||||||
|
}
|
||||||
|
```
|
114
extractor.go
Normal file
114
extractor.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/micro/go-micro/registry"
|
||||||
|
"github.com/micro/go-micro/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
privateBlocks []*net.IPNet
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for _, b := range []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"} {
|
||||||
|
if _, block, err := net.ParseCIDR(b); err == nil {
|
||||||
|
privateBlocks = append(privateBlocks, block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractAddress(addr string) (string, error) {
|
||||||
|
if len(addr) > 0 && (addr != "0.0.0.0" && addr != "[::]") {
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Failed to get interface addresses! Err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipAddr []byte
|
||||||
|
|
||||||
|
for _, rawAddr := range addrs {
|
||||||
|
var ip net.IP
|
||||||
|
switch addr := rawAddr.(type) {
|
||||||
|
case *net.IPAddr:
|
||||||
|
ip = addr.IP
|
||||||
|
case *net.IPNet:
|
||||||
|
ip = addr.IP
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip.To4() == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isPrivateIP(ip.String()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ipAddr = ip
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipAddr == nil {
|
||||||
|
return "", fmt.Errorf("No private IP address found, and explicit IP not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
return net.IP(ipAddr).String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPrivateIP(ipAddr string) bool {
|
||||||
|
ip := net.ParseIP(ipAddr)
|
||||||
|
for _, priv := range privateBlocks {
|
||||||
|
if priv.Contains(ip) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func serviceDef(opts server.Options) *registry.Service {
|
||||||
|
var advt, host string
|
||||||
|
var port int
|
||||||
|
|
||||||
|
if len(opts.Advertise) > 0 {
|
||||||
|
advt = opts.Advertise
|
||||||
|
} else {
|
||||||
|
advt = opts.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(advt, ":")
|
||||||
|
if len(parts) > 1 {
|
||||||
|
host = strings.Join(parts[:len(parts)-1], ":")
|
||||||
|
port, _ = strconv.Atoi(parts[len(parts)-1])
|
||||||
|
} else {
|
||||||
|
host = parts[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err := extractAddress(host)
|
||||||
|
if err != nil {
|
||||||
|
addr = host
|
||||||
|
}
|
||||||
|
|
||||||
|
node := ®istry.Node{
|
||||||
|
Id: opts.Name + "-" + opts.Id,
|
||||||
|
Address: addr,
|
||||||
|
Port: port,
|
||||||
|
Metadata: opts.Metadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Metadata["server"] = "http"
|
||||||
|
|
||||||
|
return ®istry.Service{
|
||||||
|
Name: opts.Name,
|
||||||
|
Version: opts.Version,
|
||||||
|
Nodes: []*registry.Node{node},
|
||||||
|
}
|
||||||
|
}
|
27
handler.go
Normal file
27
handler.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/micro/go-micro/registry"
|
||||||
|
"github.com/micro/go-micro/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
type httpHandler struct {
|
||||||
|
opts server.HandlerOptions
|
||||||
|
hd interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpHandler) Name() string {
|
||||||
|
return "handler"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpHandler) Handler() interface{} {
|
||||||
|
return h.hd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpHandler) Endpoints() []*registry.Endpoint {
|
||||||
|
return []*registry.Endpoint{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpHandler) Options() server.HandlerOptions {
|
||||||
|
return h.opts
|
||||||
|
}
|
148
http.go
Normal file
148
http.go
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// Package http implements a go-micro.Server
|
||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/micro/go-micro/registry"
|
||||||
|
"github.com/micro/go-micro/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
type httpServer struct {
|
||||||
|
sync.Mutex
|
||||||
|
opts server.Options
|
||||||
|
hd server.Handler
|
||||||
|
exit chan chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpServer) Options() server.Options {
|
||||||
|
h.Lock()
|
||||||
|
opts := h.opts
|
||||||
|
h.Unlock()
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpServer) Init(opts ...server.Option) error {
|
||||||
|
h.Lock()
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&h.opts)
|
||||||
|
}
|
||||||
|
h.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpServer) Handle(handler server.Handler) error {
|
||||||
|
if _, ok := handler.Handler().(http.Handler); !ok {
|
||||||
|
return errors.New("Handle requires http.Handler")
|
||||||
|
}
|
||||||
|
h.Lock()
|
||||||
|
h.hd = handler
|
||||||
|
h.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpServer) NewHandler(handler interface{}, opts ...server.HandlerOption) server.Handler {
|
||||||
|
var options server.HandlerOptions
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &httpHandler{
|
||||||
|
opts: options,
|
||||||
|
hd: handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpServer) NewSubscriber(topic string, handler interface{}, opts ...server.SubscriberOption) server.Subscriber {
|
||||||
|
var options server.SubscriberOptions
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &httpSubscriber{
|
||||||
|
opts: options,
|
||||||
|
topic: topic,
|
||||||
|
hd: handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpServer) Subscribe(s server.Subscriber) error {
|
||||||
|
return errors.New("subscribe is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpServer) Register() error {
|
||||||
|
h.Lock()
|
||||||
|
opts := h.opts
|
||||||
|
h.Unlock()
|
||||||
|
|
||||||
|
service := serviceDef(opts)
|
||||||
|
|
||||||
|
rOpts := []registry.RegisterOption{
|
||||||
|
registry.RegisterTTL(opts.RegisterTTL),
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts.Registry.Register(service, rOpts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpServer) Deregister() error {
|
||||||
|
h.Lock()
|
||||||
|
opts := h.opts
|
||||||
|
h.Unlock()
|
||||||
|
|
||||||
|
service := serviceDef(opts)
|
||||||
|
return opts.Registry.Deregister(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpServer) Start() error {
|
||||||
|
h.Lock()
|
||||||
|
opts := h.opts
|
||||||
|
hd := h.hd
|
||||||
|
h.Unlock()
|
||||||
|
|
||||||
|
ln, err := net.Listen("tcp", opts.Address)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Lock()
|
||||||
|
h.opts.Address = ln.Addr().String()
|
||||||
|
h.Unlock()
|
||||||
|
|
||||||
|
handler, ok := hd.Handler().(http.Handler)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("Server required http.Handler")
|
||||||
|
}
|
||||||
|
|
||||||
|
go http.Serve(ln, handler)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ch := <-h.exit
|
||||||
|
ch <- ln.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpServer) Stop() error {
|
||||||
|
ch := make(chan error)
|
||||||
|
h.exit <- ch
|
||||||
|
return <-ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpServer) String() string {
|
||||||
|
return "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
func newServer(opts ...server.Option) server.Server {
|
||||||
|
return &httpServer{
|
||||||
|
opts: newOptions(opts...),
|
||||||
|
exit: make(chan chan error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(opts ...server.Option) server.Server {
|
||||||
|
return newServer(opts...)
|
||||||
|
}
|
89
http_test.go
Normal file
89
http_test.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/micro/go-micro/registry"
|
||||||
|
"github.com/micro/go-micro/registry/mock"
|
||||||
|
"github.com/micro/go-micro/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHTTPServer(t *testing.T) {
|
||||||
|
reg := mock.NewRegistry()
|
||||||
|
|
||||||
|
// create server
|
||||||
|
srv := NewServer(server.Registry(reg))
|
||||||
|
|
||||||
|
// create server mux
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte(`hello world`))
|
||||||
|
})
|
||||||
|
|
||||||
|
// create handler
|
||||||
|
hd := srv.NewHandler(mux)
|
||||||
|
|
||||||
|
// register handler
|
||||||
|
if err := srv.Handle(hd); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// start server
|
||||||
|
if err := srv.Start(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// register server
|
||||||
|
if err := srv.Register(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup server
|
||||||
|
service, err := reg.GetService(server.DefaultName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(service) != 1 {
|
||||||
|
t.Fatalf("Expected 1 service got %d: %+v", len(service), service)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(service[0].Nodes) != 1 {
|
||||||
|
t.Fatalf("Expected 1 node got %d: %+v", len(service[0].Nodes), service[0].Nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make request
|
||||||
|
rsp, err := http.Get(fmt.Sprintf("http://%s:%d", service[0].Nodes[0].Address, service[0].Nodes[0].Port))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer rsp.Body.Close()
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(rsp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s := string(b); s != "hello world" {
|
||||||
|
t.Fatalf("Expected response %s, got %s", "hello world", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// deregister server
|
||||||
|
if err := srv.Deregister(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// try get service
|
||||||
|
service, err = reg.GetService(server.DefaultName)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected %v got %+v", registry.ErrNotFound, service)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop server
|
||||||
|
if err := srv.Stop(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
43
options.go
Normal file
43
options.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/micro/go-micro/codec"
|
||||||
|
"github.com/micro/go-micro/registry"
|
||||||
|
"github.com/micro/go-micro/server"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newOptions(opt ...server.Option) server.Options {
|
||||||
|
opts := server.Options{
|
||||||
|
Codecs: make(map[string]codec.NewCodec),
|
||||||
|
Metadata: map[string]string{},
|
||||||
|
Context: context.Background(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range opt {
|
||||||
|
o(&opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Registry == nil {
|
||||||
|
opts.Registry = registry.DefaultRegistry
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.Address) == 0 {
|
||||||
|
opts.Address = server.DefaultAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.Name) == 0 {
|
||||||
|
opts.Name = server.DefaultName
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.Id) == 0 {
|
||||||
|
opts.Id = server.DefaultId
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.Version) == 0 {
|
||||||
|
opts.Version = server.DefaultVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts
|
||||||
|
}
|
28
subscriber.go
Normal file
28
subscriber.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/micro/go-micro/registry"
|
||||||
|
"github.com/micro/go-micro/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
type httpSubscriber struct {
|
||||||
|
opts server.SubscriberOptions
|
||||||
|
topic string
|
||||||
|
hd interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpSubscriber) Topic() string {
|
||||||
|
return h.topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpSubscriber) Subscriber() interface{} {
|
||||||
|
return h.hd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpSubscriber) Endpoints() []*registry.Endpoint {
|
||||||
|
return []*registry.Endpoint{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *httpSubscriber) Options() server.SubscriberOptions {
|
||||||
|
return h.opts
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user