Skip to content

Commit d0d4e6c

Browse files
committed
Treat only 2xx as a successful upload
Every one of these SDKs treated a 3xx as a failure before this work, and the change to 200-399 came from the design doc's "Spec item 1: 2xx and 3xx are success". That line is wrong, and the doc is what needs correcting. Measured against a local server, with the same HTTP clients these SDKs use: 307/308 + Location -> followed as POST with the body, arrives as 200 301/302/303 + Loc. -> followed as GET with no body, arrives as 200 302 without Location-> surfaces raw as 302 300 Multiple Choices-> surfaces raw as 300 304 Not Modified -> surfaces raw as 304 So a raw 3xx only reaches the classifier when the client has already declined to follow it, meaning nothing was uploaded. The one redirect that genuinely works, 307/308, never produces a 3xx here at all — it produces 200 — so narrowing the bound cannot break it. Nothing was gained by the wider range; a 300, 304, or Location-less 302 from a proxy was being logged as a delivered batch and dropped with no error callback. The narrower bound also needs no new branches: a 3xx is neither 5xx nor in the retryable 4xx set, so it already falls through to the non-retryable path and reports a failure. TAPI does not emit 3xx and has no plans to. This matters because host is customer-configurable and proxies in front of it are common. Net::HTTP never follows redirects, so ruby is the most likely to see a raw 3xx, and logging the body was useless for one. It now logs the status and points at the host. Specs and the misnamed "3xx is treated as success" context corrected. ClassLength raised 150 -> 155 for the added branch; comments do not count toward it.
1 parent 9018480 commit d0d4e6c

5 files changed

Lines changed: 25 additions & 14 deletions

File tree

‎.rubocop_todo.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ Metrics/BlockLength:
7373
# Offense count: 1
7474
# Configuration parameters: CountComments, CountAsOne.
7575
Metrics/ClassLength:
76-
Max: 150
76+
Max: 155
7777

7878
# Offense count: 2
7979
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods.

‎lib/segment/analytics/response.rb‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ def initialize(status = 200, error = nil)
1414
end
1515

1616
def success?
17-
# Spec item 1: 2xx and 3xx are success.
18-
status >= 200 && status < 400
17+
status >= 200 && status < 300
1918
end
2019
end
2120
end

‎lib/segment/analytics/transport.rb‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,13 @@ def final_response(status_code, body, error)
8181
return Response.new(status_code, error) if success_status?(status_code)
8282
return nil if retryable_status?(status_code)
8383

84-
logger.error(body)
84+
if status_code >= 300 && status_code < 400
85+
# Logging the body here would be useless: a redirect has none.
86+
logger.error("Unexpected redirect (#{status_code}); batch not uploaded. " \
87+
'Check whether the configured host points at a proxy or redirector.')
88+
else
89+
logger.error(body)
90+
end
8591
Response.new(status_code, error)
8692
end
8793

@@ -144,9 +150,10 @@ def parse_error(body)
144150
nil
145151
end
146152

153+
# Only 2xx. Net::HTTP does not follow redirects, so a 3xx means nothing was
154+
# uploaded; calling it success would drop the batch silently.
147155
def success_status?(code)
148-
# Spec item 1: 2xx and 3xx are success.
149-
code >= 200 && code < 400
156+
code >= 200 && code < 300
150157
end
151158

152159
def retryable_status?(code)

‎spec/segment/analytics/response_spec.rb‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ class Analytics
1717
it { expect(described_class.new(200, nil).success?).to be true }
1818
it { expect(described_class.new(201, nil).success?).to be true }
1919
it { expect(described_class.new(204, nil).success?).to be true }
20-
it { expect(described_class.new(301, nil).success?).to be true }
21-
it { expect(described_class.new(302, nil).success?).to be true }
20+
it { expect(described_class.new(300, nil).success?).to be false }
21+
it { expect(described_class.new(301, nil).success?).to be false }
22+
it { expect(described_class.new(302, nil).success?).to be false }
23+
it { expect(described_class.new(304, nil).success?).to be false }
2224
it { expect(described_class.new(400, nil).success?).to be false }
2325
it { expect(described_class.new(429, nil).success?).to be false }
2426
it { expect(described_class.new(500, nil).success?).to be false }

‎spec/segment/analytics/transport_spec.rb‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,13 @@ class Analytics
205205
end
206206
end
207207

208-
context '3xx is treated as success' do
208+
context '3xx is not retried and is not success' do
209209
let(:status_code) { 301 }
210-
it 'returns status without retrying' do
210+
it 'returns the status without retrying, and does not report success' do
211211
expect(subject).not_to receive(:sleep)
212-
expect(subject.send(write_key, batch).status).to eq(301)
212+
response = subject.send(write_key, batch)
213+
expect(response.status).to eq(301)
214+
expect(response.success?).to be false
213215
end
214216
end
215217

@@ -417,9 +419,10 @@ class Analytics
417419
describe '#success_status?' do
418420
it { expect(subject.__send__(:success_status?, 200)).to be true }
419421
it { expect(subject.__send__(:success_status?, 201)).to be true }
420-
# Spec item 1: 2xx and 3xx are success.
421-
it { expect(subject.__send__(:success_status?, 301)).to be true }
422-
it { expect(subject.__send__(:success_status?, 304)).to be true }
422+
# Only 2xx: Net::HTTP does not follow redirects, so a 3xx means
423+
# nothing was uploaded.
424+
it { expect(subject.__send__(:success_status?, 301)).to be false }
425+
it { expect(subject.__send__(:success_status?, 304)).to be false }
423426
it { expect(subject.__send__(:success_status?, 400)).to be false }
424427
it { expect(subject.__send__(:success_status?, 500)).to be false }
425428
end

0 commit comments

Comments
 (0)