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  }