package nghttp2 /* #cgo pkg-config: libnghttp2 #include "_nghttp2.h" */ import "C" import ( "errors" "fmt" "net" "net/http" "sync" "time" "unsafe" ) // Conn http2 connection type Conn struct { conn net.Conn session *C.nghttp2_session streams map[int]*stream streamCount int closed bool isServer bool handler http.Handler lock *sync.Mutex err error errch chan error exitch chan struct{} } // RoundTrip submit http request and return the response func (c *Conn) RoundTrip(req *http.Request) (*http.Response, error) { return nil, errors.New("not implement") } // Connect submit connect request func (c *Conn) Connect(addr string) (net.Conn, error) { return nil, errors.New("not implement") } // Run run the event loop func (c *Conn) Run() { defer c.Close() go c.readloop() go c.writeloop() for { select { case err := <-c.errch: c.err = err return case <-c.exitch: return } } } // Close close the connection func (c *Conn) Close() error { if c.closed { return nil } c.closed = true for _, s := range c.streams { s.Close() } close(c.exitch) c.conn.Close() return nil } func (c *Conn) errorNotify(err error) { select { case c.errch <- err: default: } } func (c *Conn) readloop() { type data struct { buf []byte err error } var ret C.ssize_t var err error var d data datach := make(chan data) go func() { d1 := data{} var n int var err1 error for { buf := make([]byte, 16*1024) n, err1 = c.conn.Read(buf) d1.buf = buf[:n] d1.err = err1 datach <- d1 } }() for { select { case <-c.exitch: return case d = <-datach: if d.err != nil { c.errorNotify(d.err) return } c.lock.Lock() ret = C.nghttp2_session_mem_recv(c.session, (*C.uchar)(unsafe.Pointer(&d.buf[0])), C.size_t(len(d.buf))) c.lock.Unlock() if int(ret) < 0 { err = fmt.Errorf("http2 recv error: %s", C.GoString(C.nghttp2_strerror(C.int(ret)))) c.errorNotify(err) return } } } } func (c *Conn) writeloop() { var ret C.int var err error var delay = 50 * time.Millisecond for { select { case <-c.exitch: return default: } c.lock.Lock() ret = C.nghttp2_session_send(c.session) c.lock.Unlock() if int(ret) < 0 { err = fmt.Errorf("http2 send error: %s", C.GoString(C.nghttp2_strerror(C.int(ret)))) c.errorNotify(err) return } c.lock.Lock() wantWrite := C.nghttp2_session_want_write(c.session) c.lock.Unlock() if int(wantWrite) == 0 { time.Sleep(delay) } } }