Add support for streaming I/O to webserver request data

Rather than reading request body data in memory and sending it to
remote request handlers over D-Bus, send the data to the handler
over a pipe as the data are coming in.

BUG: 24166746
Change-Id: I53499f9f8ed00a9d02740556616ef9b7ea7cc493
diff --git a/libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.dbus-xml b/libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.dbus-xml
index 5eab71d..dcf51ea 100644
--- a/libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.dbus-xml
+++ b/libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.dbus-xml
@@ -39,12 +39,14 @@
         - body - Raw unparsed request data. Could be empty for POST requests
                  that have form data/uploaded files already parsed into
                  form_fields/files parameters.
+                 The data is sent over D-Bus as a file descriptor representing
+                 the read end of a pipe.
       </tp:docstring>
       <arg name="request_info" type="(sssss)" direction="in"/>
       <arg name="headers" type="a(ss)" direction="in"/>
       <arg name="params" type="a(bss)" direction="in"/>
       <arg name="files" type="a(issss)" direction="in"/>
-      <arg name="body" type="ay" direction="in"/>
+      <arg name="body" type="h" direction="in"/>
       <annotation name="org.chromium.DBus.Method.Kind" value="normal"/>
     </method>
   </interface>
diff --git a/libwebserv/request.cc b/libwebserv/request.cc
index 7b518a4..93b80c4 100644
--- a/libwebserv/request.cc
+++ b/libwebserv/request.cc
@@ -16,6 +16,7 @@
 
 #include <base/callback.h>
 #include <chromeos/http/http_utils.h>
+#include <chromeos/streams/file_stream.h>
 
 #include <libwebserv/protocol_handler.h>
 
@@ -53,8 +54,9 @@
 Request::~Request() {
 }
 
-const std::vector<uint8_t>& Request::GetData() const {
-  return raw_data_;
+chromeos::StreamPtr Request::GetDataStream() {
+  return chromeos::FileStream::FromFileDescriptor(
+      raw_data_fd_.GetPlatformFile(), false, nullptr);
 }
 
 std::vector<PairOfStrings> Request::GetFormData() const {
diff --git a/libwebserv/request.h b/libwebserv/request.h
index 161dfa8..e655be6 100644
--- a/libwebserv/request.h
+++ b/libwebserv/request.h
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include <base/callback_forward.h>
+#include <base/files/file.h>
 #include <base/macros.h>
 #include <base/memory/ref_counted.h>
 #include <chromeos/errors/error.h>
@@ -77,7 +78,10 @@
   // pre-parsed by the server (e.g. "application/x-www-form-urlencoded" and
   // "multipart/form-data"). If there is no request body, or the data has been
   // pre-parsed by the server, the returned stream will be empty.
-  const std::vector<uint8_t>& GetData() const;
+  // The stream returned is valid for as long as the Request object itself is
+  // alive. Accessing the stream after the Request object is destroyed will lead
+  // to an undefined behavior (will likely just crash).
+  chromeos::StreamPtr GetDataStream();
 
   // Returns the request path (e.g. "/path/document").
   const std::string& GetPath() const { return url_; }
@@ -137,7 +141,7 @@
   ProtocolHandler* handler_{nullptr};
   std::string url_;
   std::string method_;
-  std::vector<uint8_t> raw_data_;
+  base::File raw_data_fd_;
   bool last_posted_data_was_file_{false};
 
   std::multimap<std::string, std::string> post_data_;
diff --git a/libwebserv/server.cc b/libwebserv/server.cc
index 3ec66a8..a15f7ab 100644
--- a/libwebserv/server.cc
+++ b/libwebserv/server.cc
@@ -37,7 +37,7 @@
       const std::vector<std::tuple<bool, std::string, std::string>>& in_params,
       const std::vector<std::tuple<int32_t, std::string, std::string,
                                    std::string, std::string>>& in_files,
-      const std::vector<uint8_t>& in_body) override;
+      const dbus::FileDescriptor& in_body) override;
 
  private:
   Server* server_{nullptr};
@@ -50,9 +50,9 @@
                      std::string>& in_request_info,
     const std::vector<std::tuple<std::string, std::string>>& in_headers,
     const std::vector<std::tuple<bool, std::string, std::string>>& in_params,
-    const std::vector<std::tuple<int32_t, std::string, std::string,
-                                 std::string, std::string>>& in_files,
-    const std::vector<uint8_t>& in_body) {
+    const std::vector<std::tuple<int32_t, std::string, std::string, std::string,
+                                 std::string>>& in_files,
+    const dbus::FileDescriptor& in_body) {
   std::string protocol_handler_id = std::get<0>(in_request_info);
   std::string request_handler_id = std::get<1>(in_request_info);
   std::string request_id = std::get<2>(in_request_info);
@@ -92,7 +92,8 @@
             std::get<4>(tuple)}});  // transfer_encoding
   }
 
-  request->raw_data_ = in_body;
+  request->raw_data_fd_ = base::File(dup(in_body.value()));
+  CHECK(request->raw_data_fd_.IsValid());
 
   return protocol_handler->ProcessRequest(protocol_handler_id,
                                           request_handler_id,