Index: lldb/include/lldb/Utility/UriParser.h =================================================================== --- lldb/include/lldb/Utility/UriParser.h +++ lldb/include/lldb/Utility/UriParser.h @@ -13,20 +13,23 @@ #include "llvm/ADT/StringRef.h" namespace lldb_private { -class UriParser { -public: - // Parses - // RETURN VALUE - // if url is valid, function returns true and - // scheme/hostname/port/path are set to the parsed values - // port it set to llvm::None if it is not included in the URL - // - // if the url is invalid, function returns false and - // output parameters remain unchanged - static bool Parse(llvm::StringRef uri, llvm::StringRef &scheme, - llvm::StringRef &hostname, llvm::Optional &port, - llvm::StringRef &path); + +struct URI { + llvm::StringRef scheme; + llvm::StringRef hostname; + llvm::Optional port; + llvm::StringRef path; + + bool operator==(const URI &R) const { + return port == R.port && scheme == R.scheme && hostname == R.hostname && + path == R.path; + } + + static llvm::Optional Parse(llvm::StringRef uri); }; -} + +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const URI &U); + +} // namespace lldb_private #endif // LLDB_UTILITY_URIPARSER_H Index: lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp =================================================================== --- lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp +++ lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp @@ -155,15 +155,14 @@ if (!m_remote_platform_sp) m_remote_platform_sp = PlatformSP(new PlatformAndroidRemoteGDBServer()); - llvm::Optional port; - llvm::StringRef scheme, host, path; const char *url = args.GetArgumentAtIndex(0); if (!url) return Status("URL is null."); - if (!UriParser::Parse(url, scheme, host, port, path)) + llvm::Optional parsed_url = URI::Parse(url); + if (!parsed_url) return Status("Invalid URL: %s", url); - if (host != "localhost") - m_device_id = std::string(host); + if (parsed_url->hostname != "localhost") + m_device_id = parsed_url->hostname.str(); auto error = PlatformLinux::ConnectRemote(args); if (error.Success()) { Index: lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp =================================================================== --- lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp +++ lldb/source/Plugins/Platform/Android/PlatformAndroidRemoteGDBServer.cpp @@ -109,25 +109,25 @@ return Status( "\"platform connect\" takes a single argument: "); - llvm::Optional remote_port; - llvm::StringRef scheme, host, path; const char *url = args.GetArgumentAtIndex(0); if (!url) return Status("URL is null."); - if (!UriParser::Parse(url, scheme, host, remote_port, path)) + llvm::Optional parsed_url = URI::Parse(url); + if (!parsed_url) return Status("Invalid URL: %s", url); - if (host != "localhost") - m_device_id = std::string(host); + if (parsed_url->hostname != "localhost") + m_device_id = parsed_url->hostname.str(); m_socket_namespace.reset(); - if (scheme == "unix-connect") + if (parsed_url->scheme == "unix-connect") m_socket_namespace = AdbClient::UnixSocketNamespaceFileSystem; - else if (scheme == "unix-abstract-connect") + else if (parsed_url->scheme == "unix-abstract-connect") m_socket_namespace = AdbClient::UnixSocketNamespaceAbstract; std::string connect_url; - auto error = MakeConnectURL(g_remote_platform_pid, remote_port.getValueOr(0), - path, connect_url); + auto error = + MakeConnectURL(g_remote_platform_pid, parsed_url->port.getValueOr(0), + parsed_url->path, connect_url); if (error.Fail()) return error; @@ -206,9 +206,8 @@ // any other valid pid on android. static lldb::pid_t s_remote_gdbserver_fake_pid = 0xffffffffffffffffULL; - llvm::Optional remote_port; - llvm::StringRef scheme, host, path; - if (!UriParser::Parse(connect_url, scheme, host, remote_port, path)) { + llvm::Optional parsed_url = URI::Parse(connect_url); + if (!parsed_url) { error.SetErrorStringWithFormat("Invalid URL: %s", connect_url.str().c_str()); return nullptr; @@ -216,7 +215,8 @@ std::string new_connect_url; error = MakeConnectURL(s_remote_gdbserver_fake_pid--, - remote_port.getValueOr(0), path, new_connect_url); + parsed_url->port.getValueOr(0), parsed_url->path, + new_connect_url); if (error.Fail()) return nullptr; Index: lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp =================================================================== --- lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -305,14 +305,13 @@ if (!url) return Status("URL is null."); - llvm::Optional port; - llvm::StringRef scheme, hostname, pathname; - if (!UriParser::Parse(url, scheme, hostname, port, pathname)) + llvm::Optional parsed_url = URI::Parse(url); + if (!parsed_url) return Status("Invalid URL: %s", url); // We're going to reuse the hostname when we connect to the debugserver. - m_platform_scheme = std::string(scheme); - m_platform_hostname = std::string(hostname); + m_platform_scheme = parsed_url->scheme.str(); + m_platform_hostname = parsed_url->hostname.str(); m_gdb_client.SetConnection(std::make_unique()); if (repro::Reproducer::Instance().IsReplaying()) { Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerPlatform.cpp @@ -197,16 +197,9 @@ #endif uint16_t *port_ptr = port.getPointer(); if (m_socket_protocol == Socket::ProtocolTcp) { - llvm::StringRef platform_scheme; - llvm::StringRef platform_ip; - llvm::Optional platform_port; - llvm::StringRef platform_path; std::string platform_uri = GetConnection()->GetURI(); - bool ok = UriParser::Parse(platform_uri, platform_scheme, platform_ip, - platform_port, platform_path); - UNUSED_IF_ASSERT_DISABLED(ok); - assert(ok); - url << '[' << platform_ip.str() << "]:" << *port; + llvm::Optional parsed_uri = URI::Parse(platform_uri); + url << '[' << parsed_uri->hostname.str() << "]:" << *port; } else { socket_name = GetDomainSocketPath("gdbserver").GetPath(); url << socket_name; Index: lldb/source/Utility/UriParser.cpp =================================================================== --- lldb/source/Utility/UriParser.cpp +++ lldb/source/Utility/UriParser.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Utility/UriParser.h" +#include "llvm/Support/raw_ostream.h" #include @@ -15,25 +16,30 @@ using namespace lldb_private; -// UriParser::Parse -bool UriParser::Parse(llvm::StringRef uri, llvm::StringRef &scheme, - llvm::StringRef &hostname, llvm::Optional &port, - llvm::StringRef &path) { - llvm::StringRef tmp_scheme, tmp_hostname, tmp_path; +llvm::raw_ostream &lldb_private::operator<<(llvm::raw_ostream &OS, + const URI &U) { + OS << U.scheme << "://[" << U.hostname << ']'; + if (U.port) + OS << ':' << U.port.getValue(); + return OS << U.path; +} + +llvm::Optional URI::Parse(llvm::StringRef uri) { + URI ret; const llvm::StringRef kSchemeSep("://"); auto pos = uri.find(kSchemeSep); if (pos == std::string::npos) - return false; + return llvm::None; // Extract path. - tmp_scheme = uri.substr(0, pos); + ret.scheme = uri.substr(0, pos); auto host_pos = pos + kSchemeSep.size(); auto path_pos = uri.find('/', host_pos); if (path_pos != std::string::npos) - tmp_path = uri.substr(path_pos); + ret.path = uri.substr(path_pos); else - tmp_path = "/"; + ret.path = "/"; auto host_port = uri.substr( host_pos, @@ -44,27 +50,24 @@ // hostname is enclosed with square brackets. pos = host_port.rfind(']'); if (pos == std::string::npos) - return false; + return llvm::None; - tmp_hostname = host_port.substr(1, pos - 1); + ret.hostname = host_port.substr(1, pos - 1); host_port = host_port.drop_front(pos + 1); if (!host_port.empty() && !host_port.consume_front(":")) - return false; + return llvm::None; } else { - std::tie(tmp_hostname, host_port) = host_port.split(':'); + std::tie(ret.hostname, host_port) = host_port.split(':'); } // Extract port if (!host_port.empty()) { uint16_t port_value = 0; if (host_port.getAsInteger(0, port_value)) - return false; - port = port_value; + return llvm::None; + ret.port = port_value; } else - port = llvm::None; + ret.port = llvm::None; - scheme = tmp_scheme; - hostname = tmp_hostname; - path = tmp_path; - return true; + return ret; } Index: lldb/tools/lldb-server/Acceptor.cpp =================================================================== --- lldb/tools/lldb-server/Acceptor.cpp +++ lldb/tools/lldb-server/Acceptor.cpp @@ -84,15 +84,13 @@ error.Clear(); Socket::SocketProtocol socket_protocol = Socket::ProtocolUnixDomain; - llvm::Optional port; - StringRef scheme, host, path; // Try to match socket name as URL - e.g., tcp://localhost:5555 - if (UriParser::Parse(name, scheme, host, port, path)) { - if (!FindProtocolByScheme(scheme.str().c_str(), socket_protocol)) + if (llvm::Optional res = URI::Parse(name)) { + if (!FindProtocolByScheme(res->scheme.str().c_str(), socket_protocol)) error.SetErrorStringWithFormat("Unknown protocol scheme \"%s\"", - scheme.str().c_str()); + res->scheme.str().c_str()); else - name = name.drop_front(scheme.size() + strlen("://")); + name = name.drop_front(res->scheme.size() + strlen("://")); } else { std::string host_str; std::string port_str; Index: lldb/unittests/Host/ConnectionFileDescriptorTest.cpp =================================================================== --- lldb/unittests/Host/ConnectionFileDescriptorTest.cpp +++ lldb/unittests/Host/ConnectionFileDescriptorTest.cpp @@ -26,14 +26,9 @@ auto socket = socket_a_up.release(); ConnectionFileDescriptor connection_file_descriptor(socket); - llvm::StringRef scheme; - llvm::StringRef hostname; - llvm::Optional port; - llvm::StringRef path; std::string uri(connection_file_descriptor.GetURI()); - EXPECT_TRUE(UriParser::Parse(uri, scheme, hostname, port, path)); - EXPECT_EQ(ip, hostname); - EXPECT_EQ(socket->GetRemotePortNumber(), port.getValue()); + EXPECT_EQ((URI{"connect", ip, socket->GetRemotePortNumber(), "/"}), + URI::Parse(uri).getValue()); } }; Index: lldb/unittests/Host/SocketTest.cpp =================================================================== --- lldb/unittests/Host/SocketTest.cpp +++ lldb/unittests/Host/SocketTest.cpp @@ -171,14 +171,10 @@ CreateTCPConnectedSockets(GetParam().localhost_ip, &socket_a_up, &socket_b_up); - llvm::StringRef scheme; - llvm::StringRef hostname; - llvm::Optional port; - llvm::StringRef path; std::string uri(socket_a_up->GetRemoteConnectionURI()); - EXPECT_TRUE(UriParser::Parse(uri, scheme, hostname, port, path)); - EXPECT_EQ(scheme, "connect"); - EXPECT_EQ(port, socket_a_up->GetRemotePortNumber()); + EXPECT_EQ((URI{"connect", GetParam().localhost_ip, + socket_a_up->GetRemotePortNumber(), "/"}), + URI::Parse(uri)); } TEST_P(SocketTest, UDPGetConnectURI) { @@ -189,13 +185,8 @@ UDPSocket::Connect("127.0.0.1:0", /*child_processes_inherit=*/false); ASSERT_THAT_EXPECTED(socket, llvm::Succeeded()); - llvm::StringRef scheme; - llvm::StringRef hostname; - llvm::Optional port; - llvm::StringRef path; std::string uri = socket.get()->GetRemoteConnectionURI(); - EXPECT_TRUE(UriParser::Parse(uri, scheme, hostname, port, path)); - EXPECT_EQ(scheme, "udp"); + EXPECT_EQ((URI{"udp", "127.0.0.1", 0, "/"}), URI::Parse(uri)); } #if LLDB_ENABLE_POSIX @@ -214,14 +205,9 @@ std::unique_ptr socket_b_up; CreateDomainConnectedSockets(domain_path, &socket_a_up, &socket_b_up); - llvm::StringRef scheme; - llvm::StringRef hostname; - llvm::Optional port; - llvm::StringRef path; std::string uri(socket_a_up->GetRemoteConnectionURI()); - EXPECT_TRUE(UriParser::Parse(uri, scheme, hostname, port, path)); - EXPECT_EQ(scheme, "unix-connect"); - EXPECT_EQ(path, domain_path); + EXPECT_EQ((URI{"unix-connect", "", llvm::None, domain_path}), + URI::Parse(uri)); EXPECT_EQ(socket_b_up->GetRemoteConnectionURI(), ""); } Index: lldb/unittests/Utility/UriParserTest.cpp =================================================================== --- lldb/unittests/Utility/UriParserTest.cpp +++ lldb/unittests/Utility/UriParserTest.cpp @@ -3,174 +3,95 @@ using namespace lldb_private; -// result strings (scheme/hostname/port/path) passed into UriParser::Parse -// are initialized to kAsdf so we can verify that they are unmodified if the -// URI is invalid -static const char *kAsdf = "asdf"; - -class UriTestCase { -public: - UriTestCase(const char *uri, const char *scheme, const char *hostname, - llvm::Optional port, const char *path) - : m_uri(uri), m_result(true), m_scheme(scheme), m_hostname(hostname), - m_port(port), m_path(path) {} - - UriTestCase(const char *uri) - : m_uri(uri), m_result(false), m_scheme(kAsdf), m_hostname(kAsdf), - m_port(1138), m_path(kAsdf) {} - - const char *m_uri; - bool m_result; - const char *m_scheme; - const char *m_hostname; - llvm::Optional m_port; - const char *m_path; -}; - -#define VALIDATE \ - llvm::StringRef scheme(kAsdf); \ - llvm::StringRef hostname(kAsdf); \ - llvm::Optional port(1138); \ - llvm::StringRef path(kAsdf); \ - EXPECT_EQ(testCase.m_result, \ - UriParser::Parse(testCase.m_uri, scheme, hostname, port, path)); \ - EXPECT_STREQ(testCase.m_scheme, scheme.str().c_str()); \ - EXPECT_STREQ(testCase.m_hostname, hostname.str().c_str()); \ - EXPECT_EQ(testCase.m_port, port); \ - EXPECT_STREQ(testCase.m_path, path.str().c_str()); - TEST(UriParserTest, Minimal) { - const UriTestCase testCase("x://y", "x", "y", llvm::None, "/"); - VALIDATE + EXPECT_EQ((URI{"x", "y", llvm::None, "/"}), URI::Parse("x://y")); } TEST(UriParserTest, MinimalPort) { - const UriTestCase testCase("x://y:1", "x", "y", 1, "/"); - llvm::StringRef scheme(kAsdf); - llvm::StringRef hostname(kAsdf); - llvm::Optional port(1138); - llvm::StringRef path(kAsdf); - bool result = UriParser::Parse(testCase.m_uri, scheme, hostname, port, path); - EXPECT_EQ(testCase.m_result, result); - - EXPECT_STREQ(testCase.m_scheme, scheme.str().c_str()); - EXPECT_STREQ(testCase.m_hostname, hostname.str().c_str()); - EXPECT_EQ(testCase.m_port, port); - EXPECT_STREQ(testCase.m_path, path.str().c_str()); + EXPECT_EQ((URI{"x", "y", 1, "/"}), URI::Parse("x://y:1")); } TEST(UriParserTest, MinimalPath) { - const UriTestCase testCase("x://y/", "x", "y", llvm::None, "/"); - VALIDATE + EXPECT_EQ((URI{"x", "y", llvm::None, "/"}), URI::Parse("x://y/")); } TEST(UriParserTest, MinimalPortPath) { - const UriTestCase testCase("x://y:1/", "x", "y", 1, "/"); - VALIDATE + EXPECT_EQ((URI{"x", "y", 1, "/"}), URI::Parse("x://y:1/")); } TEST(UriParserTest, LongPath) { - const UriTestCase testCase("x://y/abc/def/xyz", "x", "y", llvm::None, - "/abc/def/xyz"); - VALIDATE + EXPECT_EQ((URI{"x", "y", llvm::None, "/abc/def/xyz"}), + URI::Parse("x://y/abc/def/xyz")); } TEST(UriParserTest, TypicalPortPathIPv4) { - const UriTestCase testCase("connect://192.168.100.132:5432/", "connect", - "192.168.100.132", 5432, "/"); - VALIDATE; + EXPECT_EQ((URI{"connect", "192.168.100.132", 5432, "/"}), + URI::Parse("connect://192.168.100.132:5432/")); } TEST(UriParserTest, TypicalPortPathIPv6) { - const UriTestCase testCase( - "connect://[2601:600:107f:db64:a42b:4faa:284:3082]:5432/", "connect", - "2601:600:107f:db64:a42b:4faa:284:3082", 5432, "/"); - VALIDATE; + EXPECT_EQ( + (URI{"connect", "2601:600:107f:db64:a42b:4faa:284:3082", 5432, "/"}), + URI::Parse("connect://[2601:600:107f:db64:a42b:4faa:284:3082]:5432/")); } TEST(UriParserTest, BracketedHostnamePort) { - const UriTestCase testCase("connect://[192.168.100.132]:5432/", "connect", - "192.168.100.132", 5432, "/"); - llvm::StringRef scheme(kAsdf); - llvm::StringRef hostname(kAsdf); - llvm::Optional port(1138); - llvm::StringRef path(kAsdf); - bool result = UriParser::Parse(testCase.m_uri, scheme, hostname, port, path); - EXPECT_EQ(testCase.m_result, result); - - EXPECT_STREQ(testCase.m_scheme, scheme.str().c_str()); - EXPECT_STREQ(testCase.m_hostname, hostname.str().c_str()); - EXPECT_EQ(testCase.m_port, port); - EXPECT_STREQ(testCase.m_path, path.str().c_str()); + EXPECT_EQ((URI{"connect", "192.168.100.132", 5432, "/"}), + URI::Parse("connect://[192.168.100.132]:5432/")); } TEST(UriParserTest, BracketedHostname) { - const UriTestCase testCase("connect://[192.168.100.132]", "connect", - "192.168.100.132", llvm::None, "/"); - VALIDATE + EXPECT_EQ((URI{"connect", "192.168.100.132", llvm::None, "/"}), + URI::Parse("connect://[192.168.100.132]")); } TEST(UriParserTest, BracketedHostnameWithPortIPv4) { // Android device over IPv4: port is a part of the hostname. - const UriTestCase testCase("connect://[192.168.100.132:1234]", "connect", - "192.168.100.132:1234", llvm::None, "/"); - VALIDATE + EXPECT_EQ((URI{"connect", "192.168.100.132:1234", llvm::None, "/"}), + URI::Parse("connect://[192.168.100.132:1234]")); } TEST(UriParserTest, BracketedHostnameWithPortIPv6) { // Android device over IPv6: port is a part of the hostname. - const UriTestCase testCase( - "connect://[[2601:600:107f:db64:a42b:4faa:284]:1234]", "connect", - "[2601:600:107f:db64:a42b:4faa:284]:1234", llvm::None, "/"); - VALIDATE + EXPECT_EQ((URI{"connect", "[2601:600:107f:db64:a42b:4faa:284]:1234", + llvm::None, "/"}), + URI::Parse("connect://[[2601:600:107f:db64:a42b:4faa:284]:1234]")); } TEST(UriParserTest, BracketedHostnameWithColon) { - const UriTestCase testCase("connect://[192.168.100.132:5555]:1234", "connect", - "192.168.100.132:5555", 1234, "/"); - VALIDATE + EXPECT_EQ((URI{"connect", "192.168.100.132:5555", 1234, "/"}), + URI::Parse("connect://[192.168.100.132:5555]:1234")); } TEST(UriParserTest, SchemeHostSeparator) { - const UriTestCase testCase("x:/y"); - VALIDATE + EXPECT_EQ(llvm::None, URI::Parse("x:/y")); } TEST(UriParserTest, SchemeHostSeparator2) { - const UriTestCase testCase("x:y"); - VALIDATE + EXPECT_EQ(llvm::None, URI::Parse("x:y")); } TEST(UriParserTest, SchemeHostSeparator3) { - const UriTestCase testCase("x//y"); - VALIDATE + EXPECT_EQ(llvm::None, URI::Parse("x//y")); } TEST(UriParserTest, SchemeHostSeparator4) { - const UriTestCase testCase("x/y"); - VALIDATE + EXPECT_EQ(llvm::None, URI::Parse("x/y")); } -TEST(UriParserTest, BadPort) { - const UriTestCase testCase("x://y:a/"); - VALIDATE -} +TEST(UriParserTest, BadPort) { EXPECT_EQ(llvm::None, URI::Parse("x://y:a/")); } TEST(UriParserTest, BadPort2) { - const UriTestCase testCase("x://y:5432a/"); - VALIDATE + EXPECT_EQ(llvm::None, URI::Parse("x://y:5432a/")); } -TEST(UriParserTest, Empty) { - const UriTestCase testCase(""); - VALIDATE -} +TEST(UriParserTest, Empty) { EXPECT_EQ(llvm::None, URI::Parse("")); } TEST(UriParserTest, PortOverflow) { - const UriTestCase testCase("x://" - "y:" - "0123456789012345678901234567890123456789012345678" - "9012345678901234567890123456789012345678901234567" - "89/"); - VALIDATE + EXPECT_EQ(llvm::None, + URI::Parse("x://" + "y:" + "0123456789012345678901234567890123456789012345678" + "9012345678901234567890123456789012345678901234567" + "89/")); }