Skip to content

Commit 06c1cdf

Browse files
committed
fix: address browser curl review findings
Made-with: Cursor
1 parent d78657a commit 06c1cdf

3 files changed

Lines changed: 80 additions & 23 deletions

File tree

‎cmd/browsers.go‎

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3301,17 +3301,43 @@ type BrowsersCurlInput struct {
33013301
Silent bool
33023302
}
33033303

3304-
func parseCurlHeaders(raw []string) map[string]string {
3304+
type silentCurlError struct {
3305+
err error
3306+
}
3307+
3308+
func (e silentCurlError) Error() string {
3309+
return e.err.Error()
3310+
}
3311+
3312+
func (e silentCurlError) Unwrap() error {
3313+
return e.err
3314+
}
3315+
3316+
func (e silentCurlError) Silent() bool {
3317+
return true
3318+
}
3319+
3320+
func curlError(in BrowsersCurlInput, err error) error {
3321+
if err == nil {
3322+
return nil
3323+
}
3324+
if in.Silent {
3325+
return silentCurlError{err: err}
3326+
}
3327+
return err
3328+
}
3329+
3330+
func parseCurlHeaders(raw []string) http.Header {
33053331
if len(raw) == 0 {
33063332
return nil
33073333
}
3308-
headers := make(map[string]string)
3334+
headers := make(http.Header)
33093335
for _, h := range raw {
33103336
k, v, ok := strings.Cut(h, ":")
33113337
if !ok {
33123338
continue
33133339
}
3314-
headers[strings.TrimSpace(k)] = strings.TrimSpace(v)
3340+
headers.Add(strings.TrimSpace(k), strings.TrimSpace(v))
33153341
}
33163342
return headers
33173343
}
@@ -3328,7 +3354,7 @@ func readCurlBody(in BrowsersCurlInput) (string, error) {
33283354
return string(data), nil
33293355
}
33303356

3331-
func hasCurlHeader(headers map[string]string, name string) bool {
3357+
func hasCurlHeader(headers http.Header, name string) bool {
33323358
for key := range headers {
33333359
if strings.EqualFold(key, name) {
33343360
return true
@@ -3375,18 +3401,18 @@ func openCurlOutputFile(path string) (io.Writer, func() error, error) {
33753401
func (b BrowsersCmd) Curl(ctx context.Context, in BrowsersCurlInput) error {
33763402
body, err := readCurlBody(in)
33773403
if err != nil {
3378-
return err
3404+
return curlError(in, err)
33793405
}
33803406

33813407
method := in.Method
33823408
if method == "" {
33833409
method = "GET"
3384-
if in.Head {
3385-
method = "HEAD"
3386-
}
33873410
if body != "" {
33883411
method = "POST"
33893412
}
3413+
if in.Head {
3414+
method = "HEAD"
3415+
}
33903416
}
33913417
include := in.Include || in.Head
33923418

@@ -3397,24 +3423,26 @@ func (b BrowsersCmd) Curl(ctx context.Context, in BrowsersCurlInput) error {
33973423

33983424
// Seed the SDK's browser route cache before constructing the raw curl client.
33993425
if _, err := b.browsers.Get(ctx, in.Identifier, kernel.BrowserGetParams{}); err != nil {
3400-
return util.CleanedUpSdkError{Err: err}
3426+
return curlError(in, util.CleanedUpSdkError{Err: err})
34013427
}
34023428

34033429
httpClient, err := b.browsers.HTTPClient(in.Identifier)
34043430
if err != nil {
3405-
return util.CleanedUpSdkError{Err: err}
3431+
return curlError(in, util.CleanedUpSdkError{Err: err})
34063432
}
34073433
if in.MaxTime > 0 {
34083434
httpClient.Timeout = in.MaxTime
34093435
}
34103436

34113437
req, err := http.NewRequestWithContext(ctx, method, in.URL, bodyReader)
34123438
if err != nil {
3413-
return fmt.Errorf("creating request: %w", err)
3439+
return curlError(in, fmt.Errorf("creating request: %w", err))
34143440
}
34153441
headers := parseCurlHeaders(in.Headers)
3416-
for key, value := range headers {
3417-
req.Header.Set(key, value)
3442+
for key, values := range headers {
3443+
for _, value := range values {
3444+
req.Header.Add(key, value)
3445+
}
34183446
}
34193447
if body != "" && !hasCurlHeader(headers, "Content-Type") {
34203448
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
@@ -3423,7 +3451,7 @@ func (b BrowsersCmd) Curl(ctx context.Context, in BrowsersCurlInput) error {
34233451
start := time.Now()
34243452
resp, err := httpClient.Do(req)
34253453
if err != nil {
3426-
return fmt.Errorf("request failed: %w", err)
3454+
return curlError(in, fmt.Errorf("request failed: %w", err))
34273455
}
34283456
defer resp.Body.Close()
34293457

@@ -3432,7 +3460,7 @@ func (b BrowsersCmd) Curl(ctx context.Context, in BrowsersCurlInput) error {
34323460
if in.OutputFile != "" {
34333461
f, err := os.Create(in.OutputFile)
34343462
if err != nil {
3435-
return fmt.Errorf("creating output file: %w", err)
3463+
return curlError(in, fmt.Errorf("creating output file: %w", err))
34363464
}
34373465
outputFile = f
34383466
defer outputFile.Close()
@@ -3442,7 +3470,7 @@ func (b BrowsersCmd) Curl(ctx context.Context, in BrowsersCurlInput) error {
34423470
if in.DumpHeader != "" {
34433471
headerWriter, closeHeaderWriter, err := openCurlOutputFile(in.DumpHeader)
34443472
if err != nil {
3445-
return fmt.Errorf("creating dump header file: %w", err)
3473+
return curlError(in, fmt.Errorf("creating dump header file: %w", err))
34463474
}
34473475
defer closeHeaderWriter()
34483476
writeCurlResponseHeaders(headerWriter, resp)
@@ -3455,7 +3483,7 @@ func (b BrowsersCmd) Curl(ctx context.Context, in BrowsersCurlInput) error {
34553483
timeTotal: time.Since(start),
34563484
}))
34573485
}
3458-
return fmt.Errorf("HTTP error: %s", resp.Status)
3486+
return curlError(in, fmt.Errorf("HTTP error: %s", resp.Status))
34593487
}
34603488

34613489
if include {
@@ -3467,9 +3495,9 @@ func (b BrowsersCmd) Curl(ctx context.Context, in BrowsersCurlInput) error {
34673495
sizeDownload, err = io.Copy(writer, resp.Body)
34683496
if err != nil {
34693497
if in.OutputFile != "" {
3470-
return fmt.Errorf("writing output file: %w", err)
3498+
return curlError(in, fmt.Errorf("writing output file: %w", err))
34713499
}
3472-
return err
3500+
return curlError(in, err)
34733501
}
34743502
}
34753503

‎cmd/browsers_test.go‎

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ func (f *FakeBrowsersService) LoadExtensions(ctx context.Context, id string, bod
125125
func TestBrowsersCurlRawUsesBrowserHTTPClient(t *testing.T) {
126126
var (
127127
gotMethod string
128-
gotHeader string
128+
gotHeaders []string
129129
gotContentType string
130130
gotBody string
131131
)
@@ -135,7 +135,7 @@ func TestBrowsersCurlRawUsesBrowserHTTPClient(t *testing.T) {
135135
require.NoError(t, err)
136136

137137
gotMethod = r.Method
138-
gotHeader = r.Header.Get("X-Test")
138+
gotHeaders = r.Header.Values("X-Test")
139139
gotContentType = r.Header.Get("Content-Type")
140140
gotBody = string(body)
141141

@@ -163,7 +163,7 @@ func TestBrowsersCurlRawUsesBrowserHTTPClient(t *testing.T) {
163163
err := b.Curl(context.Background(), BrowsersCurlInput{
164164
Identifier: "brw_123",
165165
URL: srv.URL + "/target",
166-
Headers: []string{"X-Test: yes"},
166+
Headers: []string{"X-Test: yes", "X-Test: also-yes"},
167167
Data: "hello",
168168
OutputFile: outputFile,
169169
Silent: true,
@@ -174,7 +174,7 @@ func TestBrowsersCurlRawUsesBrowserHTTPClient(t *testing.T) {
174174
require.NoError(t, err)
175175
assert.True(t, getCalled)
176176
assert.Equal(t, http.MethodPost, gotMethod)
177-
assert.Equal(t, "yes", gotHeader)
177+
assert.Equal(t, []string{"yes", "also-yes"}, gotHeaders)
178178
assert.Equal(t, "application/x-www-form-urlencoded", gotContentType)
179179
assert.Equal(t, "hello", gotBody)
180180
assert.Equal(t, "proxied", string(data))
@@ -239,6 +239,7 @@ func TestBrowsersCurlHeadWritesHeadersOnly(t *testing.T) {
239239
err := b.Curl(context.Background(), BrowsersCurlInput{
240240
Identifier: "brw_123",
241241
URL: srv.URL + "/target",
242+
Data: "hello",
242243
OutputFile: outputFile,
243244
Head: true,
244245
})
@@ -252,6 +253,29 @@ func TestBrowsersCurlHeadWritesHeadersOnly(t *testing.T) {
252253
assert.NotContains(t, string(data), "proxied")
253254
}
254255

256+
func TestBrowsersCurlSilentWrapsErrors(t *testing.T) {
257+
fake := &FakeBrowsersService{
258+
GetFunc: func(ctx context.Context, id string, query kernel.BrowserGetParams, opts ...option.RequestOption) (*kernel.BrowserGetResponse, error) {
259+
return &kernel.BrowserGetResponse{}, nil
260+
},
261+
HTTPClientFunc: func(id string, opts ...option.RequestOption) (*http.Client, error) {
262+
return http.DefaultClient, nil
263+
},
264+
}
265+
266+
b := BrowsersCmd{browsers: fake}
267+
err := b.Curl(context.Background(), BrowsersCurlInput{
268+
Identifier: "brw_123",
269+
URL: "://not-a-url",
270+
Silent: true,
271+
})
272+
require.Error(t, err)
273+
274+
var silent interface{ Silent() bool }
275+
require.ErrorAs(t, err, &silent)
276+
assert.True(t, silent.Silent())
277+
}
278+
255279
func TestBrowsersCurlDumpHeaderAndWriteOut(t *testing.T) {
256280
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
257281
w.Header().Set("X-Test", "yes")

‎cmd/root.go‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"io"
78
"os"
@@ -184,6 +185,10 @@ func Execute(m Metadata) {
184185
fang.WithCommit(metadata.Commit),
185186
fang.WithErrorHandler(func(w io.Writer, styles fang.Styles, err error) {
186187
err = util.CleanedUpSdkError{Err: err}
188+
var silent interface{ Silent() bool }
189+
if errors.As(err, &silent) && silent.Silent() {
190+
return
191+
}
187192
// remove margins so that it matches other pterm.error "style"
188193
// we should add them back later as it looks cleaner
189194
errorTextStyle := styles.ErrorText.UnsetMargins()

0 commit comments

Comments
 (0)