protocol: add command "binarylimit"
Increasing the protocol version to 0.22.4 to allow clients to detect this feature. Closes https://github.com/MusicPlayerDaemon/MPD/issues/1038
This commit is contained in:
parent
6e33566cee
commit
995aafe9cc
1
NEWS
1
NEWS
|
@ -1,5 +1,6 @@
|
||||||
ver 0.22.4 (not yet released)
|
ver 0.22.4 (not yet released)
|
||||||
* protocol
|
* protocol
|
||||||
|
- add command "binarylimit" to allow larger chunk sizes
|
||||||
- fix "readpicture" on 32 bit machines
|
- fix "readpicture" on 32 bit machines
|
||||||
- show duration and tags of songs in virtual playlist (CUE) folders
|
- show duration and tags of songs in virtual playlist (CUE) folders
|
||||||
* storage
|
* storage
|
||||||
|
|
|
@ -69,11 +69,14 @@ that, the specified number of bytes of binary data follows, then a
|
||||||
newline, and finally the ``OK`` line.
|
newline, and finally the ``OK`` line.
|
||||||
|
|
||||||
If the object to be transmitted is large, the server may choose a
|
If the object to be transmitted is large, the server may choose a
|
||||||
reasonable chunk size and transmit only a portion. Usually, the
|
reasonable chunk size and transmit only a portion. The maximum chunk
|
||||||
response also contains a ``size`` line which specifies the total
|
size can be changed by clients with the :ref:`binarylimit
|
||||||
(uncropped) size, and the command usually has a way to specify an
|
<command_binarylimit>` command.
|
||||||
offset into the object; this way, the client can copy the whole file
|
|
||||||
without blocking the connection for too long.
|
Usually, the response also contains a ``size`` line which specifies
|
||||||
|
the total (uncropped) size, and the command usually has a way to
|
||||||
|
specify an offset into the object; this way, the client can copy the
|
||||||
|
whole file without blocking the connection for too long.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
|
@ -1355,6 +1358,17 @@ Connection settings
|
||||||
:command:`ping`
|
:command:`ping`
|
||||||
Does nothing but return "OK".
|
Does nothing but return "OK".
|
||||||
|
|
||||||
|
.. _command_binarylimit:
|
||||||
|
|
||||||
|
:command:`binarylimit SIZE` [#since_0_22_4]_
|
||||||
|
|
||||||
|
Set the maximum :ref:`binary response <binary>` size for the
|
||||||
|
current connection to the specified number of bytes.
|
||||||
|
|
||||||
|
A bigger value means less overhead for transmitting large
|
||||||
|
entities, but it also means that the connection is blocked for a
|
||||||
|
longer time.
|
||||||
|
|
||||||
.. _command_tagtypes:
|
.. _command_tagtypes:
|
||||||
|
|
||||||
:command:`tagtypes`
|
:command:`tagtypes`
|
||||||
|
@ -1583,3 +1597,4 @@ client-to-client messages are local to the current partition.
|
||||||
.. [#since_0_19] Since :program:`MPD` 0.19
|
.. [#since_0_19] Since :program:`MPD` 0.19
|
||||||
.. [#since_0_20] Since :program:`MPD` 0.20
|
.. [#since_0_20] Since :program:`MPD` 0.20
|
||||||
.. [#since_0_21] Since :program:`MPD` 0.21
|
.. [#since_0_21] Since :program:`MPD` 0.21
|
||||||
|
.. [#since_0_22_4] Since :program:`MPD` 0.22.4
|
||||||
|
|
|
@ -32,7 +32,7 @@ version_conf = configuration_data()
|
||||||
version_conf.set_quoted('PACKAGE', meson.project_name())
|
version_conf.set_quoted('PACKAGE', meson.project_name())
|
||||||
version_conf.set_quoted('PACKAGE_NAME', meson.project_name())
|
version_conf.set_quoted('PACKAGE_NAME', meson.project_name())
|
||||||
version_conf.set_quoted('VERSION', meson.project_version())
|
version_conf.set_quoted('VERSION', meson.project_version())
|
||||||
version_conf.set_quoted('PROTOCOL_VERSION', '0.22.0')
|
version_conf.set_quoted('PROTOCOL_VERSION', '0.22.4')
|
||||||
configure_file(output: 'Version.h', configuration: version_conf)
|
configure_file(output: 'Version.h', configuration: version_conf)
|
||||||
|
|
||||||
conf = configuration_data()
|
conf = configuration_data()
|
||||||
|
|
|
@ -84,6 +84,12 @@ public:
|
||||||
*/
|
*/
|
||||||
TagMask tag_mask = TagMask::All();
|
TagMask tag_mask = TagMask::All();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of bytes transmitted in a binary
|
||||||
|
* response. Can be changed with the "binarylimit" command.
|
||||||
|
*/
|
||||||
|
size_t binary_limit = 8192;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr size_t MAX_SUBSCRIPTIONS = 16;
|
static constexpr size_t MAX_SUBSCRIPTIONS = 16;
|
||||||
|
|
||||||
|
@ -122,6 +128,7 @@ public:
|
||||||
~Client() noexcept;
|
~Client() noexcept;
|
||||||
|
|
||||||
using FullyBufferedSocket::GetEventLoop;
|
using FullyBufferedSocket::GetEventLoop;
|
||||||
|
using FullyBufferedSocket::GetOutputMaxSize;
|
||||||
|
|
||||||
gcc_pure
|
gcc_pure
|
||||||
bool IsExpired() const noexcept {
|
bool IsExpired() const noexcept {
|
||||||
|
|
|
@ -59,7 +59,7 @@ Response::Format(const char *fmt, ...) noexcept
|
||||||
bool
|
bool
|
||||||
Response::WriteBinary(ConstBuffer<void> payload) noexcept
|
Response::WriteBinary(ConstBuffer<void> payload) noexcept
|
||||||
{
|
{
|
||||||
assert(payload.size <= MAX_BINARY_SIZE);
|
assert(payload.size <= client.binary_limit);
|
||||||
|
|
||||||
return Format("binary: %zu\n", payload.size) &&
|
return Format("binary: %zu\n", payload.size) &&
|
||||||
Write(payload.data, payload.size) &&
|
Write(payload.data, payload.size) &&
|
||||||
|
|
|
@ -79,8 +79,6 @@ public:
|
||||||
gcc_printf(2,3)
|
gcc_printf(2,3)
|
||||||
bool Format(const char *fmt, ...) noexcept;
|
bool Format(const char *fmt, ...) noexcept;
|
||||||
|
|
||||||
static constexpr size_t MAX_BINARY_SIZE = 8192;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a binary chunk; this writes the "binary" line, the
|
* Write a binary chunk; this writes the "binary" line, the
|
||||||
* given chunk and the trailing newline.
|
* given chunk and the trailing newline.
|
||||||
|
|
|
@ -87,6 +87,7 @@ static constexpr struct command commands[] = {
|
||||||
{ "addid", PERMISSION_ADD, 1, 2, handle_addid },
|
{ "addid", PERMISSION_ADD, 1, 2, handle_addid },
|
||||||
{ "addtagid", PERMISSION_ADD, 3, 3, handle_addtagid },
|
{ "addtagid", PERMISSION_ADD, 3, 3, handle_addtagid },
|
||||||
{ "albumart", PERMISSION_READ, 2, 2, handle_album_art },
|
{ "albumart", PERMISSION_READ, 2, 2, handle_album_art },
|
||||||
|
{ "binarylimit", PERMISSION_NONE, 1, 1, handle_binary_limit },
|
||||||
{ "channels", PERMISSION_READ, 0, 0, handle_channels },
|
{ "channels", PERMISSION_READ, 0, 0, handle_channels },
|
||||||
{ "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
|
{ "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
|
||||||
{ "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },
|
{ "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },
|
||||||
|
|
|
@ -40,6 +40,21 @@ handle_ping([[maybe_unused]] Client &client, [[maybe_unused]] Request args,
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CommandResult
|
||||||
|
handle_binary_limit(Client &client, Request args,
|
||||||
|
[[maybe_unused]] Response &r)
|
||||||
|
{
|
||||||
|
size_t value = args.ParseUnsigned(0, client.GetOutputMaxSize() - 4096);
|
||||||
|
if (value < 64) {
|
||||||
|
r.Error(ACK_ERROR_ARG, "Value too small");
|
||||||
|
return CommandResult::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.binary_limit = value;
|
||||||
|
|
||||||
|
return CommandResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_password(Client &client, Request args, Response &r)
|
handle_password(Client &client, Request args, Response &r)
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,6 +32,9 @@ handle_close(Client &client, Request request, Response &response);
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_ping(Client &client, Request request, Response &response);
|
handle_ping(Client &client, Request request, Response &response);
|
||||||
|
|
||||||
|
CommandResult
|
||||||
|
handle_binary_limit(Client &client, Request request, Response &response);
|
||||||
|
|
||||||
CommandResult
|
CommandResult
|
||||||
handle_password(Client &client, Request request, Response &response);
|
handle_password(Client &client, Request request, Response &response);
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "thread/Mutex.hxx"
|
#include "thread/Mutex.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cinttypes> /* for PRIu64 */
|
#include <cinttypes> /* for PRIu64 */
|
||||||
|
|
||||||
|
@ -205,28 +206,26 @@ read_stream_art(Response &r, const char *uri, size_t offset)
|
||||||
|
|
||||||
const offset_type art_file_size = is->GetSize();
|
const offset_type art_file_size = is->GetSize();
|
||||||
|
|
||||||
if (offset >= art_file_size) {
|
|
||||||
if (offset > art_file_size) {
|
if (offset > art_file_size) {
|
||||||
r.Error(ACK_ERROR_ARG, "Offset too large");
|
r.Error(ACK_ERROR_ARG, "Offset too large");
|
||||||
return CommandResult::ERROR;
|
return CommandResult::ERROR;
|
||||||
} else {
|
|
||||||
r.Format("size: %" PRIoffset "\n", art_file_size);
|
|
||||||
r.WriteBinary(nullptr);
|
|
||||||
return CommandResult::OK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t buffer[Response::MAX_BINARY_SIZE];
|
std::size_t buffer_size =
|
||||||
size_t read_size;
|
std::min<offset_type>(art_file_size - offset,
|
||||||
|
r.GetClient().binary_limit);
|
||||||
|
|
||||||
{
|
std::unique_ptr<std::byte[]> buffer(new std::byte[buffer_size]);
|
||||||
|
|
||||||
|
std::size_t read_size = 0;
|
||||||
|
if (buffer_size > 0) {
|
||||||
std::unique_lock<Mutex> lock(mutex);
|
std::unique_lock<Mutex> lock(mutex);
|
||||||
is->Seek(lock, offset);
|
is->Seek(lock, offset);
|
||||||
read_size = is->Read(lock, &buffer, sizeof(buffer));
|
read_size = is->Read(lock, buffer.get(), buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Format("size: %" PRIoffset "\n", art_file_size);
|
r.Format("size: %" PRIoffset "\n", art_file_size);
|
||||||
r.WriteBinary({buffer, read_size});
|
r.WriteBinary({buffer.get(), read_size});
|
||||||
|
|
||||||
return CommandResult::OK;
|
return CommandResult::OK;
|
||||||
}
|
}
|
||||||
|
@ -313,8 +312,10 @@ public:
|
||||||
response.Format("type: %s\n", mime_type);
|
response.Format("type: %s\n", mime_type);
|
||||||
|
|
||||||
buffer.size -= offset;
|
buffer.size -= offset;
|
||||||
if (buffer.size > Response::MAX_BINARY_SIZE)
|
|
||||||
buffer.size = Response::MAX_BINARY_SIZE;
|
const std::size_t binary_limit = response.GetClient().binary_limit;
|
||||||
|
if (buffer.size > binary_limit)
|
||||||
|
buffer.size = binary_limit;
|
||||||
buffer.data = OffsetPointer(buffer.data, offset);
|
buffer.data = OffsetPointer(buffer.data, offset);
|
||||||
|
|
||||||
response.WriteBinary(buffer);
|
response.WriteBinary(buffer);
|
||||||
|
|
Loading…
Reference in New Issue