Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for h2c client #3

Merged
merged 1 commit into from
Feb 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# h2cli
Simple http 2.0 (h2) client in go
Simple http 2.0 (h2) client in go including h2c (http2 over clear text, not requiring TLS)


## Installation
Expand Down Expand Up @@ -33,12 +33,12 @@ flags:
Standard

```
$ go run .
$ go run . -url https://debug.fortio.org
12:50:58 I GET on https://debug.fortio.org
12:50:58 I Response code 200, proto HTTP/2.0, size 366
Φορτίο version 1.50.1 h1:5FSttAHQsyAsi3dzxDmSByfzDYByrWY/yw53bqOg+Kc= go1.19.6 amd64 linux (in fortio.org/proxy 1.10.0)
Debug server on ol1 up for 15h38m51.4s
Request from [2600:1700:1151:b24f:41e5:48a:2b96:711d]:56772 https TLS_AES_128_GCM_SHA256
Request from [2600:1700:xxx]:56772 https TLS_AES_128_GCM_SHA256

GET / HTTP/2.0

Expand Down Expand Up @@ -97,3 +97,42 @@ User-Agent: Go-http-client/2.0

body:
```

### H2C examples

h2c against debug.fortio.org is the new default for `go run .`
```
% go run .
15:28:59 I h2c GET on http://debug.fortio.org
15:29:00 I Response code 200, proto HTTP/2.0, size 334
Φορτίο version 1.52.0 h1:xHVOXkR3k5V5DvVM7/byfIHff3ia613qunnm+7O0EuQ= go1.19.6 arm64 linux (in fortio.org/proxy 1.11.1)
Debug server on a1 up for 19h4m25s
Request from [2600:1700:xxx]:52833

GET / HTTP/2.0

headers:

Host: debug.fortio.org
Accept-Encoding: gzip
User-Agent: Go-http-client/2.0

body:
```

```
% go run . -url localhost:8080/debug
15:31:16 I h2c GET on http://localhost:8080/debug
15:31:16 I Response code 200, proto HTTP/2.0, size 245
Φορτίο version dev go1.19.6 arm64 darwin echo debug server up for 26m39.3s on MacBook-Air.local - request from [::1]:53010

GET /debug HTTP/2.0

headers:

Host: localhost:8080
Accept-Encoding: gzip
User-Agent: Go-http-client/2.0

body:
```
10 changes: 7 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ module github.com/fortio/h2cli
go 1.19

require (
fortio.org/cli v1.0.0
fortio.org/log v1.2.2
fortio.org/cli v1.1.0
fortio.org/log v1.3.0
golang.org/x/net v0.7.0
)

require fortio.org/version v1.0.2 // indirect
require (
fortio.org/version v1.0.2 // indirect
golang.org/x/text v0.7.0 // indirect
)
12 changes: 8 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
fortio.org/cli v1.0.0 h1:qUyHr1DiyxFoq5RBPFJ6Hw30NcV/0KYSxmptdiLiW5I=
fortio.org/cli v1.0.0/go.mod h1:O3nVImKwJSvHKbMYWkqMbEagAXCS1xvSv1YbHwkKJjY=
fortio.org/log v1.2.2 h1:vs42JjNwiqbMbacittZjJE9+oi72Za6aekML9gKmILg=
fortio.org/log v1.2.2/go.mod h1:u/8/2lyczXq52aT5Nw6reD+3cR6m/EbS2jBiIYhgiTU=
fortio.org/cli v1.1.0 h1:ATIxi7DgA7WAexUCF8p5a0qlGYk48ZgkwSEDrvwXeN4=
fortio.org/cli v1.1.0/go.mod h1:O3nVImKwJSvHKbMYWkqMbEagAXCS1xvSv1YbHwkKJjY=
fortio.org/log v1.3.0 h1:bESPvuQGKejw7rrx41Sg3GoF+tsrB7oC08PxBs5/AM0=
fortio.org/log v1.3.0/go.mod h1:u/8/2lyczXq52aT5Nw6reD+3cR6m/EbS2jBiIYhgiTU=
fortio.org/version v1.0.2 h1:8NwxdX58aoeKx7T5xAPO0xlUu1Hpk42nRz5s6e6eKZ0=
fortio.org/version v1.0.2/go.mod h1:2JQp9Ax+tm6QKiGuzR5nJY63kFeANcgrZ0osoQFDVm0=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
53 changes: 41 additions & 12 deletions h2cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ import (
"crypto/x509"
"flag"
"io"
"net"
"net/http"
"net/url"
"os"
"strings"

"fortio.org/cli"
"fortio.org/log"
"golang.org/x/net/http2"
)

var (
h2 = flag.Bool("h2", true, "use HTTP2")
url = flag.String("url", "https://debug.fortio.org", "URL to fetch")
method = flag.String("method", "GET", "HTTP method to use")
caCert = flag.String("cacert", "",
h2 = flag.Bool("h2", true, "use HTTP2")
urlFlag = flag.String("url", "http://debug.fortio.org", "URL to fetch")
method = flag.String("method", "GET", "HTTP method to use")
caCert = flag.String("cacert", "",
"`Path` to a custom CA certificate file instead standard internet/system CAs")
)

Expand All @@ -36,20 +40,45 @@ func main() {
caCertPool.AppendCertsFromPEM(ca)
tlsConfig.RootCAs = caCertPool
}

client.Transport = &http.Transport{
TLSClientConfig: tlsConfig,
ForceAttemptHTTP2: *h2, // could also use &http2.Transport{TLSClientConfig: tlsConfig} but that's not necessary to get h2
lu := strings.ToLower(*urlFlag)
if !strings.HasPrefix(lu, "https://") && !strings.HasPrefix(lu, "http://") {
// be nice and add http:// if missing
log.LogVf("Adding http:// to url %q", *urlFlag)
*urlFlag = "http://" + *urlFlag
}
u, err := url.Parse(*urlFlag)
if err != nil {
log.Fatalf("Failed to parse url %q: %v", *urlFlag, err)
}
h2c := ""
if u.Scheme == "https" || !*h2 {
// This will do h2 over tls if the server supports it but not h2c
// but with TLS all is good in the standard package, with ForceAttemptHTTP2
client.Transport = &http.Transport{
TLSClientConfig: tlsConfig, // only used if url is https
ForceAttemptHTTP2: *h2, // *h2 for h2 over tls
}
} else {
// h2c
h2c = "h2c "
client.Transport = &http2.Transport{
AllowHTTP: true, // to get h2c
// Trick to get h2c without TLS:
// thanks to https://github.com/thrawn01/h2c-golang-example/blob/master/README.md
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return net.Dial(network, addr)
},
}
}
log.Infof("%s on %s", *method, *url)
log.Infof("%s%s on %s", h2c, *method, *urlFlag)
// Perform the request
req, err := http.NewRequestWithContext(context.Background(), *method, *url, nil)
req, err := http.NewRequestWithContext(context.Background(), *method, *urlFlag, nil)
if err != nil {
log.Fatalf("Request method %q url %q error: %v", *method, *url, err)
log.Fatalf("Request method %q url %q error: %v", *method, *urlFlag, err)
}
resp, err := client.Do(req)
if err != nil {
log.Fatalf("Failed %q %q - error: %v", *method, *url, err)
log.Fatalf("Failed %q %q - error: %v", *method, *urlFlag, err)
}
body, err := io.ReadAll(resp.Body)
resp.Body.Close()
Expand Down