2018-09-01 09:51:40 +02:00
|
|
|
// Matrix Construct
|
|
|
|
//
|
|
|
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
|
|
|
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
|
|
|
//
|
|
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
|
|
// copyright notice and this permission notice is present in all copies. The
|
|
|
|
// full license for this software is available in the LICENSE file.
|
|
|
|
|
|
|
|
namespace ircd::m::sync
|
|
|
|
{
|
|
|
|
struct args;
|
|
|
|
struct stats;
|
|
|
|
struct shortpoll;
|
|
|
|
|
2018-10-18 16:44:20 +02:00
|
|
|
extern log::log log;
|
|
|
|
extern const string_view description;
|
|
|
|
extern resource resource;
|
|
|
|
extern resource::method method_get;
|
|
|
|
|
2018-09-14 15:21:19 +02:00
|
|
|
static long notification_count(const room &, const event::idx &a, const event::idx &b);
|
|
|
|
static long highlight_count(const room &, const user &, const event::idx &a, const event::idx &b);
|
2018-09-01 09:51:40 +02:00
|
|
|
static resource::response handle_get(client &, const resource::request &);
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace ircd::m::sync::longpoll
|
|
|
|
{
|
2018-10-07 07:17:46 +02:00
|
|
|
struct accepted
|
|
|
|
:m::event
|
|
|
|
{
|
|
|
|
json::strung strung;
|
|
|
|
std::string client_txnid;
|
|
|
|
|
|
|
|
accepted(const m::vm::eval &eval)
|
|
|
|
:strung
|
|
|
|
{
|
|
|
|
*eval.event_
|
|
|
|
}
|
|
|
|
,client_txnid
|
|
|
|
{
|
|
|
|
eval.copts?
|
|
|
|
eval.copts->client_txnid:
|
|
|
|
string_view{}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
const json::object object{this->strung};
|
|
|
|
static_cast<m::event &>(*this) = m::event{object};
|
|
|
|
}
|
|
|
|
|
|
|
|
accepted(accepted &&) = default;
|
|
|
|
accepted(const accepted &) = delete;
|
|
|
|
};
|
|
|
|
|
|
|
|
size_t polling {0};
|
|
|
|
std::deque<accepted> queue;
|
|
|
|
ctx::dock dock;
|
|
|
|
|
|
|
|
static std::string sync_room(client &, const m::room &, const args &, const accepted &);
|
|
|
|
static std::string sync_rooms(client &, const m::user::id &, const m::room &, const args &, const accepted &);
|
|
|
|
static bool handle(client &, const args &, const accepted &, const m::room &);
|
|
|
|
static bool handle(client &, const args &, const accepted &);
|
2018-12-02 02:23:42 +01:00
|
|
|
static bool poll(client &, const args &);
|
2018-10-07 07:17:46 +02:00
|
|
|
|
|
|
|
static void handle_notify(const m::event &, m::vm::eval &);
|
|
|
|
extern m::hookfn<m::vm::eval &> notified;
|
2018-09-01 09:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace ircd::m::sync::linear
|
|
|
|
{
|
2018-11-05 03:11:37 +01:00
|
|
|
extern conf::item<size_t> delta_max;
|
|
|
|
|
2018-09-01 11:57:15 +02:00
|
|
|
static bool handle(client &, shortpoll &, json::stack::object &);
|
2018-09-01 09:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace ircd::m::sync::polylog
|
|
|
|
{
|
2018-10-16 09:06:44 +02:00
|
|
|
extern conf::item<bool> prefetch_state;
|
|
|
|
extern conf::item<bool> prefetch_timeline;
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
static void room_state(shortpoll &, json::stack::object &, const m::room &);
|
|
|
|
static m::event::id::buf room_timeline_events(shortpoll &, json::stack::array &, const m::room &, bool &limited);
|
|
|
|
static void room_timeline(shortpoll &, json::stack::object &, const m::room &);
|
|
|
|
static void room_ephemeral_events(shortpoll &, json::stack::array &, const m::room &);
|
|
|
|
static void room_ephemeral(shortpoll &, json::stack::object &, const m::room &);
|
|
|
|
static void room_account_data(shortpoll &, json::stack::object &, const m::room &);
|
|
|
|
static void room_unread_notifications(shortpoll &, json::stack::object &, const m::room &);
|
|
|
|
static void sync_room(shortpoll &, json::stack::object &, const m::room &, const string_view &membership);
|
2018-10-14 12:17:21 +02:00
|
|
|
static void sync_rooms(shortpoll &, json::stack::object &, const string_view &membership);
|
2018-09-01 09:51:40 +02:00
|
|
|
static void rooms(shortpoll &, json::stack::object &);
|
|
|
|
static void presence(shortpoll &, json::stack::object &);
|
|
|
|
static void account_data(shortpoll &, json::stack::object &);
|
2018-09-01 11:57:15 +02:00
|
|
|
static bool handle(client &, shortpoll &, json::stack::object &);
|
2018-09-01 09:51:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Argument parser for the client's /sync request
|
|
|
|
struct ircd::m::sync::args
|
|
|
|
{
|
|
|
|
static conf::item<milliseconds> timeout_max;
|
|
|
|
static conf::item<milliseconds> timeout_min;
|
|
|
|
static conf::item<milliseconds> timeout_default;
|
|
|
|
|
2018-09-01 11:57:15 +02:00
|
|
|
args(const resource::request &request)
|
|
|
|
:request{request}
|
|
|
|
{}
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
const resource::request &request;
|
|
|
|
|
|
|
|
string_view filter_id
|
|
|
|
{
|
|
|
|
// 6.2.1 The ID of a filter created using the filter API or a filter JSON object
|
|
|
|
// encoded as a string. The server will detect whether it is an ID or a JSON object
|
|
|
|
// by whether the first character is a "{" open brace. Passing the JSON inline is best
|
|
|
|
// suited to one off requests. Creating a filter using the filter API is recommended
|
|
|
|
// for clients that reuse the same filter multiple times, for example in long poll requests.
|
|
|
|
request.query["filter"]
|
|
|
|
};
|
|
|
|
|
|
|
|
uint64_t since
|
|
|
|
{
|
|
|
|
// 6.2.1 A point in time to continue a sync from.
|
|
|
|
request.query.get<uint64_t>("since", 0)
|
|
|
|
};
|
|
|
|
|
|
|
|
steady_point timesout{[this]
|
|
|
|
{
|
|
|
|
// 6.2.1 The maximum time to poll in milliseconds before returning this request.
|
|
|
|
auto ret(request.query.get("timeout", milliseconds(timeout_default)));
|
|
|
|
ret = std::min(ret, milliseconds(timeout_max));
|
|
|
|
ret = std::max(ret, milliseconds(timeout_min));
|
|
|
|
return now<steady_point>() + ret;
|
|
|
|
}()};
|
|
|
|
|
|
|
|
bool full_state
|
|
|
|
{
|
|
|
|
// 6.2.1 Controls whether to include the full state for all rooms the user is a member of.
|
|
|
|
// If this is set to true, then all state events will be returned, even if since is non-empty.
|
|
|
|
// The timeline will still be limited by the since parameter. In this case, the timeout
|
|
|
|
// parameter will be ignored and the query will return immediately, possibly with an
|
|
|
|
// empty timeline. If false, and since is non-empty, only state which has changed since
|
|
|
|
// the point indicated by since will be returned. By default, this is false.
|
|
|
|
request.query.get("full_state", false)
|
|
|
|
};
|
|
|
|
|
|
|
|
bool set_presence
|
|
|
|
{
|
|
|
|
// 6.2.1 Controls whether the client is automatically marked as online by polling this API.
|
|
|
|
// If this parameter is omitted then the client is automatically marked as online when it
|
|
|
|
// uses this API. Otherwise if the parameter is set to "offline" then the client is not
|
|
|
|
// marked as being online when it uses this API. One of: ["offline"]
|
|
|
|
request.query.get("set_presence", true)
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ircd::m::sync::stats
|
|
|
|
{
|
|
|
|
ircd::timer timer;
|
|
|
|
size_t flush_bytes {0};
|
|
|
|
size_t flush_count {0};
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ircd::m::sync::shortpoll
|
|
|
|
{
|
|
|
|
static conf::item<size_t> flush_hiwat;
|
|
|
|
|
2018-09-01 11:57:15 +02:00
|
|
|
shortpoll(ircd::client &client,
|
|
|
|
const sync::args &args)
|
|
|
|
:client{client}
|
|
|
|
,args{args}
|
|
|
|
{}
|
|
|
|
|
|
|
|
sync::stats stats;
|
2018-09-01 09:51:40 +02:00
|
|
|
ircd::client &client;
|
|
|
|
const sync::args &args;
|
2018-09-01 11:57:15 +02:00
|
|
|
const resource::request &request
|
|
|
|
{
|
|
|
|
args.request
|
|
|
|
};
|
2018-09-01 09:51:40 +02:00
|
|
|
|
|
|
|
const uint64_t &since
|
|
|
|
{
|
|
|
|
args.since
|
|
|
|
};
|
|
|
|
|
|
|
|
const uint64_t current
|
|
|
|
{
|
|
|
|
m::vm::current_sequence
|
|
|
|
};
|
|
|
|
|
|
|
|
const uint64_t delta
|
|
|
|
{
|
|
|
|
current - since
|
|
|
|
};
|
|
|
|
|
|
|
|
const m::user user
|
|
|
|
{
|
|
|
|
request.user_id
|
|
|
|
};
|
|
|
|
|
|
|
|
const std::string filter_buf
|
|
|
|
{
|
|
|
|
args.filter_id?
|
|
|
|
user.filter(std::nothrow, args.filter_id):
|
|
|
|
std::string{}
|
|
|
|
};
|
|
|
|
|
|
|
|
const m::filter filter
|
|
|
|
{
|
|
|
|
json::object{filter_buf}
|
|
|
|
};
|
|
|
|
|
|
|
|
const m::user::room user_room
|
|
|
|
{
|
|
|
|
user
|
|
|
|
};
|
|
|
|
|
|
|
|
const m::user::rooms rooms
|
|
|
|
{
|
|
|
|
user
|
|
|
|
};
|
|
|
|
|
|
|
|
uint64_t state_at
|
|
|
|
{
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
bool committed
|
|
|
|
{
|
|
|
|
false
|
|
|
|
};
|
|
|
|
|
2018-09-01 11:57:15 +02:00
|
|
|
unique_buffer<mutable_buffer> buf
|
|
|
|
{
|
2018-11-05 03:11:55 +01:00
|
|
|
std::max(size_t(96_KiB), size_t(flush_hiwat))
|
2018-09-01 11:57:15 +02:00
|
|
|
};
|
|
|
|
|
2018-09-01 09:51:40 +02:00
|
|
|
std::unique_ptr<resource::response::chunked> response;
|
|
|
|
json::stack out
|
|
|
|
{
|
2018-09-01 11:57:15 +02:00
|
|
|
buf, std::bind(&shortpoll::flush, this, ph::_1), size_t(flush_hiwat)
|
|
|
|
};
|
2018-09-01 09:51:40 +02:00
|
|
|
|
2018-09-01 11:57:15 +02:00
|
|
|
void commit()
|
|
|
|
{
|
|
|
|
response = std::make_unique<resource::response::chunked>
|
|
|
|
(
|
|
|
|
client, http::OK, "application/json; charset=utf-8"
|
|
|
|
);
|
|
|
|
}
|
2018-09-01 09:51:40 +02:00
|
|
|
|
2018-09-01 11:57:15 +02:00
|
|
|
const_buffer flush(const const_buffer &buf)
|
|
|
|
{
|
|
|
|
if(!committed)
|
2018-09-01 09:51:40 +02:00
|
|
|
return buf;
|
|
|
|
|
2018-09-01 11:57:15 +02:00
|
|
|
if(!response)
|
|
|
|
commit();
|
|
|
|
|
|
|
|
stats.flush_bytes += response->write(buf);
|
|
|
|
stats.flush_count++;
|
|
|
|
return buf;
|
|
|
|
}
|
2018-09-01 09:51:40 +02:00
|
|
|
};
|