github.com/micro/go-micro/v2@v2.9.1/proxy/http/http.go (about) 1 // Package http provides a micro rpc to http proxy 2 package http 3 4 import ( 5 "bytes" 6 "context" 7 "io" 8 "io/ioutil" 9 "net/http" 10 "net/url" 11 "path" 12 13 "github.com/micro/go-micro/v2/errors" 14 "github.com/micro/go-micro/v2/proxy" 15 "github.com/micro/go-micro/v2/server" 16 ) 17 18 // Proxy will proxy rpc requests as http POST requests. It is a server.Proxy 19 type Proxy struct { 20 options proxy.Options 21 22 // The http backend to call 23 Endpoint string 24 25 // first request 26 first bool 27 } 28 29 func getMethod(hdr map[string]string) string { 30 switch hdr["Micro-Method"] { 31 case "GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH": 32 return hdr["Micro-Method"] 33 default: 34 return "POST" 35 } 36 } 37 38 func getEndpoint(hdr map[string]string) string { 39 ep := hdr["Micro-Endpoint"] 40 if len(ep) > 0 && ep[0] == '/' { 41 return ep 42 } 43 return "" 44 } 45 46 func getTopic(hdr map[string]string) string { 47 ep := hdr["Micro-Topic"] 48 if len(ep) > 0 && ep[0] == '/' { 49 return ep 50 } 51 return "/" + hdr["Micro-Topic"] 52 } 53 54 // ProcessMessage handles incoming asynchronous messages 55 func (p *Proxy) ProcessMessage(ctx context.Context, msg server.Message) error { 56 if p.Endpoint == "" { 57 p.Endpoint = proxy.DefaultEndpoint 58 } 59 60 // get the header 61 hdr := msg.Header() 62 63 // get topic 64 // use /topic as endpoint 65 endpoint := getTopic(hdr) 66 67 // set the endpoint 68 if len(endpoint) == 0 { 69 endpoint = p.Endpoint 70 } else { 71 // add endpoint to backend 72 u, err := url.Parse(p.Endpoint) 73 if err != nil { 74 return errors.InternalServerError(msg.Topic(), err.Error()) 75 } 76 u.Path = path.Join(u.Path, endpoint) 77 endpoint = u.String() 78 } 79 80 // send to backend 81 hreq, err := http.NewRequest("POST", endpoint, bytes.NewReader(msg.Body())) 82 if err != nil { 83 return errors.InternalServerError(msg.Topic(), err.Error()) 84 } 85 86 // set the headers 87 for k, v := range hdr { 88 hreq.Header.Set(k, v) 89 } 90 91 // make the call 92 hrsp, err := http.DefaultClient.Do(hreq) 93 if err != nil { 94 return errors.InternalServerError(msg.Topic(), err.Error()) 95 } 96 97 // read body 98 b, err := ioutil.ReadAll(hrsp.Body) 99 hrsp.Body.Close() 100 if err != nil { 101 return errors.InternalServerError(msg.Topic(), err.Error()) 102 } 103 104 if hrsp.StatusCode != 200 { 105 return errors.New(msg.Topic(), string(b), int32(hrsp.StatusCode)) 106 } 107 108 return nil 109 } 110 111 // ServeRequest honours the server.Router interface 112 func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server.Response) error { 113 if p.Endpoint == "" { 114 p.Endpoint = proxy.DefaultEndpoint 115 } 116 117 for { 118 // get data 119 body, err := req.Read() 120 if err == io.EOF { 121 return nil 122 } 123 if err != nil { 124 return err 125 } 126 127 // get the header 128 hdr := req.Header() 129 130 // get method 131 method := getMethod(hdr) 132 133 // get endpoint 134 endpoint := getEndpoint(hdr) 135 136 // set the endpoint 137 if len(endpoint) == 0 { 138 endpoint = p.Endpoint 139 } else { 140 // add endpoint to backend 141 u, err := url.Parse(p.Endpoint) 142 if err != nil { 143 return errors.InternalServerError(req.Service(), err.Error()) 144 } 145 u.Path = path.Join(u.Path, endpoint) 146 endpoint = u.String() 147 } 148 149 // send to backend 150 hreq, err := http.NewRequest(method, endpoint, bytes.NewReader(body)) 151 if err != nil { 152 return errors.InternalServerError(req.Service(), err.Error()) 153 } 154 155 // set the headers 156 for k, v := range hdr { 157 hreq.Header.Set(k, v) 158 } 159 160 // make the call 161 hrsp, err := http.DefaultClient.Do(hreq) 162 if err != nil { 163 return errors.InternalServerError(req.Service(), err.Error()) 164 } 165 166 // read body 167 b, err := ioutil.ReadAll(hrsp.Body) 168 hrsp.Body.Close() 169 if err != nil { 170 return errors.InternalServerError(req.Service(), err.Error()) 171 } 172 173 // set response headers 174 hdr = map[string]string{} 175 for k := range hrsp.Header { 176 hdr[k] = hrsp.Header.Get(k) 177 } 178 // write the header 179 rsp.WriteHeader(hdr) 180 // write the body 181 err = rsp.Write(b) 182 if err == io.EOF { 183 return nil 184 } 185 if err != nil { 186 return errors.InternalServerError(req.Service(), err.Error()) 187 } 188 } 189 } 190 191 func (p *Proxy) String() string { 192 return "http" 193 } 194 195 // NewSingleHostProxy returns a router which sends requests to a single http backend 196 func NewSingleHostProxy(url string) proxy.Proxy { 197 return &Proxy{ 198 Endpoint: url, 199 } 200 } 201 202 // NewProxy returns a new proxy which will route using a http client 203 func NewProxy(opts ...proxy.Option) proxy.Proxy { 204 var options proxy.Options 205 for _, o := range opts { 206 o(&options) 207 } 208 209 p := new(Proxy) 210 p.Endpoint = options.Endpoint 211 p.options = options 212 213 return p 214 }