Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions lib/cloudtasker/backend/google_cloud_task_v1.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,16 @@ def self.format_task_payload(payload)
# Format dispatch_deadline to Google::Protobuf::Duration
payload[:dispatch_deadline] = format_protobuf_duration(payload[:dispatch_deadline])

# Encode job content to support UTF-8. Google Cloud Task
# expect content to be ASCII-8BIT compatible (binary)
# Setup headers
payload[:http_request][:headers] ||= {}
payload[:http_request][:headers][Cloudtasker::Config::CONTENT_TYPE_HEADER] = 'text/json'
payload[:http_request][:headers][Cloudtasker::Config::ENCODING_HEADER] = 'Base64'
payload[:http_request][:body] = Base64.encode64(payload[:http_request][:body])

# Conditionally encode job content to support UTF-8.
# Google Cloud Task expect content to be ASCII-8BIT compatible (binary)
if config.base64_encode_body
payload[:http_request][:headers][Cloudtasker::Config::ENCODING_HEADER] = 'Base64'
payload[:http_request][:body] = Base64.encode64(payload[:http_request][:body])
end

payload.compact
end
Expand Down
12 changes: 8 additions & 4 deletions lib/cloudtasker/backend/google_cloud_task_v2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,16 @@ def self.format_task_payload(payload)
# Format dispatch_deadline to Google::Protobuf::Duration
payload[:dispatch_deadline] = format_protobuf_duration(payload[:dispatch_deadline])

# Encode job content to support UTF-8.
# Google Cloud Task expect content to be ASCII-8BIT compatible (binary)
# Setup headers
payload[:http_request][:headers] ||= {}
payload[:http_request][:headers][Cloudtasker::Config::CONTENT_TYPE_HEADER] = 'text/json'
payload[:http_request][:headers][Cloudtasker::Config::ENCODING_HEADER] = 'Base64'
payload[:http_request][:body] = Base64.encode64(payload[:http_request][:body])

# Conditionally encode job content to support UTF-8.
# Google Cloud Task expect content to be ASCII-8BIT compatible (binary)
if config.base64_encode_body
payload[:http_request][:headers][Cloudtasker::Config::ENCODING_HEADER] = 'Base64'
payload[:http_request][:body] = Base64.encode64(payload[:http_request][:body])
end

payload.compact
end
Expand Down
16 changes: 15 additions & 1 deletion lib/cloudtasker/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ class Config
attr_accessor :redis, :store_payloads_in_redis, :gcp_queue_prefix
attr_writer :secret, :gcp_location_id, :gcp_project_id,
:processor_path, :logger, :mode, :max_retries,
:dispatch_deadline, :on_error, :on_dead, :oidc, :local_server_ssl_verify
:dispatch_deadline, :on_error, :on_dead, :oidc, :local_server_ssl_verify,
:base64_encode_body

# Max Cloud Task size in bytes
MAX_TASK_SIZE = 100 * 1024 # 100 KB
Expand Down Expand Up @@ -56,6 +57,9 @@ class Config
# Default on_error Proc
DEFAULT_ON_ERROR = ->(error, worker) {}

# Default base64 encoding flag
DEFAULT_BASE64_ENCODE_BODY = true

# Cache key prefix used to store workers in cache and retrieve
# them later.
WORKER_STORE_PREFIX = 'worker_store'
Expand Down Expand Up @@ -301,5 +305,15 @@ def server_middleware
def local_server_ssl_verify
@local_server_ssl_verify.nil? ? DEFAULT_LOCAL_SERVER_SSL_VERIFY_MODE : @local_server_ssl_verify
end

#
# Return whether to base64 encode the task body when sending to Cloud Tasks.
# Encoding is enabled by default to support UTF-8 content.
#
# @return [Boolean] Whether to base64 encode the body.
#
def base64_encode_body
@base64_encode_body.nil? ? DEFAULT_BASE64_ENCODE_BODY : @base64_encode_body
end
end
end
40 changes: 31 additions & 9 deletions spec/cloudtasker/backend/google_cloud_task_v1_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,19 +192,39 @@
payload[:schedule_time] = described_class.format_protobuf_time(arg_payload[:schedule_time])
payload[:dispatch_deadline] = described_class.format_protobuf_duration(arg_payload[:dispatch_deadline])
payload[:http_request][:headers]['Content-Type'] = 'text/json'
payload[:http_request][:headers]['Content-Transfer-Encoding'] = 'Base64'
payload[:http_request][:body] = Base64.encode64(arg_payload[:http_request][:body])
if config.base64_encode_body
payload[:http_request][:headers]['Content-Transfer-Encoding'] = 'Base64'
payload[:http_request][:body] = Base64.encode64(arg_payload[:http_request][:body])
end
payload.compact
end

context 'with defined keys' do
it { is_expected.to eq(expected_payload) }
context 'with base64 encoding enabled' do
before { config.base64_encode_body = true }

context 'with defined keys' do
it { is_expected.to eq(expected_payload) }
end

context 'with nil keys' do
let(:arg_payload) { job_payload.merge(some_nil_key: nil) }

it { is_expected.to eq(expected_payload) }
end
end

context 'with nil keys' do
let(:arg_payload) { job_payload.merge(some_nil_key: nil) }
context 'with base64 encoding disabled' do
before { config.base64_encode_body = false }

it { is_expected.to eq(expected_payload) }
context 'with defined keys' do
it { is_expected.to eq(expected_payload) }
end

context 'with nil keys' do
let(:arg_payload) { job_payload.merge(some_nil_key: nil) }

it { is_expected.to eq(expected_payload) }
end
end
end

Expand Down Expand Up @@ -255,8 +275,10 @@
payload[:schedule_time] = described_class.format_protobuf_time(job_payload[:schedule_time])
payload[:dispatch_deadline] = described_class.format_protobuf_duration(job_payload[:dispatch_deadline])
payload[:http_request][:headers]['Content-Type'] = 'text/json'
payload[:http_request][:headers]['Content-Transfer-Encoding'] = 'Base64'
payload[:http_request][:body] = Base64.encode64(job_payload[:http_request][:body])
if config.base64_encode_body
payload[:http_request][:headers]['Content-Transfer-Encoding'] = 'Base64'
payload[:http_request][:body] = Base64.encode64(job_payload[:http_request][:body])
end
payload
end

Expand Down
40 changes: 31 additions & 9 deletions spec/cloudtasker/backend/google_cloud_task_v2_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -204,19 +204,39 @@
payload[:schedule_time] = described_class.format_protobuf_time(arg_payload[:schedule_time])
payload[:dispatch_deadline] = described_class.format_protobuf_duration(arg_payload[:dispatch_deadline])
payload[:http_request][:headers]['Content-Type'] = 'text/json'
payload[:http_request][:headers]['Content-Transfer-Encoding'] = 'Base64'
payload[:http_request][:body] = Base64.encode64(arg_payload[:http_request][:body])
if config.base64_encode_body
payload[:http_request][:headers]['Content-Transfer-Encoding'] = 'Base64'
payload[:http_request][:body] = Base64.encode64(arg_payload[:http_request][:body])
end
payload.compact
end

context 'with defined keys' do
it { is_expected.to eq(expected_payload) }
context 'with base64 encoding enabled' do
before { config.base64_encode_body = true }

context 'with defined keys' do
it { is_expected.to eq(expected_payload) }
end

context 'with nil keys' do
let(:arg_payload) { job_payload.merge(some_nil_key: nil) }

it { is_expected.to eq(expected_payload) }
end
end

context 'with nil keys' do
let(:arg_payload) { job_payload.merge(some_nil_key: nil) }
context 'with base64 encoding disabled' do
before { config.base64_encode_body = false }

it { is_expected.to eq(expected_payload) }
context 'with defined keys' do
it { is_expected.to eq(expected_payload) }
end

context 'with nil keys' do
let(:arg_payload) { job_payload.merge(some_nil_key: nil) }

it { is_expected.to eq(expected_payload) }
end
end
end

Expand Down Expand Up @@ -267,8 +287,10 @@
payload[:schedule_time] = described_class.format_protobuf_time(job_payload[:schedule_time])
payload[:dispatch_deadline] = described_class.format_protobuf_duration(job_payload[:dispatch_deadline])
payload[:http_request][:headers]['Content-Type'] = 'text/json'
payload[:http_request][:headers]['Content-Transfer-Encoding'] = 'Base64'
payload[:http_request][:body] = Base64.encode64(job_payload[:http_request][:body])
if config.base64_encode_body
payload[:http_request][:headers]['Content-Transfer-Encoding'] = 'Base64'
payload[:http_request][:body] = Base64.encode64(job_payload[:http_request][:body])
end
payload
end

Expand Down
16 changes: 16 additions & 0 deletions spec/cloudtasker/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
let(:on_dead) { ->(e, w) {} }
let(:oidc) { nil }
let(:local_server_ssl_verify) { false }
let(:base64_encode_body) { true }

let(:rails_hosts) { [] }
let(:rails_secret) { 'rails_secret' }
Expand Down Expand Up @@ -48,6 +49,7 @@
c.on_dead = on_dead
c.oidc = oidc
c.local_server_ssl_verify = local_server_ssl_verify
c.base64_encode_body = base64_encode_body
end

Cloudtasker.config
Expand Down Expand Up @@ -337,6 +339,20 @@
end
end

describe '#base64_encode_body' do
subject { config.base64_encode_body }

context 'with value specified via config' do
it { is_expected.to eq(base64_encode_body) }
end

context 'with no value' do
let(:base64_encode_body) { nil }

it { is_expected.to eq(described_class::DEFAULT_BASE64_ENCODE_BODY) }
end
end

describe '#client_middleware' do
subject(:middlewares) { config.client_middleware }

Expand Down