@@ -31,6 +31,13 @@ import (
3131 "github.com/wstool/wst/run/services"
3232)
3333
34+ type Protocol string
35+
36+ const (
37+ ProtocolHTTP11 Protocol = "http1.1"
38+ ProtocolHTTP2 Protocol = "http2"
39+ )
40+
3441type Maker interface {
3542 Make (
3643 config * types.RequestAction ,
@@ -67,6 +74,28 @@ func (m *ActionMaker) Make(
6774 return nil , errors .New ("TLS configuration is only valid for HTTPS requests" )
6875 }
6976
77+ // Set default protocols if not specified
78+ protocols := config .Protocols
79+ if len (protocols ) == 0 {
80+ if config .Scheme == "https" {
81+ // Default for HTTPS: allow both HTTP/1.1 and HTTP/2
82+ protocols = []string {string (ProtocolHTTP11 ), string (ProtocolHTTP2 )}
83+ } else {
84+ // Default for HTTP: only HTTP/1.1 (h2c is not commonly supported)
85+ protocols = []string {string (ProtocolHTTP11 )}
86+ }
87+ }
88+
89+ // Convert strings to Protocol type and validate
90+ validatedProtocols := make ([]Protocol , 0 , len (protocols ))
91+ for _ , protoStr := range protocols {
92+ proto := Protocol (protoStr )
93+ if proto == ProtocolHTTP2 && config .Scheme != "https" {
94+ m .fnd .Logger ().Infof ("Using unencrypted HTTP/2 (h2c) over plain HTTP" )
95+ }
96+ validatedProtocols = append (validatedProtocols , proto )
97+ }
98+
7099 return & Action {
71100 fnd : m .fnd ,
72101 service : svc ,
@@ -80,6 +109,7 @@ func (m *ActionMaker) Make(
80109 method : config .Method ,
81110 headers : config .Headers ,
82111 tls : & config .TLS ,
112+ protocols : validatedProtocols ,
83113 }, nil
84114}
85115
@@ -121,6 +151,7 @@ type Action struct {
121151 method string
122152 headers types.Headers
123153 tls * types.TLSClientConfig
154+ protocols []Protocol
124155}
125156
126157func (a * Action ) When () action.When {
@@ -136,34 +167,32 @@ func (a *Action) Timeout() time.Duration {
136167}
137168
138169func (a * Action ) Execute (ctx context.Context , runData runtime.Data ) (bool , error ) {
139- a .fnd .Logger ().Infof ("Executing request action" )
170+ a .fnd .Logger ().Infof ("Executing request action with HTTP protocols: %v" , a . protocols )
140171
141- // Create transport.
172+ // Create transport
142173 tr := & http.Transport {}
174+
175+ // Configure protocols using the Protocols API
176+ protocolConfig := a .buildProtocolConfig ()
177+ tr .Protocols = protocolConfig
178+
143179 if a .scheme == "https" {
144- tlsConfig := & tls.Config {
145- InsecureSkipVerify : a .tls .SkipVerify ,
146- }
147- if a .tls .CACert != "" {
148- caCert , err := a .service .FindCertificate (a .tls .CACert )
149- if err != nil {
150- return false , errors .Errorf ("CA certificate %s not found" , a .tls .CACert )
151- }
152- caCertPool := a .fnd .X509CertPool ()
153- if ! caCertPool .AppendCertFromPEM (caCert .Certificate .CertificateData ()) {
154- return false , errors .New ("failed to parse CA certificate" )
155- }
156- tlsConfig .RootCAs = caCertPool .CertPool ()
180+ tlsConfig , err := a .buildTLSConfig ()
181+ if err != nil {
182+ return false , err
157183 }
158184 tr .TLSClientConfig = tlsConfig
159185 }
160186
187+ a .fnd .Logger ().Debugf ("Protocol configuration: HTTP/1=%t, HTTP/2=%t, UnencryptedHTTP/2=%t" ,
188+ protocolConfig .HTTP1 (), protocolConfig .HTTP2 (), protocolConfig .UnencryptedHTTP2 ())
189+
161190 publicUrl , err := a .service .PublicUrl (a .scheme , a .path )
162191 if err != nil {
163192 return false , err
164193 }
165194
166- // Create the HTTP request.
195+ // Create the HTTP request
167196 req , err := http .NewRequestWithContext (ctx , a .method , publicUrl , nil )
168197 if err != nil {
169198 return false , err
@@ -178,27 +207,27 @@ func (a *Action) Execute(ctx context.Context, runData runtime.Data) (bool, error
178207 }
179208 }
180209
181- // Add headers to the request.
210+ // Add headers to the request
182211 for key , value := range a .headers {
183212 req .Header .Add (key , value )
184213 }
185214 a .fnd .Logger ().Debugf ("Sending request: %s" , requestToString (req ))
186215
187- // Send the request.
216+ // Send the request
188217 client := a .fnd .HttpClient (tr )
189218 resp , err := client .Do (req )
190219 if err != nil {
191220 return false , err
192221 }
193222 defer resp .Body .Close ()
194223
195- // Read the response body.
224+ // Read the response body
196225 body , err := readResponse (ctx , resp .Body )
197226 if err != nil {
198227 return false , err
199228 }
200229
201- // Create a ResponseData instance to hold both body and headers.
230+ // Create a ResponseData instance to hold both body and headers
202231 responseData := ResponseData {
203232 Status : resp .Status ,
204233 StatusCode : resp .StatusCode ,
@@ -207,16 +236,57 @@ func (a *Action) Execute(ctx context.Context, runData runtime.Data) (bool, error
207236 Headers : resp .Header ,
208237 }
209238
210- // Store the ResponseData in runData.
239+ // Store the ResponseData in runData
211240 key := fmt .Sprintf ("response/%s" , a .id )
212- a .fnd .Logger ().Debugf ("Storing response %s: %s" , key , responseData )
241+ a .fnd .Logger ().Debugf ("Storing response %s: %s (protocol: %s) " , key , responseData , resp . Proto )
213242 if err := runData .Store (key , responseData ); err != nil {
214243 return false , err
215244 }
216245
217246 return true , nil
218247}
219248
249+ func (a * Action ) buildProtocolConfig () * http.Protocols {
250+ config := new (http.Protocols )
251+
252+ for _ , proto := range a .protocols {
253+ switch proto {
254+ case ProtocolHTTP11 :
255+ config .SetHTTP1 (true )
256+ case ProtocolHTTP2 :
257+ if a .scheme == "https" {
258+ // HTTP/2 over TLS
259+ config .SetHTTP2 (true )
260+ } else {
261+ // HTTP/2 cleartext (h2c) over plain HTTP
262+ config .SetUnencryptedHTTP2 (true )
263+ }
264+ }
265+ }
266+
267+ return config
268+ }
269+
270+ func (a * Action ) buildTLSConfig () (* tls.Config , error ) {
271+ tlsConfig := & tls.Config {
272+ InsecureSkipVerify : a .tls .SkipVerify ,
273+ }
274+
275+ if a .tls .CACert != "" {
276+ caCert , err := a .service .FindCertificate (a .tls .CACert )
277+ if err != nil {
278+ return nil , errors .Errorf ("CA certificate %s not found" , a .tls .CACert )
279+ }
280+ caCertPool := a .fnd .X509CertPool ()
281+ if ! caCertPool .AppendCertFromPEM (caCert .Certificate .CertificateData ()) {
282+ return nil , errors .New ("failed to parse CA certificate" )
283+ }
284+ tlsConfig .RootCAs = caCertPool .CertPool ()
285+ }
286+
287+ return tlsConfig , nil
288+ }
289+
220290func requestToString (req * http.Request ) string {
221291 var headers string
222292 for name , values := range req .Header {
0 commit comments