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