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#include <xrpl/beast/core/LexicalCast.h>
22#include <xrpl/beast/rfc2616.h>
23#include <boost/iterator/function_output_iterator.hpp>
24#include <boost/regex.hpp>
25#include <algorithm>
26#include <functional>
27
28namespace ripple {
29
36// clang-format off
38{
39 {2, 1},
40 {2, 2},
41 // Adds TMLedgerData::responseCookies and directResponse
42 {2, 3}
43};
44// clang-format on
45
46// This ugly construct ensures that supportedProtocolList is sorted in strictly
47// ascending order and doesn't contain any duplicates.
48// FIXME: With C++20 we can use std::is_sorted with an appropriate comparator
49static_assert(
50 []() constexpr -> bool {
51 auto const len = std::distance(
53
54 // There should be at least one protocol we're willing to speak.
55 if (len == 0)
56 return false;
57
58 // A list with only one entry is, by definition, sorted so we don't
59 // need to check it.
60 if (len != 1)
61 {
62 for (auto i = 0; i != len - 1; ++i)
63 {
65 return false;
66 }
67 }
68
69 return true;
70 }(),
71 "The list of supported protocols isn't properly sorted.");
72
75{
76 return "XRPL/" + std::to_string(p.first) + "." + std::to_string(p.second);
77}
78
80parseProtocolVersions(boost::beast::string_view const& value)
81{
82 static boost::regex re(
83 "^" // start of line
84 "XRPL/" // The string "XRPL/"
85 "([2-9]|(?:[1-9][0-9]+))" // a number (greater than 2 with no leading
86 // zeroes)
87 "\\." // a period
88 "(0|(?:[1-9][0-9]*))" // a number (no leading zeroes unless exactly
89 // zero)
90 "$" // The end of the string
91 ,
92 boost::regex_constants::optimize);
93
95
96 for (auto const& s : beast::rfc2616::split_commas(value))
97 {
98 boost::smatch m;
99
100 if (boost::regex_match(s, m, re))
101 {
102 std::uint16_t major;
103 std::uint16_t minor;
104 if (!beast::lexicalCastChecked(major, std::string(m[1])))
105 continue;
106
107 if (!beast::lexicalCastChecked(minor, std::string(m[2])))
108 continue;
109
110 auto const proto = make_protocol(major, minor);
111
112 // This is an extra sanity check: we check that the protocol we just
113 // decoded corresponds to the token we were parsing.
114 if (to_string(proto) == s)
115 result.push_back(make_protocol(major, minor));
116 }
117 }
118
119 // We guarantee that the returned list is sorted and contains no duplicates:
120 std::sort(result.begin(), result.end());
121 result.erase(std::unique(result.begin(), result.end()), result.end());
122
123 return result;
124}
125
128{
130
131 // The protocol version we want to negotiate is the largest item in the
132 // intersection of the versions supported by us and the peer. Since the
133 // output of std::set_intersection is sorted, that item is always going
134 // to be the last one. So we get a little clever and avoid the need for
135 // a container:
136 std::function<void(ProtocolVersion const&)> pickVersion =
137 [&result](ProtocolVersion const& v) { result = v; };
138
140 std::begin(versions),
141 std::end(versions),
144 boost::make_function_output_iterator(pickVersion));
145
146 return result;
147}
148
150negotiateProtocolVersion(boost::beast::string_view const& versions)
151{
152 auto const them = parseProtocolVersions(versions);
153
154 return negotiateProtocolVersion(them);
155}
156
157std::string const&
159{
160 static std::string const supported = []() {
161 std::string ret;
162 for (auto const& v : supportedProtocolList)
163 {
164 if (!ret.empty())
165 ret += ", ";
166 ret += to_string(v);
167 }
168
169 return ret;
170 }();
171
172 return supported;
173}
174
175bool
177{
179 std::find(
182 v);
183}
184
185} // 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:204
bool lexicalCastChecked(Out &out, In in)
Intelligently convert from one type to another.
Definition: LexicalCast.h:201
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
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:629
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)