blob: 4307272c414d8f52f34cddba7fa654d3a5e5ca37 [file] [log] [blame] [edit]
// Copyright 2021 The Verible 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
//
// http://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.
#ifndef VERIBLE_COMMON_LSP_MESSAGE_STREAM_SPLITTER_H
#define VERIBLE_COMMON_LSP_MESSAGE_STREAM_SPLITTER_H
#include <functional>
#include <memory>
#include <string>
#include "absl/status/status.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
namespace verible {
namespace lsp {
// Splits messages that are formatted as header + body coming from some
// abstracted input stream and calls a handler for each complete message it
// receives.
//
// The MessageStreamSplitter does not read data directly from a source but
// gets handed a read function to get the data from. This allows to use this
// in different environments from testing response to different behavior of the
// read() to using it with a filedescriptor event dispatcher (select()).
//
// The simplest implementation of the "ReadFun" just wraps a system read() call.
//
// The header data MUST contain a Content-Length header.
class MessageStreamSplitter {
public:
// A function that reads from some source and writes up to "size" bytes
// into the buffer. Returns the number of bytes read.
// Blocks until there is content or returns '0' on end-of-file. Values
// below zero indicate errors.
// Only the amount of bytes available at the time of the call are filled
// into the buffer, so the return value can be less than size.
// So essentially: this behaves like the standard read() system call.
using ReadFun = std::function<int(char *buf, int size)>;
// Function called with each complete message that has been extracted from
// the stream.
using MessageProcessFun =
std::function<void(absl::string_view header, absl::string_view body)>;
// Optional parameters are "initial_read_buffer_size" for the initial
// internal buffer size (will be realloc'ed when needed).
// If "strict_crlf_header_separation" is false, also allows for simple
// newline as separation character in the header. Useful for manually
// speaking the protocol.
explicit MessageStreamSplitter(size_t initial_read_buffer_size = 4096,
bool strict_crlf_header_separation = true)
: read_buffer_(initial_read_buffer_size),
lenient_lf_separation_(!strict_crlf_header_separation) {}
MessageStreamSplitter(const MessageStreamSplitter &) = delete;
// Set the function that will receive extracted message bodies.
void SetMessageProcessor(const MessageProcessFun &message_processor) {
message_processor_ = message_processor;
}
// The passed "read_fun()" is called exactly _once_ to get
// the next amount of data and calls the message processor for each complete
// message found. Partial data received is retained to be re-considered on
// the next call to PullFrom().
//
// Within the context of this method, the message processor might be
// called zero to multiple times depending on how much data arrives from
// the read.
//
// Note: The once-call behaviour allows to hook this into some file-descriptor
// event dispatcher (e.g using select()).
//
// Returns with an ok status until EOF or some error occurs.
// Code
// - kUnavailable : regular EOF, no data pending. A 'good' non-ok status.
// - kDataloss : got EOF, but still incomplete data pending.
// - kInvalidArgument : stream corrupted, couldn't read header.
absl::Status PullFrom(const ReadFun &read_fun);
// -- Statistical data
size_t StatLargestBodySeen() const { return stats_largest_body_; }
size_t StatTotalBytesRead() const { return stats_total_bytes_read_; }
private:
int ParseHeaderGetBodyOffset(absl::string_view data, int *body_size);
absl::Status ProcessContainedMessages(absl::string_view *data);
absl::Status ReadInput(const ReadFun &read_fun);
std::vector<char> read_buffer_;
const bool lenient_lf_separation_;
MessageProcessFun message_processor_;
size_t stats_largest_body_ = 0;
size_t stats_total_bytes_read_ = 0;
absl::string_view pending_data_;
};
} // namespace lsp
} // namespace verible
#endif // VERIBLE_COMMON_LSP_MESSAGE_STREAM_SPLITTER_H