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

feat: allow request with x509 cert #306

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ release:
GOOS=windows GOARCH=amd64 go build -o ./bin/$(binary)_windows_amd64
GOOS=linux GOARCH=amd64 go build -o ./bin/$(binary)_linux_amd64
GOOS=darwin GOARCH=amd64 go build -o ./bin/$(binary)_darwin_amd64
GOOS=darwin GOARCH=arm64 go build -o ./bin/$(binary)_darwin_arm64
33 changes: 26 additions & 7 deletions hey.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package main

import (
"crypto/tls"
"flag"
"fmt"
"io/ioutil"
Expand All @@ -35,7 +36,7 @@ import (
const (
headerRegexp = `^([\w-]+):\s*(.+)`
authRegexp = `^(.+):([^\s].+)`
heyUA = "hey/0.0.1"
heyVersion = "mengxi-hey/1.0.0"
)

var (
Expand Down Expand Up @@ -64,9 +65,15 @@ var (
disableKeepAlives = flag.Bool("disable-keepalive", false, "")
disableRedirects = flag.Bool("disable-redirects", false, "")
proxyAddr = flag.String("x", "", "")

certFile = flag.String("cert", "", "")
certKey = flag.String("cert-key", "", "")
)

var usage = `Usage: hey [options...] <url>
var usage = `
Version: %s

Usage: hey [options...] <url>

Options:
-n Number of requests to run. Default is 200.
Expand All @@ -88,7 +95,7 @@ Options:
-d HTTP request body.
-D HTTP request body from file. For example, /home/user/file.txt or ./file.txt.
-T Content-type, defaults to "text/html".
-U User-Agent, defaults to version "hey/0.0.1".
-U User-Agent, defaults to version "%s".
-a Basic authentication, username:password.
-x HTTP Proxy address as host:port.
-h2 Enable HTTP/2.
Expand All @@ -101,11 +108,14 @@ Options:
-disable-redirects Disable following of HTTP redirects
-cpus Number of used cpu cores.
(default for current machine is %d cores)

-cert file path to the client X509 certificate
-cert-key file path to the client X509 certidicate key
`

func main() {
flag.Usage = func() {
fmt.Fprint(os.Stderr, fmt.Sprintf(usage, runtime.NumCPU()))
fmt.Fprint(os.Stderr, fmt.Sprintf(usage, heyVersion, heyVersion, runtime.NumCPU()))
}

var hs headerSlice
Expand Down Expand Up @@ -207,20 +217,28 @@ func main() {

ua := header.Get("User-Agent")
if ua == "" {
ua = heyUA
ua = heyVersion
} else {
ua += " " + heyUA
ua += " " + heyVersion
}
header.Set("User-Agent", ua)

// set userAgent header if set
if *userAgent != "" {
ua = *userAgent + " " + heyUA
ua = *userAgent + " " + heyVersion
header.Set("User-Agent", ua)
}

req.Header = header

var cert tls.Certificate
if *certFile != "" && *certKey != "" {
cert, err = tls.LoadX509KeyPair(*certFile, *certKey)
if err != nil {
usageAndExit(err.Error())
}
}

w := &requester.Work{
Request: req,
RequestBody: bodyAll,
Expand All @@ -234,6 +252,7 @@ func main() {
H2: *h2,
ProxyAddr: proxyURL,
Output: *output,
Cert: &cert,
}
w.Init()

Expand Down
7 changes: 7 additions & 0 deletions requester/requester.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ type Work struct {
start time.Duration

report *report

// X509 Certificate
Cert *tls.Certificate
}

func (b *Work) writer() io.Writer {
Expand Down Expand Up @@ -245,6 +248,10 @@ func (b *Work) runWorkers() {
DisableKeepAlives: b.DisableKeepAlives,
Proxy: http.ProxyURL(b.ProxyAddr),
}
if b.Cert != nil {
tr.TLSClientConfig.Certificates = []tls.Certificate{*b.Cert}
}

if b.H2 {
http2.ConfigureTransport(tr)
} else {
Expand Down
66 changes: 66 additions & 0 deletions requester/requester_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ package requester

import (
"bytes"
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -132,3 +134,67 @@ func TestBody(t *testing.T) {
t.Errorf("Expected to work 10 times, found %v", count)
}
}

func TestCert(t *testing.T) {
var count int64
const clientCertFile = "unittestClient.crt"
const clientKeyFile = "unittestClient.key"
const serverCertFile = "unittestServer.crt"
const serverKeyFile = "unittestServer.key"

// Set up and run server
go func() {
// Route
handler := func(w http.ResponseWriter, r *http.Request) {
atomic.AddInt64(&count, int64(1))
}
mux := http.NewServeMux()
mux.HandleFunc("/", handler)

// Cert validation
cert, _ := ioutil.ReadFile(clientCertFile)
certPool := x509.NewCertPool()
certPool.AppendCertsFromPEM(cert)
tlsConfig := &tls.Config{
ClientCAs: certPool,
ClientAuth: tls.RequireAndVerifyClientCert,
}
tlsConfig.BuildNameToCertificate()

server := &http.Server{
Addr: ":7788",
Handler: mux,
TLSConfig: tlsConfig,
}

// Note client does not need to validate the server cert
// because `Work` has `InsecureSkipVerify: true`
// Here we specify server cert just to make it a HTTPS server
err := server.ListenAndServeTLS(serverCertFile, serverKeyFile)
if err != nil && err != http.ErrServerClosed {
t.Errorf("Failed to start HTTPS server: %v", err)
}
}()

// Have this just to ensure the server is up and running
time.Sleep(100)

// Set up and run clients
const numOfRun int64 = 20
cert, _ := tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
req, _ := http.NewRequest("GET", "https://localhost:7788/", nil)
w := &Work{
Request: req,
N: int(numOfRun),
C: 2,
Cert: &cert,
}
w.Run()

// Assert on number of requests handled by the server
// Note the test should have failed before here with `tls: bad certificate`
// if `Worker` does not handle `Cert` properly
if count != numOfRun {
t.Errorf("Expected to send %v requests, found %v", numOfRun, count)
}
}
21 changes: 21 additions & 0 deletions requester/unittestClient.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDiDCCAnACCQDzNHLvHscPqTANBgkqhkiG9w0BAQsFADCBhTELMAkGA1UEBhMC
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcx
GjAYBgNVBAoMEVlvdXIgT3JnYW5pemF0aW9uMRIwEAYDVQQLDAlZb3VyIFVuaXQx
GTAXBgNVBAMMEGNsaWVudC51bml0LnRlc3QwHhcNMjMwNzE5MDY0MDA2WhcNMzMw
NzE2MDY0MDA2WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx
FjAUBgNVBAcMDU1vdW50YWluIFZpZXcxGjAYBgNVBAoMEVlvdXIgT3JnYW5pemF0
aW9uMRIwEAYDVQQLDAlZb3VyIFVuaXQxGTAXBgNVBAMMEGNsaWVudC51bml0LnRl
c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDH6rIvdDNSgDEizFyL
dk7rgdGIeveTHCZN4Z3U08RbAraWJiebf3NjusJvo80mAZo3Tj0u5j/iu/QTQxyy
OBIdYPp96jyIEb3ipuc0Q0Y9V/CKMlnGsbWSiU6VcMEVU2MXwJV5fns4iYeeW7r2
7fjSPo93SgYEhf+2gUbC4grpdCUrgKHPnqwKyQMK3x5SJCEY4kjfelnDJYb2fGS0
RsUCuqgN+bYaxerTmzWNPT6kFlUXfXCYn1oY5B7QsG38EWwYP1MC/JwOpHdUsEAM
QWF2aG5FYtWkhdjwntFrUyhzLjectJll0RIIU/Uuftt+AKFDQ6pVbqIqVpjkDnLu
2iWRAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAC7pepPuB8Ze+DgMuED9f8Q97CxX
U+16FmwWdU/roNwshRqTNsLuTFLaORZSFviHM5N22O+4ct+dMEBgbwJhfBoaJRTN
rgGEumUfaNfCL05lKjDzKubLX1nlOO3TvJPu97D1v67FnpDJYS1cL7URqRKlif4x
E8c3Jxjh/n4lY02moeMamCYajAp12GWQCXq9dvyGthz2G+ubAtdPSh06O/C5Am8t
UIq3iTx0m7P8wdEIV4L+LLLt23STpO9wUi/vFE0Gac5bTUGRlnBld6UuInwLs4hh
sFyZh/4bVe6IjBVIW/p15TyiISSdq0nquPOnQmHvi5aksQoWEPX47p9ZoA0=
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions requester/unittestClient.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDH6rIvdDNSgDEi
zFyLdk7rgdGIeveTHCZN4Z3U08RbAraWJiebf3NjusJvo80mAZo3Tj0u5j/iu/QT
QxyyOBIdYPp96jyIEb3ipuc0Q0Y9V/CKMlnGsbWSiU6VcMEVU2MXwJV5fns4iYee
W7r27fjSPo93SgYEhf+2gUbC4grpdCUrgKHPnqwKyQMK3x5SJCEY4kjfelnDJYb2
fGS0RsUCuqgN+bYaxerTmzWNPT6kFlUXfXCYn1oY5B7QsG38EWwYP1MC/JwOpHdU
sEAMQWF2aG5FYtWkhdjwntFrUyhzLjectJll0RIIU/Uuftt+AKFDQ6pVbqIqVpjk
DnLu2iWRAgMBAAECggEBAKEJLcVJ7fl755inU7jHcSUF6nnsy7bFixlbLx78AoRp
OBjU3TzFunZQP0Vchekii04XiPNZZ4bFbgOCIQByaC0gLEb3QxE8cV+8oCsaMd9C
EjHQAz8pcSB72EBKlk4OYJkGeaFnP+y34/Ws4Hr+EFuTJ0+o4hYMtoIVuqFLIg0Q
IcK2jrEyJesqiqoZQqBWeb/FexnNmNA0I+qWZxAlhHEWjyFeha7d2vh/KeM0VgwY
bDm/R3DALq9EYpkzQeFPYjgLoNNzXgsQtROgh0tIRz9i6r0giU81iqeRIIN+dLpd
uoRv55gAf2GNg+q8tWFHIATBBYUCsKQvDQuIfyYeKMECgYEA/sTTChuU2o7PVFPq
qplFONfOf+bpmiKc3yqkoTJVjhr+P5g6Fd94c6DnLqxM6wtwTFT0bW9MkP7iAIX7
rqPJx0U/Cj8vh+z9zvKmNbzQWs7WY9bBUWMn9GAlZr7lxb9zWcCT5MA1Xd4965qH
i7VGV7ArfOAzYXaUn5XpJYepJ/kCgYEAyOIDkaSQoinFvMtBJS4Hk/eVuRQRPN3K
YKm8b0b4yAmOGJeWtkZVCUMws2fa8JruKUxHBuA3jLm5kEbir0CIEHtRDNYwUUFC
kTiVmVd4HPVLYEANTNaxPRVJzQix/hbkLHOtJ/bHyDsI8SaZ8oH1kF7KIaUVXKY9
RDgs+vi1QFkCgYEAoLqH8f6IoIIsZyUyDUL5Gu17h9GkWuuvUFPU3SWmOCrrcC+X
pakAkuJWN2nYdQkYZe/K7SekhG1pI69jo0AN0rvrE4ndcMGhNmh7V7exMzj+pKod
+Dy4PkJSFkolJ+aq3VrTcyOYB4poQjWRgiBxKm0oYnYHtFtdVHj9SAIYQ/ECgYEA
xi60dqt0RYgQnQGpc5TFxSUtgTpbB0GIt3S9gsryBefnWDu1ZH5expSTJ2v+hAFV
lUi7if0K0LsqZzyHx35Svm/qtk9Zu9A2bC726CFzTp5xjrOia3wjv6+Q78b0U0ki
MvisjBDbaJ9VYxRCLQ4pxhS+NhM3z//x0avaNH/J0YECgYEAsVrXC1tTQH8uQMZx
FW60fMndDIOFts52cqM5zMig7IduHYafBKm/nYme41hZbCcelMPZnnWhazhyQecV
MMAArAMddYqNlgojz9C/v/F8w1gvKNbRLX1mHTDySzUC+oUtWMNliQ5MxcEMsURj
ChGitvqdy5PgvnGDPMClx4kyl0w=
-----END PRIVATE KEY-----
21 changes: 21 additions & 0 deletions requester/unittestServer.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDiDCCAnACCQDWcjME6qTtXDANBgkqhkiG9w0BAQsFADCBhTELMAkGA1UEBhMC
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcx
GjAYBgNVBAoMEVlvdXIgT3JnYW5pemF0aW9uMRIwEAYDVQQLDAlZb3VyIFVuaXQx
GTAXBgNVBAMMEHNlcnZlci51bml0LnRlc3QwHhcNMjMwNzE5MDcwNDU4WhcNMzMw
NzE2MDcwNDU4WjCBhTELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx
FjAUBgNVBAcMDU1vdW50YWluIFZpZXcxGjAYBgNVBAoMEVlvdXIgT3JnYW5pemF0
aW9uMRIwEAYDVQQLDAlZb3VyIFVuaXQxGTAXBgNVBAMMEHNlcnZlci51bml0LnRl
c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4boqLg2f4X3xmrMa5
TIueyFkfjn16XWI5FCRY1WQ69LRDaFTdMyCGuf9PFTwFXFAzyLI4O+v88cKSBQSx
Z4rkaUHEvkHon1DN0whM4m05TrQtc7WuQsvUr5vWayD4Tn8NL2YEC3SqNOSuJp29
N05WjsnTN0mAkfWAPMGJTW4K/7dSVt4RfAxfwAMZ2pAHUHtuXfMqUHiyZizJg3N4
IA9t2jMicAHsY7maXM3FJ9QWDsXhMRN6/D32emJFJU64EE2Pyd41skDXfS6AMtAn
X1aBugmpVfDa7IPJHnN7LhsGDupIHEPc3foAmX+dO/xNhg9aiA54UW5piGr87b5N
EGfDAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABqQwJ4UdZKYd7xO8PApr2yPcIQH
xjHI2OvxCltQtey4Pnpn7ruMIsfcXdYiUUsziywX+lwfD9riPDiU7ytYXuFr7tES
vZLsXlHOD5pIvSOiGjl4TUPZk4sx33ZSBBg08inAiP350gsZeTcBu0W3OjRwnm1N
Rp4UC3M5zVwSp0p7VA6oa0MIR6igPdbADDh19FBsC0oSN+AxVG4do8ip0/EYuLC7
y9X4vbzMj5eONBcVOI0EFE/yvkZuN5ZNVW/J5sT84EinmQ6Y02EiYrq0i4BHA02k
bGoTZ0CEDNHUF/cxlnOGfuJcNrkyOAfxWwkxLwSwuLD7f/aDMkxPDNrU34g=
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions requester/unittestServer.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC4boqLg2f4X3xm
rMa5TIueyFkfjn16XWI5FCRY1WQ69LRDaFTdMyCGuf9PFTwFXFAzyLI4O+v88cKS
BQSxZ4rkaUHEvkHon1DN0whM4m05TrQtc7WuQsvUr5vWayD4Tn8NL2YEC3SqNOSu
Jp29N05WjsnTN0mAkfWAPMGJTW4K/7dSVt4RfAxfwAMZ2pAHUHtuXfMqUHiyZizJ
g3N4IA9t2jMicAHsY7maXM3FJ9QWDsXhMRN6/D32emJFJU64EE2Pyd41skDXfS6A
MtAnX1aBugmpVfDa7IPJHnN7LhsGDupIHEPc3foAmX+dO/xNhg9aiA54UW5piGr8
7b5NEGfDAgMBAAECggEAA81Lj9qynv0g7GDta853JlvM8oiWb5pTNwgCQ86KVWjL
+oPBFBpEtmJJMBEo/pdLk/W0LtKVZ854C1iNIzcWNuUwlOSzKR93o/aQuj0EsWS8
9B7phOEdoJHKLZ5hvJypo9TxTm4KBqQ2fpyPLeJg/AnPgUoner4HiJA4ESOGfDWG
UzR8VxTumuqNIde1b4dkEBr51F/zATGGC/KffezlbAqW1f9+xZvVzDlt1mZ93m0w
V8RXfk+NZK+T3JA0f/FCi20uWXRho3g5Me6kOgOKkc9bpUvb53/myB3aU8lIGWl7
Eb3trQoW57UNLDYkYo5Ox0k6fqYuvVusVojxVD3vqQKBgQDjP4YWH9isaYcv+JWc
41hohhwz/G+DrxTnMWRzptHBQBumVPkdsLUtNgyB9jDx3MWRTxmurFzkdq0eQkzf
MqzUaPtNfZUtjoJ86lCqzgb7Lk9OmZ1lv+a6ea84jLpTRme9ya3Z83qOqZNKH/ar
sdhsM9qt9v4cun4HSyPNU7IWRQKBgQDPxDafeaPQKw9RQVK60THyCBVrK0SOeOPk
G3/vOmYdfeVrJtcyF/FccJZlO3q3mqwplrLqRf31+OQXE1GB36GIj0R4WBfxBAgF
f+8ARkAuWa8My/pCFZBwb7eH7rdCmIIh1xzaqfQzGaNRe+J6WlzZ/Y6IIbHM8FYN
oH37XGvKZwKBgBkEkk93PBRnHcHzPQ1jngUL1qkGfoRhzwxQzL1KvUboNuMN8csJ
/Dg4/hGEuAi4hGul6K7pPOTOB+sP44SjAJH16My0Kas1SDpWVYEoY25dv89obAKa
qN0YrmisXYrzclJblK8s9e4kzxlXAYIPd0MaRlXmnj0tbtiAtjVRpIZ9AoGASvTj
MA3Wh9fBIvOKQVQPzN4VvCBUD4KV1UoGkexjwugUyN+ua6gvr8X+vG8FCxCGZEq2
KccupHsy7xBNK6newUHO1gwSNyXZLwLE2zh9FzvL57X/h6/3+FiVwjjhbOlQqZzO
ECWYsIbjYRZs0u+e6BaOQZbGasWahjgMu47QZKkCgYBKdtsdOKe8v2iIzpXUr5Zf
WLgs87PFUv79EUOYTcc+bFtIJvGHlo+tvb5MMByk9doXyWtApWnbV4e9bYAXzPwv
GRC4kG41egF3/JDr84P+fZP4jpR0sk1RCqI6o5OyoUaeV9aSYrIKr7skzdwj+GG/
UHlXK8MqTMQkDWYVRRS+mQ==
-----END PRIVATE KEY-----