Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b03077b
refactor: decouple writer in requests printing functions
xenOs76 Nov 29, 2025
ab47545
refactor: decouple enable flag and header generation for SetProxyProt…
xenOs76 Nov 29, 2025
c447ab5
refactor: PrintCmd function call in requests_handlers
xenOs76 Nov 29, 2025
6080649
ci: add some tests for requests
xenOs76 Nov 29, 2025
c562210
fix: non-TLS test case out of main loop in TestPrintResponseDebug
xenOs76 Nov 29, 2025
101de86
refactor: check nil http Response in PrintResponseDebug
xenOs76 Nov 29, 2025
cb65898
refactor: manage error in PrintRequestDebug call
xenOs76 Nov 29, 2025
7eccb1b
fix: typo in TestNewHTTPClientFromRequestConfig
xenOs76 Nov 29, 2025
6c67a1d
refactor: TestPrintRespondeDebug to decrease assert check over partia…
xenOs76 Nov 29, 2025
97b2229
fix: typos in TestPrintRequestDebug
xenOs76 Nov 29, 2025
0c05e61
refactor: SetServerName check for empty string and url as input
xenOs76 Nov 29, 2025
3e31574
refactor: tests related to SetServerName
xenOs76 Nov 29, 2025
15b1478
fix: typos in tests
xenOs76 Nov 29, 2025
93bcea6
refactor: TestPrintCmd to check for partial string match
xenOs76 Nov 29, 2025
7035520
refactor: remove unused error retuened by SetProxyProtocolV2
xenOs76 Nov 29, 2025
3f7bfa4
fix: error checking and typos
xenOs76 Nov 29, 2025
40358d3
ci: add go code checks to GitHub Actions
xenOs76 Nov 29, 2025
beaca2d
ci: update checkout and Setup Go actions
xenOs76 Nov 29, 2025
9c4eb6d
fix: error assertion in SetClientTimeout test
xenOs76 Nov 29, 2025
23d605b
ci: wrap go-version in release action and update go.mod
xenOs76 Nov 29, 2025
463ba58
refactor: error handling
xenOs76 Nov 29, 2025
e20fa09
refactor: validate transportAddress value returned by RequestConfig H…
xenOs76 Nov 29, 2025
e3f9841
refactor: validate on empty byte list instead of comparing when Print…
xenOs76 Nov 29, 2025
0e46054
refactor: update test struct to manage cases causing errors
xenOs76 Nov 29, 2025
d4203ab
refactor: test loops using t.Parallel() to import a local copy of the…
xenOs76 Nov 29, 2025
b5a27a7
ci: test errors in PrintResponseDebug
xenOs76 Nov 29, 2025
e9b5db3
refactor: replace assert with require in SetInsecureSkipVerify
xenOs76 Nov 29, 2025
de18e71
ci: test malformed HTTP client in SetServerName
xenOs76 Nov 29, 2025
969f42b
ci: add tests to check nil or incomplete HTTP client in RequestHTTPCl…
xenOs76 Nov 29, 2025
cdf659e
refactor: require no error in SetClientTimeout
xenOs76 Nov 29, 2025
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
42 changes: 42 additions & 0 deletions .github/workflows/codeChecks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
name: code checks
on:
push:
paths:
- "cmd/**"
- "internal/**"
- "pkg/**"
- "*.go"
- "go.*"
jobs:
code_check_job:

runs-on: ubuntu-latest
strategy:
matrix:
go-version: ['1.24', '1.25']

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go-version }}

- name: Install dependencies
run: go get .

- name: Build
run: go build -v ./...

- name: Test with the Go CLI
run: go test -v ./...

- name: Check for vulnerabilities
uses: golang/govulncheck-action@v1
with:
go-version-input: ${{ matrix.go-version }}
go-package: ./...
work-dir: .
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
DOCKER_CLI_EXPERIMENTAL: "enabled"
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: cachix/install-nix-action@v31
Expand All @@ -28,9 +28,9 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GH_GORELEASER_TOKEN }}
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v6
with:
go-version: 1.24
go-version: '1.24.9'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
Expand Down
22 changes: 0 additions & 22 deletions .github/workflows/vulncheck.yml

This file was deleted.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/xenos76/https-wrench

go 1.24.4
go 1.24.9

require (
github.com/alecthomas/assert/v2 v2.11.0
Expand Down
100 changes: 69 additions & 31 deletions internal/requests/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
"crypto/x509"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/http/httputil"
"os"
"strings"
"time"

Expand Down Expand Up @@ -181,11 +183,13 @@ func (r *RequestsMetaConfig) SetRequests(requests []RequestConfig) *RequestsMeta
return r
}

func (r *RequestsMetaConfig) PrintCmd() {
func (r *RequestsMetaConfig) PrintCmd(w io.Writer) {
if r.RequestVerbose {
fmt.Println()
fmt.Println(style.LgSprintf(style.Cmd, "Requests"))
fmt.Println()
fmt.Fprintf(
w,
"\n%s\n",
style.LgSprintf(style.Cmd, "Requests"),
)
}
}

Expand All @@ -201,50 +205,62 @@ func (r *RequestConfig) PrintTitle(isVerbose bool) {
}
}

func (r *RequestConfig) PrintRequestDebug(req *http.Request) {
func (r *RequestConfig) PrintRequestDebug(w io.Writer, req *http.Request) error {
if req == nil {
return errors.New("nil pointer to http.Request")
}

if r.RequestDebug {
reqDump, err := httputil.DumpRequestOut(req, true)
if err != nil {
fmt.Printf("Warning: failed to dump request: %v\n", err)
return
fmt.Fprintf(w, "Warning: failed to dump request: %v\n", err)
return err
}

fmt.Printf("Requesting url: %s\n", req.URL)
fmt.Printf("Request dump:\n%s\n", string(reqDump))
_, err = fmt.Fprintf(w, "Requesting url: %s\nRequest dump:\n%s\n", req.URL, string(reqDump))

return err
}

return nil
}

func (r *RequestConfig) PrintResponseDebug(resp *http.Response) {
func (r *RequestConfig) PrintResponseDebug(w io.Writer, resp *http.Response) {
// TODO: return an error
if resp == nil {
return
}

if r.ResponseDebug {
respDump, err := httputil.DumpResponse(resp, true)
if err != nil {
fmt.Printf("Warning: failed to dump response: %v\n", err)
fmt.Fprintf(w, "Warning: failed to dump response: %v\n", err)
return
}

fmt.Printf("Requested url: %s\n", resp.Request.URL)
fmt.Printf("Response dump:\n%s\n", string(respDump))
fmt.Fprintf(w, "Requested url: %s\n", resp.Request.URL)
fmt.Fprintf(w, "Response dump:\n%s\n", string(respDump))

if resp.TLS != nil {
fmt.Println("TLS:")
fmt.Printf("Version: %v\n", TLSVersionName(resp.TLS.Version))
fmt.Printf("CipherSuite: %v\n", cipherSuiteName(resp.TLS.CipherSuite))
fmt.Fprintln(w, "TLS:")
fmt.Fprintf(w, "Version: %v\n", TLSVersionName(resp.TLS.Version))
fmt.Fprintf(w, "CipherSuite: %v\n", cipherSuiteName(resp.TLS.CipherSuite))

for i, cert := range resp.TLS.PeerCertificates {
fmt.Printf("Certificate %d:\n", i)
fmt.Fprintf(w, "Certificate %d:\n", i)
certinfo.PrintCertInfo(cert, 1)
}

for i, chain := range resp.TLS.VerifiedChains {
fmt.Printf("Verified Chain %d:\n", i)
fmt.Fprintf(w, "Verified Chain %d:\n", i)

for j, cert := range chain {
fmt.Printf(" Cert %d:\n", j)
fmt.Fprintf(w, " Cert %d:\n", j)
certinfo.PrintCertInfo(cert, 2)
}
}
} else {
fmt.Println("TLS: Not available (non-TLS connection)")
fmt.Fprintln(w, "TLS: Not available (non-TLS connection)")
}
}
}
Expand Down Expand Up @@ -275,6 +291,14 @@ func (rc *RequestHTTPClient) SetServerName(serverName string) (*RequestHTTPClien
"*RequestHTTPClient.client is nil. Use NewRequestHTTPClient to initialize")
}

if serverName == emptyString {
return nil, errors.New("serverName cannot be empty")
}

if strings.Contains(serverName, "://") {
return nil, fmt.Errorf("serverName should be a hostname, not a URL: %s", serverName)
}

transport, ok := rc.client.Transport.(*http.Transport)
if !ok {
return nil, fmt.Errorf("expected *http.Transport, got %T", rc.client.Transport)
Expand Down Expand Up @@ -406,9 +430,15 @@ func (rc *RequestHTTPClient) SetTransportOverride(transportURL string) (*Request
return rc, nil
}

func (rc *RequestHTTPClient) SetProxyProtocolV2(header proxyproto.Header) (*RequestHTTPClient, error) {
func (rc *RequestHTTPClient) SetProxyProtocolV2(enable bool) *RequestHTTPClient {
rc.enableProxyProtoV2 = enable

return rc
}

func (rc *RequestHTTPClient) SetProxyProtocolHeader(header proxyproto.Header) (*RequestHTTPClient, error) {
if rc.transportAddress == emptyString {
return nil, errors.New("SetProxyProtocolV2 failed: transportOverrideURL not set")
return nil, errors.New("SetProxyProtocolHeader failed: transportOverrideURL not set")
}

if rc.client == nil {
Expand Down Expand Up @@ -457,18 +487,17 @@ func (rc *RequestHTTPClient) SetClientTimeout(timeout int) (*RequestHTTPClient,
"*RequestHTTPClient.client is nil. Use NewRequestHTTPClient to initialize")
}

if timeout < 0 {
return nil, fmt.Errorf("timeout value must be positive: %v provided", timeout)
}

t := time.Duration(timeout) * time.Second
rc.client.Timeout = t

return rc, nil
}

func NewHTTPClientFromRequestConfig(r RequestConfig, serverName string, caPool *x509.CertPool) (*RequestHTTPClient, error) {
if r.EnableProxyProtocolV2 && r.TransportOverrideURL == emptyString {
return nil, errors.New(
"if EnableProxyProtocolV2 is true, a TransportOverrideURL must be set")
}

reqClient := NewRequestHTTPClient()

_, err := reqClient.SetCACertsPool(caPool)
Expand Down Expand Up @@ -501,15 +530,22 @@ func NewHTTPClientFromRequestConfig(r RequestConfig, serverName string, caPool *
return nil, fmt.Errorf("SetTransportOverride error: %w", err)
}

reqClient.SetProxyProtocolV2(r.EnableProxyProtocolV2)

if r.EnableProxyProtocolV2 && r.TransportOverrideURL == emptyString {
return nil, errors.New(
"if EnableProxyProtocolV2 is true, a TransportOverrideURL must be set")
}

if r.EnableProxyProtocolV2 && reqClient.transportAddress != emptyString {
header, err := proxyProtoHeaderFromRequest(r, serverName)
if err != nil {
return nil, fmt.Errorf("error creating proxyproto Header: %w", err)
}

_, err = reqClient.SetProxyProtocolV2(header)
_, err = reqClient.SetProxyProtocolHeader(header)
if err != nil {
return nil, fmt.Errorf("SetProxyProtocolV2 error: %w", err)
return nil, fmt.Errorf("SetProxyProtocolHeader error: %w", err)
}
}

Expand Down Expand Up @@ -563,7 +599,9 @@ func processHTTPRequestsByHost(r RequestConfig, caPool *x509.CertPool, isVerbose
req.Header.Add(header.Key, header.Value)
}

r.PrintRequestDebug(req)
if err := r.PrintRequestDebug(os.Stdout, req); err != nil {
fmt.Fprintf(os.Stderr, "Warning: PrintRequestDebug failed: %v\n", err)
}

resp, err := reqClient.client.Do(req)
if err != nil {
Expand All @@ -580,7 +618,7 @@ func processHTTPRequestsByHost(r RequestConfig, caPool *x509.CertPool, isVerbose
continue
}

r.PrintResponseDebug(resp)
r.PrintResponseDebug(os.Stdout, resp)

responseData.Response = resp

Expand Down
3 changes: 2 additions & 1 deletion internal/requests/requests_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net"
"net/http"
"net/url"
"os"
"regexp"
"slices"
"strconv"
Expand Down Expand Up @@ -205,7 +206,7 @@ func proxyProtoHeaderFromRequest(r RequestConfig, serverName string) (proxyproto
func HandleRequests(cfg *RequestsMetaConfig) (map[string][]ResponseData, error) {
responseDataMap := make(map[string][]ResponseData)

cfg.PrintCmd()
cfg.PrintCmd(os.Stdout)

for _, r := range cfg.Requests {
responseDataList, err := processHTTPRequestsByHost(
Expand Down
Loading
Loading