Skip to content

Some API providers cannot interpret encoded colons in the path #1567

Closed
@ykrods

Description

@ykrods

Hi and thank you very much for this project at first.

Basic Info

  • Faraday Version: 2.9.1
  • Ruby Version: 3.3.3

Issue description

By the #1237 improvements, colons in path are now encoded as %3A. However, it is up to the server implementation to accept encoded colons, and in fact Firebase's fcm v1 api responds 404 status for encoded messages%3Asend .

https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages/send

Currently, encoding occurs only in limited cases like a below reproduction code.

And no error occurs in the following situations.

  1. Give the full url to Faraday.initialize() or post() method
  2. Give the full path to post() (like: client.post("/v1/projects/#{project_id}/messages:send") )

Steps to reproduce

(using ENV for secret values)

require "faraday"


project_id = ENV["PROJECT_ID"]
iid_token = ENV["IID_TOKEN"]

def get_access_token
  ENV["ACCESS_TOKEN"]
end

base_url = "https://fcm.googleapis.com/v1/projects/#{project_id}"
path = "messages:send"


client = Faraday.new(url: base_url) do |builder|
  builder.request :authorization, 'Bearer', get_access_token
  builder.request :json
  builder.response :json,
                   content_type: /application\/json/,
                   parser_options: { symbolize_names: true }
  builder.response :logger,
                   Logger.new(STDOUT),
                   { headers: true, bodies: true, errors: true }
end

res = client.post(path) do |req|
  req.body = {
    validate_only: true,
    message: {
      token: iid_token,
      notification: {
        title: "test",
        body: "test",
      }
    }
  }
end

Execution log

I, [2024-06-13T08:31:07.928238 #1]  INFO -- request: POST https://fcm.googleapis.com/v1/projects/***/messages%3Asend
I, [2024-06-13T08:31:07.928371 #1]  INFO -- request: User-Agent: "Faraday v2.9.1"
Authorization: "Bearer ***"
Content-Type: "application/json"
I, [2024-06-13T08:31:07.928417 #1]  INFO -- request: {"validate_only":true,"message":{"token":"***","notification":{"title":"test","body":"test"}}}
I, [2024-06-13T08:31:08.084930 #1]  INFO -- response: Status 404
I, [2024-06-13T08:31:08.085055 #1]  INFO -- response: content-type: "text/html"
date: "Thu, 13 Jun 2024 08:31:08 GMT"
server: "scaffolding on HTTPServer2"
content-length: "0"
x-xss-protection: "0"
x-frame-options: "SAMEORIGIN"
x-content-type-options: "nosniff"
alt-svc: "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"
I, [2024-06-13T08:31:08.085115 #1]  INFO -- response:

My thoughts

def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
url = nil if url.respond_to?(:empty?) && url.empty?
base = url_prefix.dup
if url && !base.path.end_with?('/')
base.path = "#{base.path}/" # ensure trailing slash
end
url = url.to_s.gsub(':', '%3A') if URI.parse(url.to_s).opaque
uri = url ? base + url : base
if params
uri.query = params.to_query(params_encoder || options.params_encoder)
end
uri.query = nil if uri.query && uri.query.empty?
uri
end

How about replace URL + string merge to string + string merge

- url = url.to_s.gsub(':', '%3A') if URI.parse(url.to_s).opaque
- uri = url ? base + url : base
+ uri = url ? URI.parse(base.to_s + url) : base

This has the following advantages and disadvantages.

Advantages.

  1. No error if colons are included in the path url.
  2. User can decide whether to encode colons or not.
  3. The path contained in base url is not ignored if the path url starts with slash.

Disadvantages.

  1. Inefficient because base is already parsed to URL.
  2. Error might occur if query or hash is contained in the base url.
  3. Needs to remove first slash because base url is assumed to end with slash.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions