Class: Neo4j::Driver::Bolt::Handshake
- Inherits:
-
Object
- Object
- Neo4j::Driver::Bolt::Handshake
- Defined in:
- lib/neo4j/driver/bolt/handshake.rb
Overview
Bolt handshake negotiation. Two paths share the same 20-byte preamble (magic + four 4-byte slots, slot 1 = manifest sentinel):
Legacy: server replies with one of the proposed versions
(4 bytes, big-endian). Pre-5.7 servers always take
this path because they don't speak manifest.
Manifest v1: server replies with the sentinel 0x000001FF,
then sends a varint count + N×4-byte version ranges +
a varlong capabilities mask. Client picks the highest
version it supports and writes back the chosen
version (4 bytes) + a varlong of capabilities
(currently always 0 — we negotiate no capabilities).
Mirrors BoltProtocolUtil / HandshakeHandler / ManifestHandlerV1
in neo4j-bolt-connection-java.
Constant Summary collapse
- MAGIC_PREAMBLE =
"\x60\x60\xB0\x17".b
- HTTP_REPLY =
First four bytes of an HTTP response ("HTTP") read as a big-endian int — a server speaking HTTP on the Bolt port.
0x4854_5450- MANIFEST_SENTINEL =
Slot 1 advertises "I speak HandshakeManifestV1". Decodes as BoltVersion(major=255, minor=1); servers that don't understand the sentinel ignore it and pick from slots 2-4 instead.
0x00_00_01_FF- SLOTS =
Range encoding: [reserved=0, range=N, minor, major]. Each slot covers
major.minordown tomajor.(minor-N). We advertise 5.0–5.8 (slot 2), 4.2–4.4 (slot 3) and 3.0 (slot 4). Bolt 3.0 is fully wired now (Protocol::V3: PULL_ALL / DISCARD_ALL, single-DB, no routing in HELLO). [ MANIFEST_SENTINEL, 0x00_08_08_05, # 5.0–5.8 0x00_02_04_04, # 4.2–4.4 0x00_00_00_03 # 3.0 ].freeze
- SUPPORTED_VERSIONS =
Versions we'd accept from a manifest, highest-preference first. Used to pick a winner from whatever the server advertises.
[ BoltVersion::V6_1, BoltVersion::V6_0, BoltVersion::V5_8, BoltVersion::V5_7, BoltVersion::V5_6, BoltVersion::V5_5, BoltVersion::V5_4, BoltVersion::V5_3, BoltVersion::V5_2, BoltVersion::V5_1, BoltVersion::V5_0, BoltVersion::V4_4, BoltVersion::V4_3, BoltVersion::V4_2, BoltVersion::V3_0 ].freeze
- MAX_MANIFEST_RANGES =
Run the handshake to completion. Returns the negotiated version as a 32-bit int in the wire's slot encoding ([reserved, range=0, minor, major]). Sanity cap on the manifest's range-count varint. The varint itself is bounded to 63 usable bits but a server (malicious or buggy) could still ask us to allocate billions of slots. 256 is comfortably above the realistic upper bound (Java currently advertises ~5 ranges) and well below anything that'd hurt to allocate.
256
Instance Method Summary collapse
-
#initialize(socket, deadline: nil, clock: Internal::Clock.new) ⇒ Handshake
constructor
deadline(monotonic) bounds the negotiation reads so a server that stalls the magic-byte exchange can't outlast the acquisition timeout. - #negotiate ⇒ Object
Constructor Details
#initialize(socket, deadline: nil, clock: Internal::Clock.new) ⇒ Handshake
deadline (monotonic) bounds the negotiation reads so a server that
stalls the magic-byte exchange can't outlast the acquisition timeout.
We read via wait_readable + read_nonblock rather than read(n)+IO#timeout
because IO#timeout is honored for read() only on CRuby, not JRuby —
wait_readable times out on both, so the bound works on every flavor.
71 72 73 74 75 |
# File 'lib/neo4j/driver/bolt/handshake.rb', line 71 def initialize(socket, deadline: nil, clock: Internal::Clock.new) @socket = socket @deadline = deadline @clock = clock end |
Instance Method Details
#negotiate ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/neo4j/driver/bolt/handshake.rb', line 88 def negotiate @socket.write(MAGIC_PREAMBLE) SLOTS.each { |slot| @socket.write([slot].pack('L>')) } @socket.flush server_reply = read_int32!('Server closed the connection during handshake') if server_reply.zero? raise Exceptions::ServiceUnavailableException, 'Server does not support any of the proposed Bolt versions' end # An HTTP server on the Bolt port replies with an HTTP status # line; its first four bytes spell "HTTP". Mirror Java's helpful # error instead of reporting a bogus protocol version. if server_reply == HTTP_REPLY raise Exceptions::ClientException, 'Server responded HTTP. Make sure you are not trying to connect to the http ' \ 'endpoint (HTTP defaults to port 7474 whereas BOLT defaults to port 7687)' end server_reply == MANIFEST_SENTINEL ? negotiate_manifest : server_reply end |