diff --git a/examples/net/webserver-httphi/main.go b/examples/net/webserver-httphi/main.go new file mode 100644 index 000000000..57b955d34 --- /dev/null +++ b/examples/net/webserver-httphi/main.go @@ -0,0 +1,210 @@ +// This example is the webserver example using the heapless httphi router +// from github.com/soypat/lneto instead of "net/http". +// +// $ curl http://10.0.0.2 + +//go:build ninafw || wioterminal + +package main + +import ( + "log" + "machine" + "net" + "strconv" + "time" + + "github.com/soypat/lneto/http/httphi" + "github.com/soypat/lneto/http/httpraw" + + "tinygo.org/x/drivers/netlink" + "tinygo.org/x/drivers/netlink/probe" +) + +var ( + ssid string + pass string + port string = ":80" +) + +const ( + numWorkers = 2 + memoryPerConn = 1024 +) + +var led = machine.LED + +func main() { + + // wait a bit for serial + time.Sleep(2 * time.Second) + + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + link, _ := probe.Probe() + + err := link.NetConnect(&netlink.ConnectParams{ + Ssid: ssid, + Passphrase: pass, + }) + if err != nil { + log.Fatal(err) + } + + var mux httphi.MuxSlice + mux.Handle("GET /{$}", root) + mux.Handle("GET /hello", hello) + mux.Handle("/cnt", cnt) + mux.Handle("GET /6", sixlines) + mux.Handle("GET /off", LED_OFF) + mux.Handle("GET /on", LED_ON) + + var router httphi.Router + cfg := httphi.DefaultRouterConfig(numWorkers, memoryPerConn, mux.MaxPathValues()) + err = router.Configure(&mux, cfg) + if err != nil { + log.Fatal(err) + } + + listener, err := net.Listen("tcp", port) + if err != nil { + log.Fatal(err) + } + + for { + conn, err := listener.Accept() + if err != nil { + println("accept:", err.Error()) + time.Sleep(time.Second) + continue + } + if err := router.Handle(conn); err != nil { + conn.Close() + } + } +} + +func root(ex *httphi.Exchange) { + access := int64(1) + + var cookie httpraw.Cookie + var cookieBuf [128]byte + cookie.Reset(cookieBuf[:0], 4) + if ex.RequestParseCookie(&cookie, "Cookie") == nil { + if v := cookie.Get("access"); v != nil { + n, err := strconv.ParseInt(string(v), 10, 0) + if err != nil { + ex.RespondString(httphi.StatusBadRequest, "text/plain", "invalid cookie value") + return + } + access = n + 1 + } + } + + var setCookie [32]byte + ex.StageHeaderBytes("Set-Cookie", strconv.AppendInt(append(setCookie[:0], "access="...), access, 10)) + ex.StageHeader("Content-Type", "text/html; charset=UTF-8") + ex.StageHeader("Connection", "close") + + var num [20]byte + ex.WriteBodyString(rootHead) + ex.WriteBody(strconv.AppendInt(num[:0], access, 10)) + ex.WriteBodyString(rootTail) +} + +const rootHead = ` + + + TinyGo HTTP Server + + + +
TinyGo HTTP Server
+ +

+ access: ` + +const rootTail = ` +

+ + /hello
+ /6
+ +

+ LED
+ /on
+ /off
+

+ + +

+ /cnt
+ cnt:
+ incrCnt()
+

+ + +
+

+ + +` + +func sixlines(ex *httphi.Exchange) { + // https://fukuno.jig.jp/3267 + ex.RespondString(httphi.StatusOK, "text/html; charset=UTF-8", ``) +} + +func LED_ON(ex *httphi.Exchange) { + led.High() + ex.RespondString(httphi.StatusOK, "text/plain; charset=UTF-8", "led.High()") +} + +func LED_OFF(ex *httphi.Exchange) { + led.Low() + ex.RespondString(httphi.StatusOK, "text/plain; charset=UTF-8", "led.Low()") +} + +func hello(ex *httphi.Exchange) { + ex.RespondString(httphi.StatusOK, "text/plain; charset=UTF-8", "hello") +} + +var counter int64 + +func cnt(ex *httphi.Exchange) { + if ex.RequestMethod() == httphi.MethPost { + var form httpraw.Form + var formBuf [128]byte + form.Reset(formBuf[:0], 4) + form.EnableBufferGrowth(false) + if err := ex.RequestParseForm(&form, true, false); err != nil { + ex.RespondString(httphi.StatusBadRequest, "text/plain", err.Error()) + return + } + if c := form.Get("cnt"); len(c) > 0 { + counter, _ = strconv.ParseInt(string(c), 0, 0) + } + } + + var body [32]byte + b := append(body[:0], `{"cnt": `...) + b = strconv.AppendInt(b, counter, 10) + b = append(b, '}') + ex.Respond(httphi.StatusOK, "application/json", b) +} diff --git a/examples/net/webstatic-httphi/images/tinygo-logo.png b/examples/net/webstatic-httphi/images/tinygo-logo.png new file mode 100644 index 000000000..d362856fd Binary files /dev/null and b/examples/net/webstatic-httphi/images/tinygo-logo.png differ diff --git a/examples/net/webstatic-httphi/index.html b/examples/net/webstatic-httphi/index.html new file mode 100644 index 000000000..03002e32d --- /dev/null +++ b/examples/net/webstatic-httphi/index.html @@ -0,0 +1,28 @@ + + + TinyGo - A Go Compiler For Small Places + + + +

TinyGo - A Go Compiler For Small Places

+ + + diff --git a/examples/net/webstatic-httphi/main.go b/examples/net/webstatic-httphi/main.go new file mode 100644 index 000000000..cd45b32de --- /dev/null +++ b/examples/net/webstatic-httphi/main.go @@ -0,0 +1,123 @@ +// This example is the webstatic example using the heapless httphi router +// from github.com/soypat/lneto instead of "net/http". + +//go:build ninafw || wioterminal + +package main + +import ( + "embed" + "log" + "net" + "path" + "time" + + "github.com/soypat/lneto/http/httphi" + + "tinygo.org/x/drivers/netlink" + "tinygo.org/x/drivers/netlink/probe" +) + +var ( + ssid string + pass string + port string = ":80" +) + +const ( + numWorkers = 2 + memoryPerConn = 1024 +) + +//go:embed index.html main.go images +var fs embed.FS + +func main() { + // wait a bit for console + time.Sleep(2 * time.Second) + + link, _ := probe.Probe() + + err := link.NetConnect(&netlink.ConnectParams{ + Ssid: ssid, + Passphrase: pass, + }) + if err != nil { + log.Fatal(err) + } + + var mux httphi.MuxSlice + mux.Handle("GET /", serveFile) + + var router httphi.Router + cfg := httphi.DefaultRouterConfig(numWorkers, memoryPerConn, mux.MaxPathValues()) + err = router.Configure(&mux, cfg) + if err != nil { + log.Fatal(err) + } + + listener, err := net.Listen("tcp", port) + if err != nil { + log.Fatal(err) + } + + for { + conn, err := listener.Accept() + if err != nil { + println("accept:", err.Error()) + time.Sleep(time.Second) + continue + } + if err := router.Handle(conn); err != nil { + conn.Close() + } + } +} + +func serveFile(ex *httphi.Exchange) { + name := string(ex.RequestPath()[1:]) + if name == "" { + name = "index.html" + } + + f, err := fs.Open(name) + if err != nil { + ex.RespondString(httphi.StatusNotFound, "text/plain", "404 page not found") + return + } + defer f.Close() + + info, err := f.Stat() + if err != nil || info.IsDir() { + ex.RespondString(httphi.StatusNotFound, "text/plain", "404 page not found") + return + } + + ex.StageHeader("Content-Type", contentType(name)) + ex.StageHeaderInt("Content-Length", info.Size()) + ex.StageHeader("Connection", "close") + + var buf [512]byte + for { + n, err := f.Read(buf[:]) + if n > 0 { + if _, werr := ex.WriteBody(buf[:n]); werr != nil { + return + } + } + if err != nil { + return + } + } +} + +func contentType(name string) string { + switch path.Ext(name) { + case ".html": + return "text/html; charset=utf-8" + case ".png": + return "image/png" + default: + return "text/plain; charset=utf-8" + } +} diff --git a/go.mod b/go.mod index 432c77ae0..9ce9b8054 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,13 @@ module tinygo.org/x/drivers -go 1.22.1 - -toolchain go1.23.1 +go 1.24 require ( github.com/eclipse/paho.mqtt.golang v1.2.0 github.com/frankban/quicktest v1.10.2 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/orsinium-labs/tinymath v1.1.0 + github.com/soypat/lneto v0.4.0 github.com/soypat/natiu-mqtt v0.5.1 github.com/tinygo-org/pio v0.3.0 golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d diff --git a/go.sum b/go.sum index 673d984d8..8a889e8e4 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/orsinium-labs/tinymath v1.1.0 h1:KomdsyLHB7vE3f1nRAJF2dyf1m/gnM2HxfTeV1vS5UA= github.com/orsinium-labs/tinymath v1.1.0/go.mod h1:WPXX6ei3KSXG7JfA03a+ekCYaY9SWN4I+JRl2p6ck+A= +github.com/soypat/lneto v0.4.0 h1:oBTcMnhEIBERXmAfwK4uDpItRaF7RiT1K2pljFVxtQc= +github.com/soypat/lneto v0.4.0/go.mod h1:Be5PjwoYukvHFiUXxpYi8+ppH2F/gw/vjGBvFdv+Ti8= github.com/soypat/natiu-mqtt v0.5.1 h1:rwaDmlvjzD2+3MCOjMZc4QEkDkNwDzbct2TJbpz+TPc= github.com/soypat/natiu-mqtt v0.5.1/go.mod h1:xEta+cwop9izVCW7xOx2W+ct9PRMqr0gNVkvBPnQTc4= github.com/tinygo-org/pio v0.3.0 h1:opEnOtw58KGB4RJD3/n/Rd0/djYGX3DeJiXLI6y/yDI= diff --git a/smoketest.go b/smoketest.go index 174526986..6219308ec 100644 --- a/smoketest.go +++ b/smoketest.go @@ -97,6 +97,7 @@ func runSmokeTest(filename string) error { _ = flagSet.String("size", "", "") _ = flagSet.String("o", "", "") _ = flagSet.String("stack-size", "", "") + _ = flagSet.String("tags", "", "") targetFlag := flagSet.String("target", "", "") err = flagSet.Parse(fields[2:]) if err != nil { diff --git a/smoketest.sh b/smoketest.sh index b9f2c4995..8fdb530c8 100755 --- a/smoketest.sh +++ b/smoketest.sh @@ -176,11 +176,14 @@ tinygo build -size short -o ./build/test.hex -target=arduino-nano33 -stack-size tinygo build -size short -o ./build/test.hex -target=nano-rp2040 -stack-size 8kb ./examples/net/websocket/dial/ tinygo build -size short -o ./build/test.hex -target=metro-m4-airlift -stack-size 8kb ./examples/net/socket/ tinygo build -size short -o ./build/test.hex -target=matrixportal-m4 -stack-size 8kb ./examples/net/webstatic/ +tinygo build -size short -o ./build/test.hex -tags=noslog -target=matrixportal-m4 -stack-size 8kb ./examples/net/webstatic-httphi/ +tinygo build -size short -o ./build/test.hex -tags=noslog -target=arduino-nano33 -stack-size 4kb ./examples/net/webserver-httphi/ tinygo build -size short -o ./build/test.hex -target=arduino-mkrwifi1010 -stack-size 8kb ./examples/net/tlsclient/ tinygo build -size short -o ./build/test.hex -target=nano-rp2040 -stack-size 8kb ./examples/net/mqttclient/natiu/ # network examples (rtl8720dn) tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webclient/ tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/webserver/ +tinygo build -size short -o ./build/test.hex -tags=noslog -target=wioterminal -stack-size 8kb ./examples/net/webserver-httphi/ tinygo build -size short -o ./build/test.hex -target=wioterminal -stack-size 8kb ./examples/net/mqttclient/paho/ # network examples (comboat) tinygo build -size short -o ./build/test.hex -target=elecrow-rp2040 -stack-size 8kb ./examples/net/tlsclient/