11package render
22
33import (
4+ "fmt"
5+ "net"
6+ "sort"
7+ "strings"
8+
49 "github.com/aorith/varnishlog-parser/vsl"
510 "github.com/aorith/varnishlog-parser/vsl/tag"
611)
712
813type HTTPRequest struct {
914 method string
1015 host string
16+ port string
1117 url string
1218 headers []Header
1319}
@@ -19,17 +25,21 @@ type Header struct {
1925
2026type Backend struct {
2127 host string
22- port int
28+ port string
29+ }
30+
31+ func NewBackend (host string , port string ) * Backend {
32+ return & Backend {host : host , port : port }
2333}
2434
2535// NewHTTPRequest constructs an HTTPRequest from a Varnish transaction.
2636// Returns nil if the transaction type is session.
2737//
2838// If fromResponse is true, fields are extracted from the response; otherwise, from the request.
2939// If received is true, initial (received) headers are used; otherwise, headers after VCL processing.
30- func NewHTTPRequest (tx * vsl.Transaction , fromResponse , received bool ) * HTTPRequest {
40+ func NewHTTPRequest (tx * vsl.Transaction , fromResponse , received bool ) ( * HTTPRequest , error ) {
3141 if tx .Type () == vsl .TxTypeSession {
32- return nil
42+ return nil , fmt . Errorf ( "cannot create an http request from a transaction of type session" )
3343 }
3444
3545 var headers vsl.Headers
@@ -40,12 +50,24 @@ func NewHTTPRequest(tx *vsl.Transaction, fromResponse, received bool) *HTTPReque
4050 }
4151
4252 host := headers .Get ("host" , received )
53+ port := ""
54+ if strings .Contains (host , ":" ) {
55+ var err error
56+ host , port , err = ParseBackend (headers .Get ("host" , received ))
57+ if err != nil {
58+ return nil , err
59+ }
60+ }
61+
4362 httpHeaders := []Header {}
4463 for name , h := range headers {
4564 if name == vsl .HdrNameHost {
4665 continue
4766 }
4867 for _ , v := range h .Values (received ) {
68+ if v .State () == vsl .HdrStateDeleted {
69+ continue
70+ }
4971 httpHeaders = append (httpHeaders , Header {name : name , value : v .Value ()})
5072 }
5173 }
@@ -60,10 +82,74 @@ func NewHTTPRequest(tx *vsl.Transaction, fromResponse, received bool) *HTTPReque
6082 url = tx .RecordValueByTag (tag .BereqURL , received )
6183 }
6284
85+ sort .Slice (httpHeaders , func (i , j int ) bool {
86+ return httpHeaders [i ].name < httpHeaders [j ].name
87+ })
88+
6389 return & HTTPRequest {
6490 method : method ,
6591 host : host ,
92+ port : port ,
6693 url : url ,
6794 headers : httpHeaders ,
95+ }, nil
96+ }
97+
98+ // CurlCommand generates a new curl command as a string
99+ // scheme can be "auto", "http://" or "https://"
100+ func (r * HTTPRequest ) CurlCommand (scheme string , backend * Backend ) string {
101+ var s strings.Builder
102+
103+ // Parse scheme
104+ switch scheme {
105+ case "auto" :
106+ if r .port == "443" {
107+ scheme = "https://"
108+ } else {
109+ // default to http for 80, empty, or any other port
110+ scheme = "http://"
111+ }
112+ case "http://" , "https://" :
113+ // keep as-is
114+ default :
115+ return "invalid scheme: " + scheme
116+ }
117+
118+ // Build host URL, append port only when provided
119+ hostURL := r .host
120+ if r .port != "" {
121+ hostURL = net .JoinHostPort (r .host , r .port )
122+ }
123+
124+ // Initial command
125+ s .WriteString (fmt .Sprintf (`curl "%s%s%s"` + " \\ \n " , scheme , hostURL , r .url ))
126+
127+ switch r .method {
128+ case "POST" , "PUT" , "PATCH" :
129+ s .WriteString (" -X " + r .method + " \\ \n " )
130+ s .WriteString (" -d '<body-unavailable>' \\ \n " )
131+ default :
132+ s .WriteString (" -X " + r .method + " \\ \n " )
133+ }
134+
135+ // Headers
136+ for _ , h := range r .headers {
137+ if h .name == vsl .HdrNameHost {
138+ continue
139+ }
140+ hdrVal := strings .ReplaceAll (h .value , `"` , `\"` )
141+ s .WriteString (fmt .Sprintf (` -H "%s: %s"` + " \\ \n " , h .name , hdrVal ))
142+ }
143+
144+ // Default parameters
145+ s .WriteString (" -qsv -k -o /dev/null" )
146+
147+ // Connect-to
148+ // --connect-to HOST1:PORT1:HOST2:PORT2
149+ // when you would connect to HOST1:PORT1, actually connect to HOST2:PORT2
150+ if backend != nil {
151+ s .WriteString (fmt .Sprintf (" \\ \n " + `--connect-to "%s:%s:%s"` , hostURL , backend .host , backend .port ))
68152 }
153+
154+ return s .String ()
69155}
0 commit comments