To pass Go object references to C and back we use hack with casting to unsafe and then to uintptr. However, we should not store such references because Go not guaratnee it will be available by the same address. Introducing map with integer key helps to avoid dereference stored address. This closes #253 and #309 issues on GitHub.
150 lines
2.4 KiB
Go
150 lines
2.4 KiB
Go
/*
|
|
* Copyright (C) Max Romanov
|
|
* Copyright (C) NGINX, Inc.
|
|
*/
|
|
|
|
package unit
|
|
|
|
/*
|
|
#include "nxt_cgo_lib.h"
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"sync"
|
|
"unsafe"
|
|
)
|
|
|
|
type cbuf struct {
|
|
b C.uintptr_t
|
|
s C.size_t
|
|
}
|
|
|
|
func buf_ref(buf []byte) C.uintptr_t {
|
|
if len(buf) == 0 {
|
|
return 0
|
|
}
|
|
|
|
return C.uintptr_t(uintptr(unsafe.Pointer(&buf[0])))
|
|
}
|
|
|
|
type StringHeader struct {
|
|
Data unsafe.Pointer
|
|
Len int
|
|
}
|
|
|
|
func str_ref(s string) C.uintptr_t {
|
|
header := (*StringHeader)(unsafe.Pointer(&s))
|
|
|
|
return C.uintptr_t(uintptr(unsafe.Pointer(header.Data)))
|
|
}
|
|
|
|
func (buf *cbuf) init_bytes(b []byte) {
|
|
buf.b = buf_ref(b)
|
|
buf.s = C.size_t(len(b))
|
|
}
|
|
|
|
func (buf *cbuf) init_string(s string) {
|
|
buf.b = str_ref(s)
|
|
buf.s = C.size_t(len(s))
|
|
}
|
|
|
|
type SliceHeader struct {
|
|
Data unsafe.Pointer
|
|
Len int
|
|
Cap int
|
|
}
|
|
|
|
func (buf *cbuf) GoBytes() []byte {
|
|
if buf == nil {
|
|
var b [0]byte
|
|
return b[:0]
|
|
}
|
|
|
|
bytesHeader := &SliceHeader{
|
|
Data: unsafe.Pointer(uintptr(buf.b)),
|
|
Len: int(buf.s),
|
|
Cap: int(buf.s),
|
|
}
|
|
|
|
return *(*[]byte)(unsafe.Pointer(bytesHeader))
|
|
}
|
|
|
|
func GoBytes(buf unsafe.Pointer, size C.int) []byte {
|
|
bytesHeader := &SliceHeader{
|
|
Data: buf,
|
|
Len: int(size),
|
|
Cap: int(size),
|
|
}
|
|
|
|
return *(*[]byte)(unsafe.Pointer(bytesHeader))
|
|
}
|
|
|
|
func nxt_go_warn(format string, args ...interface{}) {
|
|
str := fmt.Sprintf("[go] " + format, args...)
|
|
|
|
C.nxt_cgo_warn(str_ref(str), C.uint32_t(len(str)))
|
|
}
|
|
|
|
type handler_registry struct {
|
|
sync.RWMutex
|
|
next uintptr
|
|
m map[uintptr]*http.Handler
|
|
}
|
|
|
|
var handler_registry_ handler_registry
|
|
|
|
func set_handler(handler *http.Handler) uintptr {
|
|
|
|
handler_registry_.Lock()
|
|
if handler_registry_.m == nil {
|
|
handler_registry_.m = make(map[uintptr]*http.Handler)
|
|
handler_registry_.next = 1
|
|
}
|
|
|
|
h := handler_registry_.next
|
|
handler_registry_.next += 1
|
|
handler_registry_.m[h] = handler
|
|
|
|
handler_registry_.Unlock()
|
|
|
|
return h
|
|
}
|
|
|
|
func get_handler(h uintptr) http.Handler {
|
|
handler_registry_.RLock()
|
|
defer handler_registry_.RUnlock()
|
|
|
|
return *handler_registry_.m[h]
|
|
}
|
|
|
|
func reset_handler(h uintptr) {
|
|
|
|
handler_registry_.Lock()
|
|
if handler_registry_.m != nil {
|
|
delete(handler_registry_.m, h)
|
|
}
|
|
|
|
handler_registry_.Unlock()
|
|
}
|
|
|
|
func ListenAndServe(addr string, handler http.Handler) error {
|
|
if handler == nil {
|
|
handler = http.DefaultServeMux
|
|
}
|
|
|
|
h := set_handler(&handler)
|
|
|
|
rc := C.nxt_cgo_run(C.uintptr_t(h))
|
|
|
|
reset_handler(h)
|
|
|
|
if rc != 0 {
|
|
return http.ListenAndServe(addr, handler)
|
|
}
|
|
|
|
return nil
|
|
}
|