github.com/btccom/go-micro/v2@v2.9.3/api/handler/web/web.go (about) 1 // Package web contains the web handler including websocket support 2 package web 3 4 import ( 5 "errors" 6 "fmt" 7 "io" 8 "net" 9 "net/http" 10 "net/http/httputil" 11 "net/url" 12 "strings" 13 14 "github.com/btccom/go-micro/v2/api" 15 "github.com/btccom/go-micro/v2/api/handler" 16 "github.com/btccom/go-micro/v2/client/selector" 17 ) 18 19 const ( 20 Handler = "web" 21 ) 22 23 type webHandler struct { 24 opts handler.Options 25 s *api.Service 26 } 27 28 func (wh *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 29 service, err := wh.getService(r) 30 if err != nil { 31 w.WriteHeader(500) 32 return 33 } 34 35 if len(service) == 0 { 36 w.WriteHeader(404) 37 return 38 } 39 40 rp, err := url.Parse(service) 41 if err != nil { 42 w.WriteHeader(500) 43 return 44 } 45 46 if isWebSocket(r) { 47 wh.serveWebSocket(rp.Host, w, r) 48 return 49 } 50 51 httputil.NewSingleHostReverseProxy(rp).ServeHTTP(w, r) 52 } 53 54 // getService returns the service for this request from the selector 55 func (wh *webHandler) getService(r *http.Request) (string, error) { 56 var service *api.Service 57 58 if wh.s != nil { 59 // we were given the service 60 service = wh.s 61 } else if wh.opts.Router != nil { 62 // try get service from router 63 s, err := wh.opts.Router.Route(r) 64 if err != nil { 65 return "", err 66 } 67 service = s 68 } else { 69 // we have no way of routing the request 70 return "", errors.New("no route found") 71 } 72 73 // create a random selector 74 next := selector.Random(service.Services) 75 76 // get the next node 77 s, err := next() 78 if err != nil { 79 return "", nil 80 } 81 82 return fmt.Sprintf("http://%s", s.Address), nil 83 } 84 85 // serveWebSocket used to serve a web socket proxied connection 86 func (wh *webHandler) serveWebSocket(host string, w http.ResponseWriter, r *http.Request) { 87 req := new(http.Request) 88 *req = *r 89 90 if len(host) == 0 { 91 http.Error(w, "invalid host", 500) 92 return 93 } 94 95 // set x-forward-for 96 if clientIP, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { 97 if ips, ok := req.Header["X-Forwarded-For"]; ok { 98 clientIP = strings.Join(ips, ", ") + ", " + clientIP 99 } 100 req.Header.Set("X-Forwarded-For", clientIP) 101 } 102 103 // connect to the backend host 104 conn, err := net.Dial("tcp", host) 105 if err != nil { 106 http.Error(w, err.Error(), 500) 107 return 108 } 109 110 // hijack the connection 111 hj, ok := w.(http.Hijacker) 112 if !ok { 113 http.Error(w, "failed to connect", 500) 114 return 115 } 116 117 nc, _, err := hj.Hijack() 118 if err != nil { 119 return 120 } 121 122 defer nc.Close() 123 defer conn.Close() 124 125 if err = req.Write(conn); err != nil { 126 return 127 } 128 129 errCh := make(chan error, 2) 130 131 cp := func(dst io.Writer, src io.Reader) { 132 _, err := io.Copy(dst, src) 133 errCh <- err 134 } 135 136 go cp(conn, nc) 137 go cp(nc, conn) 138 139 <-errCh 140 } 141 142 func isWebSocket(r *http.Request) bool { 143 contains := func(key, val string) bool { 144 vv := strings.Split(r.Header.Get(key), ",") 145 for _, v := range vv { 146 if val == strings.ToLower(strings.TrimSpace(v)) { 147 return true 148 } 149 } 150 return false 151 } 152 153 if contains("Connection", "upgrade") && contains("Upgrade", "websocket") { 154 return true 155 } 156 157 return false 158 } 159 160 func (wh *webHandler) String() string { 161 return "web" 162 } 163 164 func NewHandler(opts ...handler.Option) handler.Handler { 165 return &webHandler{ 166 opts: handler.NewOptions(opts...), 167 } 168 } 169 170 func WithService(s *api.Service, opts ...handler.Option) handler.Handler { 171 options := handler.NewOptions(opts...) 172 173 return &webHandler{ 174 opts: options, 175 s: s, 176 } 177 }