ddtrace 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +6 -6
- data/.circleci/images/primary/Dockerfile-1.9.3 +6 -2
- data/.circleci/images/primary/Dockerfile-2.0.0 +6 -2
- data/.circleci/images/primary/Dockerfile-2.1.10 +6 -2
- data/.circleci/images/primary/Dockerfile-2.2.10 +6 -2
- data/.circleci/images/primary/{Dockerfile-2.4.4 → Dockerfile-2.3.8} +5 -3
- data/.circleci/images/primary/{Dockerfile-2.3.7 → Dockerfile-2.4.6} +2 -2
- data/CHANGELOG.md +36 -0
- data/docker-compose.yml +6 -6
- data/docs/DevelopmentGuide.md +63 -0
- data/docs/GettingStarted.md +73 -3
- data/lib/ddtrace/configuration.rb +20 -1
- data/lib/ddtrace/contrib/http/circuit_breaker.rb +39 -11
- data/lib/ddtrace/contrib/http/instrumentation.rb +2 -11
- data/lib/ddtrace/ext/forced_tracing.rb +18 -2
- data/lib/ddtrace/ext/manual_tracing.rb +9 -0
- data/lib/ddtrace/ext/metrics.rb +0 -1
- data/lib/ddtrace/ext/runtime.rb +0 -1
- data/lib/ddtrace/forced_tracing.rb +3 -3
- data/lib/ddtrace/metrics.rb +0 -3
- data/lib/ddtrace/runtime/metrics.rb +0 -1
- data/lib/ddtrace/sync_writer.rb +1 -1
- data/lib/ddtrace/tracer.rb +78 -17
- data/lib/ddtrace/transport.rb +9 -0
- data/lib/ddtrace/transport/http.rb +85 -0
- data/lib/ddtrace/transport/http/adapters/net.rb +112 -0
- data/lib/ddtrace/transport/http/adapters/registry.rb +24 -0
- data/lib/ddtrace/transport/http/adapters/test.rb +77 -0
- data/lib/ddtrace/transport/http/adapters/unix_socket.rb +64 -0
- data/lib/ddtrace/transport/http/api.rb +46 -0
- data/lib/ddtrace/transport/http/api/endpoint.rb +27 -0
- data/lib/ddtrace/transport/http/api/fallbacks.rb +22 -0
- data/lib/ddtrace/transport/http/api/instance.rb +29 -0
- data/lib/ddtrace/transport/http/api/map.rb +14 -0
- data/lib/ddtrace/transport/http/api/spec.rb +15 -0
- data/lib/ddtrace/transport/http/builder.rb +165 -0
- data/lib/ddtrace/transport/http/client.rb +108 -0
- data/lib/ddtrace/transport/http/env.rb +48 -0
- data/lib/ddtrace/transport/http/response.rb +22 -0
- data/lib/ddtrace/transport/http/traces.rb +140 -0
- data/lib/ddtrace/transport/parcel.rb +13 -0
- data/lib/ddtrace/transport/request.rb +13 -0
- data/lib/ddtrace/transport/response.rb +49 -0
- data/lib/ddtrace/transport/statistics.rb +44 -0
- data/lib/ddtrace/transport/traces.rb +33 -0
- data/lib/ddtrace/version.rb +1 -1
- data/lib/ddtrace/workers.rb +6 -2
- data/lib/ddtrace/writer.rb +48 -21
- metadata +26 -4
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'ddtrace/transport/response'
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module Transport
|
5
|
+
module HTTP
|
6
|
+
module Adapters
|
7
|
+
# Adapter for Net::HTTP
|
8
|
+
class Net
|
9
|
+
attr_reader \
|
10
|
+
:hostname,
|
11
|
+
:port,
|
12
|
+
:timeout
|
13
|
+
|
14
|
+
DEFAULT_TIMEOUT = 1
|
15
|
+
|
16
|
+
def initialize(hostname, port, options = {})
|
17
|
+
@hostname = hostname
|
18
|
+
@port = port
|
19
|
+
@timeout = options[:timeout] || DEFAULT_TIMEOUT
|
20
|
+
end
|
21
|
+
|
22
|
+
def open
|
23
|
+
# Open connection
|
24
|
+
::Net::HTTP.start(hostname, port, open_timeout: timeout, read_timeout: timeout) do |http|
|
25
|
+
yield(http)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def call(env)
|
30
|
+
if respond_to?(env.verb)
|
31
|
+
send(env.verb, env)
|
32
|
+
else
|
33
|
+
raise UnknownHTTPMethod, env
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def post(env)
|
38
|
+
post = ::Net::HTTP::Post.new(env.path, env.headers)
|
39
|
+
post.body = env.body
|
40
|
+
|
41
|
+
# Connect and send the request
|
42
|
+
http_response = open do |http|
|
43
|
+
http.request(post)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Build and return response
|
47
|
+
Response.new(http_response)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Raised when called with an unknown HTTP method
|
51
|
+
class UnknownHTTPMethod < StandardError
|
52
|
+
attr_reader :verb
|
53
|
+
|
54
|
+
def initialize(verb)
|
55
|
+
@verb = verb
|
56
|
+
end
|
57
|
+
|
58
|
+
def message
|
59
|
+
"No matching Net::HTTP function for '#{verb}'!"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# A wrapped Net::HTTP response that implements the Transport::Response interface
|
64
|
+
class Response
|
65
|
+
include Datadog::Transport::Response
|
66
|
+
|
67
|
+
attr_reader :http_response
|
68
|
+
|
69
|
+
def initialize(http_response)
|
70
|
+
@http_response = http_response
|
71
|
+
end
|
72
|
+
|
73
|
+
def payload
|
74
|
+
return super if http_response.nil?
|
75
|
+
http_response.body
|
76
|
+
end
|
77
|
+
|
78
|
+
def code
|
79
|
+
return super if http_response.nil?
|
80
|
+
http_response.code.to_i
|
81
|
+
end
|
82
|
+
|
83
|
+
def ok?
|
84
|
+
return super if http_response.nil?
|
85
|
+
code.between?(200, 299)
|
86
|
+
end
|
87
|
+
|
88
|
+
def unsupported?
|
89
|
+
return super if http_response.nil?
|
90
|
+
code == 415
|
91
|
+
end
|
92
|
+
|
93
|
+
def not_found?
|
94
|
+
return super if http_response.nil?
|
95
|
+
code == 404
|
96
|
+
end
|
97
|
+
|
98
|
+
def client_error?
|
99
|
+
return super if http_response.nil?
|
100
|
+
code.between?(400, 499)
|
101
|
+
end
|
102
|
+
|
103
|
+
def server_error?
|
104
|
+
return super if http_response.nil?
|
105
|
+
code.between?(500, 599)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Datadog
|
2
|
+
module Transport
|
3
|
+
module HTTP
|
4
|
+
module Adapters
|
5
|
+
# List of available adapters
|
6
|
+
class Registry
|
7
|
+
def initialize
|
8
|
+
@adapters = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(name)
|
12
|
+
@adapters[name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def set(klass, name = nil)
|
16
|
+
name ||= klass.to_s
|
17
|
+
return if name.nil?
|
18
|
+
@adapters[name] = klass
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'ddtrace/transport/response'
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module Transport
|
5
|
+
module HTTP
|
6
|
+
module Adapters
|
7
|
+
# Adapter for testing
|
8
|
+
class Test
|
9
|
+
attr_reader \
|
10
|
+
:buffer,
|
11
|
+
:status
|
12
|
+
|
13
|
+
def initialize(buffer = nil)
|
14
|
+
@buffer = buffer
|
15
|
+
@mutex = Mutex.new
|
16
|
+
@status = 200
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
add_request(env)
|
21
|
+
Response.new(status)
|
22
|
+
end
|
23
|
+
|
24
|
+
def buffer?
|
25
|
+
!@buffer.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def add_request(env)
|
29
|
+
@mutex.synchronize { buffer << env } if buffer?
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_status!(status)
|
33
|
+
@status = status
|
34
|
+
end
|
35
|
+
|
36
|
+
# Response for test adapter
|
37
|
+
class Response
|
38
|
+
include Datadog::Transport::Response
|
39
|
+
|
40
|
+
attr_reader \
|
41
|
+
:body,
|
42
|
+
:code
|
43
|
+
|
44
|
+
def initialize(code, body = nil)
|
45
|
+
@code = code
|
46
|
+
@body = body
|
47
|
+
end
|
48
|
+
|
49
|
+
def payload
|
50
|
+
@body
|
51
|
+
end
|
52
|
+
|
53
|
+
def ok?
|
54
|
+
code.between?(200, 299)
|
55
|
+
end
|
56
|
+
|
57
|
+
def unsupported?
|
58
|
+
code == 415
|
59
|
+
end
|
60
|
+
|
61
|
+
def not_found?
|
62
|
+
code == 404
|
63
|
+
end
|
64
|
+
|
65
|
+
def client_error?
|
66
|
+
code.between?(400, 499)
|
67
|
+
end
|
68
|
+
|
69
|
+
def server_error?
|
70
|
+
code.between?(500, 599)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'ddtrace/transport/http/adapters/net'
|
3
|
+
|
4
|
+
module Datadog
|
5
|
+
module Transport
|
6
|
+
module HTTP
|
7
|
+
module Adapters
|
8
|
+
# Adapter for Unix sockets
|
9
|
+
class UnixSocket < Adapters::Net
|
10
|
+
DEFAULT_TIMEOUT = 1
|
11
|
+
|
12
|
+
attr_reader \
|
13
|
+
:filepath,
|
14
|
+
:timeout
|
15
|
+
|
16
|
+
def initialize(filepath, options = {})
|
17
|
+
@filepath = filepath
|
18
|
+
@timeout = options.fetch(:timeout, DEFAULT_TIMEOUT)
|
19
|
+
end
|
20
|
+
|
21
|
+
def open
|
22
|
+
# Open connection
|
23
|
+
connection = HTTP.new(
|
24
|
+
filepath,
|
25
|
+
read_timeout: timeout,
|
26
|
+
continue_timeout: timeout
|
27
|
+
)
|
28
|
+
|
29
|
+
connection.start do |http|
|
30
|
+
yield(http)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Re-implements Net:HTTP with underlying Unix socket
|
35
|
+
class HTTP < ::Net::HTTP
|
36
|
+
DEFAULT_TIMEOUT = 1
|
37
|
+
|
38
|
+
attr_reader \
|
39
|
+
:filepath,
|
40
|
+
:unix_socket
|
41
|
+
|
42
|
+
def initialize(filepath, options = {})
|
43
|
+
super('localhost', 80)
|
44
|
+
@filepath = filepath
|
45
|
+
@read_timeout = options.fetch(:read_timeout, DEFAULT_TIMEOUT)
|
46
|
+
@continue_timeout = options.fetch(:continue_timeout, DEFAULT_TIMEOUT)
|
47
|
+
@debug_output = options[:debug_output] if options.key?(:debug_output)
|
48
|
+
end
|
49
|
+
|
50
|
+
def connect
|
51
|
+
@unix_socket = UNIXSocket.open(filepath)
|
52
|
+
@socket = ::Net::BufferedIO.new(@unix_socket).tap do |socket|
|
53
|
+
socket.read_timeout = @read_timeout
|
54
|
+
socket.continue_timeout = @continue_timeout
|
55
|
+
socket.debug_output = @debug_output
|
56
|
+
end
|
57
|
+
on_connect
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'ddtrace/encoding'
|
2
|
+
|
3
|
+
require 'ddtrace/transport/http/api/map'
|
4
|
+
require 'ddtrace/transport/http/api/spec'
|
5
|
+
|
6
|
+
require 'ddtrace/transport/http/traces'
|
7
|
+
|
8
|
+
module Datadog
|
9
|
+
module Transport
|
10
|
+
module HTTP
|
11
|
+
# Namespace for API components
|
12
|
+
module API
|
13
|
+
# Default API versions
|
14
|
+
V4 = 'v0.4'.freeze
|
15
|
+
V3 = 'v0.3'.freeze
|
16
|
+
V2 = 'v0.2'.freeze
|
17
|
+
|
18
|
+
module_function
|
19
|
+
|
20
|
+
def defaults
|
21
|
+
Map[
|
22
|
+
V4 => Spec.new do |s|
|
23
|
+
s.traces = Traces::API::Endpoint.new(
|
24
|
+
'/v0.4/traces'.freeze,
|
25
|
+
Encoding::MsgpackEncoder,
|
26
|
+
service_rates: true
|
27
|
+
)
|
28
|
+
end,
|
29
|
+
V3 => Spec.new do |s|
|
30
|
+
s.traces = Traces::API::Endpoint.new(
|
31
|
+
'/v0.3/traces'.freeze,
|
32
|
+
Encoding::MsgpackEncoder
|
33
|
+
)
|
34
|
+
end,
|
35
|
+
V2 => Spec.new do |s|
|
36
|
+
s.traces = Traces::API::Endpoint.new(
|
37
|
+
'/v0.2/traces'.freeze,
|
38
|
+
Encoding::JSONEncoder
|
39
|
+
)
|
40
|
+
end
|
41
|
+
].with_fallbacks(V4 => V3, V3 => V2)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module Transport
|
5
|
+
module HTTP
|
6
|
+
module API
|
7
|
+
# Endpoint
|
8
|
+
class Endpoint
|
9
|
+
attr_reader \
|
10
|
+
:verb,
|
11
|
+
:path
|
12
|
+
|
13
|
+
def initialize(verb, path)
|
14
|
+
@verb = verb
|
15
|
+
@path = path
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
env.verb = verb
|
20
|
+
env.path = path
|
21
|
+
yield(env)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Datadog
|
2
|
+
module Transport
|
3
|
+
module HTTP
|
4
|
+
module API
|
5
|
+
# Extension for Map with adds fallback versions.
|
6
|
+
module Fallbacks
|
7
|
+
def fallbacks
|
8
|
+
@fallbacks ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def with_fallbacks(fallbacks)
|
12
|
+
tap { add_fallbacks!(fallbacks) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_fallbacks!(fallbacks)
|
16
|
+
self.fallbacks.merge!(fallbacks)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Datadog
|
2
|
+
module Transport
|
3
|
+
module HTTP
|
4
|
+
module API
|
5
|
+
# An API configured with adapter and routes
|
6
|
+
class Instance
|
7
|
+
attr_reader \
|
8
|
+
:adapter,
|
9
|
+
:headers,
|
10
|
+
:spec
|
11
|
+
|
12
|
+
def initialize(spec, adapter, options = {})
|
13
|
+
@spec = spec
|
14
|
+
@adapter = adapter
|
15
|
+
@headers = options.fetch(:headers, {})
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
# Add headers to request env, unless empty.
|
20
|
+
env.headers.merge!(headers) unless headers.empty?
|
21
|
+
|
22
|
+
# Send request env to the adapter.
|
23
|
+
adapter.call(env)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Datadog
|
2
|
+
module Transport
|
3
|
+
module HTTP
|
4
|
+
module API
|
5
|
+
# Specification for an HTTP API
|
6
|
+
# Defines behaviors without specific configuration details.
|
7
|
+
class Spec
|
8
|
+
def initialize
|
9
|
+
yield(self) if block_given?
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'ddtrace/transport/http/adapters/registry'
|
2
|
+
require 'ddtrace/transport/http/api/map'
|
3
|
+
require 'ddtrace/transport/http/api/instance'
|
4
|
+
require 'ddtrace/transport/http/client'
|
5
|
+
|
6
|
+
module Datadog
|
7
|
+
module Transport
|
8
|
+
module HTTP
|
9
|
+
# Builds new instances of Transport::HTTP::Client
|
10
|
+
class Builder
|
11
|
+
REGISTRY = Adapters::Registry.new
|
12
|
+
|
13
|
+
attr_reader \
|
14
|
+
:apis,
|
15
|
+
:api_options,
|
16
|
+
:default_adapter,
|
17
|
+
:default_api,
|
18
|
+
:default_headers
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
# Global settings
|
22
|
+
@default_adapter = nil
|
23
|
+
@default_headers = {}
|
24
|
+
|
25
|
+
# Client settings
|
26
|
+
@apis = API::Map.new
|
27
|
+
@default_api = nil
|
28
|
+
|
29
|
+
# API settings
|
30
|
+
@api_options = {}
|
31
|
+
|
32
|
+
yield(self) if block_given?
|
33
|
+
end
|
34
|
+
|
35
|
+
def adapter(type, *args)
|
36
|
+
@default_adapter = if type.is_a?(Symbol)
|
37
|
+
registry_klass = REGISTRY.get(type)
|
38
|
+
raise UnknownAdapterError, type if registry_klass.nil?
|
39
|
+
registry_klass.new(*args)
|
40
|
+
else
|
41
|
+
type
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def headers(values = {})
|
46
|
+
@default_headers.merge!(values)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Adds a new API to the client
|
50
|
+
# Valid options:
|
51
|
+
# - :adapter
|
52
|
+
# - :default
|
53
|
+
# - :fallback
|
54
|
+
# - :headers
|
55
|
+
def api(key, spec, options = {})
|
56
|
+
options = options.dup
|
57
|
+
|
58
|
+
# Copy spec into API map
|
59
|
+
@apis[key] = spec
|
60
|
+
|
61
|
+
# Apply as default API, if specified to do so.
|
62
|
+
@default_api = key if options.delete(:default) || @default_api.nil?
|
63
|
+
|
64
|
+
# Save all other settings for initialization
|
65
|
+
(@api_options[key] ||= {}).merge!(options)
|
66
|
+
end
|
67
|
+
|
68
|
+
def default_api=(key)
|
69
|
+
raise UnknownApiError, key unless @apis.key?(key)
|
70
|
+
@default_api = key
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_client
|
74
|
+
raise NoDefaultApiError if @default_api.nil?
|
75
|
+
|
76
|
+
@client ||= Client.new(
|
77
|
+
to_api_instances,
|
78
|
+
@default_api
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_api_instances
|
83
|
+
raise NoApisError if @apis.empty?
|
84
|
+
|
85
|
+
@apis.inject(API::Map.new) do |instances, (key, spec)|
|
86
|
+
instances.tap do
|
87
|
+
api_options = @api_options[key].dup
|
88
|
+
|
89
|
+
# Resolve the adapter to use for this API
|
90
|
+
adapter = api_options.delete(:adapter) || @default_adapter
|
91
|
+
raise NoAdapterForApiError, key if adapter.nil?
|
92
|
+
|
93
|
+
# Resolve fallback and merge headers
|
94
|
+
fallback = api_options.delete(:fallback)
|
95
|
+
api_options[:headers] = @default_headers.merge((api_options[:headers] || {}))
|
96
|
+
|
97
|
+
# Add API::Instance with all settings
|
98
|
+
instances[key] = API::Instance.new(
|
99
|
+
spec,
|
100
|
+
adapter,
|
101
|
+
api_options
|
102
|
+
)
|
103
|
+
|
104
|
+
# Configure fallback, if provided.
|
105
|
+
instances.with_fallbacks(key => fallback) unless fallback.nil?
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Raised when the API key does not match known APIs.
|
111
|
+
class UnknownApiError < StandardError
|
112
|
+
attr_reader :key
|
113
|
+
|
114
|
+
def initialize(key)
|
115
|
+
@key = key
|
116
|
+
end
|
117
|
+
|
118
|
+
def message
|
119
|
+
"Unknown transport API '#{key}'!"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Raised when the identifier cannot be matched to an adapter.
|
124
|
+
class UnknownAdapterError < StandardError
|
125
|
+
attr_reader :type
|
126
|
+
|
127
|
+
def initialize(type)
|
128
|
+
@type = type
|
129
|
+
end
|
130
|
+
|
131
|
+
def message
|
132
|
+
"Unknown transport adapter '#{type}'!"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Raised when an adapter cannot be resolved for an API instance.
|
137
|
+
class NoAdapterForApiError < StandardError
|
138
|
+
attr_reader :key
|
139
|
+
|
140
|
+
def initialize(key)
|
141
|
+
@key = key
|
142
|
+
end
|
143
|
+
|
144
|
+
def message
|
145
|
+
"No adapter resolved for transport API '#{key}'!"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Raised when built without defining APIs.
|
150
|
+
class NoApisError < StandardError
|
151
|
+
def message
|
152
|
+
'No APIs configured for transport!'
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Raised when client built without defining a default API.
|
157
|
+
class NoDefaultApiError < StandardError
|
158
|
+
def message
|
159
|
+
'No default API configured for transport!'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|