diff --git a/examples/options/server/main.go b/examples/options/server/main.go new file mode 100644 index 00000000..451e5104 --- /dev/null +++ b/examples/options/server/main.go @@ -0,0 +1,95 @@ +package main + +import ( + "bytes" + "context" + "crypto/tls" + "fmt" + "log" + + piondtls "github.com/pion/dtls/v2" + coap "github.com/plgd-dev/go-coap/v3" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/mux" + "github.com/plgd-dev/go-coap/v3/options" + + dtlsServer "github.com/plgd-dev/go-coap/v3/dtls/server" + tcpServer "github.com/plgd-dev/go-coap/v3/tcp/server" + udpClient "github.com/plgd-dev/go-coap/v3/udp/client" +) + +func handleA(w mux.ResponseWriter, r *mux.Message) { + log.Printf("got message in handleA: %+v from %v\n", r, w.Conn().RemoteAddr()) + err := w.SetResponse(codes.GET, message.TextPlain, bytes.NewReader([]byte("A hello world"))) + if err != nil { + log.Printf("cannot set response: %v", err) + } +} + +func handleB(w mux.ResponseWriter, r *mux.Message) { + log.Printf("got message in handleB: %+v from %v\n", r, w.Conn().RemoteAddr()) + customResp := w.Conn().AcquireMessage(r.Context()) + defer w.Conn().ReleaseMessage(customResp) + customResp.SetCode(codes.Content) + customResp.SetToken(r.Token()) + customResp.SetContentFormat(message.TextPlain) + customResp.SetBody(bytes.NewReader([]byte("B hello world"))) + err := w.Conn().WriteMessage(customResp) + if err != nil { + log.Printf("cannot set response: %v", err) + } +} + +func handleOnNewConn(cc *udpClient.Conn) { + dtlsConn, ok := cc.NetConn().(*piondtls.Conn) + if !ok { + log.Fatalf("invalid type %T", cc.NetConn()) + } + clientId := dtlsConn.ConnectionState().IdentityHint + cc.SetContextValue("clientId", clientId) + cc.AddOnClose(func() { + clientId := dtlsConn.ConnectionState().IdentityHint + log.Printf("closed connection clientId: %s", clientId) + }) +} + +func main() { + m := mux.NewRouter() + m.Handle("/a", mux.HandlerFunc(handleA)) + m.Handle("/b", mux.HandlerFunc(handleB)) + + tcpOpts := []tcpServer.Option{} + tcpOpts = append(tcpOpts, + options.WithMux(m), + options.WithContext(context.Background())) + + dtlsOpts := []dtlsServer.Option{} + dtlsOpts = append(dtlsOpts, + options.WithMux(m), + options.WithContext(context.Background()), + options.WithOnNewConn(handleOnNewConn), + ) + + go func() { + // serve a tcp server on :5686 + log.Fatal(coap.ListenAndServeWithOptions("tcp", ":5686", tcpOpts)) + }() + + go func() { + // serve a tls tcp server on :5687 + log.Fatal(coap.ListenAndServeTCPTLSWithOptions("tcp", "5687", &tls.Config{}, tcpOpts...)) + }() + + go func() { + // serve a udp dtls server on :5688 + log.Fatal(coap.ListenAndServeDTLSWithOptions("udp", ":5688", &piondtls.Config{ + PSK: func(hint []byte) ([]byte, error) { + fmt.Printf("Client's hint: %s \n", hint) + return []byte{0xAB, 0xC1, 0x23}, nil + }, + PSKIdentityHint: []byte("Pion DTLS Client"), + CipherSuites: []piondtls.CipherSuiteID{piondtls.TLS_PSK_WITH_AES_128_CCM_8}, + }, dtlsOpts...)) + }() +} diff --git a/server.go b/server.go index 2a1cc24e..1df5ab69 100644 --- a/server.go +++ b/server.go @@ -7,11 +7,14 @@ import ( piondtls "github.com/pion/dtls/v2" "github.com/plgd-dev/go-coap/v3/dtls" + dtlsServer "github.com/plgd-dev/go-coap/v3/dtls/server" "github.com/plgd-dev/go-coap/v3/mux" "github.com/plgd-dev/go-coap/v3/net" "github.com/plgd-dev/go-coap/v3/options" "github.com/plgd-dev/go-coap/v3/tcp" + tcpServer "github.com/plgd-dev/go-coap/v3/tcp/server" "github.com/plgd-dev/go-coap/v3/udp" + udpServer "github.com/plgd-dev/go-coap/v3/udp/server" ) // ListenAndServe Starts a server on address and network specified Invoke handler @@ -78,3 +81,81 @@ func ListenAndServeDTLS(network string, addr string, config *piondtls.Config, ha s := dtls.NewServer(options.WithMux(handler)) return s.Serve(l) } + +// ListenAndServeWithOption Starts a server on address and network specified Invoke options +// for incoming queries. The options is only support tcpServer.Option and udpServer.Option +func ListenAndServeWithOptions(network, addr string, opts ...any) (err error) { + tcpOptions := []tcpServer.Option{} + udpOptions := []udpServer.Option{} + for _, opt := range opts { + switch o := opt.(type) { + case tcpServer.Option: + tcpOptions = append(tcpOptions, o) + case udpServer.Option: + udpOptions = append(udpOptions, o) + default: + return fmt.Errorf("only support tcpServer.Option and udpServer.Option") + } + } + + switch network { + case "udp", "udp4", "udp6", "": + l, err := net.NewListenUDP(network, addr) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := udp.NewServer(udpOptions...) + return s.Serve(l) + case "tcp", "tcp4", "tcp6": + l, err := net.NewTCPListener(network, addr) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := tcp.NewServer(tcpOptions...) + return s.Serve(l) + default: + return fmt.Errorf("invalid network (%v)", network) + } +} + +// ListenAndServeTCPTLSWithOptions Starts a server on address and network over TLS specified Invoke options +// for incoming queries. +func ListenAndServeTCPTLSWithOptions(network, addr string, config *tls.Config, opts ...tcpServer.Option) (err error) { + l, err := net.NewTLSListener(network, addr, config) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := tcp.NewServer(opts...) + return s.Serve(l) +} + +// ListenAndServeDTLSWithOptions Starts a server on address and network over DTLS specified Invoke options +// for incoming queries. +func ListenAndServeDTLSWithOptions(network string, addr string, config *piondtls.Config, opts ...dtlsServer.Option) (err error) { + l, err := net.NewDTLSListener(network, addr, config) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := dtls.NewServer(opts...) + return s.Serve(l) +}