From cd929b433a6ee5f4a16b4cb3f886eca7f5bd4bf0 Mon Sep 17 00:00:00 2001 From: hackerchai Date: Mon, 5 Aug 2024 18:05:10 +0800 Subject: [PATCH 1/7] refactor(rust/hyper): Add hyper server funcs and structs --- rust/hyper/hyper.go | 280 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 262 insertions(+), 18 deletions(-) diff --git a/rust/hyper/hyper.go b/rust/hyper/hyper.go index 2b269cf..334165b 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 c.Ulong) 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 c.Ulong, authority *byte, authorityLen c.Ulong, pathAndQuery *byte, pathAndQueryLen c.Ulong) Code { + return 0 +} + +// Version set the preferred HTTP version of the request. +// llgo:link (*Request).Version C.hyper_request_version +func (req *Request) Version() c.Int { + 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) { +} From 1307a6f2e1e5f2ca5e5ac63b2fadfba64e6a4626 Mon Sep 17 00:00:00 2001 From: hackerchai Date: Mon, 5 Aug 2024 18:05:51 +0800 Subject: [PATCH 2/7] feat(rust/hyper/demo): Add hyper server demo prototype refactor(rust/hyper): Update server demo due to llgo changes fix(rust/hyper): Fix some errs Signed-off-by: hackerchai fix(rust/hyper/demo): Fix errors in server demo Signed-off-by: hackerchai --- rust/hyper/_demo/hyper_server/server.go | 725 ++++++++++++++++++++++++ rust/hyper/hyper.go | 6 +- 2 files changed, 728 insertions(+), 3 deletions(-) create mode 100644 rust/hyper/_demo/hyper_server/server.go diff --git a/rust/hyper/_demo/hyper_server/server.go b/rust/hyper/_demo/hyper_server/server.go new file mode 100644 index 0000000..9a26237 --- /dev/null +++ b/rust/hyper/_demo/hyper_server/server.go @@ -0,0 +1,725 @@ +package main + +import ( + "fmt" + sysos "os" + "unsafe" + + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/c/libuv" + "github.com/goplus/llgo/c/net" + "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 + loop *libuv.Loop + server libuv.Tcp + sigintHandle, sigtermHandle libuv.Signal + shouldExit = false +) + +type ConnData struct { + Stream libuv.Tcp + PollHandle libuv.Poll + EventMask c.Uint + ReadWaker *hyper.Waker + WriteWaker *hyper.Waker + ConnTask *hyper.Task + IsClosing c.Int + RequestCount c.Int +} + +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) + c.Printf(c.Str("Caught signal %d... exiting\n"), signum) + shouldExit = true + sigintHandle.Stop() + sigtermHandle.Stop() + (*libuv.Handle)(unsafe.Pointer(handle)).Close(nil) + loop.Close() +} + +func closeWalkCb(handle *libuv.Handle, arg c.Pointer) { + // if handle.IsClosing() == 0 { + // handle.Close(nil) + // } + handle.Close(nil) +} + +func allocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { + buf.Base = (*c.Char)(c.Malloc(suggestedSize)) + buf.Len = suggestedSize +} + +func onClose(handle *libuv.Handle) { + c.Free(unsafe.Pointer(handle)) +} + +func closeConn(handle *libuv.Handle) { + conn := (*ConnData)(handle.GetData()) + if conn != nil { + fmt.Printf("Closing connection after handling %d requests\n", conn.RequestCount) + 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 + } + c.Free(unsafe.Pointer(conn)) + } + c.Free(unsafe.Pointer(handle)) +} + +func onPoll(handle *libuv.Poll, status c.Int, events c.Int) { + conn := (*ConnData)(unsafe.Pointer((*libuv.Handle)(unsafe.Pointer(handle)).GetData())) + + if status < 0 { + //fmt.Fprintf(os.Stderr, "Poll error: %s\n", libuv.Strerror(libuv.Errno(status))) + c.Printf(c.Str("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))) + c.Printf(c.Str("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 syscall.Errno(ret) != syscall.EAGAIN && syscall.Errno(ret) != 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 syscall.Errno(ret) != syscall.EAGAIN && syscall.Errno(ret) != 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(client *libuv.Tcp) *ConnData { + conn := (*ConnData)(c.Calloc(1, unsafe.Sizeof(ConnData{}))) + if conn == nil { + //fmt.Fprintf(os.Stderr, "Failed to allocate conn_data\n") + c.Printf(c.Str("Failed to malloc conn_data mem\n")) + return nil + } + c.Memcpy(unsafe.Pointer(&conn.Stream), unsafe.Pointer(client), unsafe.Sizeof(libuv.Tcp{})) + //c.Memmove(unsafe.Pointer(&conn.Stream), unsafe.Pointer(client), unsafe.Sizeof(libuv.Tcp{})) + conn.IsClosing = 0 + conn.RequestCount = 0 + + r := libuv.PollInit(loop, &conn.PollHandle, libuv.OsFd(client.GetIoWatcherFd())) + if r < 0 { + //fmt.Fprintf(os.Stderr, "uv_poll_init error: %s\n", libuv.Strerror(libuv.Errno(r))) + c.Printf(c.Str("uv_poll_init error: %s\n"), libuv.Strerror(libuv.Errno(r))) + c.Free(unsafe.Pointer(conn)) + return nil + } + + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).SetData(unsafe.Pointer(conn)) + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).SetData(unsafe.Pointer(conn)) + + if !updateConnDataRegistrations(conn, true) { + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(nil) + c.Free(unsafe.Pointer(conn)) + return nil + } + + return conn +} + +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)(c.Calloc(1, unsafe.Sizeof(ServiceUserdata{}))) + if userdata == nil { + //fmt.Fprintf(os.Stderr, "Failed to allocate service_userdata\n") + c.Printf(c.Str("Failed to allocate service_userdata\n")) + } + return userdata +} + +func freeServiceUserdata(userdata c.Pointer) { + castUserdata := (*ServiceUserdata)(userdata) + if castUserdata != nil { + // Note: We don't free conn here because it's managed separately + c.Free(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)))) + c.Printf(c.Str("%.*s: %.*s\n"), nameLen, (*c.Char)(unsafe.Pointer(name)), valueLen, (*c.Char)(unsafe.Pointer(value))) + return hyper.IterContinue +} + +func printBodyChunk(userdata c.Pointer, chunk *hyper.Buf) c.Int { + buf := chunk.Bytes() + len := chunk.Len() + os.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 { + c.Printf(c.Str("Chunk %d\n"), *chunkCount) + 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") + c.Printf(c.Str("Error: No connection data available\n")) + return + } + + conn.RequestCount++ + // fmt.Printf("Handling request %d on connection from %s:%s\n", conn.RequestCount, + // c.GoString((*c.Char)(&serviceData.Host[0])), c.GoString((*c.Char)(&serviceData.Port[0]))) + c.Printf(c.Str("Handling request %d on connection from %s:%s\n"), c.Int(conn.RequestCount), + (*c.Char)(&serviceData.Host[0]), (*c.Char)(&serviceData.Port[0])) + + // fmt.Printf("Received request from %s:%s\n", c.GoString((*c.Char)(&serviceData.Host[0])), + // c.GoString((*c.Char)(&serviceData.Port[0]))) + c.Printf(c.Str("Received request from %s:%s\n"), (*c.Char)(&serviceData.Host[0]), (*c.Char)(&serviceData.Port[0])) + + if request == nil { + //fmt.Fprintf(os.Stderr, "Error: Received null request\n") + c.Printf(c.Str("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])) + c.Printf(c.Str("Scheme: %.*s\n"), c.Int(schemeLen), (*c.Char)(unsafe.Pointer(&scheme[0]))) + //fmt.Printf("Authority: %s\n", string(authority[:authorityLen])) + c.Printf(c.Str("Authority: %.*s\n"), c.Int(authorityLen), (*c.Char)(unsafe.Pointer(&authority[0]))) + //fmt.Printf("Path and Query: %s\n", string(pathAndQuery[:pathAndQueryLen])) + c.Printf(c.Str("Path and Query: %.*s\n"), c.Int(pathAndQueryLen), (*c.Char)(unsafe.Pointer(&pathAndQuery[0]))) + } else { + //fmt.Fprintf(os.Stderr, "Failed to get URI parts. Error code: %d\n", uriResult) + c.Printf(c.Str("Failed to get URI parts. Error code: %d\n"), uriResult) + } + + version := request.Version() + //fmt.Printf("HTTP Version: ") + c.Printf(c.Str("HTTP Version: ")) + switch version { + case hyper.HTTPVersionNone: + //fmt.Println("None") + c.Printf(c.Str("None\n")) + case hyper.HTTPVersion10: + //fmt.Println("HTTP/1.0") + c.Printf(c.Str("HTTP/1.0\n")) + case hyper.HTTPVersion11: + //fmt.Println("HTTP/1.1") + c.Printf(c.Str("HTTP/1.1\n")) + case hyper.HTTPVersion2: + //fmt.Println("HTTP/2") + c.Printf(c.Str("HTTP/2\n")) + default: + //fmt.Printf("Unknown (%d)\n", version) + c.Printf(c.Str("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])) + c.Printf(c.Str("Method: %.*s\n"), c.Int(methodLen), (*c.Char)(unsafe.Pointer(&method[0]))) + } else { + //fmt.Fprintf(os.Stderr, "Failed to get request method. Error code: %d\n", methodResult) + c.Printf(c.Str("Failed to get request method. Error code: %d\n"), methodResult) + } + + //fmt.Println("Headers:") + c.Printf(c.Str("Headers:\n")) + reqHeaders := request.Headers() + if reqHeaders != nil { + reqHeaders.Foreach(printEachHeader, nil) + } else { + //fmt.Fprintf(os.Stderr, "Error: Failed to get request headers\n") + c.Printf(c.Str("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:") + c.Printf(c.Str("Request Body:\n")) + 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") + c.Printf(c.Str("Error: Failed to push body foreach task: %d\n"), r) + task.Free() + } + } else { + //fmt.Fprintf(os.Stderr, "Error: Failed to create body foreach task\n") + c.Printf(c.Str("Error: Failed to create body foreach task\n")) + } + } else { + //fmt.Fprintf(os.Stderr, "Error: Failed to get request body\n") + c.Printf(c.Str("Error: Failed to get request body\n")) + } + } + + response := hyper.NewResponse() + // if response != nil { + // response.SetStatus(200) + // rspHeaders := response.Headers() + // if rspHeaders != nil { + // rspHeaders.Set((*byte)(unsafe.Pointer(c.Str("Content-Type"))), uintptr(12), (*byte)(unsafe.Pointer(c.Str("text/plain"))), uintptr(10)) + // rspHeaders.Set((*byte)(unsafe.Pointer(c.Str("Cache-Control"))), uintptr(13), (*byte)(unsafe.Pointer(c.Str("no-cache"))), uintptr(8)) + // } else { + // //fmt.Fprintf(os.Stderr, "Error: Failed to get response headers\n") + // c.Printf(c.Str("Error: Failed to get response headers\n")) + // } + + // if methodLen > 0 && c.Strncmp((*c.Char)(unsafe.Pointer(&method[0])), c.Str("GET"), methodLen) == 0 { + // c.Printf(c.Str("Sending GET response\n")) + // 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") + // c.Printf(c.Str("Error: Failed to allocate chunk_count\n")) + // } + // } else { + // //fmt.Fprintf(os.Stderr, "Error: Failed to create response body\n") + // c.Printf(c.Str("Error: Failed to create response body\n")) + // } + // } + + // channel.Send(response) + // } else { + // //fmt.Fprintf(os.Stderr, "Error: Failed to create response\n") + // c.Printf(c.Str("Error: Failed to create response\n")) + // } + if response != nil { + response.SetStatus(200) + channel.Send(response) + } else { + c.Printf(c.Str("Error: Failed to create response\n")) + } + + // We don't close the connection here. Let hyper handle keep-alive. +} + +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))) + c.Printf(c.Str("New connection error %s\n"), libuv.Strerror(libuv.Errno(status))) + return + } + + client := (*libuv.Tcp)(c.Malloc(unsafe.Sizeof(libuv.Tcp{}))) + libuv.InitTcp(loop, client) + + if serverStream.Accept((*libuv.Stream)(unsafe.Pointer(client))) == 0 { + userdata := createServiceUserdata() + if userdata == nil { + //fmt.Fprintf(os.Stderr, "Failed to create service_userdata\n") + c.Printf(c.Str("Failed to create service_userdata\n")) + (*libuv.Handle)(unsafe.Pointer(client)).Close(onClose) + return + } + userdata.Executor = exec + + var addr net.SockaddrStorage + addrlen := c.Int(unsafe.Sizeof(addr)) + client.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"), 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"), Ntohs(s.Port)) + } + + // fmt.Printf("New incoming connection from (%s:%s)\n", c.GoString((*c.Char)(&userdata.Host[0])), + // c.GoString((*c.Char)(&userdata.Port[0]))) + c.Printf(c.Str("New incoming connection from (%s:%s)\n"), (*c.Char)(&userdata.Host[0]), + (*c.Char)(&userdata.Port[0])) + + conn := createConnData(client) + if conn == nil { + //fmt.Fprintf(os.Stderr, "Failed to create conn_data\n") + c.Printf(c.Str("Failed to create conn_data\n")) + (*libuv.Handle)(unsafe.Pointer(client)).Close(onClose) + freeServiceUserdata(unsafe.Pointer(userdata)) + return + } + + userdata.Conn = conn + + io := createIo(conn) + + service := hyper.ServiceNew(serverCallback) + service.SetUserdata(unsafe.Pointer(userdata), freeServiceUserdata) + + http1Opts := hyper.Http1ServerconnOptionsNew(userdata.Executor) + http1Opts.HeaderReadTimeout(1000 * 5) + + 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(client)).Close(onClose) + } +} + +func main() { + exec = hyper.NewExecutor() + if exec == nil { + //fmt.Fprintf(os.Stderr, "Failed to create hyper executor\n") + c.Printf(c.Str("Failed to create hyper executor\n")) + os.Exit(1) + } + + host := "127.0.0.1" + port := "1234" + if len(sysos.Args) > 1 { + host = sysos.Args[1] + } + if len(sysos.Args) > 2 { + port = sysos.Args[2] + } + //fmt.Printf("listening on port %s on %s...\n", port, host) + c.Printf(c.Str("listening on port %s on %s...\n"), c.AllocaCStr(port), c.AllocaCStr(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))) + c.Printf(c.Str("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))) + c.Printf(c.Str("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))) + c.Printf(c.Str("Listen error %s\n"), libuv.Strerror(libuv.Errno(r))) + os.Exit(1) + } + + libuv.SignalInit(loop, &sigintHandle) + sigintHandle.Start(onSignal, c.Int(syscall.SIGINT)) + + libuv.SignalInit(loop, &sigtermHandle) + sigtermHandle.Start(onSignal, c.Int(syscall.SIGTERM)) + + //fmt.Printf("http handshake (hyper v%s) ...\n", c.GoString(hyper.Version())) + c.Printf(c.Str("http handshake (hyper v%s) ...\n"), 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") + c.Printf(c.Str("\nEmpty task received: connection closed\n")) + if taskUserdata != nil { + conn := (*ConnData)(taskUserdata) + //fmt.Printf("Connection task completed for request %d\n", conn.RequestCount) + c.Printf(c.Str("Connection task completed for request %d\n"), conn.RequestCount) + c.Printf(c.Str("IsClosing: %d\n"), c.Int(conn.IsClosing)) + // if conn.IsClosing == 0 { + // c.Printf(c.Str("Closing connection\n")) + // conn.IsClosing = 1 + // if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() != 0 { + // c.Printf(c.Str("Closing poll handle\n")) + // (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(onClose) + // } + // if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() != 0 { + // c.Printf(c.Str("Closing stream\n")) + // (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) + // } + // } + if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() == 0 { + c.Printf(c.Str("Closing poll handle\n")) + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(onClose) + } + if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() == 0 { + c.Printf(c.Str("Closing stream\n")) + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) + } + } + conn := (*ConnData)(taskUserdata) + if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() == 0 { + c.Printf(c.Str("Closing poll handle\n")) + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(onClose) + } + if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() == 0 { + c.Printf(c.Str("Closing stream\n")) + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) + } + break + + case hyper.TaskError: + err := (*hyper.Error)(task.Value()) + var errbuf [256]byte + errlen := err.Print(&errbuf[0], unsafe.Sizeof(errbuf)) + //fmt.Fprintf(os.Stderr, "Task error: %.*s\n", int(errlen), c.GoString((*c.Char)(unsafe.Pointer(&errbuf[0])))) + c.Printf(c.Str("Task error: %.*s\n"), errlen, (*c.Char)(unsafe.Pointer(&errbuf[0]))) + err.Free() + conn := (*ConnData)(taskUserdata) + c.Printf(c.Str("IsClosing: %d\n"), c.Int(conn.IsClosing)) + // if conn.IsClosing == 0 { + // c.Printf(c.Str("Closing connection\n")) + // conn.IsClosing = 1 + // if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() != 0 { + // c.Printf(c.Str("Closing poll handle\n")) + // (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(onClose) + // } + // if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() != 0 { + // c.Printf(c.Str("Closing stream\n")) + // (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) + // } + // } + if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() == 0 { + c.Printf(c.Str("Closing poll handle\n")) + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(onClose) + } + if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() == 0 { + c.Printf(c.Str("Closing stream\n")) + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) + } + break + + case hyper.TaskClientConn: + //fmt.Fprintf(os.Stderr, "Unexpected HYPER_TASK_CLIENTCONN in server context\n") + c.Printf(c.Str("Unexpected HYPER_TASK_CLIENTCONN in server context\n")) + break + + case hyper.TaskResponse: + //fmt.Println("Response task received") + c.Printf(c.Str("Response task received\n")) + break + + case hyper.TaskBuf: + //fmt.Println("Buffer task received") + c.Printf(c.Str("Buffer task received\n")) + break + + case hyper.TaskServerconn: + //fmt.Println("Server connection task received: ready for new connection...") + c.Printf(c.Str("Server connection task received: ready for new connection...\n")) + break + + default: + //fmt.Fprintf(os.Stderr, "Unknown task type: %d\n", taskType) + c.Printf(c.Str("Unknown task type: %d\n"), taskType) + break + } + + if taskUserdata == nil && taskType != hyper.TaskEmpty && taskType != hyper.TaskServerconn { + //fmt.Fprintf(os.Stderr, "Warning: Task with no associated connection data. Type: %d\n", taskType) + c.Printf(c.Str("Warning: Task with no associated connection data. Type: %d\n"), taskType) + } + + task.Free() + if !shouldExit { + task = exec.Poll() + } + } + + if shouldExit { + //fmt.Println("Shutdown initiated, cleaning up...") + c.Printf(c.Str("Shutdown initiated, cleaning up...\n")) + break + } + + // Handle any pending closures + loop.Run(libuv.RUN_NOWAIT) + } + + // Cleanup + //fmt.Println("Closing all handles...") + c.Printf(c.Str("Closing all handles...\n")) + loop.Walk(closeWalkCb, nil) + + loop.Run(libuv.RUN_DEFAULT) + + loop.Close() + exec.Free() + + //fmt.Println("Shutdown complete.") + c.Printf(c.Str("Shutdown complete.\n")) +} + +// Ntohs converts a 16-bit integer from network byte order to host byte order. +func Ntohs(x uint16) uint16 { + if isLittleEndian() { + return ((x & 0xFF00) >> 8) | ((x & 0x00FF) << 8) + } + return x +} + +// isLittleEndian checks if the host machine is little-endian. +func isLittleEndian() bool { + var i int32 = 0x01020304 + return *(*byte)(unsafe.Pointer(&i)) == 0x04 +} diff --git a/rust/hyper/hyper.go b/rust/hyper/hyper.go index 334165b..d487eb9 100644 --- a/rust/hyper/hyper.go +++ b/rust/hyper/hyper.go @@ -331,19 +331,19 @@ func (req *Request) OnInformational(callback RequestOnInformationalCallback, dat // Method get the HTTP Method of the request. // llgo:link (*Request).Method C.hyper_request_method -func (req *Request) Method(method *byte, methodLen c.Ulong) Code { +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 c.Ulong, authority *byte, authorityLen c.Ulong, pathAndQuery *byte, pathAndQueryLen c.Ulong) Code { +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() c.Int { +func (req *Request) Version() HTTPVersion { return 0 } From ff826464ef00c47194d1a135017c41173cb9f6f2 Mon Sep 17 00:00:00 2001 From: hackerchai Date: Mon, 12 Aug 2024 18:02:07 +0800 Subject: [PATCH 3/7] fix(rust/hyper/demo): Fix struct mem bug & hyper response task error Signed-off-by: hackerchai fix(rust/hyper/demo): Fix hyper response task error Signed-off-by: hackerchai --- rust/hyper/_demo/hyper_server/server.go | 214 +++++++++--------------- 1 file changed, 82 insertions(+), 132 deletions(-) diff --git a/rust/hyper/_demo/hyper_server/server.go b/rust/hyper/_demo/hyper_server/server.go index 9a26237..102303e 100644 --- a/rust/hyper/_demo/hyper_server/server.go +++ b/rust/hyper/_demo/hyper_server/server.go @@ -49,15 +49,14 @@ func onSignal(handle *libuv.Signal, signum c.Int) { shouldExit = true sigintHandle.Stop() sigtermHandle.Stop() - (*libuv.Handle)(unsafe.Pointer(handle)).Close(nil) + (*libuv.Handle)(unsafe.Pointer(&server)).Close(nil) loop.Close() } func closeWalkCb(handle *libuv.Handle, arg c.Pointer) { - // if handle.IsClosing() == 0 { - // handle.Close(nil) - // } - handle.Close(nil) + if handle.IsClosing() == 0 { + handle.Close(nil) + } } func allocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { @@ -91,7 +90,7 @@ func closeConn(handle *libuv.Handle) { } func onPoll(handle *libuv.Poll, status c.Int, events c.Int) { - conn := (*ConnData)(unsafe.Pointer((*libuv.Handle)(unsafe.Pointer(handle)).GetData())) + conn := (*ConnData)((*libuv.Handle)(unsafe.Pointer(handle)).GetData()) if status < 0 { //fmt.Fprintf(os.Stderr, "Poll error: %s\n", libuv.Strerror(libuv.Errno(status))) @@ -136,7 +135,7 @@ func readCb(userdata c.Pointer, ctx *hyper.Context, buf *byte, bufLen uintptr) u return uintptr(ret) } - if syscall.Errno(ret) != syscall.EAGAIN && syscall.Errno(ret) != syscall.EWOULDBLOCK { + if uintptr(os.Errno) != syscall.EAGAIN && uintptr(os.Errno) != syscall.EWOULDBLOCK { return hyper.IoError } @@ -163,7 +162,7 @@ func writeCb(userdata c.Pointer, ctx *hyper.Context, buf *byte, bufLen uintptr) return uintptr(ret) } - if syscall.Errno(ret) != syscall.EAGAIN && syscall.Errno(ret) != syscall.EWOULDBLOCK { + if uintptr(os.Errno) != syscall.EAGAIN && uintptr(os.Errno) != syscall.EWOULDBLOCK { return hyper.IoError } @@ -190,7 +189,6 @@ func createConnData(client *libuv.Tcp) *ConnData { return nil } c.Memcpy(unsafe.Pointer(&conn.Stream), unsafe.Pointer(client), unsafe.Sizeof(libuv.Tcp{})) - //c.Memmove(unsafe.Pointer(&conn.Stream), unsafe.Pointer(client), unsafe.Sizeof(libuv.Tcp{})) conn.IsClosing = 0 conn.RequestCount = 0 @@ -202,8 +200,8 @@ func createConnData(client *libuv.Tcp) *ConnData { return nil } - (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).SetData(unsafe.Pointer(conn)) - (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).SetData(unsafe.Pointer(conn)) + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Data = unsafe.Pointer(conn) + conn.Stream.Data = unsafe.Pointer(conn) if !updateConnDataRegistrations(conn, true) { (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(nil) @@ -267,7 +265,6 @@ func printBodyChunk(userdata c.Pointer, chunk *hyper.Buf) c.Int { func sendEachBodyChunk(userdata c.Pointer, ctx *hyper.Context, chunk **hyper.Buf) c.Int { chunkCount := (*c.Int)(userdata) if *chunkCount > 0 { - c.Printf(c.Str("Chunk %d\n"), *chunkCount) 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]))) @@ -292,12 +289,11 @@ func serverCallback(userdata c.Pointer, request *hyper.Request, channel *hyper.R conn.RequestCount++ // fmt.Printf("Handling request %d on connection from %s:%s\n", conn.RequestCount, // c.GoString((*c.Char)(&serviceData.Host[0])), c.GoString((*c.Char)(&serviceData.Port[0]))) - c.Printf(c.Str("Handling request %d on connection from %s:%s\n"), c.Int(conn.RequestCount), + c.Printf(c.Str("Handling request %d on connection from %s:%s\n"), conn.RequestCount, (*c.Char)(&serviceData.Host[0]), (*c.Char)(&serviceData.Port[0])) // fmt.Printf("Received request from %s:%s\n", c.GoString((*c.Char)(&serviceData.Host[0])), - // c.GoString((*c.Char)(&serviceData.Port[0]))) - c.Printf(c.Str("Received request from %s:%s\n"), (*c.Char)(&serviceData.Host[0]), (*c.Char)(&serviceData.Port[0])) + //c.Printf(c.Str("Received request from %s:%s\n"), (*c.Char)(&serviceData.Host[0]), (*c.Char)(&serviceData.Port[0])) if request == nil { //fmt.Fprintf(os.Stderr, "Error: Received null request\n") @@ -392,46 +388,47 @@ func serverCallback(userdata c.Pointer, request *hyper.Request, channel *hyper.R } response := hyper.NewResponse() - // if response != nil { - // response.SetStatus(200) - // rspHeaders := response.Headers() - // if rspHeaders != nil { - // rspHeaders.Set((*byte)(unsafe.Pointer(c.Str("Content-Type"))), uintptr(12), (*byte)(unsafe.Pointer(c.Str("text/plain"))), uintptr(10)) - // rspHeaders.Set((*byte)(unsafe.Pointer(c.Str("Cache-Control"))), uintptr(13), (*byte)(unsafe.Pointer(c.Str("no-cache"))), uintptr(8)) - // } else { - // //fmt.Fprintf(os.Stderr, "Error: Failed to get response headers\n") - // c.Printf(c.Str("Error: Failed to get response headers\n")) - // } - - // if methodLen > 0 && c.Strncmp((*c.Char)(unsafe.Pointer(&method[0])), c.Str("GET"), methodLen) == 0 { - // c.Printf(c.Str("Sending GET response\n")) - // 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") - // c.Printf(c.Str("Error: Failed to allocate chunk_count\n")) - // } - // } else { - // //fmt.Fprintf(os.Stderr, "Error: Failed to create response body\n") - // c.Printf(c.Str("Error: Failed to create response body\n")) - // } - // } - - // channel.Send(response) - // } else { - // //fmt.Fprintf(os.Stderr, "Error: Failed to create response\n") - // c.Printf(c.Str("Error: Failed to create response\n")) - // } 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 { + c.Printf(c.Str("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 { + c.Printf(c.Str("Error: Failed to set response headers\n")) + } + // Print all headers + //rspHeaders.Foreach(printEachHeader, nil) + } else { + //fmt.Fprintf(os.Stderr, "Error: Failed to get response headers\n") + c.Printf(c.Str("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") + c.Printf(c.Str("Error: Failed to allocate chunk_count\n")) + } + } else { + //fmt.Fprintf(os.Stderr, "Error: Failed to create response body\n") + c.Printf(c.Str("Error: Failed to create response body\n")) + } + } + channel.Send(response) } else { + //fmt.Fprintf(os.Stderr, "Error: Failed to create response\n") c.Printf(c.Str("Error: Failed to create response\n")) } @@ -465,11 +462,11 @@ func onNewConnection(serverStream *libuv.Stream, status c.Int) { 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"), Ntohs(s.Port)) + 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"), Ntohs(s.Port)) + c.Snprintf((*c.Char)(&userdata.Port[0]), unsafe.Sizeof(userdata.Port), c.Str("%d"), net.Ntohs(s.Port)) } // fmt.Printf("New incoming connection from (%s:%s)\n", c.GoString((*c.Char)(&userdata.Host[0])), @@ -561,11 +558,23 @@ func main() { os.Exit(1) } - libuv.SignalInit(loop, &sigintHandle) - sigintHandle.Start(onSignal, c.Int(syscall.SIGINT)) + signalRes := libuv.SignalInit(loop, &sigintHandle) + if signalRes != 0 { + c.Printf(c.Str("Failed to initialize signal handler: %d\n"), signalRes) + } + signalHandleRes := sigintHandle.Start(onSignal, c.Int(syscall.SIGINT)) + if signalHandleRes != 0 { + c.Printf(c.Str("Failed to start signal handler: %d\n"), signalHandleRes) + } - libuv.SignalInit(loop, &sigtermHandle) - sigtermHandle.Start(onSignal, c.Int(syscall.SIGTERM)) + signalRes = libuv.SignalInit(loop, &sigtermHandle) + if signalRes != 0 { + c.Printf(c.Str("Failed to initialize signal handler: %d\n"), signalRes) + } + signalHandleRes = sigtermHandle.Start(onSignal, c.Int(syscall.SIGTERM)) + if signalHandleRes != 0 { + c.Printf(c.Str("Failed to start signal handler: %d\n"), signalHandleRes) + } //fmt.Printf("http handshake (hyper v%s) ...\n", c.GoString(hyper.Version())) c.Printf(c.Str("http handshake (hyper v%s) ...\n"), hyper.Version()) @@ -585,94 +594,49 @@ func main() { conn := (*ConnData)(taskUserdata) //fmt.Printf("Connection task completed for request %d\n", conn.RequestCount) c.Printf(c.Str("Connection task completed for request %d\n"), conn.RequestCount) - c.Printf(c.Str("IsClosing: %d\n"), c.Int(conn.IsClosing)) - // if conn.IsClosing == 0 { - // c.Printf(c.Str("Closing connection\n")) - // conn.IsClosing = 1 - // if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() != 0 { - // c.Printf(c.Str("Closing poll handle\n")) - // (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(onClose) - // } - // if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() != 0 { - // c.Printf(c.Str("Closing stream\n")) - // (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) - // } - // } - if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() == 0 { - c.Printf(c.Str("Closing poll handle\n")) - (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(onClose) - } - if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() == 0 { - c.Printf(c.Str("Closing stream\n")) - (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) + c.Printf(c.Str("hyper.TaskEmpty IsClosing: %d\n"), conn.IsClosing) + if conn.IsClosing == 0 { + c.Printf(c.Str("Closing connection\n")) + conn.IsClosing = 1 + if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() == 0 { + c.Printf(c.Str("Closing poll handle\n")) + (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(nil) + } + if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() == 0 { + c.Printf(c.Str("Closing stream\n")) + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(closeConn) + } } } - conn := (*ConnData)(taskUserdata) - if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() == 0 { - c.Printf(c.Str("Closing poll handle\n")) - (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(onClose) - } - if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() == 0 { - c.Printf(c.Str("Closing stream\n")) - (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) - } - break case hyper.TaskError: err := (*hyper.Error)(task.Value()) var errbuf [256]byte errlen := err.Print(&errbuf[0], unsafe.Sizeof(errbuf)) - //fmt.Fprintf(os.Stderr, "Task error: %.*s\n", int(errlen), c.GoString((*c.Char)(unsafe.Pointer(&errbuf[0])))) + //fmt.Fprintf(os.Stderr, c.Str("Task error: %.*s\n"), errlen, (*c.Char)(unsafe.Pointer(&errbuf[0]))) c.Printf(c.Str("Task error: %.*s\n"), errlen, (*c.Char)(unsafe.Pointer(&errbuf[0]))) + c.Printf(c.Str("Error code: %d\n"), c.Int(err.Code())) err.Free() - conn := (*ConnData)(taskUserdata) - c.Printf(c.Str("IsClosing: %d\n"), c.Int(conn.IsClosing)) - // if conn.IsClosing == 0 { - // c.Printf(c.Str("Closing connection\n")) - // conn.IsClosing = 1 - // if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() != 0 { - // c.Printf(c.Str("Closing poll handle\n")) - // (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(onClose) - // } - // if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() != 0 { - // c.Printf(c.Str("Closing stream\n")) - // (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) - // } - // } - if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() == 0 { - c.Printf(c.Str("Closing poll handle\n")) - (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(onClose) - } - if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() == 0 { - c.Printf(c.Str("Closing stream\n")) - (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) - } - break case hyper.TaskClientConn: //fmt.Fprintf(os.Stderr, "Unexpected HYPER_TASK_CLIENTCONN in server context\n") c.Printf(c.Str("Unexpected HYPER_TASK_CLIENTCONN in server context\n")) - break case hyper.TaskResponse: //fmt.Println("Response task received") c.Printf(c.Str("Response task received\n")) - break case hyper.TaskBuf: //fmt.Println("Buffer task received") c.Printf(c.Str("Buffer task received\n")) - break case hyper.TaskServerconn: //fmt.Println("Server connection task received: ready for new connection...") c.Printf(c.Str("Server connection task received: ready for new connection...\n")) - break default: //fmt.Fprintf(os.Stderr, "Unknown task type: %d\n", taskType) c.Printf(c.Str("Unknown task type: %d\n"), taskType) - break } if taskUserdata == nil && taskType != hyper.TaskEmpty && taskType != hyper.TaskServerconn { @@ -700,7 +664,6 @@ func main() { //fmt.Println("Closing all handles...") c.Printf(c.Str("Closing all handles...\n")) loop.Walk(closeWalkCb, nil) - loop.Run(libuv.RUN_DEFAULT) loop.Close() @@ -708,18 +671,5 @@ func main() { //fmt.Println("Shutdown complete.") c.Printf(c.Str("Shutdown complete.\n")) -} - -// Ntohs converts a 16-bit integer from network byte order to host byte order. -func Ntohs(x uint16) uint16 { - if isLittleEndian() { - return ((x & 0xFF00) >> 8) | ((x & 0x00FF) << 8) - } - return x -} - -// isLittleEndian checks if the host machine is little-endian. -func isLittleEndian() bool { - var i int32 = 0x01020304 - return *(*byte)(unsafe.Pointer(&i)) == 0x04 -} + os.Exit(0) +} \ No newline at end of file From 3bbb19edcdb67845b78933d1f16419cb80f8f124 Mon Sep 17 00:00:00 2001 From: hackerchai Date: Tue, 13 Aug 2024 16:32:58 +0800 Subject: [PATCH 4/7] refactor(rust/hyper/demo): Replace c Printf with go fmt.Printf Signed-off-by: hackerchai --- rust/hyper/_demo/hyper_server/server.go | 218 +++++++++--------------- 1 file changed, 78 insertions(+), 140 deletions(-) diff --git a/rust/hyper/_demo/hyper_server/server.go b/rust/hyper/_demo/hyper_server/server.go index 102303e..18354a4 100644 --- a/rust/hyper/_demo/hyper_server/server.go +++ b/rust/hyper/_demo/hyper_server/server.go @@ -2,13 +2,13 @@ package main import ( "fmt" - sysos "os" + "os" "unsafe" "github.com/goplus/llgo/c" "github.com/goplus/llgo/c/libuv" "github.com/goplus/llgo/c/net" - "github.com/goplus/llgo/c/os" + cos "github.com/goplus/llgo/c/os" "github.com/goplus/llgo/c/syscall" "github.com/goplus/llgoexamples/rust/hyper" ) @@ -43,9 +43,8 @@ type ServiceUserdata struct { Conn *ConnData } -func onSignal(handle *libuv.Signal, signum c.Int) { - //fmt.Printf("Caught signal %d... exiting\n", signum) - c.Printf(c.Str("Caught signal %d... exiting\n"), signum) +func onSignal(handle *libuv.Signal, sigNum c.Int) { + fmt.Printf("Caught signal %d... exiting\n", sigNum) shouldExit = true sigintHandle.Stop() sigtermHandle.Stop() @@ -93,8 +92,7 @@ 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))) - c.Printf(c.Str("Poll error: %s\n"), libuv.Strerror(libuv.Errno(status))) + fmt.Fprintf(os.Stderr, "Poll error: %s\n", libuv.Strerror(libuv.Errno(status))) return } @@ -120,8 +118,7 @@ func updateConnDataRegistrations(conn *ConnData, create bool) bool { r := conn.PollHandle.Start(events, onPoll) if r < 0 { - //fmt.Fprintf(os.Stderr, "uv_poll_start error: %s\n", libuv.Strerror(libuv.Errno(r))) - c.Printf(c.Str("uv_poll_start error: %s\n"), libuv.Strerror(libuv.Errno(r))) + fmt.Fprintf(os.Stderr, "uv_poll_start error: %s\n", libuv.Strerror(libuv.Errno(r))) return false } return true @@ -135,7 +132,7 @@ func readCb(userdata c.Pointer, ctx *hyper.Context, buf *byte, bufLen uintptr) u return uintptr(ret) } - if uintptr(os.Errno) != syscall.EAGAIN && uintptr(os.Errno) != syscall.EWOULDBLOCK { + if uintptr(cos.Errno) != syscall.EAGAIN && uintptr(cos.Errno) != syscall.EWOULDBLOCK { return hyper.IoError } @@ -162,7 +159,7 @@ func writeCb(userdata c.Pointer, ctx *hyper.Context, buf *byte, bufLen uintptr) return uintptr(ret) } - if uintptr(os.Errno) != syscall.EAGAIN && uintptr(os.Errno) != syscall.EWOULDBLOCK { + if uintptr(cos.Errno) != syscall.EAGAIN && uintptr(cos.Errno) != syscall.EWOULDBLOCK { return hyper.IoError } @@ -184,8 +181,7 @@ func writeCb(userdata c.Pointer, ctx *hyper.Context, buf *byte, bufLen uintptr) func createConnData(client *libuv.Tcp) *ConnData { conn := (*ConnData)(c.Calloc(1, unsafe.Sizeof(ConnData{}))) if conn == nil { - //fmt.Fprintf(os.Stderr, "Failed to allocate conn_data\n") - c.Printf(c.Str("Failed to malloc conn_data mem\n")) + fmt.Fprintf(os.Stderr, "Failed to allocate conn_data\n") return nil } c.Memcpy(unsafe.Pointer(&conn.Stream), unsafe.Pointer(client), unsafe.Sizeof(libuv.Tcp{})) @@ -194,8 +190,7 @@ func createConnData(client *libuv.Tcp) *ConnData { r := libuv.PollInit(loop, &conn.PollHandle, libuv.OsFd(client.GetIoWatcherFd())) if r < 0 { - //fmt.Fprintf(os.Stderr, "uv_poll_init error: %s\n", libuv.Strerror(libuv.Errno(r))) - c.Printf(c.Str("uv_poll_init error: %s\n"), libuv.Strerror(libuv.Errno(r))) + fmt.Fprintf(os.Stderr, "uv_poll_init error: %s\n", libuv.Strerror(libuv.Errno(r))) c.Free(unsafe.Pointer(conn)) return nil } @@ -233,8 +228,7 @@ func createIo(conn *ConnData) *hyper.Io { func createServiceUserdata() *ServiceUserdata { userdata := (*ServiceUserdata)(c.Calloc(1, unsafe.Sizeof(ServiceUserdata{}))) if userdata == nil { - //fmt.Fprintf(os.Stderr, "Failed to allocate service_userdata\n") - c.Printf(c.Str("Failed to allocate service_userdata\n")) + fmt.Fprintf(os.Stderr, "Failed to allocate service_userdata\n") } return userdata } @@ -248,16 +242,15 @@ func freeServiceUserdata(userdata c.Pointer) { } 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)))) - c.Printf(c.Str("%.*s: %.*s\n"), nameLen, (*c.Char)(unsafe.Pointer(name)), valueLen, (*c.Char)(unsafe.Pointer(value))) + 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() - os.Write(1, unsafe.Pointer(buf), len) + cos.Write(1, unsafe.Pointer(buf), len) return hyper.IterContinue } @@ -281,23 +274,16 @@ func serverCallback(userdata c.Pointer, request *hyper.Request, channel *hyper.R conn := serviceData.Conn if conn == nil { - //fmt.Fprintf(os.Stderr, "Error: No connection data available\n") - c.Printf(c.Str("Error: No connection data available\n")) + fmt.Fprintf(os.Stderr, "Error: No connection data available\n") return } conn.RequestCount++ - // fmt.Printf("Handling request %d on connection from %s:%s\n", conn.RequestCount, - // c.GoString((*c.Char)(&serviceData.Host[0])), c.GoString((*c.Char)(&serviceData.Port[0]))) - c.Printf(c.Str("Handling request %d on connection from %s:%s\n"), conn.RequestCount, - (*c.Char)(&serviceData.Host[0]), (*c.Char)(&serviceData.Port[0])) - - // fmt.Printf("Received request from %s:%s\n", c.GoString((*c.Char)(&serviceData.Host[0])), - //c.Printf(c.Str("Received request from %s:%s\n"), (*c.Char)(&serviceData.Host[0]), (*c.Char)(&serviceData.Port[0])) + fmt.Printf("Handling request %d on connection from %s:%s\n", conn.RequestCount, + 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") - c.Printf(c.Str("Error: Received null request\n")) + fmt.Fprintf(os.Stderr, "Error: Received null request\n") return } @@ -310,80 +296,62 @@ func serverCallback(userdata c.Pointer, request *hyper.Request, channel *hyper.R uriResult := request.URIParts(&scheme[0], &schemeLen, &authority[0], &authorityLen, &pathAndQuery[0], &pathAndQueryLen) if uriResult == hyper.OK { - //fmt.Printf("Scheme: %s\n", string(scheme[:schemeLen])) - c.Printf(c.Str("Scheme: %.*s\n"), c.Int(schemeLen), (*c.Char)(unsafe.Pointer(&scheme[0]))) - //fmt.Printf("Authority: %s\n", string(authority[:authorityLen])) - c.Printf(c.Str("Authority: %.*s\n"), c.Int(authorityLen), (*c.Char)(unsafe.Pointer(&authority[0]))) - //fmt.Printf("Path and Query: %s\n", string(pathAndQuery[:pathAndQueryLen])) - c.Printf(c.Str("Path and Query: %.*s\n"), c.Int(pathAndQueryLen), (*c.Char)(unsafe.Pointer(&pathAndQuery[0]))) + 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) - c.Printf(c.Str("Failed to get URI parts. Error code: %d\n"), uriResult) + fmt.Fprintf(os.Stderr, "Failed to get URI parts. Error code: %d\n", uriResult) } version := request.Version() - //fmt.Printf("HTTP Version: ") - c.Printf(c.Str("HTTP Version: ")) + fmt.Printf("HTTP Version: ") switch version { case hyper.HTTPVersionNone: - //fmt.Println("None") - c.Printf(c.Str("None\n")) + fmt.Println("None") case hyper.HTTPVersion10: - //fmt.Println("HTTP/1.0") - c.Printf(c.Str("HTTP/1.0\n")) + fmt.Println("HTTP/1.0") case hyper.HTTPVersion11: - //fmt.Println("HTTP/1.1") - c.Printf(c.Str("HTTP/1.1\n")) + fmt.Println("HTTP/1.1") case hyper.HTTPVersion2: - //fmt.Println("HTTP/2") - c.Printf(c.Str("HTTP/2\n")) + fmt.Println("HTTP/2") default: - //fmt.Printf("Unknown (%d)\n", version) - c.Printf(c.Str("Unknown (%d)\n"), version) + 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])) - c.Printf(c.Str("Method: %.*s\n"), c.Int(methodLen), (*c.Char)(unsafe.Pointer(&method[0]))) + fmt.Printf("Method: %s\n", string(method[:methodLen])) } else { - //fmt.Fprintf(os.Stderr, "Failed to get request method. Error code: %d\n", methodResult) - c.Printf(c.Str("Failed to get request method. Error code: %d\n"), methodResult) + fmt.Fprintf(os.Stderr, "Failed to get request method. Error code: %d\n", methodResult) } - //fmt.Println("Headers:") - c.Printf(c.Str("Headers:\n")) + 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") - c.Printf(c.Str("Error: Failed to get request headers\n")) + 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:") - c.Printf(c.Str("Request Body:\n")) + 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") - c.Printf(c.Str("Error: Failed to push body foreach task: %d\n"), r) + 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") - c.Printf(c.Str("Error: Failed to create body foreach task\n")) + fmt.Fprintf(os.Stderr, "Error: Failed to create body foreach task\n") } } else { - //fmt.Fprintf(os.Stderr, "Error: Failed to get request body\n") - c.Printf(c.Str("Error: Failed to get request body\n")) + fmt.Fprintf(os.Stderr, "Error: Failed to get request body\n") } } @@ -394,17 +362,14 @@ func serverCallback(userdata c.Pointer, request *hyper.Request, channel *hyper.R 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 { - c.Printf(c.Str("Error: Failed to set response headers\n")) + 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 { - c.Printf(c.Str("Error: Failed to set response headers\n")) + fmt.Fprintf(os.Stderr, "Error: Failed to set response headers\n") } - // Print all headers - //rspHeaders.Foreach(printEachHeader, nil) } else { - //fmt.Fprintf(os.Stderr, "Error: Failed to get response headers\n") - c.Printf(c.Str("Error: Failed to get response headers\n")) + 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 { @@ -417,19 +382,16 @@ func serverCallback(userdata c.Pointer, request *hyper.Request, channel *hyper.R 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") - c.Printf(c.Str("Error: Failed to allocate chunk_count\n")) + fmt.Fprintf(os.Stderr, "Error: Failed to allocate chunk_count\n") } } else { - //fmt.Fprintf(os.Stderr, "Error: Failed to create response body\n") - c.Printf(c.Str("Error: Failed to create response body\n")) + 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") - c.Printf(c.Str("Error: Failed to create response\n")) + fmt.Fprintf(os.Stderr, "Error: Failed to create response\n") } // We don't close the connection here. Let hyper handle keep-alive. @@ -437,8 +399,7 @@ func serverCallback(userdata c.Pointer, request *hyper.Request, channel *hyper.R 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))) - c.Printf(c.Str("New connection error %s\n"), libuv.Strerror(libuv.Errno(status))) + fmt.Fprintf(os.Stderr, "New connection error %s\n", libuv.Strerror(libuv.Errno(status))) return } @@ -448,8 +409,7 @@ func onNewConnection(serverStream *libuv.Stream, status c.Int) { if serverStream.Accept((*libuv.Stream)(unsafe.Pointer(client))) == 0 { userdata := createServiceUserdata() if userdata == nil { - //fmt.Fprintf(os.Stderr, "Failed to create service_userdata\n") - c.Printf(c.Str("Failed to create service_userdata\n")) + fmt.Fprintf(os.Stderr, "Failed to create service_userdata\n") (*libuv.Handle)(unsafe.Pointer(client)).Close(onClose) return } @@ -469,15 +429,12 @@ func onNewConnection(serverStream *libuv.Stream, status c.Int) { c.Snprintf((*c.Char)(&userdata.Port[0]), unsafe.Sizeof(userdata.Port), c.Str("%d"), net.Ntohs(s.Port)) } - // fmt.Printf("New incoming connection from (%s:%s)\n", c.GoString((*c.Char)(&userdata.Host[0])), - // c.GoString((*c.Char)(&userdata.Port[0]))) - c.Printf(c.Str("New incoming connection from (%s:%s)\n"), (*c.Char)(&userdata.Host[0]), - (*c.Char)(&userdata.Port[0])) + fmt.Printf("New incoming connection from (%s:%s)\n", c.GoString((*c.Char)(&userdata.Host[0])), + c.GoString((*c.Char)(&userdata.Port[0]))) conn := createConnData(client) if conn == nil { - //fmt.Fprintf(os.Stderr, "Failed to create conn_data\n") - c.Printf(c.Str("Failed to create conn_data\n")) + fmt.Fprintf(os.Stderr, "Failed to create conn_data\n") (*libuv.Handle)(unsafe.Pointer(client)).Close(onClose) freeServiceUserdata(unsafe.Pointer(userdata)) return @@ -512,21 +469,19 @@ func onNewConnection(serverStream *libuv.Stream, status c.Int) { func main() { exec = hyper.NewExecutor() if exec == nil { - //fmt.Fprintf(os.Stderr, "Failed to create hyper executor\n") - c.Printf(c.Str("Failed to create hyper executor\n")) + fmt.Fprintf(os.Stderr, "Failed to create hyper executor\n") os.Exit(1) } host := "127.0.0.1" port := "1234" - if len(sysos.Args) > 1 { - host = sysos.Args[1] + if len(os.Args) > 1 { + host = os.Args[1] } - if len(sysos.Args) > 2 { - port = sysos.Args[2] + if len(os.Args) > 2 { + port = os.Args[2] } - //fmt.Printf("listening on port %s on %s...\n", port, host) - c.Printf(c.Str("listening on port %s on %s...\n"), c.AllocaCStr(port), c.AllocaCStr(host)) + fmt.Printf("listening on port %s on %s...\n", port, host) loop = libuv.DefaultLoop() @@ -537,8 +492,7 @@ func main() { 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))) - c.Printf(c.Str("Bind error %s\n"), libuv.Strerror(libuv.Errno(r))) + fmt.Fprintf(os.Stderr, "Bind error %s\n", libuv.Strerror(libuv.Errno(r))) os.Exit(1) } @@ -546,38 +500,39 @@ func main() { 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))) - c.Printf(c.Str("setsockopt error %s\n"), libuv.Strerror(libuv.Errno(r))) + 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))) - c.Printf(c.Str("Listen error %s\n"), libuv.Strerror(libuv.Errno(r))) + fmt.Fprintf(os.Stderr, "Listen error %s\n", libuv.Strerror(libuv.Errno(r))) os.Exit(1) } signalRes := libuv.SignalInit(loop, &sigintHandle) if signalRes != 0 { - c.Printf(c.Str("Failed to initialize signal handler: %d\n"), signalRes) + 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 { - c.Printf(c.Str("Failed to start signal handler: %d\n"), signalHandleRes) + fmt.Fprintf(os.Stderr, "Failed to start signal handler: %d\n", signalHandleRes) + os.Exit(1) } signalRes = libuv.SignalInit(loop, &sigtermHandle) if signalRes != 0 { - c.Printf(c.Str("Failed to initialize signal handler: %d\n"), signalRes) + 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 { - c.Printf(c.Str("Failed to start signal handler: %d\n"), signalHandleRes) + 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())) - c.Printf(c.Str("http handshake (hyper v%s) ...\n"), hyper.Version()) + fmt.Printf("http handshake (hyper v%s) ...\n", c.GoString(hyper.Version())) for { loop.Run(libuv.RUN_NOWAIT) @@ -588,22 +543,16 @@ func main() { switch taskType { case hyper.TaskEmpty: - //fmt.Printf("\nEmpty task received: connection closed\n") - c.Printf(c.Str("\nEmpty task received: connection closed\n")) + fmt.Printf("\nEmpty task received: connection closed\n") if taskUserdata != nil { conn := (*ConnData)(taskUserdata) - //fmt.Printf("Connection task completed for request %d\n", conn.RequestCount) - c.Printf(c.Str("Connection task completed for request %d\n"), conn.RequestCount) - c.Printf(c.Str("hyper.TaskEmpty IsClosing: %d\n"), conn.IsClosing) + fmt.Printf("Connection task completed for request %d\n", conn.RequestCount) if conn.IsClosing == 0 { - c.Printf(c.Str("Closing connection\n")) conn.IsClosing = 1 if (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).IsClosing() == 0 { - c.Printf(c.Str("Closing poll handle\n")) (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(nil) } if (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).IsClosing() == 0 { - c.Printf(c.Str("Closing stream\n")) (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(closeConn) } } @@ -613,35 +562,27 @@ func main() { err := (*hyper.Error)(task.Value()) var errbuf [256]byte errlen := err.Print(&errbuf[0], unsafe.Sizeof(errbuf)) - //fmt.Fprintf(os.Stderr, c.Str("Task error: %.*s\n"), errlen, (*c.Char)(unsafe.Pointer(&errbuf[0]))) - c.Printf(c.Str("Task error: %.*s\n"), errlen, (*c.Char)(unsafe.Pointer(&errbuf[0]))) - c.Printf(c.Str("Error code: %d\n"), c.Int(err.Code())) + fmt.Fprintf(os.Stderr, "Task error: %.*s\n", errlen, (*c.Char)(unsafe.Pointer(&errbuf[0]))) + 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") - c.Printf(c.Str("Unexpected HYPER_TASK_CLIENTCONN in server context\n")) + fmt.Fprintf(os.Stderr, "Unexpected HYPER_TASK_CLIENTCONN in server context\n") case hyper.TaskResponse: - //fmt.Println("Response task received") - c.Printf(c.Str("Response task received\n")) + fmt.Println("Response task received") case hyper.TaskBuf: - //fmt.Println("Buffer task received") - c.Printf(c.Str("Buffer task received\n")) + fmt.Println("Buffer task received") case hyper.TaskServerconn: - //fmt.Println("Server connection task received: ready for new connection...") - c.Printf(c.Str("Server connection task received: ready for new connection...\n")) - + fmt.Println("Server connection task received: ready for new connection...") default: - //fmt.Fprintf(os.Stderr, "Unknown task type: %d\n", taskType) - c.Printf(c.Str("Unknown task type: %d\n"), taskType) + 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) - c.Printf(c.Str("Warning: Task with no associated connection data. Type: %d\n"), taskType) + fmt.Fprintf(os.Stderr, "Warning: Task with no associated connection data. Type: %d\n", taskType) } task.Free() @@ -651,8 +592,7 @@ func main() { } if shouldExit { - //fmt.Println("Shutdown initiated, cleaning up...") - c.Printf(c.Str("Shutdown initiated, cleaning up...\n")) + fmt.Println("Shutdown initiated, cleaning up...") break } @@ -661,15 +601,13 @@ func main() { } // Cleanup - //fmt.Println("Closing all handles...") - c.Printf(c.Str("Closing all handles...\n")) + fmt.Println("Closing all handles...") loop.Walk(closeWalkCb, nil) loop.Run(libuv.RUN_DEFAULT) loop.Close() exec.Free() - //fmt.Println("Shutdown complete.") - c.Printf(c.Str("Shutdown complete.\n")) + fmt.Println("Shutdown complete.") os.Exit(0) } \ No newline at end of file From ef3110464370a6d78bea88a29ccf34f7e533bb98 Mon Sep 17 00:00:00 2001 From: hackerchai Date: Tue, 13 Aug 2024 16:36:01 +0800 Subject: [PATCH 5/7] chore(deps): Update llgo Signed-off-by: hackerchai # Conflicts: # go.mod # go.sum --- go.mod | 2 +- go.sum | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) 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= From ef72e722554963b6f6459ff39b9b23f287bccc65 Mon Sep 17 00:00:00 2001 From: hackerchai Date: Tue, 27 Aug 2024 17:51:40 +0800 Subject: [PATCH 6/7] refactor(rust/hyper/demo): Refactor server libuv using uv_read & uv_write replacing poll logic Signed-off-by: hackerchai --- rust/hyper/_demo/hyper_server/server.go | 269 ++++++++++++------------ 1 file changed, 130 insertions(+), 139 deletions(-) diff --git a/rust/hyper/_demo/hyper_server/server.go b/rust/hyper/_demo/hyper_server/server.go index 18354a4..ec14772 100644 --- a/rust/hyper/_demo/hyper_server/server.go +++ b/rust/hyper/_demo/hyper_server/server.go @@ -14,7 +14,8 @@ import ( ) const ( - MAX_EVENTS = 128 + MAX_EVENTS = 128 + READ_BUFFER_SIZE = 65536 ) var ( @@ -26,14 +27,15 @@ var ( ) type ConnData struct { - Stream libuv.Tcp - PollHandle libuv.Poll - EventMask c.Uint - ReadWaker *hyper.Waker - WriteWaker *hyper.Waker - ConnTask *hyper.Task - IsClosing c.Int - RequestCount c.Int + 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 { @@ -59,8 +61,29 @@ func closeWalkCb(handle *libuv.Handle, arg c.Pointer) { } func allocBuffer(handle *libuv.Handle, suggestedSize uintptr, buf *libuv.Buf) { - buf.Base = (*c.Char)(c.Malloc(suggestedSize)) - buf.Len = suggestedSize + 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) { @@ -69,8 +92,8 @@ func onClose(handle *libuv.Handle) { func closeConn(handle *libuv.Handle) { conn := (*ConnData)(handle.GetData()) + if conn != nil { - fmt.Printf("Closing connection after handling %d requests\n", conn.RequestCount) if conn.ReadWaker != nil { conn.ReadWaker.Free() conn.ReadWaker = nil @@ -83,128 +106,101 @@ func closeConn(handle *libuv.Handle) { conn.ConnTask.Free() conn.ConnTask = nil } - c.Free(unsafe.Pointer(conn)) } - c.Free(unsafe.Pointer(handle)) } -func onPoll(handle *libuv.Poll, status c.Int, events c.Int) { - conn := (*ConnData)((*libuv.Handle)(unsafe.Pointer(handle)).GetData()) +func onRead(client *libuv.Stream, nread c.Long, buf *libuv.Buf) { + conn := (*ConnData)((*libuv.Handle)(unsafe.Pointer(client)).GetData()) - if status < 0 { - fmt.Fprintf(os.Stderr, "Poll error: %s\n", libuv.Strerror(libuv.Errno(status))) - return + 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 events&c.Int(libuv.READABLE) != 0 && conn.ReadWaker != nil { + if 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 { +func readCb(userdata unsafe.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) - } + // 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 - if uintptr(cos.Errno) != syscall.EAGAIN && uintptr(cos.Errno) != syscall.EWOULDBLOCK { - return hyper.IoError + return toCopy } + // Set the read waker 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) - } +func onWrite(req *libuv.Write, status c.Int) { + conn := (*ConnData)(req.Data) - if uintptr(cos.Errno) != syscall.EAGAIN && uintptr(cos.Errno) != syscall.EWOULDBLOCK { - return hyper.IoError + // 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.Free() + conn.WriteWaker.Wake() + conn.WriteWaker = nil } +} - if conn.EventMask&c.Uint(libuv.WRITABLE) == 0 { - conn.EventMask |= c.Uint(libuv.WRITABLE) - if !updateConnDataRegistrations(conn, false) { - return hyper.IoError - } - } +func writeCb(userdata unsafe.Pointer, ctx *hyper.Context, buf *byte, bufLen uintptr) uintptr { + conn := (*ConnData)(userdata) - conn.WriteWaker = ctx.Waker() - return hyper.IoPending -} + conn.WriteBuffer = libuv.InitBuf((*c.Char)(unsafe.Pointer(buf)), c.Uint(bufLen)) + conn.WriteReq.Data = unsafe.Pointer(conn) -func createConnData(client *libuv.Tcp) *ConnData { - conn := (*ConnData)(c.Calloc(1, unsafe.Sizeof(ConnData{}))) - if conn == nil { - fmt.Fprintf(os.Stderr, "Failed to allocate conn_data\n") - return nil + // Initiate the write operation + r := conn.WriteReq.Write((*libuv.Stream)(unsafe.Pointer(&conn.Stream)), &conn.WriteBuffer, 1, onWrite) + if r >= 0 { + return bufLen } - c.Memcpy(unsafe.Pointer(&conn.Stream), unsafe.Pointer(client), unsafe.Sizeof(libuv.Tcp{})) - conn.IsClosing = 0 - conn.RequestCount = 0 - r := libuv.PollInit(loop, &conn.PollHandle, libuv.OsFd(client.GetIoWatcherFd())) - if r < 0 { - fmt.Fprintf(os.Stderr, "uv_poll_init error: %s\n", libuv.Strerror(libuv.Errno(r))) - c.Free(unsafe.Pointer(conn)) - return nil + // Set the write_waker + if conn.WriteWaker != nil { + conn.WriteWaker.Free() } + conn.WriteWaker = ctx.Waker() - (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Data = unsafe.Pointer(conn) - conn.Stream.Data = unsafe.Pointer(conn) + return hyper.IoPending +} - if !updateConnDataRegistrations(conn, true) { - (*libuv.Handle)(unsafe.Pointer(&conn.PollHandle)).Close(nil) - c.Free(unsafe.Pointer(conn)) - return nil +func createConnData() (*ConnData, error) { + conn := &ConnData{} + if conn == nil { + return nil, fmt.Errorf("failed to allocate conn_data") } - - return conn + return conn, nil } func freeConnData(userdata c.Pointer) { @@ -226,21 +222,13 @@ func createIo(conn *ConnData) *hyper.Io { } func createServiceUserdata() *ServiceUserdata { - userdata := (*ServiceUserdata)(c.Calloc(1, unsafe.Sizeof(ServiceUserdata{}))) + userdata := &ServiceUserdata{} if userdata == nil { fmt.Fprintf(os.Stderr, "Failed to allocate service_userdata\n") } return userdata } -func freeServiceUserdata(userdata c.Pointer) { - castUserdata := (*ServiceUserdata)(userdata) - if castUserdata != nil { - // Note: We don't free conn here because it's managed separately - c.Free(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)))) @@ -251,6 +239,8 @@ 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 } @@ -278,9 +268,7 @@ func serverCallback(userdata c.Pointer, request *hyper.Request, channel *hyper.R return } - conn.RequestCount++ - fmt.Printf("Handling request %d on connection from %s:%s\n", conn.RequestCount, - c.GoString((*c.Char)(&serviceData.Host[0])), c.GoString((*c.Char)(&serviceData.Port[0]))) + 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") @@ -397,27 +385,42 @@ func serverCallback(userdata c.Pointer, request *hyper.Request, channel *hyper.R // We don't close the connection here. Let hyper handle keep-alive. } -func onNewConnection(serverStream *libuv.Stream, status c.Int) { +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 } - client := (*libuv.Tcp)(c.Malloc(unsafe.Sizeof(libuv.Tcp{}))) - libuv.InitTcp(loop, client) + 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 + } - if serverStream.Accept((*libuv.Stream)(unsafe.Pointer(client))) == 0 { userdata := createServiceUserdata() if userdata == nil { fmt.Fprintf(os.Stderr, "Failed to create service_userdata\n") - (*libuv.Handle)(unsafe.Pointer(client)).Close(onClose) + (*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)) - client.Getpeername((*net.SockAddr)(c.Pointer(&addr)), &addrlen) + conn.Stream.Getpeername((*net.SockAddr)(unsafe.Pointer(&addr)), &addrlen) if addr.Family == net.AF_INET { s := (*net.SockaddrIn)(unsafe.Pointer(&addr)) @@ -429,32 +432,19 @@ func onNewConnection(serverStream *libuv.Stream, status c.Int) { c.Snprintf((*c.Char)(&userdata.Port[0]), unsafe.Sizeof(userdata.Port), c.Str("%d"), net.Ntohs(s.Port)) } - fmt.Printf("New incoming connection from (%s:%s)\n", c.GoString((*c.Char)(&userdata.Host[0])), - c.GoString((*c.Char)(&userdata.Port[0]))) - - conn := createConnData(client) - if conn == nil { - fmt.Fprintf(os.Stderr, "Failed to create conn_data\n") - (*libuv.Handle)(unsafe.Pointer(client)).Close(onClose) - freeServiceUserdata(unsafe.Pointer(userdata)) - return - } - - userdata.Conn = conn - io := createIo(conn) - service := hyper.ServiceNew(serverCallback) - service.SetUserdata(unsafe.Pointer(userdata), freeServiceUserdata) + service.SetUserdata(unsafe.Pointer(userdata), nil) http1Opts := hyper.Http1ServerconnOptionsNew(userdata.Executor) - http1Opts.HeaderReadTimeout(1000 * 5) + 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) @@ -462,7 +452,7 @@ func onNewConnection(serverStream *libuv.Stream, status c.Int) { http1Opts.Free() http2Opts.Free() } else { - (*libuv.Handle)(unsafe.Pointer(client)).Close(onClose) + (*libuv.Handle)(unsafe.Pointer(&conn.Stream)).Close(onClose) } } @@ -533,6 +523,7 @@ func main() { } fmt.Printf("http handshake (hyper v%s) ...\n", c.GoString(hyper.Version())) + for { loop.Run(libuv.RUN_NOWAIT) @@ -545,13 +536,11 @@ func main() { case hyper.TaskEmpty: fmt.Printf("\nEmpty task received: connection closed\n") if taskUserdata != nil { + fmt.Println("taskUserdata is not nil") conn := (*ConnData)(taskUserdata) - fmt.Printf("Connection task completed for request %d\n", conn.RequestCount) if conn.IsClosing == 0 { + fmt.Println("conn.IsClosing is 0") conn.IsClosing = 1 - 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(closeConn) } @@ -559,10 +548,11 @@ func main() { } 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.Fprintf(os.Stderr, "Task error: %.*s\n", errlen, (*c.Char)(unsafe.Pointer(&errbuf[0]))) + fmt.Println("Error message:", string(errbuf[:errlen])) fmt.Printf("Error code: %d\n", int(err.Code())) err.Free() @@ -585,10 +575,12 @@ func main() { fmt.Fprintf(os.Stderr, "Warning: Task with no associated connection data. Type: %d\n", taskType) } - task.Free() if !shouldExit { task = exec.Poll() } + + task.Free() + } if shouldExit { @@ -606,8 +598,7 @@ func main() { loop.Run(libuv.RUN_DEFAULT) loop.Close() - exec.Free() fmt.Println("Shutdown complete.") os.Exit(0) -} \ No newline at end of file +} From 3197685b3b761cd48e5d88673d2988bbcafa6706 Mon Sep 17 00:00:00 2001 From: hackerchai Date: Mon, 2 Sep 2024 18:20:14 +0800 Subject: [PATCH 7/7] feat(rust/hyper/demo): Add server_legacy poll implement Signed-off-by: hackerchai --- .../hyper/_demo/hyper_server/server_legacy.go | 598 ++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 rust/hyper/_demo/hyper_server/server_legacy.go 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