googleapis / googleapis/gapic-showcase

FR(resumable uploads): Add ability to omit X-Goog-Upload-Status header

Open
#1,685 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
183
Forks
55
Avg merge
1d 1h
Merged PRs (30d)
8

Description

As requested in https://github.com/googleapis/gapic-showcase/issues/1682#issuecomment-5637628971, it would be great to have functionality in showcase to omit the `X-Goog-Upload-Status` header.

intermediate load balancers or proxies often return HTTP 429 or 5xx errors without attaching custom Google headers (`X-Goog-Upload-Status: active`). Showcase needs to simulate these proxy-level failures so clients can test their fallback recovery flows (such as querying upload progress after a raw proxy error).

## Current Behavior

Injected transient failures (e.g., `non_fatal_error_on_...`) always append the `X-Goog-Upload-Status: active` header to error responses.

## Desired Behavior
Add a boolean parameter `omit_status_header` to `X-Goog-Test-Scenario-Config`.

https://github.com/googleapis/gapic-showcase/blob/afd7bbb8e0d3e0a26cc4ac92bb281725b1ffbf1f/server/resumableupload/resumableupload.go#L42

When true, error responses use the configured code (like 429, 502, 503, or 504) but suppress the `X-Goog-Upload-Status` header.

The underlying session must remain active so recovery retries and queries can succeed.

## Proposed Fields

`omit_status_header` : (bool, default: false): Suppresses the upload status header on failure when true.

## Test Case

```
func TestProxyErrorWithoutUploadStatusOnFinalize(t *testing.T) {
mgr := resumableupload.NewManager()
handler := mgr.Middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
}))

// 1. Init session with omit_status_header enabled
reqStart := httptest.NewRequest("POST", "http://localhost:7469/upload", strings.NewReader(`{"name":"proxy-test.txt"}`))
reqStart.Header.Set("X-Goog-Upload-Protocol", "resumable")
reqStart.Header.Set("X-Goog-Upload-Command", "start")
reqStart.Header.Set("X-Goog-Test-Scenario", "non_fatal_error_on_finalize")
reqStart.Header.Set("X-Goog-Test-Scenario-Config", `{"error_code":502,"failure_count":1,"omit_status_header":true}`)
recStart := httptest.NewRecorder()
handler.ServeHTTP(recStart, reqStart)
uploadURL := recStart.Header().Get("X-Goog-Upload-URL")

// 2. Upload chunk
reqChunk := httptest.NewRequest("POST", uploadURL, strings.NewReader("sample payload"))
reqChunk.Header.Set("X-Goog-Upload-Command", "upload")
reqChunk.Header.Set("X-Goog-Upload-Offset", "0")
recChunk := httptest.NewRecorder()
handler.ServeHTTP(recChunk, reqChunk)

// 3. Finalize fails with proxy 502 and NO X-Goog-Upload-Status header
reqFinalizeFailed := httptest.NewRequest("POST", uploadURL, nil)
reqFinalizeFailed.Header.Set("X-Goog-Upload-Command", "finalize")
recFinalizeFailed := httptest.NewRecorder()
handler.ServeHTTP(recFinalizeFailed, reqFinalizeFailed)

if recFinalizeFailed.Code != http.StatusBadGateway {
t.Fatalf("expected 502, got %d", recFinalizeFailed.Code)
}
if statusHeader := recFinalizeFailed.Header().Get("X-Goog-Upload-Status"); statusHeader != "" {
t.Fatalf("expected status header to be omitted, got %q", statusHeader)
}

// 4. Query recovers upload state
reqQuery := httptest.NewRequest("POST", uploadURL, nil)
reqQuery.Header.Set("X-Goog-Upload-Command", "query")
recQuery := httptest.NewRecorder()
handler.ServeHTTP(recQuery, reqQuery)

if got := recQuery.Header().Get("X-Goog-Upload-Size-Received"); got != "14" {
t.Fatalf("expected committed size 14, got %q", got)
}
}

```

```
partheniou@partheniou-vm-3:~/git/gapic-showcase$ go test -count=1 -v ./server/resumableupload -run "TestProxyErrorWithoutUploadStatusOnFinalize"
=== RUN TestProxyErrorWithoutUploadStatusOnFinalize
resumableupload_test.go:648: expected 502, got 200
--- FAIL: TestProxyErrorWithoutUploadStatusOnFinalize (0.00s)
FAIL
FAIL github.com/googleapis/gapic-showcase/server/resumableupload 0.012s
FAIL
```

Contributor guide

Open the contributing guide

Research direction

Start in server/resumableupload/resumableupload.go, then inspect the related tests in server/resumableupload/resumableupload_test.go and run the focused test command shown in the issue. Add support for omit_status_header in X-Goog-Test-Scenario-Config while preserving the configured error code and active session state. Done means the finalize request returns 502 without X-Goog-Upload-Status and a subsequent query reports the committed upload size.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api, backend
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.