Skip to content

Commit 0243ca3

Browse files
committed
add printResponseCertificates option
1 parent 3e39658 commit 0243ca3

9 files changed

Lines changed: 336 additions & 197 deletions

File tree

README.md

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
</p>
88

99
**HTTPS Wrench** is a Golang CLI program to make HTTPS requests based on a YAML configuration file.
10-
**HTTPS Wrench** was born from the desire of a disposable Bash script to become a reliable tool for mechanics of the World Wide Web.
11-
`https-wrench` will, one day, take the place of `curl` in the hearts and the eyes of whoever is about to migrate a DNS record from a webserver to a load balancer, reverse proxy, Ingress Gateway, Cloudfront distibution.
10+
**HTTPS Wrench** was born from the desire of a disposable Bash script to become a reliable tool
11+
for mechanics of the World Wide Web.
12+
`https-wrench` will, one day, take the place of `curl` in the hearts and the eyes of whoever is about
13+
to migrate a DNS record from a webserver to a load balancer, reverse proxy, Ingress Gateway,
14+
Cloudfront distribution.
1215

1316
## How to use
1417

@@ -49,18 +52,25 @@ https-wrench --show-sample-config > sample-wrench.yaml
4952
debug: false
5053
verbose: true
5154
requests:
52-
- name: httpBunCom
55+
- name: httpBunComGet
56+
5357
transportOverrideUrl: https://cat.httpbun.com:443
54-
userAgent: wrench-httpbun-ua
58+
clientTimeout: 3
59+
60+
requestDebug: false
61+
responseDebug: false
62+
63+
printResponseBody: true
64+
printResponseHeaders: true
65+
66+
userAgent: wrench-custom-ua
5567

5668
requestHeaders:
5769
- key: x-custom-header
5870
value: custom-header-value
5971
- key: x-api-key
6072
value: api-value
6173

62-
printResponseBody: true
63-
printResponseHeaders: true
6474
responseHeadersFilter:
6575
- X-Powered-By
6676
- Via
@@ -70,11 +80,18 @@ requests:
7080
- name: httpbun.com
7181
uriList:
7282
- /headers
73-
- /ip
7483
- /status/302
7584
- /status/404
7685
- /status/503
86+
87+
- name: httpBunComCerts
88+
89+
printResponseCertificates: true
90+
91+
hosts:
92+
- name: httpbun.com
7793
```
94+
7895
</details>
7996
8097
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/xenOs76/https-wrench/refs/heads/main/https-wrench.schema.json
2+
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
3+
---
4+
debug: false
5+
verbose: true
6+
requests:
7+
- name: badsslComErrors
8+
responseDebug: true
9+
# printResponseBody: true
10+
hosts:
11+
- name: untrusted-root.badssl.com
12+
- name: expired.badssl.com
13+
- name: wrong.host.badssl.com
14+
- name: incomplete-chain.badssl.com
15+
16+
- name: badsslComValid
17+
responseDebug: true
18+
# printResponseBody: true
19+
hosts:
20+
- name: sha256.badssl.com
21+
22+
# expired
23+
- name: sha384.badssl.com
24+
25+
# expired
26+
- name: sha512.badssl.com
27+
28+
- name: ecc256.badssl.com
29+
- name: ecc384.badssl.com
30+
- name: rsa2048.badssl.com
31+
- name: rsa4096.badssl.com
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,23 @@
44
debug: false
55
verbose: true
66
requests:
7-
- name: httpBinOrg
8-
# transportOverrideUrl: https://httpbin.org:443
7+
- name: httpBinGoOrg
8+
# transportOverrideUrl: https://httpbingo.org:443
99

10-
userAgent: httpbin-requests-ua
10+
# responseDebug: true
11+
userAgent: httpbingo-requests-ua
1112

1213
requestHeaders:
1314
- key: x-api-key
1415
value: aaa-bbb-ccc
1516
- key: x-custom-header
1617
value: custom-header-value
1718

19+
printResponseCertificates: true
1820
printResponseBody: true
1921

2022
hosts:
21-
- name: httpbin.org
23+
- name: httpbingo.org
2224
uriList:
2325
- /headers
2426
- /ip

https-wrench.schema.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@
5555
"printResponseHeaders": {
5656
"type": "boolean"
5757
},
58+
"printResponseCertificates": {
59+
"type": "boolean"
60+
},
5861
"responseHeadersFilter": {
5962
"type": "array",
6063
"items": {

src/cmd/config.go

Lines changed: 106 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,31 +25,33 @@ type RequestHeader struct {
2525
}
2626

2727
type RequestConfig struct {
28-
Name string `mapstructure:"name"`
29-
ClientTimeout time.Duration `mapstructure:"clientTimeout"`
30-
UserAgent string `mapstructure:"userAgent"`
31-
TransportOverrideUrl string `mapstructure:"transportOverrideUrl"`
32-
RequestDebug bool `mapstructure:"requestDebug"`
33-
RequestHeaders []RequestHeader `mapstructure:"requestHeaders"`
34-
RequestMethod string `mapstructure:"requestMethod"`
35-
RequestBody string `mapstructure:"requestBody"`
36-
ResponseDebug bool `mapstructure:"responseDebug"`
37-
ResponseHeadersFilter []string `mapstructure:"responseHeadersFilter"`
38-
PrintResponseBody bool `mapstructure:"printResponseBody"`
39-
PrintResponseHeaders bool `mapstructure:"printResponseHeaders"`
40-
Hosts []Host `mapstructure:"hosts"`
28+
Name string `mapstructure:"name"`
29+
ClientTimeout time.Duration `mapstructure:"clientTimeout"`
30+
UserAgent string `mapstructure:"userAgent"`
31+
TransportOverrideUrl string `mapstructure:"transportOverrideUrl"`
32+
RequestDebug bool `mapstructure:"requestDebug"`
33+
RequestHeaders []RequestHeader `mapstructure:"requestHeaders"`
34+
RequestMethod string `mapstructure:"requestMethod"`
35+
RequestBody string `mapstructure:"requestBody"`
36+
ResponseDebug bool `mapstructure:"responseDebug"`
37+
ResponseHeadersFilter []string `mapstructure:"responseHeadersFilter"`
38+
PrintResponseBody bool `mapstructure:"printResponseBody"`
39+
PrintResponseHeaders bool `mapstructure:"printResponseHeaders"`
40+
PrintResponseCertificates bool `mapstructure:"printResponseCertificates"`
41+
Hosts []Host `mapstructure:"hosts"`
4142
}
4243

4344
type ResponseData struct {
44-
RequestName string
45-
TransportAddress string
46-
Url string
47-
PrintResponseBody bool
48-
PrintResponseHeaders bool
49-
ResponseHeadersFilter []string
50-
ResponseBody string
51-
Response *http.Response
52-
Error error
45+
RequestName string
46+
TransportAddress string
47+
Url string
48+
PrintResponseBody bool
49+
PrintResponseHeaders bool
50+
PrintResponseCertificates bool
51+
ResponseHeadersFilter []string
52+
ResponseBody string
53+
Response *http.Response
54+
Error error
5355
}
5456

5557
type Config struct {
@@ -98,3 +100,85 @@ func (rd *ResponseData) ImportResponseBody() {
98100
}
99101
rd.ResponseBody = string(body)
100102
}
103+
104+
func (rd ResponseData) PrintResponseData() {
105+
fmt.Println(lgSprintf(styleItemKey,
106+
"- Url: %s",
107+
styleUrl.Render(rd.Url)),
108+
)
109+
110+
fmt.Print(lgSprintf(styleItemKeyP3, "StatusCode: "))
111+
112+
if rd.Error != nil {
113+
fmt.Println(lgSprintf(styleStatusError, "000"))
114+
fmt.Println(lgSprintf(
115+
styleItemKeyP3,
116+
"Error: %s",
117+
styleError.Render(rd.Error.Error())),
118+
)
119+
fmt.Println()
120+
} else {
121+
fmt.Println(lgSprintf(styleStatus, "%v", statusCodeParse(rd.Response.StatusCode)))
122+
123+
if rd.PrintResponseCertificates {
124+
RenderTlsData(rd.Response)
125+
}
126+
127+
if rd.PrintResponseHeaders {
128+
headersStr := parseResponseHeaders(rd.Response.Header, rd.ResponseHeadersFilter)
129+
130+
fmt.Println(lgSprintf(styleItemKeyP3, "Headers: "))
131+
fmt.Println(
132+
lgSprintf(
133+
styleHeaders,
134+
"%s",
135+
headersStr,
136+
),
137+
)
138+
}
139+
140+
if rd.PrintResponseBody {
141+
fmt.Println(lgSprintf(styleItemKeyP3, "Body:"))
142+
fmt.Println(rd.ResponseBody)
143+
}
144+
fmt.Println()
145+
}
146+
}
147+
148+
func RenderTlsData(r *http.Response) {
149+
150+
tls := r.TLS
151+
152+
fmt.Println(lgSprintf(styleItemKeyP3, "TLS:"))
153+
154+
if tls == nil {
155+
fmt.Println(lgSprintf(styleCertKeyP4, "%s", styleError.Render("No TLS connection state available")))
156+
return
157+
}
158+
159+
fmt.Println(lgSprintf(styleCertKeyP4, "Version: %s", styleCertValue.Render(tlsVersionName(tls.Version))))
160+
161+
fmt.Println(lgSprintf(styleCertKeyP4, "CipherSuite: %v", styleCertValue.Render(cipherSuiteName(tls.CipherSuite))))
162+
163+
for i, cert := range tls.PeerCertificates {
164+
165+
fmt.Println(lgSprintf(styleCertKeyP4.Bold(true), "Certificate %v:", i))
166+
fmt.Println(lgSprintf(styleCertKeyP5, "Subject: %s", styleCertValue.Render(cert.Subject.String())))
167+
168+
if len(cert.DNSNames) > 0 {
169+
dnsnames := "[ "
170+
for _, name := range cert.DNSNames {
171+
dnsnames += name + " "
172+
}
173+
dnsnames += "]"
174+
175+
fmt.Println(lgSprintf(styleCertKeyP5, "DNS Names: %v", styleCertValue.Render(dnsnames)))
176+
}
177+
178+
fmt.Println(lgSprintf(styleCertKeyP5, "Issuer: %s", styleCertValue.Render(cert.Issuer.String())))
179+
fmt.Println(lgSprintf(styleCertKeyP5, "Valid From: %s", styleCertValue.Render(cert.NotBefore.Format(time.RFC1123))))
180+
fmt.Println(lgSprintf(styleCertKeyP5, "Valid To: %s", styleCertValue.Render(cert.NotAfter.Format(time.RFC1123))))
181+
182+
}
183+
184+
}

src/cmd/embedded/config-example.yaml

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -36,42 +36,9 @@ requests:
3636
- /status/404
3737
- /status/503
3838

39-
- name: httpBinGoPost
39+
- name: httpBunComCerts
4040

41-
userAgent: wrench-request-post
42-
43-
printResponseBody: true
44-
printResponseHeaders: true
45-
46-
requestHeaders:
47-
- key: Content-Type
48-
value: text/plain
49-
50-
requestMethod: POST
51-
requestBody: request body via post
52-
53-
responseHeadersFilter:
54-
- Content-Type
41+
printResponseCertificates: true
5542

5643
hosts:
57-
- name: httpbingo.org
58-
uriList:
59-
- /post
60-
61-
- name: httpBinGoHead
62-
63-
# requestDebug: true
64-
# responseDebug: true
65-
66-
printResponseHeaders: true
67-
68-
requestHeaders:
69-
- key: Content-Type
70-
value: text/plain
71-
72-
requestMethod: HEAD
73-
74-
hosts:
75-
- name: httpbingo.org
76-
uriList:
77-
- /head
44+
- name: httpbun.com

src/cmd/handlers.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package cmd
2+
3+
import (
4+
"crypto/tls"
5+
"crypto/x509"
6+
"fmt"
7+
"time"
8+
)
9+
10+
func tlsVersionName(v uint16) string {
11+
switch v {
12+
case tls.VersionSSL30:
13+
return "SSL 3.0"
14+
case tls.VersionTLS10:
15+
return "TLS 1.0"
16+
case tls.VersionTLS11:
17+
return "TLS 1.1"
18+
case tls.VersionTLS12:
19+
return "TLS 1.2"
20+
case tls.VersionTLS13:
21+
return "TLS 1.3"
22+
default:
23+
return fmt.Sprintf("Unknown (0x%x)", v)
24+
}
25+
}
26+
27+
func cipherSuiteName(id uint16) string {
28+
cs := tls.CipherSuiteName(id)
29+
if cs == "" {
30+
return fmt.Sprintf("Unknown (0x%x)", id)
31+
}
32+
return cs
33+
}
34+
func printCertInfo(cert *x509.Certificate, depth int) {
35+
prefix := ""
36+
for i := 0; i < depth; i++ {
37+
prefix += " "
38+
}
39+
fmt.Printf("%sSubject: %s\n", prefix, cert.Subject)
40+
fmt.Printf("%sIssuer: %s\n", prefix, cert.Issuer)
41+
fmt.Printf("%sValid From: %s\n", prefix, cert.NotBefore.Format(time.RFC1123))
42+
fmt.Printf("%sValid To: %s\n", prefix, cert.NotAfter.Format(time.RFC1123))
43+
fmt.Printf("%sDNS Names: %v\n", prefix, cert.DNSNames)
44+
// fmt.Printf("%sEmail: %v\n", prefix, cert.EmailAddresses)
45+
// fmt.Printf("%sIP Addrs: %v\n", prefix, cert.IPAddresses)
46+
fmt.Println()
47+
}

0 commit comments

Comments
 (0)