You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nghttp2-go/callbacks.go

435 lines
12 KiB
Go

6 years ago
package nghttp2
/*
#include "_nghttp2.h"
*/
import "C"
import (
"bytes"
"crypto/tls"
6 years ago
"io"
"net/http"
"net/url"
"strconv"
6 years ago
"strings"
"sync"
"unsafe"
)
const (
NGHTTP2_NO_ERROR = 0
NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE = -521
NGHTTP2_ERR_CALLBACK_FAILURE = -902
NGHTTP2_ERR_DEFERRED = -508
)
/*
// onServerDataRecvCallback callback function for libnghttp2 library
// want receive data from network.
//
//export onServerDataRecvCallback
func onServerDataRecvCallback(ptr unsafe.Pointer, data unsafe.Pointer,
6 years ago
length C.size_t) C.ssize_t {
conn := (*ServerConn)(ptr)
buf := make([]byte, int(length))
n, err := conn.conn.Read(buf)
if err != nil {
return -1
}
cbuf := C.CBytes(buf[:n])
defer C.free(cbuf)
C.memcpy(data, cbuf, C.size_t(n))
return C.ssize_t(n)
}
*/
6 years ago
// onServerDataSendCallback callback function for libnghttp2 library
// want send data to network.
//
//export onServerDataSendCallback
func onServerDataSendCallback(ptr unsafe.Pointer, data unsafe.Pointer,
6 years ago
length C.size_t) C.ssize_t {
//log.Println("server data send")
conn := (*ServerConn)(ptr)
buf := C.GoBytes(data, C.int(length))
n, err := conn.conn.Write(buf)
if err != nil {
return NGHTTP2_ERR_CALLBACK_FAILURE
6 years ago
}
//log.Println("send ", n, " bytes to network ", buf)
return C.ssize_t(n)
}
// onServerDataChunkRecv callback function for libnghttp2 library's data chunk recv.
//
//export onServerDataChunkRecv
func onServerDataChunkRecv(ptr unsafe.Pointer, streamID C.int,
6 years ago
data unsafe.Pointer, length C.size_t) C.int {
conn := (*ServerConn)(ptr)
s, ok := conn.streams[int(streamID)]
if !ok {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
}
6 years ago
bp := s.req.Body.(*bodyProvider)
buf := C.GoBytes(data, C.int(length))
bp.Write(buf)
return C.int(length)
}
// onServerBeginHeaderCallback callback function for begin begin header recv.
//
//export onServerBeginHeaderCallback
func onServerBeginHeaderCallback(ptr unsafe.Pointer, streamID C.int) C.int {
6 years ago
conn := (*ServerConn)(ptr)
var TLS tls.ConnectionState
if tlsconn, ok := conn.conn.(*tls.Conn); ok {
TLS = tlsconn.ConnectionState()
}
6 years ago
s := &ServerStream{
streamID: int(streamID),
conn: conn,
req: &http.Request{
//URL: &url.URL{},
6 years ago
Header: http.Header{},
Proto: "HTTP/2.0",
ProtoMajor: 2,
ProtoMinor: 0,
RemoteAddr: conn.conn.RemoteAddr().String(),
TLS: &TLS,
6 years ago
},
//buf: new(bytes.Buffer),
}
//conn.lock.Lock()
6 years ago
conn.streams[int(streamID)] = s
//conn.lock.Unlock()
return NGHTTP2_NO_ERROR
6 years ago
}
// onServerHeaderCallback callback function for each header recv.
//
//export onServerHeaderCallback
func onServerHeaderCallback(ptr unsafe.Pointer, streamID C.int,
6 years ago
name unsafe.Pointer, namelen C.int,
value unsafe.Pointer, valuelen C.int) C.int {
conn := (*ServerConn)(ptr)
s, ok := conn.streams[int(streamID)]
if !ok {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
}
6 years ago
hdrname := C.GoStringN((*C.char)(name), namelen)
hdrvalue := C.GoStringN((*C.char)(value), valuelen)
hdrname = strings.ToLower(hdrname)
switch hdrname {
case ":method":
s.req.Method = hdrvalue
case ":scheme":
// s.req.URL.Scheme = hdrvalue
6 years ago
case ":path":
s.req.RequestURI = hdrvalue
u, _ := url.ParseRequestURI(s.req.RequestURI)
s.req.URL = u
6 years ago
case ":authority":
s.req.Host = hdrvalue
case "content-length":
s.req.Header.Add(hdrname, hdrvalue)
n, err := strconv.ParseInt(hdrvalue, 10, 64)
if err == nil {
s.req.ContentLength = n
}
6 years ago
default:
s.req.Header.Add(hdrname, hdrvalue)
}
return NGHTTP2_NO_ERROR
6 years ago
}
// onServerStreamEndCallback callback function for the stream when END_STREAM flag set
//
//export onServerStreamEndCallback
func onServerStreamEndCallback(ptr unsafe.Pointer, streamID C.int) C.int {
6 years ago
conn := (*ServerConn)(ptr)
s, ok := conn.streams[int(streamID)]
if !ok {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
}
6 years ago
s.streamEnd = true
bp := s.req.Body.(*bodyProvider)
if s.req.Method != "CONNECT" {
bp.closed = true
//log.Println("stream end flag set, begin to serve")
6 years ago
go conn.serve(s)
}
return NGHTTP2_NO_ERROR
6 years ago
}
// onServerHeadersDoneCallback callback function for the stream when all headers received.
//
//export onServerHeadersDoneCallback
func onServerHeadersDoneCallback(ptr unsafe.Pointer, streamID C.int) C.int {
6 years ago
conn := (*ServerConn)(ptr)
s, ok := conn.streams[int(streamID)]
if !ok {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
}
6 years ago
s.headersDone = true
bp := &bodyProvider{
buf: new(bytes.Buffer),
lock: new(sync.Mutex),
}
s.req.Body = bp
if s.req.Method == "CONNECT" {
go conn.serve(s)
}
return NGHTTP2_NO_ERROR
6 years ago
}
// onServerStreamClose callback function for the stream when closed.
//
//export onServerStreamClose
func onServerStreamClose(ptr unsafe.Pointer, streamID C.int) C.int {
6 years ago
conn := (*ServerConn)(ptr)
s, ok := conn.streams[int(streamID)]
if !ok {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
}
//conn.lock.Lock()
6 years ago
delete(conn.streams, int(streamID))
//conn.lock.Unlock()
go s.Close()
return NGHTTP2_NO_ERROR
6 years ago
}
// onDataSourceReadCallback callback function for libnghttp2 library
// want read data from data provider source,
// return NGHTTP2_ERR_DEFERRED will cause data frame defered,
// application later call nghttp2_session_resume_data will re-quene the data frame
6 years ago
//
//export onDataSourceReadCallback
func onDataSourceReadCallback(ptr unsafe.Pointer,
6 years ago
buf unsafe.Pointer, length C.size_t) C.ssize_t {
//log.Println("onDataSourceReadCallback begin")
6 years ago
dp := (*dataProvider)(ptr)
gobuf := make([]byte, int(length))
n, err := dp.Read(gobuf)
if err != nil {
if err == io.EOF {
//log.Println("onDataSourceReadCallback end")
6 years ago
return 0
}
if err == errAgain {
//log.Println("onDataSourceReadCallback end")
dp.deferred = true
return NGHTTP2_ERR_DEFERRED
6 years ago
}
//log.Println("onDataSourceReadCallback end")
return NGHTTP2_ERR_CALLBACK_FAILURE
6 years ago
}
cbuf := C.CBytes(gobuf)
defer C.free(cbuf)
C.memcpy(buf, cbuf, C.size_t(n))
//log.Println("onDataSourceReadCallback end")
6 years ago
return C.ssize_t(n)
}
// onClientDataChunkRecv callback function for libnghttp2 library data chunk received.
//
//export onClientDataChunkRecv
func onClientDataChunkRecv(ptr unsafe.Pointer, streamID C.int,
6 years ago
buf unsafe.Pointer, length C.size_t) C.int {
//log.Println("onClientDataChunkRecv begin")
6 years ago
conn := (*ClientConn)(ptr)
gobuf := C.GoBytes(buf, C.int(length))
s, ok := conn.streams[int(streamID)]
if !ok {
//log.Println("onClientDataChunkRecv end")
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
}
if s.res.Body == nil {
//log.Println("empty body")
//log.Println("onClientDataChunkRecv end")
return C.int(length)
}
if bp, ok := s.res.Body.(*bodyProvider); ok {
n, err := bp.Write(gobuf)
if err != nil {
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
}
//log.Println("onClientDataChunkRecv end")
return C.int(n)
}
//log.Println("onClientDataChunkRecv end")
return C.int(length)
6 years ago
}
/*
// onClientDataRecvCallback callback function for libnghttp2 library want read data from network.
//
//export onClientDataRecvCallback
func onClientDataRecvCallback(ptr unsafe.Pointer, data unsafe.Pointer, size C.size_t) C.ssize_t {
6 years ago
//log.Println("data read req", int(size))
conn := (*ClientConn)(ptr)
buf := make([]byte, int(size))
//log.Println(conn.conn.RemoteAddr())
n, err := conn.conn.Read(buf)
if err != nil {
//log.Println(err)
return -1
}
cbuf := C.CBytes(buf)
//log.Println("read from network ", n, buf[:n])
C.memcpy(data, cbuf, C.size_t(n))
return C.ssize_t(n)
}
*/
6 years ago
// onClientDataSendCallback callback function for libnghttp2 library want send data to network.
//
//export onClientDataSendCallback
func onClientDataSendCallback(ptr unsafe.Pointer, data unsafe.Pointer, size C.size_t) C.ssize_t {
//log.Println("onClientDataSendCallback begin")
6 years ago
//log.Println("data write req ", int(size))
conn := (*ClientConn)(ptr)
buf := C.GoBytes(data, C.int(size))
//log.Println(conn.conn.RemoteAddr())
n, err := conn.conn.Write(buf)
if err != nil {
//log.Println("onClientDataSendCallback end")
return NGHTTP2_ERR_CALLBACK_FAILURE
6 years ago
}
//log.Printf("write %d bytes to network ", n)
//log.Println("onClientDataSendCallback end")
6 years ago
return C.ssize_t(n)
}
// onClientBeginHeaderCallback callback function for begin header receive.
//
//export onClientBeginHeaderCallback
func onClientBeginHeaderCallback(ptr unsafe.Pointer, streamID C.int) C.int {
//log.Println("onClientBeginHeaderCallback begin")
//log.Printf("stream %d begin headers", int(streamID))
6 years ago
conn := (*ClientConn)(ptr)
s, ok := conn.streams[int(streamID)]
if !ok {
//log.Println("onClientBeginHeaderCallback end")
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
}
var TLS tls.ConnectionState
if tlsconn, ok := conn.conn.(*tls.Conn); ok {
TLS = tlsconn.ConnectionState()
}
s.res = &http.Response{
Header: make(http.Header),
Body: &bodyProvider{
buf: new(bytes.Buffer),
lock: new(sync.Mutex),
},
TLS: &TLS,
}
//log.Println("onClientBeginHeaderCallback end")
return NGHTTP2_NO_ERROR
6 years ago
}
// onClientHeaderCallback callback function for each header received.
//
//export onClientHeaderCallback
func onClientHeaderCallback(ptr unsafe.Pointer, streamID C.int,
6 years ago
name unsafe.Pointer, namelen C.int,
value unsafe.Pointer, valuelen C.int) C.int {
//log.Println("onClientHeaderCallback begin")
6 years ago
//log.Println("header")
conn := (*ClientConn)(ptr)
goname := string(C.GoBytes(name, namelen))
govalue := string(C.GoBytes(value, valuelen))
s, ok := conn.streams[int(streamID)]
if !ok {
//log.Println("onClientHeaderCallback end")
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
}
goname = strings.ToLower(goname)
switch goname {
case ":status":
statusCode, _ := strconv.Atoi(govalue)
s.res.StatusCode = statusCode
s.res.Status = http.StatusText(statusCode)
s.res.Proto = "HTTP/2.0"
s.res.ProtoMajor = 2
s.res.ProtoMinor = 0
case "content-length":
s.res.Header.Add(goname, govalue)
n, err := strconv.ParseInt(govalue, 10, 64)
if err == nil {
s.res.ContentLength = n
}
case "transfer-encoding":
s.res.Header.Add(goname, govalue)
s.res.TransferEncoding = append(s.res.TransferEncoding, govalue)
default:
s.res.Header.Add(goname, govalue)
}
//log.Println("onClientHeaderCallback end")
return NGHTTP2_NO_ERROR
6 years ago
}
// onClientHeadersDoneCallback callback function for the stream when all headers received.
//
//export onClientHeadersDoneCallback
func onClientHeadersDoneCallback(ptr unsafe.Pointer, streamID C.int) C.int {
//log.Println("onClientHeadersDoneCallback begin")
//log.Printf("stream %d headers done", int(streamID))
6 years ago
conn := (*ClientConn)(ptr)
s, ok := conn.streams[int(streamID)]
if !ok {
//log.Println("onClientHeadersDoneCallback end")
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
}
select {
case s.resch <- s.res:
default:
}
//log.Println("onClientHeadersDoneCallback end")
return NGHTTP2_NO_ERROR
6 years ago
}
// onClientStreamClose callback function for the stream when closed.
//
//export onClientStreamClose
func onClientStreamClose(ptr unsafe.Pointer, streamID C.int) C.int {
//log.Println("onClientStreamClose begin")
//log.Printf("stream %d closed", int(streamID))
6 years ago
conn := (*ClientConn)(ptr)
stream, ok := conn.streams[int(streamID)]
if ok {
go stream.Close()
//conn.lock.Lock()
delete(conn.streams, int(streamID))
//go stream.Close()
//conn.lock.Unlock()
//log.Println("onClientStreamClose end")
return NGHTTP2_NO_ERROR
}
//log.Println("onClientStreamClose end")
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE
6 years ago
}
//export onClientConnectionCloseCallback
func onClientConnectionCloseCallback(ptr unsafe.Pointer) {
conn := (*ClientConn)(ptr)
conn.err = io.EOF
6 years ago
// signal all goroutings exit
for i := 0; i < 4; i++ {
select {
case conn.exitch <- struct{}{}:
default:
}
}
}