github.com/volts-dev/volts@v0.0.0-20240120094013-5e9c65924106/transport/http_sock.go (about)

     1  package transport
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"io"
     8  	"io/ioutil"
     9  	"net"
    10  	"net/http"
    11  	"sync"
    12  	"time"
    13  )
    14  
    15  type (
    16  	HttpConn struct {
    17  		ht *HttpTransport
    18  		w  http.ResponseWriter
    19  		r  *http.Request
    20  		rw *bufio.ReadWriter
    21  
    22  		mtx sync.RWMutex
    23  
    24  		// the hijacked when using http 1
    25  		conn net.Conn
    26  		// for the first request
    27  		ch chan *http.Request
    28  
    29  		// h2 things
    30  		buf *bufio.Reader
    31  		// indicate if socket is closed
    32  		closed chan bool
    33  
    34  		// local/remote ip
    35  		local  string
    36  		remote string
    37  	}
    38  )
    39  
    40  func (t *HttpConn) Conn() net.Conn {
    41  	return t.conn
    42  }
    43  
    44  func (h *HttpConn) Local() string {
    45  	return h.local
    46  }
    47  
    48  func (h *HttpConn) Remote() string {
    49  	return h.remote
    50  }
    51  
    52  func (h *HttpConn) Recv(m *Message) error {
    53  	if m == nil {
    54  		return errors.New("message passed in is nil")
    55  	}
    56  	if m.Header == nil {
    57  		m.Header = make(map[string]string, len(h.r.Header))
    58  	}
    59  
    60  	// process http 1
    61  	if h.r.ProtoMajor == 1 {
    62  		// set timeout if its greater than 0
    63  		if h.ht.config.ReadTimeout > time.Duration(0) {
    64  			h.conn.SetDeadline(time.Now().Add(h.ht.config.ReadTimeout))
    65  		}
    66  
    67  		var r *http.Request
    68  
    69  		select {
    70  		// get first request
    71  		case r = <-h.ch:
    72  		// read next request
    73  		default:
    74  			rr, err := http.ReadRequest(h.rw.Reader)
    75  			if err != nil {
    76  				return err
    77  			}
    78  			r = rr
    79  		}
    80  
    81  		// read body
    82  		b, err := ioutil.ReadAll(r.Body)
    83  		if err != nil {
    84  			return err
    85  		}
    86  
    87  		// set body
    88  		r.Body.Close()
    89  		m.Body = b
    90  
    91  		// set headers
    92  		for k, v := range r.Header {
    93  			if len(v) > 0 {
    94  				m.Header[k] = v[0]
    95  			} else {
    96  				m.Header[k] = ""
    97  			}
    98  		}
    99  
   100  		// return early early
   101  		return nil
   102  	}
   103  
   104  	// only process if the socket is open
   105  	select {
   106  	case <-h.closed:
   107  		return io.EOF
   108  	default:
   109  		// no op
   110  	}
   111  
   112  	// processing http2 request
   113  	// read streaming body
   114  
   115  	// set max buffer size
   116  	// TODO: adjustable buffer size
   117  	buf := make([]byte, 4*1024*1024)
   118  
   119  	// read the request body
   120  	n, err := h.buf.Read(buf)
   121  	// not an eof error
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	// check if we have data
   127  	if n > 0 {
   128  		m.Body = buf[:n]
   129  	}
   130  
   131  	// set headers
   132  	for k, v := range h.r.Header {
   133  		if len(v) > 0 {
   134  			m.Header[k] = v[0]
   135  		} else {
   136  			m.Header[k] = ""
   137  		}
   138  	}
   139  
   140  	// set path
   141  	m.Header[":path"] = h.r.URL.Path
   142  
   143  	return nil
   144  }
   145  
   146  func (h *HttpConn) Send(m *Message) error {
   147  	if h.r.ProtoMajor == 1 {
   148  		// make copy of header
   149  		hdr := make(http.Header)
   150  
   151  		for k, v := range h.r.Header {
   152  			hdr[k] = v
   153  		}
   154  
   155  		rsp := &http.Response{
   156  			Header:        hdr,
   157  			Body:          ioutil.NopCloser(bytes.NewReader(m.Body)),
   158  			Status:        "200 OK",
   159  			StatusCode:    200,
   160  			Proto:         "HTTP/1.1",
   161  			ProtoMajor:    1,
   162  			ProtoMinor:    1,
   163  			ContentLength: int64(len(m.Body)),
   164  		}
   165  
   166  		for k, v := range m.Header {
   167  			rsp.Header.Set(k, v)
   168  		}
   169  
   170  		// set timeout if its greater than 0
   171  		if h.ht.config.WriteTimeout > time.Duration(0) {
   172  			h.conn.SetDeadline(time.Now().Add(h.ht.config.WriteTimeout))
   173  		}
   174  
   175  		return rsp.Write(h.conn)
   176  	}
   177  
   178  	// only process if the socket is open
   179  	select {
   180  	case <-h.closed:
   181  		return io.EOF
   182  	default:
   183  		// no op
   184  	}
   185  
   186  	// we need to lock to protect the write
   187  	h.mtx.RLock()
   188  	defer h.mtx.RUnlock()
   189  
   190  	// set headers
   191  	for k, v := range m.Header {
   192  		h.w.Header().Set(k, v)
   193  	}
   194  
   195  	// write request
   196  	_, err := h.w.Write(m.Body)
   197  
   198  	// flush the trailers
   199  	h.w.(http.Flusher).Flush()
   200  
   201  	return err
   202  }
   203  
   204  func (h *HttpConn) error(m *Message) error {
   205  	if h.r.ProtoMajor == 1 {
   206  		rsp := &http.Response{
   207  			Header:        make(http.Header),
   208  			Body:          ioutil.NopCloser(bytes.NewReader(m.Body)),
   209  			Status:        "500 Internal Server Error",
   210  			StatusCode:    500,
   211  			Proto:         "HTTP/1.1",
   212  			ProtoMajor:    1,
   213  			ProtoMinor:    1,
   214  			ContentLength: int64(len(m.Body)),
   215  		}
   216  
   217  		for k, v := range m.Header {
   218  			rsp.Header.Set(k, v)
   219  		}
   220  
   221  		return rsp.Write(h.conn)
   222  	}
   223  
   224  	return nil
   225  }
   226  
   227  func (self *HttpConn) Request() *http.Request {
   228  	return self.r
   229  }
   230  
   231  func (self *HttpConn) Response() http.ResponseWriter {
   232  	return self.w
   233  }
   234  
   235  func (h *HttpConn) Close() error {
   236  	h.mtx.Lock()
   237  	defer h.mtx.Unlock()
   238  	select {
   239  	case <-h.closed:
   240  		return nil
   241  	default:
   242  		// close the channel
   243  		close(h.closed)
   244  
   245  		// close the buffer
   246  		h.r.Body.Close()
   247  
   248  		// close the connection
   249  		if h.r.ProtoMajor == 1 {
   250  			return h.conn.Close()
   251  		}
   252  	}
   253  
   254  	return nil
   255  }