From a3d8e1eb864e11dbb55c063c4131a0f1caadc966 Mon Sep 17 00:00:00 2001 From: Nicolas Werner <nicolas.werner@hotmail.de> Date: Thu, 27 Jun 2019 13:30:28 +0200 Subject: [PATCH] Add .well-known support --- CMakeLists.txt | 3 ++- include/mtx/responses.hpp | 1 + include/mtx/responses/login.hpp | 9 +++++++- include/mtx/responses/well-known.hpp | 31 ++++++++++++++++++++++++++++ include/mtxclient/http/client.hpp | 12 ++++++++--- include/mtxclient/http/session.hpp | 7 ++++--- lib/http/client.cpp | 13 ++++++++++++ lib/structs/responses/login.cpp | 3 +++ lib/structs/responses/well-known.cpp | 26 +++++++++++++++++++++++ tests/responses.cpp | 31 +++++++++++++++++++++++++++- 10 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 include/mtx/responses/well-known.hpp create mode 100644 lib/structs/responses/well-known.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 554bcbe13..a02e13938 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,7 +153,8 @@ add_library(matrix_client lib/structs/responses/profile.cpp lib/structs/responses/register.cpp lib/structs/responses/sync.cpp - lib/structs/responses/version.cpp) + lib/structs/responses/version.cpp + lib/structs/responses/well-known.cpp) add_library(MatrixClient::MatrixClient ALIAS matrix_client) target_include_directories(matrix_client SYSTEM diff --git a/include/mtx/responses.hpp b/include/mtx/responses.hpp index 120915d52..0f62b848f 100644 --- a/include/mtx/responses.hpp +++ b/include/mtx/responses.hpp @@ -14,3 +14,4 @@ #include "responses/register.hpp" #include "responses/sync.hpp" #include "responses/version.hpp" +#include "responses/well-known.hpp" diff --git a/include/mtx/responses/login.hpp b/include/mtx/responses/login.hpp index 5aaf4e7d7..f2c54bd72 100644 --- a/include/mtx/responses/login.hpp +++ b/include/mtx/responses/login.hpp @@ -5,6 +5,7 @@ #include <nlohmann/json.hpp> #include "mtx/identifiers.hpp" +#include "well-known.hpp" namespace mtx { namespace responses { @@ -18,10 +19,16 @@ struct Login //! This access token can then be used to authorize other requests. std::string access_token; //! The hostname of the homeserver on which the account has been registered. - std::string home_server; + [[deprecated("Clients should extract the server_name from user_id (by splitting at the " + "first colon) if they require it.")]] std::string home_server; //! ID of the logged-in device. //! Will be the same as the corresponding parameter in the request, if one was specified. std::string device_id; + + //! Optional client configuration provided by the server. + //! If present, clients SHOULD use the provided object to reconfigure themselves, + //! optionally validating the URLs within. + boost::optional<WellKnown> well_known; }; void diff --git a/include/mtx/responses/well-known.hpp b/include/mtx/responses/well-known.hpp new file mode 100644 index 000000000..792c00679 --- /dev/null +++ b/include/mtx/responses/well-known.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <boost/optional.hpp> +#include <nlohmann/json.hpp> + +namespace mtx { +namespace responses { +struct ServerInformation +{ + //! Required. The base URL for client-server connections. + std::string base_url; +}; + +//! Response from the `GET /.well-known/matrix/client` endpoint. +//! May also be returned from `POST /_matrix/client/r0/login`. +// +//! Gets discovery information about the domain +struct WellKnown +{ + //! Required. Used by clients to discover homeserver information. + ServerInformation homeserver; + //! Used by clients to discover identity server information. + boost::optional<ServerInformation> identity_server; +}; + +void +from_json(const nlohmann::json &obj, WellKnown &response); +void +from_json(const nlohmann::json &obj, ServerInformation &response); +} +} diff --git a/include/mtxclient/http/client.hpp b/include/mtxclient/http/client.hpp index 635d3a520..00376a25d 100644 --- a/include/mtxclient/http/client.hpp +++ b/include/mtxclient/http/client.hpp @@ -144,6 +144,9 @@ public: const std::string &device_name, Callback<mtx::responses::Login> cb); void login(const mtx::requests::Login &req, Callback<mtx::responses::Login> cb); + //! Lookup real server to connect to. + //! Call set_server with the returned homeserver url after this + void well_known(Callback<mtx::responses::WellKnown> cb); //! Register by not expecting a registration flow. void registration(const std::string &user, @@ -343,7 +346,8 @@ private: template<class Response> void get(const std::string &endpoint, HeadersCallback<Response> cb, - bool requires_auth = true); + bool requires_auth = true, + const std::string &endpoint_namespace = "/_matrix"); template<class Response> std::shared_ptr<Session> create_session(HeadersCallback<Response> callback); @@ -437,7 +441,8 @@ template<class Response> void mtx::http::Client::get(const std::string &endpoint, HeadersCallback<Response> callback, - bool requires_auth) + bool requires_auth, + const std::string &endpoint_namespace) { auto session = create_session<Response>(callback); @@ -445,7 +450,8 @@ mtx::http::Client::get(const std::string &endpoint, return; setup_auth(session.get(), requires_auth); - setup_headers<std::string, boost::beast::http::verb::get>(session.get(), {}, endpoint); + setup_headers<std::string, boost::beast::http::verb::get>( + session.get(), {}, endpoint, "", endpoint_namespace); session->run(); } diff --git a/include/mtxclient/http/session.hpp b/include/mtxclient/http/session.hpp index 21fe948c3..2fde50361 100644 --- a/include/mtxclient/http/session.hpp +++ b/include/mtxclient/http/session.hpp @@ -83,14 +83,15 @@ void setup_headers(mtx::http::Session *session, const Request &req, const std::string &endpoint, - const std::string &content_type = "") + const std::string &content_type = "", + const std::string &endpoint_namespace = "/_matrix") { - session->request.set(boost::beast::http::field::user_agent, "mtxclient v0.2.0"); + session->request.set(boost::beast::http::field::user_agent, "mtxclient v0.3.0"); session->request.set(boost::beast::http::field::accept_encoding, "gzip,deflate"); session->request.set(boost::beast::http::field::host, session->host); session->request.method(HttpVerb); - session->request.target("/_matrix" + endpoint); + session->request.target(endpoint_namespace + endpoint); session->request.body() = client::utils::serialize(req); session->request.prepare_payload(); diff --git a/lib/http/client.cpp b/lib/http/client.cpp index 97feceb2f..24ab2b557 100644 --- a/lib/http/client.cpp +++ b/lib/http/client.cpp @@ -111,6 +111,19 @@ Client::login(const mtx::requests::Login &req, Callback<mtx::responses::Login> c }, false); } + +void +Client::well_known(Callback<mtx::responses::WellKnown> callback) +{ + get<mtx::responses::WellKnown>( + "/matrix/client", + [callback](const mtx::responses::WellKnown &res, HeaderFields, RequestErr err) { + callback(res, err); + }, + false, + "/.well-known"); +} + void Client::logout(Callback<mtx::responses::Logout> callback) { diff --git a/lib/structs/responses/login.cpp b/lib/structs/responses/login.cpp index c7097ca61..f63763daf 100644 --- a/lib/structs/responses/login.cpp +++ b/lib/structs/responses/login.cpp @@ -16,6 +16,9 @@ from_json(const json &obj, Login &response) if (obj.count("device_id") != 0) response.device_id = obj.at("device_id").get<std::string>(); + + if (obj.count("well_known") != 0) + response.well_known = obj.at("well_known").get<WellKnown>(); } } } diff --git a/lib/structs/responses/well-known.cpp b/lib/structs/responses/well-known.cpp new file mode 100644 index 000000000..660ee371e --- /dev/null +++ b/lib/structs/responses/well-known.cpp @@ -0,0 +1,26 @@ +#include <regex> +#include <string> + +#include "mtx/responses/well-known.hpp" + +using json = nlohmann::json; + +namespace mtx { +namespace responses { + +void +from_json(const json &obj, WellKnown &response) +{ + response.homeserver = obj.at("m.homeserver").get<ServerInformation>(); + + if (obj.count("m.identity_server")) + response.identity_server = obj.at("m.identity_server").get<ServerInformation>(); +} + +void +from_json(const json &obj, ServerInformation &response) +{ + response.base_url = obj.at("base_url"); +} +} +} diff --git a/tests/responses.cpp b/tests/responses.cpp index acdf677f9..d4f598792 100644 --- a/tests/responses.cpp +++ b/tests/responses.cpp @@ -457,6 +457,25 @@ TEST(Responses, Versions) ASSERT_THROW(Versions versions = error_data, std::invalid_argument); } +TEST(Responses, WellKnown) +{ + json data = R"({ + "m.homeserver": { + "base_url": "https://matrix.example.com" + }, + "m.identity_server": { + "base_url": "https://identity.example.com" + }, + "org.example.custom.property": { + "app_url": "https://custom.app.example.org" + } + })"_json; + + WellKnown wellknown = data; + EXPECT_EQ(wellknown.homeserver.base_url, "https://matrix.example.com"); + EXPECT_EQ(wellknown.identity_server->base_url, "https://identity.example.com"); +} + TEST(Responses, CreateRoom) { json data = R"({"room_id" : "!sefiuhWgwghwWgh:example.com"})"_json; @@ -475,7 +494,15 @@ TEST(Responses, Login) "user_id": "@cheeky_monkey:matrix.org", "access_token": "abc123", "home_server": "matrix.org", - "device_id": "GHTYAJCE" + "device_id": "GHTYAJCE", + "well_known": { + "m.homeserver": { + "base_url": "https://example.org" + }, + "m.identity_server": { + "base_url": "https://id.example.org" + } + } })"_json; Login login = data; @@ -483,6 +510,8 @@ TEST(Responses, Login) EXPECT_EQ(login.access_token, "abc123"); EXPECT_EQ(login.home_server, "matrix.org"); EXPECT_EQ(login.device_id, "GHTYAJCE"); + EXPECT_EQ(login.well_known->homeserver.base_url, "https://example.org"); + EXPECT_EQ(login.well_known->identity_server->base_url, "https://id.example.org"); json data2 = R"({ "user_id": "@cheeky_monkey:matrix.org", -- GitLab