178 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package http provides a micro rpc to http proxy
 | |
| package http
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"context"
 | |
| 	"io"
 | |
| 	"io/ioutil"
 | |
| 	"net/http"
 | |
| 	"net/url"
 | |
| 	"path"
 | |
| 
 | |
| 	"github.com/micro/go-micro"
 | |
| 	"github.com/micro/go-micro/errors"
 | |
| 	"github.com/micro/go-micro/server"
 | |
| )
 | |
| 
 | |
| // Router will proxy rpc requests as http POST requests. It is a server.Router
 | |
| type Router struct {
 | |
| 	// The http backend to call
 | |
| 	Backend string
 | |
| 
 | |
| 	// first request
 | |
| 	first bool
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	// The default backend
 | |
| 	DefaultBackend = "http://localhost:9090"
 | |
| 	// The default router
 | |
| 	DefaultRouter = &Router{}
 | |
| )
 | |
| 
 | |
| func getMethod(hdr map[string]string) string {
 | |
| 	switch hdr["Micro-Method"] {
 | |
| 	case "GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH":
 | |
| 		return hdr["Micro-Method"]
 | |
| 	default:
 | |
| 		return "POST"
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func getEndpoint(hdr map[string]string) string {
 | |
| 	ep := hdr["Micro-Endpoint"]
 | |
| 	if len(ep) > 0 && ep[0] == '/' {
 | |
| 		return ep
 | |
| 	}
 | |
| 	return ""
 | |
| }
 | |
| 
 | |
| // ServeRequest honours the server.Router interface
 | |
| func (p *Router) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error {
 | |
| 	if p.Backend == "" {
 | |
| 		p.Backend = DefaultBackend
 | |
| 	}
 | |
| 
 | |
| 	for {
 | |
| 		// get data
 | |
| 		body, err := req.Read()
 | |
| 		if err == io.EOF {
 | |
| 			return nil
 | |
| 		}
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		// get the header
 | |
| 		hdr := req.Header()
 | |
| 
 | |
| 		// get method
 | |
| 		method := getMethod(hdr)
 | |
| 
 | |
| 		// get endpoint
 | |
| 		endpoint := getEndpoint(hdr)
 | |
| 
 | |
| 		// set the endpoint
 | |
| 		if len(endpoint) == 0 {
 | |
| 			endpoint = p.Backend
 | |
| 		} else {
 | |
| 			// add endpoint to backend
 | |
| 			u, err := url.Parse(p.Backend)
 | |
| 			if err != nil {
 | |
| 				return errors.InternalServerError(req.Service(), err.Error())
 | |
| 			}
 | |
| 			u.Path = path.Join(u.Path, endpoint)
 | |
| 			endpoint = u.String()
 | |
| 		}
 | |
| 
 | |
| 		// send to backend
 | |
| 		hreq, err := http.NewRequest(method, endpoint, bytes.NewReader(body))
 | |
| 		if err != nil {
 | |
| 			return errors.InternalServerError(req.Service(), err.Error())
 | |
| 		}
 | |
| 
 | |
| 		// set the headers
 | |
| 		for k, v := range hdr {
 | |
| 			hreq.Header.Set(k, v)
 | |
| 		}
 | |
| 
 | |
| 		// make the call
 | |
| 		hrsp, err := http.DefaultClient.Do(hreq)
 | |
| 		if err != nil {
 | |
| 			return errors.InternalServerError(req.Service(), err.Error())
 | |
| 		}
 | |
| 
 | |
| 		// read body
 | |
| 		b, err := ioutil.ReadAll(hrsp.Body)
 | |
| 		hrsp.Body.Close()
 | |
| 		if err != nil {
 | |
| 			return errors.InternalServerError(req.Service(), err.Error())
 | |
| 		}
 | |
| 
 | |
| 		// set response headers
 | |
| 		hdr = map[string]string{}
 | |
| 		for k, _ := range hrsp.Header {
 | |
| 			hdr[k] = hrsp.Header.Get(k)
 | |
| 		}
 | |
| 		// write the header
 | |
| 		rsp.WriteHeader(hdr)
 | |
| 		// write the body
 | |
| 		err = rsp.Write(b)
 | |
| 		if err == io.EOF {
 | |
| 			return nil
 | |
| 		}
 | |
| 		if err != nil {
 | |
| 			return errors.InternalServerError(req.Service(), err.Error())
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // NewSingleHostRouter returns a router which sends requests to a single http backend
 | |
| //
 | |
| // It is used by setting it in a new micro service to act as a proxy for a http backend.
 | |
| //
 | |
| // Usage:
 | |
| //
 | |
| // Create a new router to the http backend
 | |
| //
 | |
| // 	r := NewSingleHostRouter("http://localhost:10001")
 | |
| //
 | |
| // 	// Create your new service
 | |
| // 	service := micro.NewService(
 | |
| // 		micro.Name("greeter"),
 | |
| //		// Set the router
 | |
| //		http.WithRouter(r),
 | |
| // 	)
 | |
| //
 | |
| // 	// Run the service
 | |
| // 	service.Run()
 | |
| func NewSingleHostRouter(url string) *Router {
 | |
| 	return &Router{
 | |
| 		Backend: url,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NewService returns a new http proxy. It acts as a micro service proxy.
 | |
| // Any request on the transport is routed to a fixed http backend.
 | |
| //
 | |
| // Usage:
 | |
| //
 | |
| // 	service := NewService(
 | |
| //		micro.Name("greeter"),
 | |
| //		// Sets the default http endpoint
 | |
| //		http.WithBackend("http://localhost:10001"),
 | |
| //	 )
 | |
| //
 | |
| func NewService(opts ...micro.Option) micro.Service {
 | |
| 	// prepend router to opts
 | |
| 	opts = append([]micro.Option{
 | |
| 		WithRouter(DefaultRouter),
 | |
| 	}, opts...)
 | |
| 
 | |
| 	// create the new service
 | |
| 	return micro.NewService(opts...)
 | |
| }
 |