diff --git a/Makefile b/Makefile index cfa69619..92bedcb7 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ build: scion-bat \ example-helloworld \ example-helloquic \ example-shttp-client example-shttp-server example-shttp-fileserver example-shttp-proxy \ + example-shttp3-server \ example-sgrpc-server example-sgrpc-client clean: @@ -122,6 +123,10 @@ example-shttp-fileserver: example-shttp-proxy: cd _examples && go build -tags=$(TAGS) -o ../$(BIN)/$@ ./shttp/proxy +.PHONY: example-shttp3-server +example-shttp3-server: + cd _examples && go build -tags=$(TAGS) -o ../$(BIN)/$@ ./shttp3/server + .PHONY: example-sgrpc-server example-sgrpc-server: cd _examples && go build -tags=$(TAGS) -o ../$(BIN)/$@ ./sgrpc/server diff --git a/README.md b/README.md index 011f5506..304eaf36 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,8 @@ The directory _examples contains examples for the usage of the SCION libraries. Example for using gRPC over SCION/QUIC with the PAN library. * [_examples/shttp](_examples/shttp/README.md): Examples for using HTTP over SCION/QUIC, examples for servers, proxies, and clients. +* [_examples/shttp3](_examples/shttp3/README.md): + Example for using HTTP/3 over SCION/QUIC. ## bat diff --git a/_examples/go.mod b/_examples/go.mod index 03fed147..c75bf3ea 100644 --- a/_examples/go.mod +++ b/_examples/go.mod @@ -42,6 +42,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.53.0 // indirect github.com/prometheus/procfs v0.14.0 // indirect + github.com/quic-go/qpack v0.4.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/scionproto/scion v0.12.1-0.20241223103250-0b42cbc42486 // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect diff --git a/_examples/go.sum b/_examples/go.sum index 7ab667a1..59297c88 100644 --- a/_examples/go.sum +++ b/_examples/go.sum @@ -110,6 +110,8 @@ github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+a github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= github.com/prometheus/procfs v0.14.0 h1:Lw4VdGGoKEZilJsayHf0B+9YgLGREba2C6xr+Fdfq6s= github.com/prometheus/procfs v0.14.0/go.mod h1:XL+Iwz8k8ZabyZfMFHPiilCniixqQarAy5Mu67pHlNQ= +github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= +github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/quic-go v0.43.1 h1:fLiMNfQVe9q2JvSsiXo4fXOEguXHGGl9+6gLp4RPeZQ= github.com/quic-go/quic-go v0.43.1/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= diff --git a/_examples/shttp3/server/README.md b/_examples/shttp3/server/README.md new file mode 100644 index 00000000..5ee7b0b8 --- /dev/null +++ b/_examples/shttp3/server/README.md @@ -0,0 +1,4 @@ +# Example for HTTP/3 over SCION/QUIC + +## Simple server example +The server example `bin/example-shttp3-server` is analogous to the [shttp simple server example](../../shttp/README.md#simple-server-example), except that it always uses HTTPS and thus requires a certificate and key. \ No newline at end of file diff --git a/_examples/shttp3/server/main.go b/_examples/shttp3/server/main.go new file mode 100644 index 00000000..2c60fc8d --- /dev/null +++ b/_examples/shttp3/server/main.go @@ -0,0 +1,99 @@ +// Copyright 2025 ETH Zurich +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "encoding/json" + "flag" + "fmt" + "log" + "net/http" + "os" + "time" + + "github.com/gorilla/handlers" + "github.com/netsec-ethz/scion-apps/pkg/shttp3" +) + +func main() { + certFile := flag.String("cert", "", "Path to TLS server certificate for optional https") + keyFile := flag.String("key", "", "Path to TLS server key for optional https") + flag.Parse() + + if *certFile == "" || *keyFile == "" { + flag.Usage() + os.Exit(2) + } + + m := http.NewServeMux() + + // handler that responds with a friendly greeting + m.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { + // Status 200 OK will be set implicitly + w.Header().Set("Content-Type", "text/plain") + _, _ = w.Write([]byte(`Oh, hello!`)) + }) + + // handler that responds with an image file + m.HandleFunc("/image", func(w http.ResponseWriter, r *http.Request) { + // serve the sample JPG file + // Status 200 OK will be set implicitly + // Content-Length will be inferred by server + // Content-Type will be detected by server + http.ServeFile(w, r, "dog.jpg") + }) + + // GET handler that responds with some json data + m.HandleFunc("/json", func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + data := struct { + Time string + Agent string + Proto string + Message string + }{ + Time: time.Now().Format("2006.01.02 15:04:05"), + Agent: r.UserAgent(), + Proto: r.Proto, + Message: "success", + } + resp, _ := json.Marshal(data) + w.Header().Set("Content-Type", "application/json") + fmt.Fprint(w, string(resp)) + } else { + http.Error(w, "wrong method: "+r.Method, http.StatusForbidden) + } + }) + + // POST handler that responds by parsing form values and returns them as string + m.HandleFunc("/form", func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodPost { + if err := r.ParseForm(); err != nil { + http.Error(w, "invalid form data", http.StatusBadRequest) + return + } + w.Header().Set("Content-Type", "text/plain") + fmt.Fprint(w, "received following data:\n") + for s := range r.PostForm { + fmt.Fprint(w, s, "=", r.PostFormValue(s), "\n") + } + } else { + http.Error(w, "wrong method: "+r.Method, http.StatusForbidden) + } + }) + + handler := handlers.LoggingHandler(os.Stdout, m) + log.Fatal(shttp3.ListenAndServe(":443", *certFile, *keyFile, handler)) +}