You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by an...@apache.org on 2016/11/22 03:27:30 UTC
[10/13] mesos git commit: Introduced a streaming request decoder in
libprocess.
Introduced a streaming request decoder in libprocess.
This would become the default de facto decoder used by libprocess
and replace the existing `DataDecoder`.
Review: https://reviews.apache.org/r/53486/
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/32e203ea
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/32e203ea
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/32e203ea
Branch: refs/heads/master
Commit: 32e203ea194e3531ea8c5ded4d538bacb7cc2781
Parents: e8e3fe5
Author: Anand Mazumdar <an...@apache.org>
Authored: Mon Nov 21 18:07:41 2016 -0800
Committer: Anand Mazumdar <an...@apache.org>
Committed: Mon Nov 21 19:27:08 2016 -0800
----------------------------------------------------------------------
3rdparty/libprocess/src/decoder.hpp | 303 +++++++++++++++++++++++++++++++
1 file changed, 303 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/32e203ea/3rdparty/libprocess/src/decoder.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/decoder.hpp b/3rdparty/libprocess/src/decoder.hpp
index 0a60128..88ef1d1 100644
--- a/3rdparty/libprocess/src/decoder.hpp
+++ b/3rdparty/libprocess/src/decoder.hpp
@@ -740,6 +740,309 @@ private:
std::deque<http::Response*> responses;
};
+
+// Provides a request decoder that returns 'PIPE' requests once
+// the request headers are received, but before the body data
+// is received. Callers are expected to read the body from the
+// Pipe::Reader in the request.
+class StreamingRequestDecoder
+{
+public:
+ explicit StreamingRequestDecoder()
+ : failure(false), header(HEADER_FIELD), request(nullptr)
+ {
+ settings.on_message_begin =
+ &StreamingRequestDecoder::on_message_begin;
+ settings.on_url =
+ &StreamingRequestDecoder::on_url;
+ settings.on_header_field =
+ &StreamingRequestDecoder::on_header_field;
+ settings.on_header_value =
+ &StreamingRequestDecoder::on_header_value;
+ settings.on_headers_complete =
+ &StreamingRequestDecoder::on_headers_complete;
+ settings.on_body =
+ &StreamingRequestDecoder::on_body;
+ settings.on_message_complete =
+ &StreamingRequestDecoder::on_message_complete;
+ settings.on_chunk_complete =
+ &StreamingRequestDecoder::on_chunk_complete;
+ settings.on_chunk_header =
+ &StreamingRequestDecoder::on_chunk_header;
+
+ http_parser_init(&parser, HTTP_REQUEST);
+
+ parser.data = this;
+ }
+
+ ~StreamingRequestDecoder()
+ {
+ delete request;
+
+ if (writer.isSome()) {
+ writer->fail("Decoder is being deleted");
+ }
+
+ foreach (http::Request* request, requests) {
+ delete request;
+ }
+ }
+
+ std::deque<http::Request*> decode(const char* data, size_t length)
+ {
+ size_t parsed = http_parser_execute(&parser, &settings, data, length);
+ if (parsed != length) {
+ // TODO(bmahler): joyent/http-parser exposes error reasons.
+ failure = true;
+
+ // If we're still writing the body, fail the writer!
+ if (writer.isSome()) {
+ http::Pipe::Writer writer_ = writer.get(); // Remove const.
+ writer_.fail("failed to decode body");
+ writer = None();
+ }
+ }
+
+ if (!requests.empty()) {
+ std::deque<http::Request*> result = requests;
+ requests.clear();
+ return result;
+ }
+
+ return std::deque<http::Request*>();
+ }
+
+ bool failed() const
+ {
+ return failure;
+ }
+
+private:
+ static int on_message_begin(http_parser* p)
+ {
+ StreamingRequestDecoder* decoder = (StreamingRequestDecoder*) p->data;
+
+ CHECK(!decoder->failure);
+
+ decoder->header = HEADER_FIELD;
+ decoder->field.clear();
+ decoder->value.clear();
+ decoder->query.clear();
+ decoder->url.clear();
+
+ CHECK(decoder->request == nullptr);
+ CHECK_NONE(decoder->writer);
+
+ decoder->request = new http::Request();
+ decoder->request->type = http::Request::PIPE;
+ decoder->writer = None();
+ decoder->decompressor.reset();
+
+ return 0;
+ }
+
+ static int on_chunk_complete(http_parser* p)
+ {
+ return 0;
+ }
+
+ static int on_chunk_header(http_parser* p)
+ {
+ return 0;
+ }
+
+ static int on_url(http_parser* p, const char* data, size_t length)
+ {
+ StreamingRequestDecoder* decoder = (StreamingRequestDecoder*) p->data;
+
+ CHECK_NOTNULL(decoder->request);
+
+ // The current http_parser library (version 2.6.2 and below)
+ // does not support incremental parsing of URLs. To compensate
+ // we incrementally collect the data and parse it in
+ // `on_header_complete`.
+ decoder->url.append(data, length);
+
+ return 0;
+ }
+
+ static int on_header_field(http_parser* p, const char* data, size_t length)
+ {
+ StreamingRequestDecoder* decoder = (StreamingRequestDecoder*) p->data;
+
+ CHECK_NOTNULL(decoder->request);
+
+ if (decoder->header != HEADER_FIELD) {
+ decoder->request->headers[decoder->field] = decoder->value;
+ decoder->field.clear();
+ decoder->value.clear();
+ }
+
+ decoder->field.append(data, length);
+ decoder->header = HEADER_FIELD;
+
+ return 0;
+ }
+
+ static int on_header_value(http_parser* p, const char* data, size_t length)
+ {
+ StreamingRequestDecoder* decoder = (StreamingRequestDecoder*) p->data;
+
+ CHECK_NOTNULL(decoder->request);
+
+ decoder->value.append(data, length);
+ decoder->header = HEADER_VALUE;
+ return 0;
+ }
+
+ static int on_headers_complete(http_parser* p)
+ {
+ StreamingRequestDecoder* decoder = (StreamingRequestDecoder*) p->data;
+
+ CHECK_NOTNULL(decoder->request);
+
+ // Add final header.
+ decoder->request->headers[decoder->field] = decoder->value;
+ decoder->field.clear();
+ decoder->value.clear();
+
+ decoder->request->method =
+ http_method_str((http_method) decoder->parser.method);
+
+ decoder->request->keepAlive = http_should_keep_alive(&decoder->parser) != 0;
+
+ // Parse the URL. This data was incrementally built up during calls
+ // to `on_url`.
+ http_parser_url url;
+ http_parser_url_init(&url);
+ int parse_url =
+ http_parser_parse_url(decoder->url.data(), decoder->url.size(), 0, &url);
+
+ if (parse_url != 0) {
+ return parse_url;
+ }
+
+ if (url.field_set & (1 << UF_PATH)) {
+ decoder->request->url.path = std::string(
+ decoder->url.data() + url.field_data[UF_PATH].off,
+ url.field_data[UF_PATH].len);
+ }
+
+ if (url.field_set & (1 << UF_FRAGMENT)) {
+ decoder->request->url.fragment = std::string(
+ decoder->url.data() + url.field_data[UF_FRAGMENT].off,
+ url.field_data[UF_FRAGMENT].len);
+ }
+
+ if (url.field_set & (1 << UF_QUERY)) {
+ decoder->query = std::string(
+ decoder->url.data() + url.field_data[UF_QUERY].off,
+ url.field_data[UF_QUERY].len);
+ }
+
+ // Parse the query key/values.
+ Try<hashmap<std::string, std::string>> decoded =
+ http::query::decode(decoder->query);
+
+ if (decoded.isError()) {
+ return 1;
+ }
+
+ decoder->request->url.query = std::move(decoded.get());
+
+ Option<std::string> encoding =
+ decoder->request->headers.get("Content-Encoding");
+
+ if (encoding.isSome() && encoding.get() == "gzip") {
+ decoder->decompressor =
+ Owned<gzip::Decompressor>(new gzip::Decompressor());
+ }
+
+ CHECK_NONE(decoder->writer);
+
+ http::Pipe pipe;
+ decoder->writer = pipe.writer();
+ decoder->request->reader = pipe.reader();
+
+ // Send the request to the caller, but keep a Pipe::Writer for
+ // streaming the body content into the request.
+ decoder->requests.push_back(decoder->request);
+ decoder->request = nullptr;
+
+ return 0;
+ }
+
+ static int on_body(http_parser* p, const char* data, size_t length)
+ {
+ StreamingRequestDecoder* decoder = (StreamingRequestDecoder*) p->data;
+
+ CHECK_SOME(decoder->writer);
+
+ http::Pipe::Writer writer = decoder->writer.get(); // Remove const.
+
+ std::string body;
+ if (decoder->decompressor.get() != nullptr) {
+ Try<std::string> decompressed =
+ decoder->decompressor->decompress(std::string(data, length));
+
+ if (decompressed.isError()) {
+ return 1;
+ }
+
+ body = std::move(decompressed.get());
+ } else {
+ body = std::string(data, length);
+ }
+
+ writer.write(std::move(body));
+
+ return 0;
+ }
+
+ static int on_message_complete(http_parser* p)
+ {
+ StreamingRequestDecoder* decoder = (StreamingRequestDecoder*) p->data;
+
+ CHECK_SOME(decoder->writer);
+
+ http::Pipe::Writer writer = decoder->writer.get(); // Remove const.
+
+ if (decoder->decompressor.get() != nullptr &&
+ !decoder->decompressor->finished()) {
+ writer.fail("Failed to decompress body");
+ return 1;
+ }
+
+ writer.close();
+
+ decoder->writer = None();
+
+ return 0;
+ }
+
+ bool failure;
+
+ http_parser parser;
+ http_parser_settings settings;
+
+ enum
+ {
+ HEADER_FIELD,
+ HEADER_VALUE
+ } header;
+
+ std::string field;
+ std::string value;
+ std::string query;
+ std::string url;
+
+ http::Request* request;
+ Option<http::Pipe::Writer> writer;
+ Owned<gzip::Decompressor> decompressor;
+
+ std::deque<http::Request*> requests;
+};
+
} // namespace process {
#endif // __DECODER_HPP__