|
|
|
package nghttp2
|
|
|
|
|
|
|
|
/*
|
|
|
|
#include "_nghttp2.h"
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ClientStream http2 client stream
|
|
|
|
type ClientStream struct {
|
|
|
|
streamID int
|
|
|
|
conn *ClientConn
|
|
|
|
cdp *C.nghttp2_data_provider
|
|
|
|
dp *dataProvider
|
|
|
|
// application read data from stream
|
|
|
|
//r *io.PipeReader
|
|
|
|
// recv stream data from session
|
|
|
|
//w *io.PipeWriter
|
|
|
|
res *http.Response
|
|
|
|
resch chan *http.Response
|
|
|
|
errch chan error
|
|
|
|
closed bool
|
|
|
|
lock *sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
// Response return response of the current stream
|
|
|
|
func (s *ClientStream) Response() *http.Response {
|
|
|
|
return s.res
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read read stream data
|
|
|
|
func (s *ClientStream) Read(buf []byte) (n int, err error) {
|
|
|
|
if s.closed || s.res == nil || s.res.Body == nil {
|
|
|
|
return 0, io.EOF
|
|
|
|
}
|
|
|
|
return s.res.Body.Read(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write write data to stream
|
|
|
|
func (s *ClientStream) Write(buf []byte) (n int, err error) {
|
|
|
|
if s.closed {
|
|
|
|
return 0, io.EOF
|
|
|
|
}
|
|
|
|
if s.dp != nil {
|
|
|
|
return s.dp.Write(buf)
|
|
|
|
}
|
|
|
|
return 0, fmt.Errorf("empty data provider")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close close the stream
|
|
|
|
func (s *ClientStream) Close() error {
|
|
|
|
//s.lock.Lock()
|
|
|
|
//defer s.lock.Unlock()
|
|
|
|
if s.closed {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
s.closed = true
|
|
|
|
err := io.EOF
|
|
|
|
//log.Printf("close stream %d", int(s.streamID))
|
|
|
|
select {
|
|
|
|
case s.errch <- err:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
//log.Println("close stream resch")
|
|
|
|
//close(s.resch)
|
|
|
|
//log.Println("close stream errch")
|
|
|
|
//close(s.errch)
|
|
|
|
//log.Println("close pipe w")
|
|
|
|
if s.res != nil && s.res.Body != nil {
|
|
|
|
s.res.Body.Close()
|
|
|
|
}
|
|
|
|
//log.Println("close stream done")
|
|
|
|
if s.dp != nil {
|
|
|
|
s.dp.Close()
|
|
|
|
//s.conn.lock.Lock()
|
|
|
|
//C.nghttp2_submit_rst_stream(s.conn.session, 0, C.int(s.streamID), 0)
|
|
|
|
//s.conn.lock.Unlock()
|
|
|
|
s.dp = nil
|
|
|
|
}
|
|
|
|
if s.cdp != nil {
|
|
|
|
C.free(unsafe.Pointer(s.cdp))
|
|
|
|
s.cdp = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
s.conn.lock.Lock()
|
|
|
|
defer s.conn.lock.Unlock()
|
|
|
|
if _, ok := s.conn.streams[s.streamID]; ok {
|
|
|
|
delete(s.conn.streams, s.streamID)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServerStream server stream
|
|
|
|
type ServerStream struct {
|
|
|
|
streamID int
|
|
|
|
// headers receive done
|
|
|
|
headersDone bool
|
|
|
|
// is stream_end flag received
|
|
|
|
streamEnd bool
|
|
|
|
// request
|
|
|
|
req *http.Request
|
|
|
|
// response header
|
|
|
|
header http.Header
|
|
|
|
// response statusCode
|
|
|
|
statusCode int
|
|
|
|
// response has send
|
|
|
|
responseSend bool
|
|
|
|
|
|
|
|
// server connection
|
|
|
|
conn *ServerConn
|
|
|
|
|
|
|
|
// data provider
|
|
|
|
dp *dataProvider
|
|
|
|
cdp *C.nghttp2_data_provider
|
|
|
|
|
|
|
|
closed bool
|
|
|
|
//buf *bytes.Buffer
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write write data to stream,
|
|
|
|
// implements http.ResponseWriter
|
|
|
|
func (s *ServerStream) Write(buf []byte) (int, error) {
|
|
|
|
if s.closed {
|
|
|
|
return 0, io.EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
if !s.responseSend {
|
|
|
|
s.WriteHeader(http.StatusOK)
|
|
|
|
}
|
|
|
|
return s.dp.Write(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteHeader set response code and send reponse,
|
|
|
|
// implements http.ResponseWriter
|
|
|
|
func (s *ServerStream) WriteHeader(code int) {
|
|
|
|
if s.closed {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if s.responseSend {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
s.responseSend = true
|
|
|
|
s.statusCode = code
|
|
|
|
nvIndex := 0
|
|
|
|
nvMax := 25
|
|
|
|
nva := C.new_nv_array(C.size_t(nvMax))
|
|
|
|
setNvArray(nva, nvIndex, ":status", fmt.Sprintf("%d", code), 0)
|
|
|
|
nvIndex++
|
|
|
|
|
|
|
|
for k, v := range s.header {
|
|
|
|
if strings.ToLower(k) == "host" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
//log.Printf("header %s: %s", k, v)
|
|
|
|
setNvArray(nva, nvIndex, strings.Title(k), v[0], 0)
|
|
|
|
nvIndex++
|
|
|
|
}
|
|
|
|
var dp *dataProvider
|
|
|
|
var cdp *C.nghttp2_data_provider
|
|
|
|
dp, cdp = newDataProvider(s.conn.lock)
|
|
|
|
dp.streamID = s.streamID
|
|
|
|
dp.session = s.conn.session
|
|
|
|
s.dp = dp
|
|
|
|
s.cdp = cdp
|
|
|
|
|
|
|
|
s.conn.lock.Lock()
|
|
|
|
ret := C.nghttp2_submit_response(
|
|
|
|
s.conn.session, C.int(s.streamID), nva.nv, C.size_t(nvIndex), cdp)
|
|
|
|
s.conn.lock.Unlock()
|
|
|
|
|
|
|
|
C.delete_nv_array(nva)
|
|
|
|
if int(ret) < 0 {
|
|
|
|
panic(fmt.Sprintf("sumit response error %s", C.GoString(C.nghttp2_strerror(ret))))
|
|
|
|
}
|
|
|
|
//log.Printf("stream %d send response", s.streamID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Header return the http.Header,
|
|
|
|
// implements http.ResponseWriter
|
|
|
|
func (s *ServerStream) Header() http.Header {
|
|
|
|
if s.header == nil {
|
|
|
|
s.header = http.Header{}
|
|
|
|
}
|
|
|
|
return s.header
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close close the stream
|
|
|
|
func (s *ServerStream) Close() error {
|
|
|
|
if s.closed {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
s.closed = true
|
|
|
|
//C.nghttp2_submit_rst_stream(s.conn.session, 0, C.int(s.streamID), 0)
|
|
|
|
if s.req.Body != nil {
|
|
|
|
s.req.Body.Close()
|
|
|
|
}
|
|
|
|
if s.dp != nil {
|
|
|
|
s.dp.Close()
|
|
|
|
s.dp = nil
|
|
|
|
}
|
|
|
|
if s.cdp != nil {
|
|
|
|
C.free(unsafe.Pointer(s.cdp))
|
|
|
|
s.cdp = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
s.conn.lock.Lock()
|
|
|
|
s.conn.lock.Unlock()
|
|
|
|
if _, ok := s.conn.streams[s.streamID]; ok {
|
|
|
|
delete(s.conn.streams, s.streamID)
|
|
|
|
}
|
|
|
|
//log.Printf("stream %d closed", s.streamID)
|
|
|
|
return nil
|
|
|
|
}
|