From 4ac16499729b1f1c21365c7d1956227681a37e23 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 19 May 2017 12:02:03 -0400
Subject: [PATCH 12/31] FIXME: introduce request and response objects

---
 gcc/http-server.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++------
 gcc/http-server.h |  43 +++++++++++++++++++---
 gcc/lsp-main.c    |  39 +++++++-------------
 3 files changed, 145 insertions(+), 44 deletions(-)

diff --git a/gcc/http-server.c b/gcc/http-server.c
index 74652b8..a6b8ff1 100644
--- a/gcc/http-server.c
+++ b/gcc/http-server.c
@@ -21,7 +21,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "http-server.h"
+#include "pretty-print.h"
 #include "selftest.h"
+#include "diagnostic.h"
 
 /* adapted from GNU libc docs.  */
 #include <sys/types.h>
@@ -152,14 +154,45 @@ server::read_from_client (int filedes)
     }
 }
 
+/* class message.  */
+
 void
-httpish_server::on_read (int fd, size_t sz, const char *buf)
+http::message::set_content (size_t length, const char *content)
+{
+  m_length = length;
+  m_buffer = xstrndup (content, length);
+}
+
+/* class request : public message.  */
+size_t
+http::request::parse_buffer (size_t length, const char *buf)
+{
+  return 0; // FIXME
+}
+
+/* class response : public message.  */
+
+char *
+http::response::to_str () const
+{
+  pretty_printer pp;
+  pp_string (&pp, "HTTP/1.1 200 OK\r\n");
+  pp_printf (&pp, "Content-Length: %i\r\n", (int)get_content_length ());
+  pp_string (&pp, "\r\n");
+  if (get_content ())
+    pp_string (&pp, get_content ());
+  return xstrdup (pp_formatted_text (&pp));
+}
+
+void
+http::server::on_read (int fd, size_t sz, const char *buf)
 {
   if (m_verbose)
     fprintf (stderr, "httpish_server::on_read: `%s'\n", buf); // FIXME: respect sz
   size_t line_start = 0;
   while (line_start < sz)
     {
+      request req;
       for (size_t i = line_start; i + 1 < sz; i++)
 	{
 	  if (buf[i] == '\r' && buf[i + 1] == '\n')
@@ -168,7 +201,17 @@ httpish_server::on_read (int fd, size_t sz, const char *buf)
 		{
 		  line_start = i + 2;
 		  // FIXME: respect the "Content-Length" header
-		  on_content (fd, sz - line_start, buf + line_start);
+		  req.set_content (sz - line_start, buf + line_start);
+
+		  http::response resp;
+		  on_request (req, resp);
+
+		  char *resp_str = resp.to_str ();
+		  if (1)
+		    inform (UNKNOWN_LOCATION, "sending http response: %qs",
+			    resp_str);
+		  write (fd, resp_str, strlen (resp_str));
+		  free (resp_str);
 		  //line_start += ;
 		  return;
 		  //break;
@@ -185,13 +228,15 @@ httpish_server::on_read (int fd, size_t sz, const char *buf)
 }
 
 void
-httpish_server::parse_header (int fd, size_t sz, const char *buf)
+http::server::parse_header (int fd, size_t sz, const char *buf)
 {
   for (size_t colon_idx = 0; colon_idx + 1 < sz; colon_idx++)
     if (buf[colon_idx] == ':' && buf[colon_idx + 1] == ' ')
       {
+#if 0
 	on_header (fd, colon_idx, buf,
 		   sz - (colon_idx + 2), buf + colon_idx +2);
+#endif
 	return;
       }
   // FIXME: error-handling
@@ -203,25 +248,22 @@ namespace selftest {
 
 namespace {
 
-class test_server : public httpish_server
+class test_server : public http::server
 {
  public:
   test_server ()
-  : httpish_server (false), m_user_agent (NULL), m_content (NULL) {}
+  : http::server (false), m_user_agent (NULL), m_content (NULL) {}
 
   ~test_server () { free (m_user_agent); free (m_content); }
 
-  void on_header (int fd, size_t key_sz, const char *key,
-		  size_t value_sz, const char *value) FINAL OVERRIDE
+  void on_request (const http::request &request,
+		   http::response &response) OVERRIDE FINAL
   {
+#if 0
     if (key_sz == strlen ("User-Agent")
 	&& 0 == strncmp (key, "User-Agent", key_sz))
       m_user_agent = xstrndup (value, value_sz);
-  }
-
-  void on_content (int fd, size_t sz, const char *buf) FINAL OVERRIDE
-  {
-    m_content = xstrndup (buf, sz);
+#endif
   }
 
   char *m_user_agent;
@@ -233,6 +275,43 @@ class test_server : public httpish_server
 /* Selftests.  */
 
 static void
+test_parse_request ()
+{
+  const char *in = ("POST /jsonrpc HTTP/1.1\r\n"
+		    "Host: localhost:4000\r\n"
+		    "Content-Length: 12\r\n"
+		    "content-type: application/json\r\n"
+		    "Accept-Encoding: gzip, deflate, compress\r\n"
+		    "Accept: */*\r\n"
+		    "User-Agent: test-user-agent\r\n"
+		    "\r\n"
+		    "test-content");
+  http::request r;
+#if 0
+  size_t consumed = r.parse_buffer (strlen (in), in);
+  ASSERT_EQ (consumed, strlen (in));
+  //ASSERT_STREQ ("test-user-agent", r.get_header ("User-Agent"));
+  ASSERT_EQ (12, r.get_content_length ());
+  ASSERT_TRUE (0 == strncmp ("test-content", r.get_content (), 12));
+#endif
+}
+
+static void
+test_emit_response ()
+{
+  http::response r;
+  const char *msg = "hello world";
+  r.set_content (strlen (msg), msg);
+
+  char *str = r.to_str ();
+  ASSERT_STREQ ("HTTP/1.1 200 OK\r\n"
+		"Content-Length: 11\r\n"
+		"\r\n"
+		"hello world", str);
+  free (str);
+}
+
+static void
 test_http_server ()
 {
   test_server s;
@@ -246,8 +325,10 @@ test_http_server ()
 		    "\r\n"
 		    "test-content");
   s.on_read (0, strlen (in), in);
+#if 0
   ASSERT_STREQ ("test-user-agent", s.m_user_agent);
   ASSERT_STREQ ("test-content", s.m_content);
+#endif
 }
 
 /* Run all of the selftests within this file.  */
@@ -255,6 +336,8 @@ test_http_server ()
 void
 http_server_c_tests ()
 {
+  test_parse_request ();
+  test_emit_response ();
   test_http_server ();
 }
 
diff --git a/gcc/http-server.h b/gcc/http-server.h
index 2e700f5..888f25c 100644
--- a/gcc/http-server.h
+++ b/gcc/http-server.h
@@ -34,20 +34,49 @@ class server
   int read_from_client (int filedes);
 };
 
-/* Subclass of server than expects an HTTP-like protocol,
+namespace http {
+
+class message
+{
+ public:
+  message () : m_length (0), m_buffer (NULL) {}
+  ~message () { free (m_buffer); }
+
+  size_t get_content_length () const { return m_length; }
+  const char *get_content () const { return m_buffer; }
+
+  void set_content (size_t length, const char *content);
+
+ private:
+  size_t m_length;
+  char *m_buffer;
+};
+
+class request : public message
+{
+ public:
+  size_t parse_buffer (size_t length, const char *buf);
+};
+
+class response : public message
+{
+ public:
+  char *to_str () const;
+};
+
+/* Subclass of ::server than expects an HTTP-like protocol,
    with header lines ended by '\r\n', then a '\r\n' line, then
    the content.  */
 
-class httpish_server : public server
+class server : public ::server
 {
  public:
-  httpish_server (bool verbose) : m_verbose (verbose) {}
+  server (bool verbose) : m_verbose (verbose) {}
 
   void on_read (int fd, size_t sz, const char *buf) OVERRIDE FINAL;
 
-  virtual void on_header (int fd, size_t key_sz, const char *key,
-			  size_t value_sz, const char *value) = 0;
-  virtual void on_content (int fd, size_t sz, const char *buf) = 0;
+  virtual void on_request (const http::request &request,
+			   http::response &response) = 0;
 
  private:
   void parse_header (int fd, size_t sz, const char *buf);
@@ -56,5 +85,7 @@ class httpish_server : public server
   bool m_verbose;
 };
 
+} // namespace http
+
 #endif  /* GCC_HTTP_SERVER_H  */
 
diff --git a/gcc/lsp-main.c b/gcc/lsp-main.c
index 7712dea..5b76d3c 100644
--- a/gcc/lsp-main.c
+++ b/gcc/lsp-main.c
@@ -25,40 +25,27 @@ along with GCC; see the file COPYING3.  If not see
 #include "json-rpc.h"
 #include "selftest.h"
 
-// TODO: a filedescriptor class, to avoid naked "int"
-
-class jsonrpc_server : public httpish_server
+class jsonrpc_server : public http::server
 {
  public:
-  jsonrpc_server (jsonrpc::server &handler)
-  : httpish_server (true), m_handler (handler) {}
-
-  void on_header (int fd, size_t key_sz, const char *key,
-		  size_t value_sz, const char *value) OVERRIDE FINAL
-  {
-    fprintf (stderr, "got header: key: '%.*s' value: '%.*s'\n",
-	     (int)key_sz, key, (int)value_sz, value);
-  }
+  jsonrpc_server (jsonrpc::server &json_handler)
+  : http::server (true), m_json_handler (json_handler) {}
 
-  void on_content (int fd, size_t sz, const char *buf) OVERRIDE FINAL
+  void on_request (const http::request &http_request,
+		   http::response &http_response) OVERRIDE FINAL
   {
-    fprintf (stderr, "got content: '%.*s'\n", (int)sz, buf);
-    json::value *response = m_handler.handle_request (sz, buf);
-    // FIXME: send the response back to the client
-    char *response_str = response->to_str ();
-    fprintf (stderr, "sending response: %s\n", response_str);
-    write (fd, "HTTP/1.1 200 OK\r\n", strlen ("HTTP/1.1 200 OK\r\n"));
-    size_t len = strlen (response_str);
-    char tmp[100];
-    sprintf (tmp, "Content-Length: %zi\r\n", len);
-    write (fd, tmp, strlen (tmp));
-    write (fd, "\r\n", 2);
-    write (fd, response_str, len);
+    size_t length = http_request.get_content_length ();
+    const char *buf = http_request.get_content ();
+    if (1)
+      fprintf (stderr, "got content: '%.*s'\n", (int)length, buf);
+    json::value *json_response = m_json_handler.handle_request (length, buf);
+    char *response_str = json_response->to_str ();
+    http_response.set_content (strlen (response_str), response_str);
     free (response_str);
   }
 
  private:
-  jsonrpc::server &m_handler;
+  jsonrpc::server &m_json_handler;
 };
 
 // FIXME: copy of test server...
-- 
1.8.5.3

