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