| // 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 |