@@ -3,6 +3,7 @@ package main
33import (
44 "bytes"
55 "context"
6+ "crypto/tls"
67 "encoding/json"
78 "io"
89 "net/http"
@@ -35,7 +36,7 @@ func (a *App) startup(ctx context.Context) {
3536 a .ctx = ctx
3637}
3738
38- func makeRequest (c * http.Client ,
39+ func doRequest (c * http.Client ,
3940 r * http.Request ) ([]byte , * http.Response , error ) {
4041 resp , err := c .Do (r )
4142 if err != nil {
@@ -62,6 +63,29 @@ func HeadersToStr(h *http.Header) string {
6263 return result
6364}
6465
66+ // BodySpec describes the request body type and content.
67+ type BodySpec struct {
68+ Type string `json:"type"` // "none" | "json" | "form" | "raw"
69+ Raw string `json:"raw"` // raw/json text or pre-encoded form body
70+ }
71+
72+ // RequestSettings contains per-request client settings.
73+ type RequestSettings struct {
74+ TimeoutMs int `json:"timeoutMs"` // 0 = use default (50s)
75+ FollowRedirects bool `json:"followRedirects"` // true = follow
76+ VerifyTLS bool `json:"verifyTLS"` // true = verify
77+ }
78+
79+ // RequestSpec is the structured request used by Send().
80+ type RequestSpec struct {
81+ Method string `json:"method"`
82+ URL string `json:"url"`
83+ Headers map [string ]string `json:"headers"`
84+ Body BodySpec `json:"body"`
85+ Settings RequestSettings `json:"settings"`
86+ }
87+
88+ // RequestResult is the response shape returned to the UI.
6589type RequestResult struct {
6690 Method string `json:"Method"`
6791 URL string `json:"URL"`
@@ -70,6 +94,97 @@ type RequestResult struct {
7094 Body string `json:"Body"`
7195 HeadersStr string `json:"HeadersStr"`
7296 Error string `json:"Error"`
97+ Status int `json:"Status"`
98+ StatusText string `json:"StatusText"`
99+ DurationMs int64 `json:"DurationMs"`
100+ SizeBytes int `json:"SizeBytes"`
101+ }
102+
103+ // buildClient creates an http.Client configured from RequestSettings.
104+ func buildClient (s RequestSettings ) * http.Client {
105+ timeout := 50 * time .Second
106+ if s .TimeoutMs > 0 {
107+ timeout = time .Duration (s .TimeoutMs ) * time .Millisecond
108+ }
109+
110+ tr := & http.Transport {
111+ TLSClientConfig : & tls.Config {InsecureSkipVerify : ! s .VerifyTLS }, //nolint:gosec
112+ }
113+
114+ client := & http.Client {
115+ Timeout : timeout ,
116+ Transport : tr ,
117+ }
118+
119+ if ! s .FollowRedirects {
120+ client .CheckRedirect = func (_ * http.Request , _ []* http.Request ) error {
121+ return http .ErrUseLastResponse
122+ }
123+ }
124+
125+ return client
126+ }
127+
128+ // Send builds a per-request http.Client from Settings and executes the request.
129+ func (a * App ) Send (spec RequestSpec ) RequestResult {
130+ result := RequestResult {
131+ URL : spec .URL ,
132+ Method : spec .Method ,
133+ }
134+
135+ var bodyReader io.Reader
136+ if spec .Body .Type != "none" && spec .Body .Raw != "" {
137+ bodyReader = strings .NewReader (spec .Body .Raw )
138+ result .RequestBody = spec .Body .Raw
139+ }
140+
141+ r , err := http .NewRequest (spec .Method , spec .URL , bodyReader )
142+ if err != nil {
143+ result .Error = err .Error ()
144+ return result
145+ }
146+
147+ for key , value := range spec .Headers {
148+ r .Header .Add (key , value )
149+ }
150+
151+ result .ReqHeaders = HeadersToStr (& r .Header )
152+
153+ client := buildClient (spec .Settings )
154+
155+ start := time .Now ()
156+ res , httpResp , err := doRequest (client , r )
157+ result .DurationMs = time .Since (start ).Milliseconds ()
158+
159+ if err != nil {
160+ result .Error = err .Error ()
161+ // still capture status if response was partially received
162+ if httpResp != nil {
163+ result .Status = httpResp .StatusCode
164+ result .StatusText = httpResp .Status
165+ }
166+ return result
167+ }
168+
169+ result .Status = httpResp .StatusCode
170+ result .StatusText = httpResp .Status
171+ result .HeadersStr = HeadersToStr (& httpResp .Header )
172+ result .SizeBytes = len (res )
173+
174+ // Pretty-print JSON bodies; on failure, return raw body without error
175+ b := bytes .NewBuffer (make ([]byte , 0 , len (res )))
176+ if jsonErr := json .Indent (b , res , "" , " " ); jsonErr == nil {
177+ result .Body = b .String ()
178+ } else {
179+ result .Body = string (res )
180+ }
181+
182+ return result
183+ }
184+
185+ // SaveTextFile opens a native save dialog and writes text content to the chosen file.
186+ func (a * App ) SaveTextFile (filename , contents string ) error {
187+ return saveTextFile (a .ctx , filename , contents )
73188}
74189
75190func (a * App ) RunCurl (curl string ) RequestResult {
@@ -88,50 +203,42 @@ func (a *App) RunCurl(curl string) RequestResult {
88203 return res
89204}
90205
206+ // Header is a single key/value header pair (kept for Export compatibility).
91207type Header struct {
92208 Key string
93209 Value string
94210}
95211
96- // Greet returns a greeting for the given name
212+ // MakeRequest is kept for backward compatibility; it delegates to Send.
97213func (a * App ) MakeRequest (
98214 urlIn string ,
99215 method string ,
100216 body string ,
101217 headers Headers ,
102218) RequestResult {
103- result := RequestResult {
104- URL : urlIn ,
105- Method : method ,
106- RequestBody : body ,
107- }
108- rbody := bytes .NewBuffer ([]byte (body ))
109- r , err := http .NewRequest (method , urlIn , rbody )
110- if err != nil {
111- result .Error = err .Error ()
112- return result
219+ hdrs := make (map [string ]string , len (headers ))
220+ for k , v := range headers {
221+ hdrs [k ] = v
113222 }
114223
115- for key , value := range headers {
116- r .Header .Add (key , value )
117- }
118-
119- res , httpResp , err := makeRequest (a .client , r )
120- if err != nil {
121- result .Error = err .Error ()
122- return result
123- }
124-
125- result .HeadersStr = HeadersToStr (& httpResp .Header )
126- b := bytes .NewBuffer (make ([]byte , 0 , len (res )))
127- err = json .Indent (b , res , "\n " , " " )
128- if err != nil {
129- return RequestResult {
130- Body : string (res ),
131- Error : err .Error (),
132- }
224+ spec := RequestSpec {
225+ Method : method ,
226+ URL : urlIn ,
227+ Headers : hdrs ,
228+ Body : BodySpec {
229+ Type : "raw" ,
230+ Raw : body ,
231+ },
232+ Settings : RequestSettings {
233+ FollowRedirects : true ,
234+ VerifyTLS : true ,
235+ },
133236 }
134237
135- result .Body = b .String ()
238+ result := a .Send (spec )
239+ // preserve old fields that Send populates differently
240+ result .Method = method
241+ result .URL = urlIn
242+ result .RequestBody = body
136243 return result
137244}
0 commit comments