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

Added standalone TLS example #436

Open
wants to merge 8 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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ See [`_examples`](_examples) directory for complete standalone examples.

Testing with custom formatter for assertion messages.

* [`tls_test.go`](_examples/tls_test.go)

Testing a tls server made with `net/http` and `crypto/tls`

## Quick start

##### Hello, world!
Expand Down
137 changes: 137 additions & 0 deletions _examples/tls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package examples

import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"log"
"net/http"
"net/http/httptest"
"path"
"strconv"
)

// NewRootCertPool creates a new custom root-certificate set.
//
// In this example, it's used so that the server's certificates are trusted.
// In real world use it's better to omit this in order to use the
// default root set of the current operating system.
func NewRootCertPool() *x509.CertPool {
const rootPEM = `
-----BEGIN CERTIFICATE-----
MIIBUzCB+6ADAgECAgEBMAoGCCqGSM49BAMCMBIxEDAOBgNVBAoTB1Rlc3QgQ0Ew
HhcNMjMxMTEzMTIyNTEzWhcNMjQwNTExMTIyNTEzWjASMRAwDgYDVQQKEwdUZXN0
IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHBm1CiEs4CKw0ynUlzaTz9Pi
ROnBwosfX3xYIEz5l1rN119FEJLWQFx8xBASkpZDz+Eehw9QdPaqwapDKGVgbaNC
MEAwDgYDVR0PAQH/BAQDAgIEMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFExX
luCP8Bp73F1L7UuFCM/NFgPkMAoGCCqGSM49BAMCA0cAMEQCIFLFQRgIUbjzA0c1
Pennq6gP/WiJpppZPQq5IYR4V7BfAiBVoGh+32UOJ13YYO8HsL/6P7KIwZKXkJpJ
LoibTriVMg==
-----END CERTIFICATE-----
`
roots := x509.NewCertPool()
ok := roots.AppendCertsFromPEM([]byte(rootPEM))
if !ok {
panic("failed to parse root certificate")
}
return roots
}

// ExampleTLSServer creates a httptest.Server with hardcoded key pair.
func ExampleTLSServer() *httptest.Server {
certPem := []byte(`-----BEGIN CERTIFICATE-----
MIIBbDCCARKgAwIBAgIBAjAKBggqhkjOPQQDAjASMRAwDgYDVQQKEwdUZXN0IENB
MB4XDTIzMTExMzEyMjUxN1oXDTI0MDUxMTEyMjUxN1owEjEQMA4GA1UEChMHQWNt
ZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEvlkPnSh5jYMD4MSkjJH7HW
iDR/UnqIJrI3nV0FTotWly0z3nMy0FCM1VxyGJc8HcKi2KPIaQmVF2sYCLwu8xuj
WTBXMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSME
GDAWgBRMV5bgj/Aae9xdS+1LhQjPzRYD5DAPBgNVHREECDAGhwR/AAABMAoGCCqG
SM49BAMCA0gAMEUCIQDHQVvWrOvagkYT9/qeSZ7xUwTTWiRfvWmlCgLf5NXu7AIg
ea/Q6OcG41k25PXVn3VRLRBEfSFIsuJzTyTNXCHx8vY=
-----END CERTIFICATE-----`)

keyPem := []byte(`-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIHHIE/n9wJI/dm1vnwhd8Jm/Wi04R+m8wYfUnkCFu4QnoAoGCCqGSM49
AwEHoUQDQgAES+WQ+dKHmNgwPgxKSMkfsdaINH9SeogmsjedXQVOi1aXLTPeczLQ
UIzVXHIYlzwdwqLYo8hpCZUXaxgIvC7zGw==
-----END EC PRIVATE KEY-----`)

cert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
log.Fatal(err)
}

server := httptest.NewUnstartedServer(TLSHandler())
server.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
return server
}

// TLSHandler creates http.Handler for tls server
//
// Routes:
//
// GET /fruits get item map
// GET /fruits/{name} get item amount
// PUT /fruits/{name} add or update fruit (amount in body)
func TLSHandler() http.Handler {
items := map[string]int{}

mux := http.NewServeMux()

mux.HandleFunc("/tls/", func(writer http.ResponseWriter, request *http.Request) {
_, name := path.Split(request.URL.Path)

switch request.Method {
case "PUT":
var data int
if err := json.NewDecoder(request.Body).Decode(&data); err != nil {
panic(err)
}
items[name] += data
writer.WriteHeader(http.StatusNoContent)

case "DELETE":
var data int
if err := json.NewDecoder(request.Body).Decode(&data); err != nil {
panic(err)
}
if _, ok := items[name]; ok {
items[name] -= data
writer.WriteHeader(http.StatusNoContent)
} else {
writer.WriteHeader(http.StatusNotFound)
}

case "GET":
if amount, ok := items[name]; ok {
_, err := writer.Write([]byte(strconv.Itoa(amount)))
if err != nil {
writer.WriteHeader(http.StatusServiceUnavailable)
}
} else {
writer.WriteHeader(http.StatusNotFound)
}

default:
writer.WriteHeader(http.StatusBadRequest)
}
})

mux.HandleFunc("/tls", func(writer http.ResponseWriter, request *http.Request) {
switch request.Method {
case "GET":

all, err := json.Marshal(&items)
if err != nil {
panic(err)
}
writer.Header().Set("Content-Type", "application/json")
writer.Write(all)

default:
writer.WriteHeader(http.StatusBadRequest)
}
})

return mux
}
90 changes: 90 additions & 0 deletions _examples/tls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package examples

import (
"crypto/tls"
"github.com/gavv/httpexpect/v2"
"github.com/stretchr/testify/assert"
"net/http"
"strconv"
"testing"
)

func TLSClient() *http.Client {
cfg := tls.Config{RootCAs: NewRootCertPool()}
return &http.Client{Transport: &http.Transport{TLSClientConfig: &cfg}}
}

func TestExampleTlsServer(t *testing.T) {

server := ExampleTLSServer() // ExampleTlsServer()
server.StartTLS()
defer server.Close()

config := httpexpect.Config{
BaseURL: server.URL, Client: TLSClient(),
Reporter: httpexpect.NewRequireReporter(t),
Printers: []httpexpect.Printer{
httpexpect.NewDebugPrinter(t, true),
}}

e := httpexpect.WithConfig(config)

e.PUT("/tls/car").WithText("1").
Expect().
Status(http.StatusNoContent).NoContent()

e.PUT("/tls/car").WithText("10").
Expect().
Status(http.StatusNoContent).NoContent()

e.GET("/tls/car").
Expect().
Status(http.StatusOK).Body().IsEqual("11")

e.DELETE("/tls/car").WithText("1").
Expect().
Status(http.StatusNoContent).NoContent()

e.GET("/tls/car").
Expect().
Status(http.StatusOK).Body().IsEqual("10")

e.GET("/tls/not_there").
Expect().
Status(http.StatusNotFound).NoContent()

e.DELETE("/tls/car").WithText("10").
Expect().
Status(http.StatusNoContent).NoContent()

items := map[string]int{
"car": 2,
"house": 1,
"cat": 6,
"fridge": 3,
}
for item, amount := range items {
e.PUT("/tls/" + item).WithText(strconv.Itoa(amount)).
Expect().
Status(http.StatusNoContent).NoContent()
}

e.DELETE("/tls/car").WithText("1").
Expect().
Status(http.StatusNoContent).NoContent()

items["car"]--

var m map[string]int

r := e.GET("/tls").Expect().Status(http.StatusOK)

r.Header("Content-Type").IsEqual("application/json")

r.JSON().Decode(&m)

for item, amount := range m {
assert.Equal(t, amount, items[item])
}

}