Class: Docker::API::Connection
- Inherits:
-
Object
- Object
- Docker::API::Connection
- Defined in:
- lib/docker/api/connection.rb,
sig/docker/api/core.rbs
Overview
Everything between "a socket exists" and "a Ruby value comes back".
A connection owns request building, API version negotiation, error mapping, streaming and hijack. It is immutable once built: there is no setter for the URL or for credentials, which is what lets two connections address two daemons in one process without interfering.
Constant Summary collapse
- UNVERSIONED =
Endpoints that must not carry a version prefix, because they are how we discover which version to use.
["/_ping"].freeze
- VERBS =
HTTP verbs mapped to the Net::HTTP request classes that implement them.
{ get: Net::HTTP::Get, post: Net::HTTP::Post, put: Net::HTTP::Put, patch: Net::HTTP::Patch, delete: Net::HTTP::Delete, head: Net::HTTP::Head, }.freeze
Instance Attribute Summary collapse
-
#logger ⇒ Logger?
readonly
Where request metadata is logged, if anywhere.
-
#transport ⇒ Docker::API::Transport::Base
readonly
The transport in use.
Instance Method Summary collapse
-
#api_version ⇒ String?
The API version this connection prefixes its requests with.
-
#hijack(method, path, query: {}, body: nil, headers: {}, operation: nil) ⇒ IO
Take over the socket after an upgrade, for interactive exec and attach.
-
#initialize(transport:, api_version: :negotiate, logger: nil, read_timeout: 60, open_timeout: 10) ⇒ Connection
constructor
A new instance of Connection.
-
#ping ⇒ Docker::API::Response
Ask the daemon whether it is alive.
-
#request(method, path, query: {}, body: nil, headers: {}, expects: [200], operation: nil, content_type: nil) {|chunk| ... } ⇒ Docker::API::Response
Send a request and interpret the answer.
- #to_s ⇒ String (also: #inspect)
Constructor Details
#initialize(transport:, api_version: :negotiate, logger: nil, read_timeout: 60, open_timeout: 10) ⇒ Connection
Returns a new instance of Connection.
42 43 44 45 46 47 48 49 50 |
# File 'lib/docker/api/connection.rb', line 42 def initialize(transport:, api_version: :negotiate, logger: nil, read_timeout: 60, open_timeout: 10) @transport = transport @configured_api_version = api_version @logger = logger @read_timeout = read_timeout @open_timeout = open_timeout @version_lock = Mutex.new end |
Instance Attribute Details
#logger ⇒ Logger? (readonly)
Returns where request metadata is logged, if anywhere.
33 34 35 |
# File 'lib/docker/api/connection.rb', line 33 def logger @logger end |
#transport ⇒ Docker::API::Transport::Base (readonly)
Returns the transport in use.
30 31 32 |
# File 'lib/docker/api/connection.rb', line 30 def transport @transport end |
Instance Method Details
#api_version ⇒ String?
The API version this connection prefixes its requests with.
Negotiation happens once, lazily, on the first request that needs it.
58 59 60 61 62 63 64 65 |
# File 'lib/docker/api/connection.rb', line 58 def api_version return @api_version if defined?(@api_version) @version_lock.synchronize do @api_version = resolve_api_version unless defined?(@api_version) end @api_version end |
#hijack(method, path, query: {}, body: nil, headers: {}, operation: nil) ⇒ IO
Take over the socket after an upgrade, for interactive exec and attach.
This deliberately does not go through Net::HTTP. An interactive session
needs the socket itself, and Net::BufferedIO may already have read
ahead past the response headers. Rather than prise a buffered socket
back out of the stdlib -- the problem that produced docker-api's custom
Excon middleware -- this writes the request itself and hands back the
socket with nothing consumed but the response head.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/docker/api/connection.rb', line 119 def hijack(method, path, query: {}, body: nil, headers: {}, operation: nil) io = transport.connect full_path = build_path(path, query) payload, computed_type = encode_body(body) write_raw_request(io, method, full_path, payload, headers, computed_type) status, response_headers = read_raw_response_head(io) unless [101, 200].include?(status) error_body = io.read.to_s io.close unless io.closed? raise Error.for( status: status, operation: operation, response: Response.new(status: status, headers: response_headers, body: error_body) ) end io rescue Errno::EPIPE, Errno::ECONNRESET, EOFError => e io&.close unless io.nil? || io.closed? raise ConnectionError.new("the daemon closed the connection during #{operation}: #{e.}") end |
#ping ⇒ Docker::API::Response
Ask the daemon whether it is alive. Never version-prefixed.
70 71 72 |
# File 'lib/docker/api/connection.rb', line 70 def ping request(:get, "/_ping", operation: "ping") end |
#request(method, path, query: {}, body: nil, headers: {}, expects: [200], operation: nil, content_type: nil) {|chunk| ... } ⇒ Docker::API::Response
Send a request and interpret the answer.
88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/docker/api/connection.rb', line 88 def request(method, path, query: {}, body: nil, headers: {}, expects: [200], operation: nil, content_type: nil, &block) full_path = build_path(path, query) payload, computed_type = encode_body(body) request_headers = build_headers(headers, content_type || computed_type, payload) log(method, full_path, operation) response = perform(method, full_path, payload, request_headers, expects, &block) return response if expects.include?(response.status) raise Error.for(status: response.status, operation: operation, response: response) end |
#to_s ⇒ String Also known as: inspect
143 144 145 146 |
# File 'lib/docker/api/connection.rb', line 143 def to_s "#<Docker::API::Connection transport=#{transport} api_version=#{ defined?(@api_version) ? @api_version : "unnegotiated"}>" end |