go-micro.dev/v5@v5.12.0/transport/http_client.go (about) 1 package transport 2 3 import ( 4 "bufio" 5 "bytes" 6 "io" 7 "net" 8 "net/http" 9 "net/url" 10 "sync" 11 "time" 12 13 "github.com/pkg/errors" 14 15 log "go-micro.dev/v5/logger" 16 "go-micro.dev/v5/util/buf" 17 ) 18 19 type httpTransportClient struct { 20 dialOpts DialOptions 21 conn net.Conn 22 ht *httpTransport 23 24 // request must be stored for response processing 25 req chan *http.Request 26 buff *bufio.Reader 27 addr string 28 29 // local/remote ip 30 local string 31 remote string 32 reqList []*http.Request 33 34 sync.RWMutex 35 36 once sync.Once 37 38 closed bool 39 } 40 41 func (h *httpTransportClient) Local() string { 42 return h.local 43 } 44 45 func (h *httpTransportClient) Remote() string { 46 return h.remote 47 } 48 49 func (h *httpTransportClient) Send(m *Message) error { 50 logger := h.ht.Options().Logger 51 52 header := make(http.Header) 53 for k, v := range m.Header { 54 header.Set(k, v) 55 } 56 57 b := buf.New(bytes.NewBuffer(m.Body)) 58 defer func() { 59 if err := b.Close(); err != nil { 60 logger.Logf(log.ErrorLevel, "failed to close buffer: %v", err) 61 } 62 }() 63 64 req := &http.Request{ 65 Method: http.MethodPost, 66 URL: &url.URL{ 67 Scheme: "http", 68 Host: h.addr, 69 }, 70 Header: header, 71 Body: b, 72 ContentLength: int64(b.Len()), 73 Host: h.addr, 74 Close: h.dialOpts.ConnClose, 75 } 76 77 if !h.dialOpts.Stream { 78 h.Lock() 79 if h.closed { 80 h.Unlock() 81 return io.EOF 82 } 83 84 h.reqList = append(h.reqList, req) 85 86 select { 87 case h.req <- h.reqList[0]: 88 h.reqList = h.reqList[1:] 89 default: 90 } 91 h.Unlock() 92 } 93 94 // set timeout if its greater than 0 95 if h.ht.opts.Timeout > time.Duration(0) { 96 if err := h.conn.SetDeadline(time.Now().Add(h.ht.opts.Timeout)); err != nil { 97 return err 98 } 99 } 100 101 return req.Write(h.conn) 102 103 } 104 105 // Recv receives a message. 106 func (h *httpTransportClient) Recv(msg *Message) (err error) { 107 if msg == nil { 108 return errors.New("message passed in is nil") 109 } 110 111 var req *http.Request 112 113 if !h.dialOpts.Stream { 114 115 var rc *http.Request 116 var ok bool 117 118 h.Lock() 119 select { 120 case rc, ok = <-h.req: 121 default: 122 } 123 124 if !ok { 125 if len(h.reqList) == 0 { 126 h.Unlock() 127 return io.EOF 128 } 129 130 rc = h.reqList[0] 131 h.reqList = h.reqList[1:] 132 } 133 h.Unlock() 134 135 req = rc 136 } 137 138 // set timeout if its greater than 0 139 if h.ht.opts.Timeout > time.Duration(0) { 140 if err = h.conn.SetDeadline(time.Now().Add(h.ht.opts.Timeout)); err != nil { 141 return err 142 } 143 } 144 145 h.Lock() 146 defer h.Unlock() 147 148 if h.closed { 149 return io.EOF 150 } 151 152 rsp, err := http.ReadResponse(h.buff, req) 153 if err != nil { 154 return err 155 } 156 157 defer func() { 158 if err2 := rsp.Body.Close(); err2 != nil { 159 err = errors.Wrap(err2, "failed to close body") 160 } 161 }() 162 163 b, err := io.ReadAll(rsp.Body) 164 if err != nil { 165 return err 166 } 167 168 if rsp.StatusCode != http.StatusOK { 169 return errors.New(rsp.Status + ": " + string(b)) 170 } 171 172 msg.Body = b 173 174 if msg.Header == nil { 175 msg.Header = make(map[string]string, len(rsp.Header)) 176 } 177 178 for k, v := range rsp.Header { 179 if len(v) > 0 { 180 msg.Header[k] = v[0] 181 } else { 182 msg.Header[k] = "" 183 } 184 } 185 186 return nil 187 } 188 189 func (h *httpTransportClient) Close() error { 190 if !h.dialOpts.Stream { 191 h.once.Do( 192 func() { 193 h.Lock() 194 h.buff.Reset(nil) 195 h.closed = true 196 h.Unlock() 197 close(h.req) 198 }, 199 ) 200 201 return h.conn.Close() 202 } 203 204 err := h.conn.Close() 205 h.once.Do( 206 func() { 207 h.Lock() 208 h.buff.Reset(nil) 209 h.closed = true 210 h.Unlock() 211 close(h.req) 212 }, 213 ) 214 215 return err 216 }