rippled
Loading...
Searching...
No Matches
ProtocolVersion.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2019 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/overlay/detail/ProtocolVersion.h>
21
22#include <xrpl/beast/core/LexicalCast.h>
23#include <xrpl/beast/rfc2616.h>
24
25#include <boost/iterator/function_output_iterator.hpp>
26#include <boost/regex.hpp>
27
28#include <algorithm>
29#include <functional>
30
31namespace ripple {
32
39// clang-format off
41{
42 {2, 1},
43 {2, 2}
44};
45// clang-format on
46
47// This ugly construct ensures that supportedProtocolList is sorted in strictly
48// ascending order and doesn't contain any duplicates.
49// FIXME: With C++20 we can use std::is_sorted with an appropriate comparator
50static_assert(
51 []() constexpr -> bool {
52 auto const len = std::distance(
54
55 // There should be at least one protocol we're willing to speak.
56 if (len == 0)
57 return false;
58
59 // A list with only one entry is, by definition, sorted so we don't
60 // need to check it.
61 if (len != 1)
62 {
63 for (auto i = 0; i != len - 1; ++i)
64 {
66 return false;
67 }
68 }
69
70 return true;
71 }(),
72 "The list of supported protocols isn't properly sorted.");
73
76{
77 return "XRPL/" + std::to_string(p.first) + "." + std::to_string(p.second);
78}
79
81parseProtocolVersions(boost::beast::string_view const& value)
82{
83 static boost::regex re(
84 "^" // start of line
85 "XRPL/" // The string "XRPL/"
86 "([2-9]|(?:[1-9][0-9]+))" // a number (greater than 2 with no leading
87 // zeroes)
88 "\\." // a period
89 "(0|(?:[1-9][0-9]*))" // a number (no leading zeroes unless exactly
90 // zero)
91 "$" // The end of the string
92 ,
93 boost::regex_constants::optimize);
94
96
97 for (auto const& s : beast::rfc2616::split_commas(value))
98 {
99 boost::smatch m;
100
101 if (boost::regex_match(s, m, re))
102 {
103 std::uint16_t major;
104 std::uint16_t minor;
105 if (!beast::lexicalCastChecked(major, std::string(m[1])))
106 continue;
107
108 if (!beast::lexicalCastChecked(minor, std::string(m[2])))
109 continue;
110
111 auto const proto = make_protocol(major, minor);
112
113 // This is an extra sanity check: we check that the protocol we just
114 // decoded corresponds to the token we were parsing.
115 if (to_string(proto) == s)
116 result.push_back(make_protocol(major, minor));
117 }
118 }
119
120 // We guarantee that the returned list is sorted and contains no duplicates:
121 std::sort(result.begin(), result.end());
122 result.erase(std::unique(result.begin(), result.end()), result.end());
123
124 return result;
125}
126
129{
131
132 // The protocol version we want to negotiate is the largest item in the
133 // intersection of the versions supported by us and the peer. Since the
134 // output of std::set_intersection is sorted, that item is always going
135 // to be the last one. So we get a little clever and avoid the need for
136 // a container:
137 std::function<void(ProtocolVersion const&)> pickVersion =
138 [&result](ProtocolVersion const& v) { result = v; };
139
141 std::begin(versions),
142 std::end(versions),
145 boost::make_function_output_iterator(pickVersion));
146
147 return result;
148}
149
151negotiateProtocolVersion(boost::beast::string_view const& versions)
152{
153 auto const them = parseProtocolVersions(versions);
154
155 return negotiateProtocolVersion(them);
156}
157
158std::string const&
160{
161 static std::string const supported = []() {
162 std::string ret;
163 for (auto const& v : supportedProtocolList)
164 {
165 if (!ret.empty())
166 ret += ", ";
167 ret += to_string(v);
168 }
169
170 return ret;
171 }();
172
173 return supported;
174}
175
176bool
185
186} // namespace ripple
T begin(T... args)
T distance(T... args)
T empty(T... args)
T end(T... args)
T erase(T... args)
T find(T... args)
Result split_commas(FwdIt first, FwdIt last)
Definition rfc2616.h:199
bool lexicalCastChecked(Out &out, In in)
Intelligently convert from one type to another.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::optional< ProtocolVersion > negotiateProtocolVersion(std::vector< ProtocolVersion > const &versions)
Given a list of supported protocol versions, choose the one we prefer.
std::vector< ProtocolVersion > parseProtocolVersions(boost::beast::string_view const &value)
Parse a set of protocol versions.
constexpr ProtocolVersion const supportedProtocolList[]
The list of protocol versions we speak and we prefer to use.
constexpr ProtocolVersion make_protocol(std::uint16_t major, std::uint16_t minor)
std::string const & supportedProtocolVersions()
The list of all the protocol versions we support.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
bool isProtocolSupported(ProtocolVersion const &v)
Determine whether we support a specific protocol version.
T push_back(T... args)
T set_intersection(T... args)
T sort(T... args)
T to_string(T... args)
T unique(T... args)