From 24eedf5405da6f4d64ed0475e38a31f6fbe9b96c Mon Sep 17 00:00:00 2001 From: Petr Heinz Date: Thu, 1 Oct 2026 18:41:34 +0200 Subject: [PATCH 1/2] Add failing tests for headers_json when to_json raises With json 3 and ActiveSupport 8.0 or older, a direct #to_json call goes through ActiveSupport's encoder, which passes quirks_mode: to JSON.generate and json 3 rejects it. HTTPRequest and HTTPResponse build headers_json with #to_json, so every request raises there. Co-Authored-By: Claude Opus 5.5 --- spec/logtail-rack/http_request_spec.rb | 12 ++++++++++++ spec/logtail-rack/http_response_spec.rb | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/spec/logtail-rack/http_request_spec.rb b/spec/logtail-rack/http_request_spec.rb index 354eff0..273c761 100755 --- a/spec/logtail-rack/http_request_spec.rb +++ b/spec/logtail-rack/http_request_spec.rb @@ -3,4 +3,16 @@ require "spec_helper" RSpec.describe Logtail::Integrations::Rack::HTTPRequest do + it "build headers_json when to_json raises, as it does with json 3 and ActiveSupport 8.0 or older" do + # ActiveSupport up to 8.0 encodes a direct #to_json call itself and passes quirks_mode: to JSON.generate, + # which json 3 rejects. JSON.generate calls #to_json with a JSON::State, which ActiveSupport hands to json. + allow_any_instance_of(Hash).to receive(:to_json).and_wrap_original do |to_json, *args| + raise ArgumentError, "unknown keyword: :quirks_mode" unless args.first.is_a?(JSON::State) + to_json.call(*args) + end + + http_request = described_class.new(headers: { "Content_Type" => "text/plain", "Accept" => "*/*" }) + + expect(http_request.headers_json).to eq('{"Content_Type":"text/plain","Accept":"*/*"}') + end end diff --git a/spec/logtail-rack/http_response_spec.rb b/spec/logtail-rack/http_response_spec.rb index c4a5a49..7755644 100755 --- a/spec/logtail-rack/http_response_spec.rb +++ b/spec/logtail-rack/http_response_spec.rb @@ -1,4 +1,16 @@ require "spec_helper" RSpec.describe Logtail::Integrations::Rack::HTTPResponse do + it "build headers_json when to_json raises, as it does with json 3 and ActiveSupport 8.0 or older" do + # ActiveSupport up to 8.0 encodes a direct #to_json call itself and passes quirks_mode: to JSON.generate, + # which json 3 rejects. JSON.generate calls #to_json with a JSON::State, which ActiveSupport hands to json. + allow_any_instance_of(Hash).to receive(:to_json).and_wrap_original do |to_json, *args| + raise ArgumentError, "unknown keyword: :quirks_mode" unless args.first.is_a?(JSON::State) + to_json.call(*args) + end + + http_response = described_class.new(headers: { "content-type" => "text/plain", "content-length" => "5" }, status: 200) + + expect(http_response.headers_json).to eq('{"content-type":"text/plain","content-length":"5"}') + end end From ea8de80ecca972282fbf6dc0bf77fa99269e9c23 Mon Sep 17 00:00:00 2001 From: Petr Heinz Date: Thu, 1 Oct 2026 18:50:32 +0200 Subject: [PATCH 2/2] Build headers_json with JSON.generate instead of to_json ActiveSupport older than 7.2.4 and every 8.0 release routes a direct #to_json call through its own encoder, which passes quirks_mode: to JSON.generate. json 3 rejects that keyword, so with both installed every request raised in HTTPEvents. JSON.generate encodes the headers Hash natively. With ActiveSupport loaded, <, > and & are no longer escaped as <, > and &; the JSON means the same. Co-Authored-By: Claude Opus 5.5 --- lib/logtail-rack/http_request.rb | 4 +++- lib/logtail-rack/http_response.rb | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/logtail-rack/http_request.rb b/lib/logtail-rack/http_request.rb index 7cc3b95..d0db3a3 100755 --- a/lib/logtail-rack/http_request.rb +++ b/lib/logtail-rack/http_request.rb @@ -1,3 +1,5 @@ +require "json" + require "logtail-rack/util/encoding" module Logtail @@ -26,7 +28,7 @@ def initialize(attributes) @service_name = attributes[:service_name] if @headers - @headers_json = Util::Encoding.force_utf8_encoding(@headers).to_json + @headers_json = ::JSON.generate(Util::Encoding.force_utf8_encoding(@headers)) end end diff --git a/lib/logtail-rack/http_response.rb b/lib/logtail-rack/http_response.rb index 4599012..72c1520 100755 --- a/lib/logtail-rack/http_response.rb +++ b/lib/logtail-rack/http_response.rb @@ -1,3 +1,5 @@ +require "json" + require "logtail-rack/util/encoding" module Logtail @@ -21,7 +23,7 @@ def initialize(attributes) @duration_ms = attributes[:duration_ms] if @headers - @headers_json = Util::Encoding.force_utf8_encoding(@headers).to_json + @headers_json = ::JSON.generate(Util::Encoding.force_utf8_encoding(@headers)) end end