Skip to content

Commit 1fda3ab

Browse files
authored
Add Go tests for requests handlers (#11)
* ci: add go tests for requests handlers
1 parent 0798051 commit 1fda3ab

9 files changed

Lines changed: 645 additions & 44 deletions

File tree

cmd/requests.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ Examples:
9494
fmt.Print(err)
9595
}
9696

97-
responseMap, err := requests.HandleRequests(requestsCfg)
97+
responseMap, err := requests.HandleRequests(os.Stdout, requestsCfg)
9898
if err != nil {
9999
fmt.Print(err)
100100
}

devenv.lock

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
"devenv": {
44
"locked": {
55
"dir": "src/modules",
6-
"lastModified": 1763386227,
6+
"lastModified": 1764449550,
77
"owner": "cachix",
88
"repo": "devenv",
9-
"rev": "9f855598530d6ee075cc126e4f13812fd008209a",
9+
"rev": "dfb58ac03bed07b93f629df55034bc50394d3971",
1010
"type": "github"
1111
},
1212
"original": {
@@ -40,10 +40,10 @@
4040
]
4141
},
4242
"locked": {
43-
"lastModified": 1763319842,
43+
"lastModified": 1763988335,
4444
"owner": "cachix",
4545
"repo": "git-hooks.nix",
46-
"rev": "7275fa67fbbb75891c16d9dee7d88e58aea2d761",
46+
"rev": "50b9238891e388c9fdc6a5c49e49c42533a1b5ce",
4747
"type": "github"
4848
},
4949
"original": {
@@ -87,11 +87,27 @@
8787
"type": "github"
8888
}
8989
},
90+
"nixpkgsStable": {
91+
"locked": {
92+
"lastModified": 1764560356,
93+
"owner": "NixOS",
94+
"repo": "nixpkgs",
95+
"rev": "6c8f0cca84510cc79e09ea99a299c9bc17d03cb6",
96+
"type": "github"
97+
},
98+
"original": {
99+
"owner": "NixOS",
100+
"ref": "nixos-25.05",
101+
"repo": "nixpkgs",
102+
"type": "github"
103+
}
104+
},
90105
"root": {
91106
"inputs": {
92107
"devenv": "devenv",
93108
"git-hooks": "git-hooks",
94109
"nixpkgs": "nixpkgs",
110+
"nixpkgsStable": "nixpkgsStable",
95111
"pre-commit-hooks": [
96112
"git-hooks"
97113
]

devenv.nix

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
config,
55
inputs,
66
...
7-
}: {
7+
}: let
8+
# TODO: align go version between nixos/nixvim and devenv
9+
pkgsStable = import inputs.nixpkgsStable {system = pkgs.stdenv.system;};
10+
in {
811
env = {
912
GUM_FORMAT_THEME = "tokyo-night";
1013
CAROOT = "tests/certs";
@@ -49,7 +52,12 @@
4952
};
5053
};
5154

52-
# languages.go.enable = true;
55+
# https://devenv.sh/reference/options/#languagesgoenable
56+
# TODO: align go related versions used by vim to this before enabling
57+
# languages.go = {
58+
# enable = true;
59+
# package = pkgs.go;
60+
# };
5361

5462
services.nginx = {
5563
enable = true;

devenv.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
---
12
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
23
inputs:
34
nixpkgs:
45
url: github:cachix/devenv-nixpkgs/rolling
6+
nixpkgsStable:
7+
url: "github:NixOS/nixpkgs/nixos-25.05"
58

69
# If you're using non-OSS software, you can set allowUnfree to true.
710
# allowUnfree: true

internal/certinfo/certinfo_handlers.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"crypto/x509"
1111
"encoding/hex"
1212
"fmt"
13+
"io"
1314
"net"
1415
"os"
1516
"strconv"
@@ -62,7 +63,7 @@ func (c *CertinfoConfig) PrintData() {
6263
))
6364
}
6465

65-
CertsToTables(c.CertsBundle)
66+
CertsToTables(os.Stdout, c.CertsBundle)
6667
}
6768

6869
if len(c.TLSEndpointCerts) > 0 {
@@ -96,7 +97,7 @@ func (c *CertinfoConfig) PrintData() {
9697
))
9798
}
9899

99-
CertsToTables(c.TLSEndpointCerts)
100+
CertsToTables(os.Stdout, c.TLSEndpointCerts)
100101
}
101102

102103
if len(c.CACertsFilePath) > 0 {
@@ -116,7 +117,7 @@ func (c *CertinfoConfig) PrintData() {
116117
return
117118
}
118119

119-
CertsToTables(rootCerts)
120+
CertsToTables(os.Stdout, rootCerts)
120121
}
121122
}
122123

@@ -162,7 +163,7 @@ func (c *CertinfoConfig) GetRemoteCerts() {
162163
}
163164
}
164165

165-
func CertsToTables(certs []*x509.Certificate) {
166+
func CertsToTables(w io.Writer, certs []*x509.Certificate) {
166167
sl := style.CertKeyP4.Render
167168
sv := style.CertValue.Render
168169
svn := style.CertValueNotice.Render
@@ -214,7 +215,7 @@ func CertsToTables(certs []*x509.Certificate) {
214215
t.Row(sl("SignatureAlgorithm"), sv(signatureAlgorithm))
215216
t.Row(sl("SerialNumber"), sv(serialNumber))
216217
t.Row(sl("Fingerprint SHA-256"), sv(fingerprintSha256))
217-
fmt.Println(t.Render())
218+
fmt.Fprintln(w, t.Render())
218219
t.ClearRows()
219220
}
220221
}

internal/requests/main_test.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ type demoHttpServerData struct {
3636
serverAddr string
3737
proxyprotoEnabled bool
3838
serverName string
39+
tlsCipherSuites []uint16
40+
tlsMaxVersion uint16
3941
}
4042

4143
var (
@@ -209,7 +211,30 @@ func NewHTTPSTestServer(data demoHttpServerData) (*httptest.Server, error) {
209211
return nil, err
210212
}
211213

212-
ts.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
214+
// Set default TLS CipherSuites to TLS 1.3 cipher suites
215+
// https://pkg.go.dev/crypto/tls#pkg-constants
216+
tlsCipherSuites := []uint16{
217+
tls.TLS_AES_128_GCM_SHA256,
218+
tls.TLS_AES_256_GCM_SHA384,
219+
tls.TLS_CHACHA20_POLY1305_SHA256,
220+
}
221+
222+
if len(data.tlsCipherSuites) > 0 {
223+
tlsCipherSuites = data.tlsCipherSuites
224+
}
225+
226+
// Set default TLS MaxVersion to 1.3
227+
var tlsMaxVersion uint16 = tls.VersionTLS13
228+
229+
if data.tlsMaxVersion > 0 {
230+
tlsMaxVersion = data.tlsMaxVersion
231+
}
232+
233+
ts.TLS = &tls.Config{
234+
Certificates: []tls.Certificate{cert},
235+
CipherSuites: tlsCipherSuites,
236+
MaxVersion: tlsMaxVersion,
237+
}
213238

214239
ts.StartTLS()
215240

internal/requests/requests.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,10 @@ func processHTTPRequestsByHost(r RequestConfig, caPool *x509.CertPool, isVerbose
568568
return nil, err
569569
}
570570

571-
urlList := getUrlsFromHost(host)
571+
urlList, err := getUrlsFromHost(host)
572+
if err != nil {
573+
return nil, err
574+
}
572575

573576
for _, reqURL := range urlList {
574577
responseData := ResponseData{

internal/requests/requests_handlers.go

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func (h ResponseHeader) String() string {
2727
}
2828

2929
func (u URI) Parse() bool {
30+
// URIs must start with a slash as in /uri
3031
matched, err := regexp.Match(`^\/.*`, []byte(u))
3132
if err != nil {
3233
return false
@@ -54,14 +55,14 @@ func TLSVersionName(v uint16) string {
5455

5556
func cipherSuiteName(id uint16) string {
5657
cs := tls.CipherSuiteName(id)
57-
if cs == "" {
58+
if strings.Contains(cs, "0x") {
5859
return fmt.Sprintf("Unknown (0x%x)", id)
5960
}
6061

6162
return cs
6263
}
6364

64-
func parseResponseHeaders(headers http.Header, filter []string) string {
65+
func filterResponseHeaders(headers http.Header, filter []string) string {
6566
var outputStr string
6667

6768
var outputMap map[string][]string
@@ -94,49 +95,48 @@ func parseResponseHeaders(headers http.Header, filter []string) string {
9495
return outputStr
9596
}
9697

97-
func getUrlsFromHost(h Host) []string {
98+
func getUrlsFromHost(h Host) ([]string, error) {
9899
var list []string
99100

100101
if len(h.URIList) == 0 {
101102
s := httpClientDefaultScheme + "://" + h.Name
102103
list = append(list, s)
103104

104-
return list
105+
return list, nil
105106
}
106107

107108
for _, uri := range h.URIList {
108109
if parsed := uri.Parse(); !parsed {
109-
fmt.Printf("Invalid uri %s for host %s", uri, h.Name)
110-
111-
break
110+
return nil, fmt.Errorf("invalid uri %s for host %s", uri, h.Name)
112111
}
113112

114113
s := fmt.Sprintf("%s://%s%s", httpClientDefaultScheme, h.Name, uri)
115114
list = append(list, s)
116115
}
117116

118-
return list
119-
}
120-
121-
func transportAddressFromRequest(r RequestConfig) (string, error) {
122-
addr, err := transportAddressFromURLString(r.TransportOverrideURL)
123-
if err != nil {
124-
return emptyString, err
125-
}
126-
127-
return addr, nil
117+
return list, nil
128118
}
129119

130120
func transportAddressFromURLString(transportURL string) (string, error) {
131121
var addr string
132122

123+
if transportURL == emptyString {
124+
return emptyString, errors.New("empty string provided as transportURL")
125+
}
126+
127+
// Add HTTPS scheme if missing from transportURL
128+
if match, _ := regexp.MatchString("^https://", transportURL); !match {
129+
transportURL = "https://" + transportURL
130+
}
131+
133132
overrideURL, err := url.Parse(transportURL)
134133
if err != nil {
135134
return "", err
136135
}
137136

138137
addr = overrideURL.Host
139138

139+
// Add default HTTPS port if a port is missing from transportURL
140140
if match, _ := regexp.MatchString("\\:\\d+$", addr); !match {
141141
addr += ":443"
142142
}
@@ -203,10 +203,10 @@ func proxyProtoHeaderFromRequest(r RequestConfig, serverName string) (proxyproto
203203
return header, nil
204204
}
205205

206-
func HandleRequests(cfg *RequestsMetaConfig) (map[string][]ResponseData, error) {
206+
func HandleRequests(w io.Writer, cfg *RequestsMetaConfig) (map[string][]ResponseData, error) {
207207
responseDataMap := make(map[string][]ResponseData)
208208

209-
cfg.PrintCmd(os.Stdout)
209+
cfg.PrintCmd(w)
210210

211211
for _, r := range cfg.Requests {
212212
responseDataList, err := processHTTPRequestsByHost(
@@ -304,11 +304,11 @@ func (rd ResponseData) PrintResponseData(isVerbose bool) {
304304
style.StatusCodeParse(rd.Response.StatusCode)))
305305

306306
if rd.Request.PrintResponseCertificates {
307-
RenderTLSData(rd.Response)
307+
RenderTLSData(os.Stdout, rd.Response)
308308
}
309309

310310
if rd.Request.PrintResponseHeaders {
311-
headersStr := parseResponseHeaders(
311+
headersStr := filterResponseHeaders(
312312
rd.Response.Header,
313313
rd.Request.ResponseHeadersFilter)
314314

@@ -330,26 +330,36 @@ func (rd ResponseData) PrintResponseData(isVerbose bool) {
330330
}
331331
}
332332

333-
func RenderTLSData(r *http.Response) {
333+
func RenderTLSData(w io.Writer, r *http.Response) {
334334
respTLS := r.TLS
335335
sl := style.CertKeyP4.Render
336336
sv := style.CertValue.Render
337337

338-
fmt.Println(style.LgSprintf(style.ItemKeyP3, "TLS:"))
338+
fmt.Fprintln(w, style.LgSprintf(style.ItemKeyP3, "TLS:"))
339339

340340
if respTLS == nil {
341-
fmt.Println(style.LgSprintf(style.CertKeyP4,
342-
"%s",
343-
style.Error.Render("No TLS connection state available")))
341+
fmt.Fprintln(
342+
w,
343+
style.LgSprintf(style.CertKeyP4,
344+
"%s",
345+
style.Error.Render("No TLS connection state available"),
346+
),
347+
)
344348

345349
return
346350
}
347351

348352
t := table.New().Border(style.LGDefBorder)
349-
t.Row(sl("Version"), sv(TLSVersionName(respTLS.Version)))
350-
t.Row(sl("CipherSuite"), sv(cipherSuiteName(respTLS.CipherSuite)))
351-
fmt.Println(t.Render())
353+
t.Row(
354+
sl("Version"),
355+
sv(TLSVersionName(respTLS.Version)),
356+
)
357+
t.Row(
358+
sl("CipherSuite"),
359+
sv(cipherSuiteName(respTLS.CipherSuite)),
360+
)
361+
fmt.Fprintln(w, t.Render())
352362
t.ClearRows()
353363

354-
certinfo.CertsToTables(respTLS.PeerCertificates)
364+
certinfo.CertsToTables(w, respTLS.PeerCertificates)
355365
}

0 commit comments

Comments
 (0)