third_party.pigweed.src/pw_transfer/context.cc
Alexei Frolov 86e05def88 pw_rpc: Add call IDs to protocol
This expands the RPC protocol to include an optional call_id field, used
to map server->client packets to the client call object that made them.
This fixes an issue where a reinvoked call could receive a response
intended for the original invocation, with potentially stale data.

This feature is opt-in for RPC clients. Setting all call_ids to the same
number (such as the proto3 default 0) results in the previous client
behavior. Therefore, it is backwards compatible with all existing RPC
clients.

Change-Id: I6f1894afd491e6308dca5c31ae1704af920381c6
Requires: pigweed-internal:16840
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/66064
Commit-Queue: Alexei Frolov <frolv@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
2021-10-20 03:56:59 +00:00

77 lines
2.9 KiB
C++

// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_transfer/internal/context.h"
#include "pw_assert/check.h"
#include "pw_status/try.h"
#include "pw_varint/varint.h"
namespace pw::transfer::internal {
uint32_t Context::MaxWriteChunkSize(uint32_t max_chunk_size_bytes,
uint32_t channel_id) const {
// Start with the user-provided maximum chunk size, which should be the usable
// payload length on the RPC ingress path after any transport overhead.
ssize_t max_size = max_chunk_size_bytes;
// Subtract the RPC overhead (pw_rpc/internal/packet.proto).
//
// type: 1 byte key, 1 byte value (CLIENT_STREAM)
// channel_id: 1 byte key, varint value (calculate from stream)
// service_id: 1 byte key, 4 byte value
// method_id: 1 byte key, 4 byte value
// payload: 1 byte key, varint length (remaining space)
// status: 0 bytes (not set in stream packets)
//
// TOTAL: 14 bytes + encoded channel_id size + encoded payload length
//
max_size -= 14;
max_size -= varint::EncodedSize(channel_id);
max_size -= varint::EncodedSize(max_size);
// TODO(frolv): Temporarily add 5 bytes for the new call_id change. The RPC
// overhead calculation will be moved into an RPC helper to avoid having
// pw_transfer depend on RPC internals.
max_size -= 5;
// Subtract the transfer service overhead for a client write chunk
// (pw_transfer/transfer.proto).
//
// transfer_id: 1 byte key, varint value (calculate)
// offset: 1 byte key, varint value (calculate)
// data: 1 byte key, varint length (remaining space)
//
// TOTAL: 3 + encoded transfer_id + encoded offset + encoded data length
//
size_t max_offset_in_window = offset() + pending_bytes();
max_size -= 3;
max_size -= varint::EncodedSize(transfer_id());
max_size -= varint::EncodedSize(max_offset_in_window);
max_size -= varint::EncodedSize(max_size);
// A resulting value of zero (or less) renders write transfers unusable, as
// there is no space to send any payload. This should be considered a
// programmer error in the transfer service setup.
PW_CHECK_INT_GT(
max_size,
0,
"Transfer service maximum chunk size is too small to fit a payload. "
"Increase max_chunk_size_bytes to support write transfers.");
return max_size;
}
} // namespace pw::transfer::internal