diff --git a/go.mod b/go.mod index 7e12cef..97a52e0 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/goplus/llgoexamples go 1.20 -require github.com/goplus/llgo v0.9.3-0.20240726020431-98d075728f2b +require github.com/goplus/llgo v0.9.6 diff --git a/go.sum b/go.sum index fdc017f..1f51184 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,2 @@ -github.com/goplus/llgo v0.9.0 h1:yaJzQperGUafEaHc9VlVQVskIngacoTNweEXY0GRi0Q= -github.com/goplus/llgo v0.9.0/go.mod h1:M3UwiYdPZFyx7m2J0+6Ti1dYVA3uOO1WvSBocuE8N7M= -github.com/goplus/llgo v0.9.1-0.20240709104849-d6a38a567fda h1:UIPwlgzCb8dV/7WFMyprhZuq8CSLAQIqwFpH5AhrNOM= -github.com/goplus/llgo v0.9.1-0.20240709104849-d6a38a567fda/go.mod h1:zsrtWZapL4aklZc99xBSZRynGzLTIT1mLRjP0VSn9iw= -github.com/goplus/llgo v0.9.1-0.20240712060421-858d38d314a3 h1:2fZ2zQ8S58KvOsJTx6s6MHoi6n1K4sqQwIbTauMrgEE= -github.com/goplus/llgo v0.9.1-0.20240712060421-858d38d314a3/go.mod h1:zsrtWZapL4aklZc99xBSZRynGzLTIT1mLRjP0VSn9iw= -github.com/goplus/llgo v0.9.3-0.20240726020431-98d075728f2b h1:z9FUoeAALL5ytBhhGhE1dXm4+L1Q2eMUTcfiqLAZgf8= -github.com/goplus/llgo v0.9.3-0.20240726020431-98d075728f2b/go.mod h1:zsrtWZapL4aklZc99xBSZRynGzLTIT1mLRjP0VSn9iw= +github.com/goplus/llgo v0.9.6 h1:QHEE39x+DRapuoz5vmGMGhAZ3Vaw29ZqRo4X8C//PqE= +github.com/goplus/llgo v0.9.6/go.mod h1:5Fs+08NslqofJ7xtOiIXugkurYOoQvY02ZkFNWA1uEI= diff --git a/rust/hyper/_demo/hyper_server/server.go b/rust/hyper/_demo/hyper_server/server.go new file mode 100644 index 0000000..ec14772 --- /dev/null +++ b/rust/hyper/_demo/hyper_server/server.go @@ -0,0 +1,604 @@ +package main + +import ( + "fmt" + "os" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" + "github.com/goplus/llgo/c/net" + cos "github.com/goplus/llgo/c/os" + "github.com/goplus/llgo/c/syscall" + "github.com/goplus/llgoexamples/rust/hyper" +) + +const ( + MAX_EVENTS = 128 + READ_BUFFER_SIZE = 65536 +) + +var ( + exec *hyper.Executor + loop *libuv.Loop + server libuv.Tcp + sigintHandle, sigtermHandle libuv.Signal + shouldExit = false +) + +type ConnData struct { + Stream libuv.Tcp + ReadWaker *hyper.Waker + WriteWaker *hyper.Waker + ConnTask *hyper.Task + IsClosing int + ReadBuffer libuv.Buf + ReadBufferFilled uintptr + WriteBuffer libuv.Buf + WriteReq libuv.Write +} + +type ServiceUserdata struct { + Host [128]c.Char + Port [8]c.Char + Executor *hyper.Executor + Conn *ConnData +} + +func onSignal(handle *libuv.Signal, sigNum c.Int) { + fmt.Printf("Caught signal %d... exiting\n", sigNum) + shouldExit = true + sigintHandle.Stop() + sigtermHandle.Stop() + (*libuv.Handle)(unsafe.Pointer(&server)).Close(nil) + loop.Close() +} + +func closeWalkCb(handle *libuv.Handle, arg c.Pointer) { + if handle.IsClosing() == 0 { + handle.Close(nil) + } +} + +func allocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { + conn := (*ConnData)(handle.GetData()) + + if conn.ReadBuffer.Base == nil { + conn.ReadBuffer.Base = (*c.Char)(c.Malloc(suggestedSize)) + if conn.ReadBuffer.Base == nil { + fmt.Fprintf(os.Stderr, "Failed to allocate read buffer\n") + *buf = libuv.InitBuf(nil, 0) + return + } + conn.ReadBuffer.Len = READ_BUFFER_SIZE + conn.ReadBufferFilled = 0 + } + + available := READ_BUFFER_SIZE - conn.ReadBufferFilled + toAlloc := available + if uintptr(available) > suggestedSize { + toAlloc = uintptr(suggestedSize) + } + + *buf = libuv.InitBuf( + (*c.Char)(unsafe.Pointer(uintptr(unsafe.Pointer(conn.ReadBuffer.Base))+uintptr(conn.ReadBufferFilled))), + c.Uint(toAlloc), + ) +} + +func onClose(handle *libuv.Handle) { + c.Free(unsafe.Pointer(handle)) +} + +func closeConn(handle *libuv.Handle) { + conn := (*ConnData)(handle.GetData()) + + if conn != nil { + if conn.ReadWaker != nil { + conn.ReadWaker.Free() + conn.ReadWaker = nil + } + if conn.WriteWaker != nil { + conn.WriteWaker.Free() + conn.WriteWaker = nil + } + if conn.ConnTask != nil { + conn.ConnTask.Free() + conn.ConnTask = nil + } + } +} + +func onRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { + conn := (*ConnData)((*libuv.Handle)(unsafe.Pointer(client)).GetData()) + + if nread < 0 { + if libuv.Errno(nread) != libuv.EOF { + fmt.Println("Read error") + (*libuv.Handle)(unsafe.Pointer(client)).Close(closeConn) + //c.Free(unsafe.Pointer(buf.Base)) + } + } else if nread > 0 { + conn.ReadBufferFilled += uintptr(nread) + } + + if conn.ReadWaker != nil { + conn.ReadWaker.Wake() + conn.ReadWaker = nil + } +} + +func readCb(userdata unsafe.Pointer, ctx *hyper.Context, buf *byte, bufLen uintptr) uintptr { + conn := (*ConnData)(userdata) + + // Copy data from the read buffer to the hyper context buffer + if conn.ReadBufferFilled > 0 { + toCopy := conn.ReadBufferFilled + if uintptr(toCopy) > bufLen { + toCopy = uintptr(bufLen) + } + c.Memcpy(unsafe.Pointer(buf), unsafe.Pointer(conn.ReadBuffer.Base), toCopy) + + c.Memmove( + unsafe.Pointer(conn.ReadBuffer.Base), + unsafe.Pointer(uintptr(unsafe.Pointer(conn.ReadBuffer.Base))+toCopy), + uintptr(conn.ReadBufferFilled)-toCopy, + ) + conn.ReadBufferFilled -= toCopy + + return toCopy + } + + // Set the read waker + if conn.ReadWaker != nil { + conn.ReadWaker.Free() + } + + conn.ReadWaker = ctx.Waker() + return hyper.IoPending +} + +func onWrite(req *libuv.Write, status c.Int) { + conn := (*ConnData)(req.Data) + + // Check if the status is less than 0 + if status < 0 { + fmt.Fprintf(os.Stderr, "Write completed with error: %s\n", + libuv.Strerror(libuv.Errno(status))) + } + + // Wake up the write waker + if conn.WriteWaker != nil { + conn.WriteWaker.Wake() + conn.WriteWaker = nil + } +} + +func writeCb(userdata unsafe.Pointer, ctx *hyper.Context, buf *byte, bufLen uintptr) uintptr { + conn := (*ConnData)(userdata) + + conn.WriteBuffer = libuv.InitBuf((*c.Char)(unsafe.Pointer(buf)), c.Uint(bufLen)) + conn.WriteReq.Data = unsafe.Pointer(conn) + + // Initiate the write operation + r := conn.WriteReq.Write((*libuv.Stream)(unsafe.Pointer(&conn.Stream)), &conn.WriteBuffer, 1, onWrite) + if r >= 0 { + return bufLen + } + + // Set the write_waker + if conn.WriteWaker != nil { + conn.WriteWaker.Free() + } + conn.WriteWaker = ctx.Waker() + + return hyper.IoPending +} + +func createConnData() (*ConnData, error) { + conn := &ConnData{} + if conn == nil { + return nil, fmt.Errorf("failed to allocate conn_data") + } + return conn, nil +} + +func freeConnData(userdata c.Pointer) { + conn := (*ConnData)(userdata) + if conn != nil && conn.IsClosing == 0 { + conn.IsClosing = 1 + // We don't immediately close the connection here. + // Instead, we'll let the main loop handle the closure when appropriate. + } +} + +func createIo(conn *ConnData) *hyper.Io { + io := hyper.NewIo() + io.SetUserdata(unsafe.Pointer(conn), freeConnData) + io.SetRead(readCb) + io.SetWrite(writeCb) + + return io +} + +func createServiceUserdata() *ServiceUserdata { + userdata := &ServiceUserdata{} + if userdata == nil { + fmt.Fprintf(os.Stderr, "Failed to allocate service_userdata\n") + } + return userdata +} + +func printEachHeader(userdata c.Pointer, name *byte, nameLen uintptr, value *byte, valueLen uintptr) c.Int { + fmt.Printf("%.*s: %.*s\n", int(nameLen), c.GoString((*c.Char)(c.Pointer(name))), + int(valueLen), c.GoString((*c.Char)(unsafe.Pointer(value)))) + return hyper.IterContinue +} + +func printBodyChunk(userdata c.Pointer, chunk *hyper.Buf) c.Int { + buf := chunk.Bytes() + len := chunk.Len() + cos.Write(1, unsafe.Pointer(buf), len) + _ = buf + _ = len + + return hyper.IterContinue +} + +func sendEachBodyChunk(userdata c.Pointer, ctx *hyper.Context, chunk **hyper.Buf) c.Int { + chunkCount := (*c.Int)(userdata) + if *chunkCount > 0 { + var data [64]c.Char + c.Snprintf((*c.Char)(&data[0]), unsafe.Sizeof(data), c.Str("Chunk %d\n"), *chunkCount) + *chunk = hyper.CopyBuf((*byte)(unsafe.Pointer(&data[0])), c.Strlen((*c.Char)(&data[0]))) + *chunkCount-- + return hyper.PollReady + } else { + *chunk = nil + return hyper.PollReady + } +} + +func serverCallback(userdata c.Pointer, request *hyper.Request, channel *hyper.ResponseChannel) { + serviceData := (*ServiceUserdata)(userdata) + + conn := serviceData.Conn + if conn == nil { + fmt.Fprintf(os.Stderr, "Error: No connection data available\n") + return + } + + fmt.Printf("Handling request on connection from %s:%s\n", c.GoString((*c.Char)(&serviceData.Host[0])), c.GoString((*c.Char)(&serviceData.Port[0]))) + + if request == nil { + fmt.Fprintf(os.Stderr, "Error: Received null request\n") + return + } + + var scheme [64]byte + var authority [256]byte + var pathAndQuery [1024]byte + schemeLen := unsafe.Sizeof(scheme) + authorityLen := unsafe.Sizeof(authority) + pathAndQueryLen := unsafe.Sizeof(pathAndQuery) + + uriResult := request.URIParts(&scheme[0], &schemeLen, &authority[0], &authorityLen, &pathAndQuery[0], &pathAndQueryLen) + if uriResult == hyper.OK { + fmt.Printf("Scheme: %s\n", string(scheme[:schemeLen])) + fmt.Printf("Authority: %s\n", string(authority[:authorityLen])) + fmt.Printf("Path and Query: %s\n", string(pathAndQuery[:pathAndQueryLen])) + } else { + fmt.Fprintf(os.Stderr, "Failed to get URI parts. Error code: %d\n", uriResult) + } + + version := request.Version() + fmt.Printf("HTTP Version: ") + switch version { + case hyper.HTTPVersionNone: + fmt.Println("None") + case hyper.HTTPVersion10: + fmt.Println("HTTP/1.0") + case hyper.HTTPVersion11: + fmt.Println("HTTP/1.1") + case hyper.HTTPVersion2: + fmt.Println("HTTP/2") + default: + fmt.Printf("Unknown (%d)\n", version) + } + + var method [32]byte + methodLen := unsafe.Sizeof(method) + methodResult := request.Method(&method[0], &methodLen) + if methodResult == hyper.OK { + fmt.Printf("Method: %s\n", string(method[:methodLen])) + } else { + fmt.Fprintf(os.Stderr, "Failed to get request method. Error code: %d\n", methodResult) + } + + fmt.Println("Headers:") + reqHeaders := request.Headers() + if reqHeaders != nil { + reqHeaders.Foreach(printEachHeader, nil) + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to get request headers\n") + } + + if methodLen > 0 && (c.Strncmp((*c.Char)(unsafe.Pointer(&method[0])), c.Str("POST"), methodLen) == 0 || + c.Strncmp((*c.Char)(unsafe.Pointer(&method[0])), c.Str("PUT"), methodLen) == 0) { + fmt.Println("Request Body:") + body := request.Body() + if body != nil { + task := body.Foreach(printBodyChunk, nil, nil) + if task != nil { + r := serviceData.Executor.Push(task) + if r != hyper.OK { + fmt.Fprintf(os.Stderr, "Error: Failed to push body foreach task\n") + task.Free() + } + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to create body foreach task\n") + } + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to get request body\n") + } + } + + response := hyper.NewResponse() + if response != nil { + response.SetStatus(200) + rspHeaders := response.Headers() + if rspHeaders != nil { + hres := rspHeaders.Set((*byte)(unsafe.Pointer(c.Str("Content-Type"))), uintptr(12), (*byte)(unsafe.Pointer(c.Str("text/plain"))), uintptr(10)) + if hres != hyper.OK { + fmt.Fprintf(os.Stderr, "Error: Failed to set response headers\n") + } + hres = rspHeaders.Set((*byte)(unsafe.Pointer(c.Str("Cache-Control"))), uintptr(13), (*byte)(unsafe.Pointer(c.Str("no-cache"))), uintptr(8)) + if hres != hyper.OK { + fmt.Fprintf(os.Stderr, "Error: Failed to set response headers\n") + } + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to get response headers\n") + } + + if methodLen > 0 && c.Strncmp((*c.Char)(unsafe.Pointer(&method[0])), c.Str("GET"), methodLen) == 0 { + body := hyper.NewBody() + if body != nil { + body.SetDataFunc(sendEachBodyChunk) + chunkCount := (*c.Int)(c.Malloc(unsafe.Sizeof(c.Int(0)))) + if chunkCount != nil { + *chunkCount = 10 + body.SetUserdata(unsafe.Pointer(chunkCount), func(p c.Pointer) { c.Free(p) }) + response.SetBody(body) + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to allocate chunk_count\n") + } + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to create response body\n") + } + } + + channel.Send(response) + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to create response\n") + } + + // We don't close the connection here. Let hyper handle keep-alive. +} + +func onNewConnection(server *libuv.Stream, status c.Int) { + if status < 0 { + fmt.Fprintf(os.Stderr, "New connection error %s\n", libuv.Strerror(libuv.Errno(status))) + return + } + + conn, err := createConnData() + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create conn_data\n") + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) + return + } + + libuv.InitTcp(loop, &conn.Stream) + if server.Accept((*libuv.Stream)(unsafe.Pointer(&conn.Stream))) == 0 { + conn.Stream.Data = unsafe.Pointer(conn) + + r := (*libuv.Stream)(unsafe.Pointer(&conn.Stream)).StartRead(allocBuffer, onRead) + if r < 0 { + fmt.Fprintf(os.Stderr, "uv_read_start error: %s\n", libuv.Strerror(libuv.Errno(r))) + c.Free(unsafe.Pointer(conn)) + return + } + + userdata := createServiceUserdata() + if userdata == nil { + fmt.Fprintf(os.Stderr, "Failed to create service_userdata\n") + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) + return + } + userdata.Executor = exec + userdata.Conn = conn + + var addr net.SockaddrStorage + addrlen := c.Int(unsafe.Sizeof(addr)) + conn.Stream.Getpeername((*net.SockAddr)(unsafe.Pointer(&addr)), &addrlen) + + if addr.Family == net.AF_INET { + s := (*net.SockaddrIn)(unsafe.Pointer(&addr)) + libuv.Ip4Name(s, (*c.Char)(&userdata.Host[0]), unsafe.Sizeof(userdata.Host)) + c.Snprintf((*c.Char)(&userdata.Port[0]), unsafe.Sizeof(userdata.Port), c.Str("%d"), net.Ntohs(s.Port)) + } else if addr.Family == net.AF_INET6 { + s := (*net.SockaddrIn6)(unsafe.Pointer(&addr)) + libuv.Ip6Name(s, (*c.Char)(&userdata.Host[0]), unsafe.Sizeof(userdata.Host)) + c.Snprintf((*c.Char)(&userdata.Port[0]), unsafe.Sizeof(userdata.Port), c.Str("%d"), net.Ntohs(s.Port)) + } + + io := createIo(conn) + service := hyper.ServiceNew(serverCallback) + service.SetUserdata(unsafe.Pointer(userdata), nil) + + http1Opts := hyper.Http1ServerconnOptionsNew(userdata.Executor) + http1Opts.HeaderReadTimeout(5000) + + http2Opts := hyper.Http2ServerconnOptionsNew(userdata.Executor) + http2Opts.KeepAliveInterval(5) + http2Opts.KeepAliveTimeout(5) + + serverconn := hyper.ServeHttpXConnection(http1Opts, http2Opts, io, service) + + conn.ConnTask = serverconn + serverconn.SetUserdata(unsafe.Pointer(conn), freeConnData) + userdata.Executor.Push(serverconn) + + http1Opts.Free() + http2Opts.Free() + } else { + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) + } +} + +func main() { + exec = hyper.NewExecutor() + if exec == nil { + fmt.Fprintf(os.Stderr, "Failed to create hyper executor\n") + os.Exit(1) + } + + host := "127.0.0.1" + port := "1234" + if len(os.Args) > 1 { + host = os.Args[1] + } + if len(os.Args) > 2 { + port = os.Args[2] + } + fmt.Printf("listening on port %s on %s...\n", port, host) + + loop = libuv.DefaultLoop() + + libuv.InitTcp(loop, &server) + + var addr net.SockaddrIn + libuv.Ip4Addr(c.AllocaCStr(host), c.Atoi(c.AllocaCStr(port)), &addr) + + r := server.Bind((*net.SockAddr)(unsafe.Pointer(&addr)), 0) + if r != 0 { + fmt.Fprintf(os.Stderr, "Bind error %s\n", libuv.Strerror(libuv.Errno(r))) + os.Exit(1) + } + + // Set SO_REUSEADDR + yes := c.Int(1) + r = net.SetSockOpt(server.GetIoWatcherFd(), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, unsafe.Pointer(&yes), c.Uint(unsafe.Sizeof(yes))) + if r != 0 { + fmt.Fprintf(os.Stderr, "setsockopt error %s\n", libuv.Strerror(libuv.Errno(r))) + os.Exit(1) + } + + r = (*libuv.Stream)(&server).Listen(syscall.SOMAXCONN, onNewConnection) + if r != 0 { + fmt.Fprintf(os.Stderr, "Listen error %s\n", libuv.Strerror(libuv.Errno(r))) + os.Exit(1) + } + + signalRes := libuv.SignalInit(loop, &sigintHandle) + if signalRes != 0 { + fmt.Fprintf(os.Stderr, "Failed to initialize signal handler: %d\n", signalRes) + os.Exit(1) + } + signalHandleRes := sigintHandle.Start(onSignal, c.Int(syscall.SIGINT)) + if signalHandleRes != 0 { + fmt.Fprintf(os.Stderr, "Failed to start signal handler: %d\n", signalHandleRes) + os.Exit(1) + } + + signalRes = libuv.SignalInit(loop, &sigtermHandle) + if signalRes != 0 { + fmt.Fprintf(os.Stderr, "Failed to initialize signal handler: %d\n", signalRes) + os.Exit(1) + } + signalHandleRes = sigtermHandle.Start(onSignal, c.Int(syscall.SIGTERM)) + if signalHandleRes != 0 { + fmt.Fprintf(os.Stderr, "Failed to start signal handler: %d\n", signalHandleRes) + os.Exit(1) + } + + fmt.Printf("http handshake (hyper v%s) ...\n", c.GoString(hyper.Version())) + + for { + loop.Run(libuv.RUN_NOWAIT) + + task := exec.Poll() + for task != nil && !shouldExit { + taskType := task.Type() + taskUserdata := task.Userdata() + + switch taskType { + case hyper.TaskEmpty: + fmt.Printf("\nEmpty task received: connection closed\n") + if taskUserdata != nil { + fmt.Println("taskUserdata is not nil") + conn := (*ConnData)(taskUserdata) + if conn.IsClosing == 0 { + fmt.Println("conn.IsClosing is 0") + conn.IsClosing = 1 + if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() == 0 { + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(closeConn) + } + } + } + + case hyper.TaskError: + fmt.Println("Task error:") + err := (*hyper.Error)(task.Value()) + var errbuf [256]byte + errlen := err.Print(&errbuf[0], unsafe.Sizeof(errbuf)) + fmt.Println("Error message:", string(errbuf[:errlen])) + fmt.Printf("Error code: %d\n", int(err.Code())) + err.Free() + + case hyper.TaskClientConn: + fmt.Fprintf(os.Stderr, "Unexpected HYPER_TASK_CLIENTCONN in server context\n") + + case hyper.TaskResponse: + fmt.Println("Response task received") + + case hyper.TaskBuf: + fmt.Println("Buffer task received") + + case hyper.TaskServerconn: + fmt.Println("Server connection task received: ready for new connection...") + default: + fmt.Fprintf(os.Stderr, "Unknown task type: %d\n", taskType) + } + + if taskUserdata == nil && taskType != hyper.TaskEmpty && taskType != hyper.TaskServerconn { + fmt.Fprintf(os.Stderr, "Warning: Task with no associated connection data. Type: %d\n", taskType) + } + + if !shouldExit { + task = exec.Poll() + } + + task.Free() + + } + + if shouldExit { + fmt.Println("Shutdown initiated, cleaning up...") + break + } + + // Handle any pending closures + loop.Run(libuv.RUN_NOWAIT) + } + + // Cleanup + fmt.Println("Closing all handles...") + loop.Walk(closeWalkCb, nil) + loop.Run(libuv.RUN_DEFAULT) + + loop.Close() + + fmt.Println("Shutdown complete.") + os.Exit(0) +} diff --git a/rust/hyper/_demo/hyper_server/server_legacy.go b/rust/hyper/_demo/hyper_server/server_legacy.go new file mode 100644 index 0000000..afcdd4f --- /dev/null +++ b/rust/hyper/_demo/hyper_server/server_legacy.go @@ -0,0 +1,598 @@ +package main + +import ( + "fmt" + "os" + "unsafe" + "sync/atomic" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" + "github.com/goplus/llgo/c/net" + cos "github.com/goplus/llgo/c/os" + "github.com/goplus/llgo/c/syscall" + + "github.com/goplus/llgoexamples/rust/hyper" +) + +const ( + MAX_EVENTS = 128 +) + +var ( + exec *hyper.Executor + http1Opts *hyper.Http1ServerconnOptions + http2Opts *hyper.Http2ServerconnOptions + loop *libuv.Loop + server libuv.Tcp + checkHandle libuv.Check + sigintHandle, sigtermHandle libuv.Signal + shouldExit atomic.Bool +) + +type ConnData struct { + Stream libuv.Tcp + PollHandle libuv.Poll + EventMask c.Uint + ReadWaker *hyper.Waker + WriteWaker *hyper.Waker + IsClosing atomic.Bool + ClosedHandles int32 +} + +type ServiceUserdata struct { + Host [128]c.Char + Port [8]c.Char + Executor *hyper.Executor +} + +func onSignal(handle *libuv.Signal, sigNum c.Int) { + fmt.Printf("Caught signal %d... exiting\n", sigNum) + shouldExit.Store(true) + sigintHandle.Stop() + sigtermHandle.Stop() + (*libuv.Handle)(unsafe.Pointer(&server)).Close(nil) + loop.Close() +} + +func closeWalkCb(handle *libuv.Handle, arg c.Pointer) { + if handle.IsClosing() == 0 { + handle.Close(nil) + } +} + + +func onPoll(handle *libuv.Poll, status c.Int, events c.Int) { + conn := (*ConnData)((*libuv.Handle)(unsafe.Pointer(handle)).GetData()) + + if status < 0 { + fmt.Fprintf(os.Stderr, "Poll error: %s\n", libuv.Strerror(libuv.Errno(status))) + return + } + + if events&c.Int(libuv.READABLE) != 0 && conn.ReadWaker != nil { + conn.ReadWaker.Wake() + conn.ReadWaker = nil + } + + if events&c.Int(libuv.WRITABLE) != 0 && conn.WriteWaker != nil { + conn.WriteWaker.Wake() + conn.WriteWaker = nil + } +} + +func updateConnDataRegistrations(conn *ConnData, create bool) bool { + events := c.Int(0) + if conn.EventMask&c.Uint(libuv.READABLE) != 0 { + events |= c.Int(libuv.READABLE) + } + if conn.EventMask&c.Uint(libuv.WRITABLE) != 0 { + events |= c.Int(libuv.WRITABLE) + } + + r := conn.PollHandle.Start(events, onPoll) + if r < 0 { + fmt.Fprintf(os.Stderr, "uv_poll_start error: %s\n", libuv.Strerror(libuv.Errno(r))) + return false + } + return true +} + +func readCb(userdata c.Pointer, ctx *hyper.Context, buf *byte, bufLen uintptr) uintptr { + conn := (*ConnData)(userdata) + ret := net.Recv(conn.Stream.GetIoWatcherFd(), unsafe.Pointer(buf), bufLen, 0) + + if ret >= 0 { + return uintptr(ret) + } + + if uintptr(cos.Errno) != syscall.EAGAIN && uintptr(cos.Errno) != syscall.EWOULDBLOCK { + return hyper.IoError + } + + if conn.ReadWaker != nil { + conn.ReadWaker.Free() + } + + if conn.EventMask&c.Uint(libuv.READABLE) == 0 { + conn.EventMask |= c.Uint(libuv.READABLE) + if !updateConnDataRegistrations(conn, false) { + return hyper.IoError + } + } + + conn.ReadWaker = ctx.Waker() + return hyper.IoPending +} + +func writeCb(userdata c.Pointer, ctx *hyper.Context, buf *byte, bufLen uintptr) uintptr { + conn := (*ConnData)(userdata) + ret := net.Send(conn.Stream.GetIoWatcherFd(), unsafe.Pointer(buf), bufLen, 0) + + if ret >= 0 { + return uintptr(ret) + } + + if uintptr(cos.Errno) != syscall.EAGAIN && uintptr(cos.Errno) != syscall.EWOULDBLOCK { + return hyper.IoError + } + + if conn.WriteWaker != nil { + conn.WriteWaker.Free() + } + + if conn.EventMask&c.Uint(libuv.WRITABLE) == 0 { + conn.EventMask |= c.Uint(libuv.WRITABLE) + if !updateConnDataRegistrations(conn, false) { + return hyper.IoError + } + } + + conn.WriteWaker = ctx.Waker() + return hyper.IoPending +} + +func createConnData() (*ConnData, error) { + conn := &ConnData{} + if conn == nil { + return nil, fmt.Errorf("failed to allocate conn_data") + } + conn.IsClosing.Store(false) + conn.ClosedHandles = 0 + + return conn, nil +} + +func freeConnData(userdata c.Pointer) { + conn := (*ConnData)(userdata) + if conn != nil && !conn.IsClosing.Swap(true){ + fmt.Printf("Closing connection...\n") + if conn.ReadWaker != nil { + conn.ReadWaker.Free() + conn.ReadWaker = nil + } + if conn.WriteWaker != nil { + conn.WriteWaker.Free() + conn.WriteWaker = nil + } + + if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() == 0 { + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(nil) + } + + if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() == 0 { + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(nil) + } + } +} + +func createIo(conn *ConnData) *hyper.Io { + io := hyper.NewIo() + io.SetUserdata(unsafe.Pointer(conn), freeConnData) + io.SetRead(readCb) + io.SetWrite(writeCb) + + return io +} + +func createServiceUserdata() *ServiceUserdata { + userdata := &ServiceUserdata{} + if userdata == nil { + fmt.Fprintf(os.Stderr, "Failed to allocate service_userdata\n") + } + return userdata +} + +func printEachHeader(userdata c.Pointer, name *byte, nameLen uintptr, value *byte, valueLen uintptr) c.Int { + fmt.Printf("%.*s: %.*s\n", int(nameLen), c.GoString((*c.Char)(c.Pointer(name))), + int(valueLen), c.GoString((*c.Char)(unsafe.Pointer(value)))) + return hyper.IterContinue +} + +func printBodyChunk(userdata c.Pointer, chunk *hyper.Buf) c.Int { + buf := chunk.Bytes() + len := chunk.Len() + cos.Write(1, unsafe.Pointer(buf), len) + + return hyper.IterContinue +} + +func sendEachBodyChunk(userdata c.Pointer, ctx *hyper.Context, chunk **hyper.Buf) c.Int { + chunkCount := (*c.Int)(userdata) + if *chunkCount > 0 { + var data [64]c.Char + c.Snprintf((*c.Char)(&data[0]), unsafe.Sizeof(data), c.Str("Chunk %d\n"), *chunkCount) + *chunk = hyper.CopyBuf((*byte)(unsafe.Pointer(&data[0])), c.Strlen((*c.Char)(&data[0]))) + *chunkCount-- + return hyper.PollReady + } else { + *chunk = nil + return hyper.PollReady + } +} + +func serverCallback(userdata c.Pointer, request *hyper.Request, channel *hyper.ResponseChannel) { + serviceData := (*ServiceUserdata)(userdata) + + fmt.Printf("Handling request on connection from %s:%s\n", + c.GoString((*c.Char)(&serviceData.Host[0])), c.GoString((*c.Char)(&serviceData.Port[0]))) + + if request == nil { + fmt.Fprintf(os.Stderr, "Error: Received null request\n") + return + } + + var scheme [64]byte + var authority [256]byte + var pathAndQuery [1024]byte + schemeLen := unsafe.Sizeof(scheme) + authorityLen := unsafe.Sizeof(authority) + pathAndQueryLen := unsafe.Sizeof(pathAndQuery) + + uriResult := request.URIParts(&scheme[0], &schemeLen, &authority[0], &authorityLen, &pathAndQuery[0], &pathAndQueryLen) + if uriResult == hyper.OK { + fmt.Printf("Scheme: %s\n", string(scheme[:schemeLen])) + fmt.Printf("Authority: %s\n", string(authority[:authorityLen])) + fmt.Printf("Path and Query: %s\n", string(pathAndQuery[:pathAndQueryLen])) + } else { + fmt.Fprintf(os.Stderr, "Failed to get URI parts. Error code: %d\n", uriResult) + } + + version := request.Version() + fmt.Printf("HTTP Version: ") + switch version { + case hyper.HTTPVersionNone: + fmt.Println("None") + case hyper.HTTPVersion10: + fmt.Println("HTTP/1.0") + case hyper.HTTPVersion11: + fmt.Println("HTTP/1.1") + case hyper.HTTPVersion2: + fmt.Println("HTTP/2") + default: + fmt.Printf("Unknown (%d)\n", version) + } + + var method [32]byte + methodLen := unsafe.Sizeof(method) + methodResult := request.Method(&method[0], &methodLen) + if methodResult == hyper.OK { + fmt.Printf("Method: %s\n", string(method[:methodLen])) + } else { + fmt.Fprintf(os.Stderr, "Failed to get request method. Error code: %d\n", methodResult) + } + + fmt.Println("Headers:") + reqHeaders := request.Headers() + if reqHeaders != nil { + reqHeaders.Foreach(printEachHeader, nil) + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to get request headers\n") + } + + if methodLen > 0 && (c.Strncmp((*c.Char)(unsafe.Pointer(&method[0])), c.Str("POST"), methodLen) == 0 || + c.Strncmp((*c.Char)(unsafe.Pointer(&method[0])), c.Str("PUT"), methodLen) == 0) { + fmt.Println("Request Body:") + body := request.Body() + if body != nil { + task := body.Foreach(printBodyChunk, nil, nil) + if task != nil { + r := serviceData.Executor.Push(task) + if r != hyper.OK { + fmt.Fprintf(os.Stderr, "Error: Failed to push body foreach task\n") + task.Free() + } + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to create body foreach task\n") + } + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to get request body\n") + } + } + + response := hyper.NewResponse() + if response != nil { + response.SetStatus(200) + rspHeaders := response.Headers() + if rspHeaders != nil { + hres := rspHeaders.Set((*byte)(unsafe.Pointer(c.Str("Content-Type"))), uintptr(12), (*byte)(unsafe.Pointer(c.Str("text/plain"))), uintptr(10)) + if hres != hyper.OK { + fmt.Fprintf(os.Stderr, "Error: Failed to set response headers\n") + } + hres = rspHeaders.Set((*byte)(unsafe.Pointer(c.Str("Cache-Control"))), uintptr(13), (*byte)(unsafe.Pointer(c.Str("no-cache"))), uintptr(8)) + if hres != hyper.OK { + fmt.Fprintf(os.Stderr, "Error: Failed to set response headers\n") + } + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to get response headers\n") + } + + if methodLen > 0 && c.Strncmp((*c.Char)(unsafe.Pointer(&method[0])), c.Str("GET"), methodLen) == 0 { + body := hyper.NewBody() + if body != nil { + body.SetDataFunc(sendEachBodyChunk) + chunkCount := (*c.Int)(c.Malloc(unsafe.Sizeof(c.Int(0)))) + if chunkCount != nil { + *chunkCount = 10 + body.SetUserdata(unsafe.Pointer(chunkCount), func(p c.Pointer) { c.Free(p) }) + response.SetBody(body) + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to allocate chunk_count\n") + } + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to create response body\n") + } + } + + channel.Send(response) + } else { + fmt.Fprintf(os.Stderr, "Error: Failed to create response\n") + } + + // We don't close the connection here. Let hyper handle keep-alive. + //free request + request.Free() +} + +func onNewConnection(serverStream *libuv.Stream, status c.Int) { + if status < 0 { + fmt.Fprintf(os.Stderr, "New connection error %s\n", libuv.Strerror(libuv.Errno(status))) + return + } + + conn, err := createConnData() + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create conn_data\n") + return + } + + libuv.InitTcp(loop, &conn.Stream) + conn.Stream.Data = unsafe.Pointer(conn) + + if serverStream.Accept((*libuv.Stream)(unsafe.Pointer(&conn.Stream))) == 0 { + r := libuv.PollInit(loop, &conn.PollHandle, libuv.OsFd(conn.Stream.GetIoWatcherFd())) + if r < 0 { + fmt.Fprintf(os.Stderr, "uv_poll_init error: %s\n", libuv.Strerror(libuv.Errno(r))) + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(nil) + return + } + + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Data = unsafe.Pointer(conn) + + if !updateConnDataRegistrations(conn, true) { + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(nil) + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(nil) + return + } + + userdata := createServiceUserdata() + if userdata == nil { + fmt.Fprintf(os.Stderr, "Failed to create service_userdata\n") + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(nil) + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(nil) + return + } + userdata.Executor = exec + + var addr net.SockaddrStorage + addrlen := c.Int(unsafe.Sizeof(addr)) + conn.Stream.Getpeername((*net.SockAddr)(c.Pointer(&addr)), &addrlen) + + if addr.Family == net.AF_INET { + s := (*net.SockaddrIn)(unsafe.Pointer(&addr)) + libuv.Ip4Name(s, (*c.Char)(&userdata.Host[0]), unsafe.Sizeof(userdata.Host)) + c.Snprintf((*c.Char)(&userdata.Port[0]), unsafe.Sizeof(userdata.Port), c.Str("%d"), net.Ntohs(s.Port)) + } else if addr.Family == net.AF_INET6 { + s := (*net.SockaddrIn6)(unsafe.Pointer(&addr)) + libuv.Ip6Name(s, (*c.Char)(&userdata.Host[0]), unsafe.Sizeof(userdata.Host)) + c.Snprintf((*c.Char)(&userdata.Port[0]), unsafe.Sizeof(userdata.Port), c.Str("%d"), net.Ntohs(s.Port)) + } + + io := createIo(conn) + + service := hyper.ServiceNew(serverCallback) + service.SetUserdata(unsafe.Pointer(userdata), nil) + + serverconn := hyper.ServeHttpXConnection(http1Opts, http2Opts, io, service) + userdata.Executor.Push(serverconn) + + } else { + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(nil) + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(nil) + } +} + +func onCheck(handle *libuv.Check) { + task := exec.Poll() + for task != nil { + taskType := task.Type() + if taskType == hyper.TaskError { + fmt.Println("hyper task failed with error!") + + err := (*hyper.Error)(task.Value()) + fmt.Printf("error code: %d\n", err.Code()) + + var errbuf [256]byte + errlen := err.Print(&errbuf[0], unsafe.Sizeof(errbuf)) + fmt.Printf("details: %s\n", errbuf[:errlen]) + + err.Free() + task.Free() + } else if taskType == hyper.TaskEmpty { + fmt.Println("internal hyper task complete") + task.Free() + } else if taskType == hyper.TaskServerconn { + fmt.Println("server connection task complete") + task.Free() + } + + task = exec.Poll() + } + + if shouldExit.Load() { + fmt.Println("Shutdown initiated, cleaning up...") + handle.Stop() + } +} + +func main() { + shouldExit.Store(false) + exec = hyper.NewExecutor() + if exec == nil { + fmt.Fprintf(os.Stderr, "Failed to create hyper executor\n") + os.Exit(1) + } + + http1Opts = hyper.Http1ServerconnOptionsNew(exec) + if http1Opts == nil { + fmt.Fprintf(os.Stderr, "Failed to create http1_opts\n") + os.Exit(1) + } + result := http1Opts.HeaderReadTimeout(5 * 1000) + if result != hyper.OK { + fmt.Fprintf(os.Stderr, "Failed to set header read timeout for http1_opts\n") + os.Exit(1) + } + + http2Opts = hyper.Http2ServerconnOptionsNew(exec) + if http2Opts == nil { + fmt.Fprintf(os.Stderr, "Failed to create http2_opts\n") + os.Exit(1) + } + result = http2Opts.KeepAliveInterval(5) + if result != hyper.OK { + fmt.Fprintf(os.Stderr, "Failed to set keep alive interval for http2_opts\n") + os.Exit(1) + } + result = http2Opts.KeepAliveTimeout(5) + if result != hyper.OK { + fmt.Fprintf(os.Stderr, "Failed to set keep alive timeout for http2_opts\n") + os.Exit(1) + } + + host := "127.0.0.1" + port := "1234" + if len(os.Args) > 1 { + host = os.Args[1] + } + if len(os.Args) > 2 { + port = os.Args[2] + } + fmt.Printf("listening on port %s on %s...\n", port, host) + + loop = libuv.DefaultLoop() + + r := libuv.InitTcp(loop, &server) + if r != 0 { + fmt.Fprintf(os.Stderr, "Failed to initialize TCP server: %d\n", r) + os.Exit(1) + } + + var addr net.SockaddrIn + r = libuv.Ip4Addr(c.AllocaCStr(host), c.Atoi(c.AllocaCStr(port)), &addr) + if r != 0 { + fmt.Fprintf(os.Stderr, "Failed to parse address: %d\n", r) + os.Exit(1) + } + + r = server.Bind((*net.SockAddr)(unsafe.Pointer(&addr)), 0) + if r != 0 { + fmt.Fprintf(os.Stderr, "Bind error %s\n", libuv.Strerror(libuv.Errno(r))) + os.Exit(1) + } + + // Set SO_REUSEADDR + yes := c.Int(1) + r = net.SetSockOpt(server.GetIoWatcherFd(), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, unsafe.Pointer(&yes), c.Uint(unsafe.Sizeof(yes))) + if r != 0 { + fmt.Fprintf(os.Stderr, "setsockopt error %s\n", libuv.Strerror(libuv.Errno(r))) + os.Exit(1) + } + + r = (*libuv.Stream)(&server).Listen(syscall.SOMAXCONN, onNewConnection) + if r != 0 { + fmt.Fprintf(os.Stderr, "Listen error %s\n", libuv.Strerror(libuv.Errno(r))) + os.Exit(1) + } + + r = libuv.SignalInit(loop, &sigintHandle) + if r != 0 { + fmt.Fprintf(os.Stderr, "Failed to initialize signal handler: %d\n", r) + os.Exit(1) + } + r = sigintHandle.Start(onSignal, c.Int(syscall.SIGINT)) + if r != 0 { + fmt.Fprintf(os.Stderr, "Failed to start signal handler: %d\n", r) + os.Exit(1) + } + + r = libuv.SignalInit(loop, &sigtermHandle) + if r != 0 { + fmt.Fprintf(os.Stderr, "Failed to initialize signal handler: %d\n", r) + os.Exit(1) + } + r = sigtermHandle.Start(onSignal, c.Int(syscall.SIGTERM)) + if r != 0 { + fmt.Fprintf(os.Stderr, "Failed to start signal handler: %d\n", r) + os.Exit(1) + } + + r = libuv.InitCheck(loop, &checkHandle) + if r != 0 { + fmt.Fprintf(os.Stderr, "Failed to initialize check handler: %d\n", r) + os.Exit(1) + } + + r = checkHandle.Start(onCheck) + if r != 0 { + fmt.Fprintf(os.Stderr, "Failed to start check handler: %d\n", r) + os.Exit(1) + } + + fmt.Printf("http handshake (hyper v%s) ...\n", c.GoString(hyper.Version())) + + r = loop.Run(libuv.RUN_DEFAULT) + if r != 0 { + fmt.Fprintf(os.Stderr, "Error in event loop: %v\n", r) + os.Exit(1) + } + + // Cleanup + fmt.Println("Closing all handles...") + loop.Walk(closeWalkCb, nil) + loop.Run(libuv.RUN_DEFAULT) + + loop.Close() + if exec != nil { + exec.Free() + } + if http1Opts != nil { + http1Opts.Free() + } + if http2Opts != nil { + http2Opts.Free() + } + + fmt.Println("Shutdown complete.") + os.Exit(0) +} \ No newline at end of file diff --git a/rust/hyper/hyper.go b/rust/hyper/hyper.go index 2b269cf..d487eb9 100644 --- a/rust/hyper/hyper.go +++ b/rust/hyper/hyper.go @@ -39,6 +39,7 @@ const ( AbortedByCallback FeatureNotEnabled InvalidPeerMessage + InsufficientSpace ) type TaskReturnType c.Int @@ -49,6 +50,7 @@ const ( TaskClientConn TaskResponse TaskBuf + TaskServerconn ) type ExampleId c.Int @@ -112,6 +114,22 @@ type Waker struct { Unused [0]byte } +type Http1ServerconnOptions struct { + Unused [0]byte +} + +type Http2ServerconnOptions struct { + Unused [0]byte +} + +type ResponseChannel struct { + Unused [0]byte +} + +type Service struct { + Unused [0]byte +} + // llgo:type C type BodyForeachCallback func(c.Pointer, *Buf) c.Int @@ -122,13 +140,19 @@ type BodyDataCallback func(c.Pointer, *Context, **Buf) c.Int type RequestOnInformationalCallback func(c.Pointer, *Response) // llgo:type C -type HeadersForeachCallback func(c.Pointer, *uint8, uintptr, *uint8, uintptr) c.Int +type HeadersForeachCallback func(c.Pointer, *byte, uintptr, *byte, uintptr) c.Int // llgo:type C -type IoReadCallback func(c.Pointer, *Context, *uint8, uintptr) uintptr +type IoReadCallback func(c.Pointer, *Context, *byte, uintptr) uintptr // llgo:type C -type IoWriteCallback func(c.Pointer, *Context, *uint8, uintptr) uintptr +type IoWriteCallback func(c.Pointer, *Context, *byte, uintptr) uintptr + +//llgo:type C +type UserdataDrop func(c.Pointer) + +//llgo:type C +type ServiceCallback func(c.Pointer, *Request, *ResponseChannel) // Returns a static ASCII (null terminated) string of the hyper_client version. // llgo:link Version C.hyper_version @@ -152,13 +176,13 @@ func (body *Body) Data() *Task { // Creates a task to execute the callback with each body chunk received. // llgo:link (*Body).Foreach C.hyper_body_foreach -func (body *Body) Foreach(callback BodyForeachCallback, userdata c.Pointer) *Task { +func (body *Body) Foreach(callback BodyForeachCallback, userdata c.Pointer, drop UserdataDrop) *Task { return nil } // Set userdata on this body, which will be passed to callback functions. // llgo:link (*Body).SetUserdata C.hyper_body_set_userdata -func (body *Body) SetUserdata(userdata c.Pointer) {} +func (body *Body) SetUserdata(userdata c.Pointer, drop UserdataDrop) {} // Set the outgoing data callback for this body. // llgo:link (*Body).SetDataFunc C.hyper_body_set_data_func @@ -166,13 +190,13 @@ func (body *Body) SetDataFunc(callback BodyDataCallback) {} // Create a new `hyper_buf *` by copying the provided bytes. // llgo:link CopyBuf C.hyper_buf_copy -func CopyBuf(buf *uint8, len uintptr) *Buf { +func CopyBuf(buf *byte, len uintptr) *Buf { return nil } // Get a pointer to the bytes in this buffer. // llgo:link (*Buf).Bytes C.hyper_buf_bytes -func (buf *Buf) Bytes() *uint8 { +func (buf *Buf) Bytes() *byte { return nil } @@ -249,7 +273,7 @@ func (err *Error) Code() Code { // Print the details of this error to a buffer. // llgo:link (*Error).Print C.hyper_error_print -func (err *Error) Print(dst *uint8, dstLen uintptr) uintptr { +func (err *Error) Print(dst *byte, dstLen uintptr) uintptr { return 0 } @@ -265,19 +289,19 @@ func (req *Request) Free() {} // Set the HTTP Method of the request. // llgo:link (*Request).SetMethod C.hyper_request_set_method -func (req *Request) SetMethod(method *uint8, methodLen uintptr) Code { +func (req *Request) SetMethod(method *byte, methodLen uintptr) Code { return 0 } // Set the URI of the request. // llgo:link (*Request).SetURI C.hyper_request_set_uri -func (req *Request) SetURI(uri *uint8, uriLen uintptr) Code { +func (req *Request) SetURI(uri *byte, uriLen uintptr) Code { return 0 } // Set the URI of the request with separate scheme, authority, and // llgo:link (*Request).SetURIParts C.hyper_request_set_uri_parts -func (req *Request) SetURIParts(scheme *uint8, schemeLen uintptr, authority *uint8, authorityLen uintptr, pathAndQuery *uint8, pathAndQueryLen uintptr) Code { +func (req *Request) SetURIParts(scheme *byte, schemeLen uintptr, authority *byte, authorityLen uintptr, pathAndQuery *byte, pathAndQueryLen uintptr) Code { return 0 } @@ -301,7 +325,44 @@ func (req *Request) SetBody(body *Body) Code { // Set an informational (1xx) response callback. // llgo:link (*Request).OnInformational C.hyper_request_on_informational -func (req *Request) OnInformational(callback RequestOnInformationalCallback, data c.Pointer) Code { +func (req *Request) OnInformational(callback RequestOnInformationalCallback, data c.Pointer, drop UserdataDrop) Code { + return 0 +} + +// Method get the HTTP Method of the request. +// llgo:link (*Request).Method C.hyper_request_method +func (req *Request) Method(method *byte, methodLen *uintptr) Code { + return 0 +} + +// URIParts get the URI of the request split into scheme, authority and path/query strings. +// llgo:link (*Request).URIParts C.hyper_request_uri_parts +func (req *Request) URIParts(scheme *byte, schemeLen *uintptr, authority *byte, authorityLen *uintptr, pathAndQuery *byte, pathAndQueryLen *uintptr) Code { + return 0 +} + +// Version set the preferred HTTP version of the request. +// llgo:link (*Request).Version C.hyper_request_version +func (req *Request) Version() HTTPVersion { + return 0 +} + +// Body take ownership of the body of this request. +// llgo:link (*Request).Body C.hyper_request_body +func (req *Request) Body() *Body { + return nil +} + +// New construct a new HTTP 200 Ok response +// +//go:linkname NewResponse C.hyper_response_new +func NewResponse() *Response { + return nil +} + +// SetBody set the body of the response. +// llgo:link (*Response).SetBody C.hyper_response_set_body +func (resp *Response) SetBody(body *Body) Code { return 0 } @@ -315,15 +376,20 @@ func (resp *Response) Status() uint16 { return 0 } +// SetStatus sets the HTTP Status-Code of this response. +// llgo:link (*Response).SetStatus C.hyper_response_set_status +func (resp *Response) SetStatus(status uint16) { +} + // Get a pointer to the reason-phrase of this response. // llgo:link (*Response).ReasonPhrase C.hyper_response_reason_phrase -func (resp *Response) ReasonPhrase() *uint8 { +func (resp *Response) ReasonPhrase() *byte { return nil } // Get the length of the reason-phrase of this response. // llgo:link (*Response).ReasonPhraseLen C.hyper_response_reason_phrase_len -func (resp *Response) ReasonPhraseLen() uintptr { +func (resp *Response) ReasonPhraseLen() byte { return 0 } @@ -351,13 +417,13 @@ func (headers *Headers) Foreach(callback HeadersForeachCallback, userdata c.Poin // Sets the header with the provided name to the provided value. // llgo:link (*Headers).Set C.hyper_headers_set -func (headers *Headers) Set(name *uint8, nameLen uintptr, value *uint8, valueLen uintptr) Code { +func (headers *Headers) Set(name *byte, nameLen uintptr, value *byte, valueLen uintptr) Code { return 0 } // Adds the provided value to the list of the provided name. // llgo:link (*Headers).Add C.hyper_headers_add -func (headers *Headers) Add(name *uint8, nameLen uintptr, value *uint8, valueLen uintptr) Code { +func (headers *Headers) Add(name *byte, nameLen uintptr, value *byte, valueLen uintptr) Code { return 0 } @@ -373,7 +439,7 @@ func (io *Io) Free() {} // Set the user data pointer for this IO to some value. // llgo:link (*Io).SetUserdata C.hyper_io_set_userdata -func (io *Io) SetUserdata(data c.Pointer) {} +func (io *Io) SetUserdata(data c.Pointer, drop UserdataDrop) {} // Set the read function for this IO transport. // llgo:link (*Io).SetRead C.hyper_io_set_read @@ -383,6 +449,12 @@ func (io *Io) SetRead(callback IoReadCallback) {} // llgo:link (*Io).SetWrite C.hyper_io_set_write func (io *Io) SetWrite(callback IoWriteCallback) {} +// GetUserdata set the user data pointer for this IO to some value. +// llgo:link (*Io).GetUserdata C.hyper_io_get_userdata +func (io *Io) GetUserdata() c.Pointer { + return nil +} + // Creates a new task executor. // llgo:link NewExecutor C.hyper_executor_new func NewExecutor() *Executor { @@ -405,6 +477,12 @@ func (exec *Executor) Poll() *Task { return nil } +// NextTimerPop returns the time until the executor will be able to make progress on tasks due to internal timers. +// llgo:link (*Executor).NextTimerPop C.hyper_executor_next_timer_pop +func (exec *Executor) NextTimerPop() c.Int { + return 0 +} + // Free a task. // llgo:link (*Task).Free C.hyper_task_free func (task *Task) Free() {} @@ -423,7 +501,7 @@ func (task *Task) Type() TaskReturnType { // Set a user data pointer to be associated with this task. // llgo:link (*Task).SetUserdata C.hyper_task_set_userdata -func (task *Task) SetUserdata(userdata c.Pointer) {} +func (task *Task) SetUserdata(userdata c.Pointer, drop UserdataDrop) {} // Retrieve the userdata that has been set via `hyper_task_set_userdata`. // llgo:link (*Task).Userdata C.hyper_task_userdata @@ -444,3 +522,169 @@ func (waker *Waker) Free() {} // Wake up the task associated with a waker. // llgo:link (*Waker).Wake C.hyper_waker_wake func (waker *Waker) Wake() {} + +// New create a new HTTP/1 serverconn options object. +// +//go:linkname Http1ServerconnOptionsNew C.hyper_http1_serverconn_options_new +func Http1ServerconnOptionsNew(executor *Executor) *Http1ServerconnOptions { + return nil +} + +// Free a `Http1ServerconnOptions`. +// llgo:link (*Http1ServerconnOptions).Free C.hyper_http1_serverconn_options_free +func (opts *Http1ServerconnOptions) Free() {} + +// HalfClose set whether HTTP/1 connections should support half-closures. +// llgo:link (*Http1ServerconnOptions).HalfClose C.hyper_http1_serverconn_options_half_close +func (opts *Http1ServerconnOptions) HalfClose(enabled bool) Code { + return 0 +} + +// KeepAlive enables or disables HTTP/1 keep-alive. +// llgo:link (*Http1ServerconnOptions).KeepAlive C.hyper_http1_serverconn_options_keep_alive +func (opts *Http1ServerconnOptions) KeepAlive(enabled bool) Code { + return 0 +} + +// TitleCaseHeaders set whether HTTP/1 connections will write header names as title case at the socket level. +// llgo:link (*Http1ServerconnOptions).TitleCaseHeaders C.hyper_http1_serverconn_options_title_case_headers +func (opts *Http1ServerconnOptions) TitleCaseHeaders(enabled bool) Code { + return 0 +} + +// HeaderReadTimeout sets a timeout for reading client request headers. +// If a client does not send a complete set of headers within this time, the connection will be closed. +// llgo:link (*Http1ServerconnOptions).HeaderReadTimeout C.hyper_http1_serverconn_options_header_read_timeout +func (opts *Http1ServerconnOptions) HeaderReadTimeout(millis c.UlongLong) Code { + return 0 +} + +// Writev sets whether HTTP/1 connections should try to use vectored writes, or always flatten into a single buffer. +// llgo:link (*Http1ServerconnOptions).Writev C.hyper_http1_serverconn_options_writev +func (opts *Http1ServerconnOptions) Writev(enabled bool) Code { + return 0 +} + +// MaxBufSize sets the maximum buffer size for the HTTP/1 connection. Must be no lower than 8192. +// llgo:link (*Http1ServerconnOptions).MaxBufSize C.hyper_http1_serverconn_options_max_buf_size +func (opts *Http1ServerconnOptions) MaxBufSize(maxBufSize c.Ulong) Code { + return 0 +} + +// PipelineFlush aggregates flushes to better support pipelined responses. +// llgo:link (*Http1ServerconnOptions).PipelineFlush C.hyper_http1_serverconn_options_pipeline_flush +func (opts *Http1ServerconnOptions) PipelineFlush(enabled bool) Code { + return 0 +} + +// New creates a new HTTP/2 serverconn options object bound to the provided executor. +// +//go:linkname Http2ServerconnOptionsNew C.hyper_http2_serverconn_options_new +func Http2ServerconnOptionsNew(exec *Executor) *Http2ServerconnOptions { + return nil +} + +// Free releases resources associated with the Http2ServerconnOptions. +// llgo:link (*Http2ServerconnOptions).Free C.hyper_http2_serverconn_options_free +func (opts *Http2ServerconnOptions) Free() {} + +// InitialStreamWindowSize sets the `SETTINGS_INITIAL_WINDOW_SIZE` option for HTTP/2 stream-level flow control. +// llgo:link (*Http2ServerconnOptions).InitialStreamWindowSize C.hyper_http2_serverconn_options_initial_stream_window_size +func (opts *Http2ServerconnOptions) InitialStreamWindowSize(windowSize c.Uint) Code { + return 0 +} + +// InitialConnectionWindowSize sets the max connection-level flow control for HTTP/2. +// llgo:link (*Http2ServerconnOptions).InitialConnectionWindowSize C.hyper_http2_serverconn_options_initial_connection_window_size +func (opts *Http2ServerconnOptions) InitialConnectionWindowSize(windowSize c.Uint) Code { + return 0 +} + +// AdaptiveWindow sets whether to use an adaptive flow control. +// llgo:link (*Http2ServerconnOptions).AdaptiveWindow C.hyper_http2_serverconn_options_adaptive_window +func (opts *Http2ServerconnOptions) AdaptiveWindow(enabled bool) Code { + return 0 +} + +// MaxFrameSize sets the maximum frame size to use for HTTP/2. +// llgo:link (*Http2ServerconnOptions).MaxFrameSize C.hyper_http2_serverconn_options_max_frame_size +func (opts *Http2ServerconnOptions) MaxFrameSize(frameSize c.Uint) Code { + return 0 +} + +// MaxConcurrentStreams sets the `SETTINGS_MAX_CONCURRENT_STREAMS` option for HTTP/2 connections. +// llgo:link (*Http2ServerconnOptions).MaxConcurrentStreams C.hyper_http2_serverconn_options_max_concurrent_streams +func (opts *Http2ServerconnOptions) MaxConcurrentStreams(maxStreams c.Uint) Code { + return 0 +} + +// KeepAliveInterval sets an interval for HTTP/2 Ping frames should be sent to keep a connection alive. +// llgo:link (*Http2ServerconnOptions).KeepAliveInterval C.hyper_http2_serverconn_options_keep_alive_interval +func (opts *Http2ServerconnOptions) KeepAliveInterval(intervalSeconds c.UlongLong) Code { + return 0 +} + +// KeepAliveTimeout sets a timeout for receiving an acknowledgement of the keep-alive ping. +// llgo:link (*Http2ServerconnOptions).KeepAliveTimeout C.hyper_http2_serverconn_options_keep_alive_timeout +func (opts *Http2ServerconnOptions) KeepAliveTimeout(timeoutSeconds c.UlongLong) Code { + return 0 +} + +// MaxSendBufSize sets the maximum write buffer size for each HTTP/2 stream. +// llgo:link (*Http2ServerconnOptions).MaxSendBufSize C.hyper_http2_serverconn_options_max_send_buf_size +func (opts *Http2ServerconnOptions) MaxSendBufSize(maxBufSize c.Ulong) Code { + return 0 +} + +// EnableConnectProtocol enables the extended `CONNECT` protocol. +// llgo:link (*Http2ServerconnOptions).EnableConnectProtocol C.hyper_http2_serverconn_options_enable_connect_protocol +func (opts *Http2ServerconnOptions) EnableConnectProtocol() Code { + return 0 +} + +// MaxHeaderListSize sets the max size of received header frames. +// llgo:link (*Http2ServerconnOptions).MaxHeaderListSize C.hyper_http2_serverconn_options_max_header_list_size +func (opts *Http2ServerconnOptions) MaxHeaderListSize(max c.Uint) Code { + return 0 +} + +// New creates a service from a wrapped callback function. +// +//go:linkname ServiceNew C.hyper_service_new +func ServiceNew(serviceFn ServiceCallback) *Service { + return nil +} + +// SetUserdata registers opaque userdata with the Service. +// This userdata must be Send in a rust +// llgo:link (*Service).SetUserdata C.hyper_service_set_userdata +func (s *Service) SetUserdata(userdata c.Pointer, drop UserdataDrop) { +} + +// Free frees a Service object if no longer needed +// llgo:link (*Service).Free C.hyper_service_free +func (s *Service) Free() { +} + +// ServeHttp1Connection serves the provided Service as an HTTP/1 endpoint over the provided IO +// llgo:link ServeHttp1Connection C.hyper_serve_http1_connection +func ServeHttp1Connection(serverconnOptions *Http1ServerconnOptions, io *Io, service *Service) *Task { + return nil +} + +// ServeHttp2Connection serves the provided Service as an HTTP/2 endpoint over the provided IO +// llgo:link ServeHttp2Connection C.hyper_serve_http2_connection +func ServeHttp2Connection(serverconnOptions *Http2ServerconnOptions, io *Io, service *Service) *Task { + return nil +} + +// ServeHttpXConnection serves the provided Service as either an HTTP/1 or HTTP/2 (depending on what the client supports) endpoint over the provided IO +// llgo:link ServeHttpXConnection C.hyper_serve_httpX_connection +func ServeHttpXConnection(http1ServerconnOptions *Http1ServerconnOptions, http2ServerconnOptions *Http2ServerconnOptions, io *Io, service *Service) *Task { + return nil +} + +// Send sends a Response back to the client. This function consumes the response and the channel. +// llgo:link (*ResponseChannel).Send C.hyper_response_channel_send +func (rc *ResponseChannel) Send(response *Response) { +}