[sheepdog] [PATCH v2 07/10] sheep: add framework for http drivers

MORITA Kazutaka morita.kazutaka at gmail.com
Thu Oct 31 18:08:10 CET 2013


From: MORITA Kazutaka <morita.kazutaka at lab.ntt.co.jp>

This introduces a framework of http driver for object storage feature.
We can specify several http drivers to control http requests.  The
drivers are stackable and work like a WSGI pipeline of Python.

Usage
  $ sheep -r [host=<hostname>,][port=<port>,]<http driver>[,<http driver>][,...]

Example:
  - Enable object storage service with Swift API
    $ sheep -r swift /store

  - Enable object storage service with S3 API and S3 authorization
    $ sheep -r s3_auth,s3 /store

  - Enable Swift API and use localhost:7001 to communicate with http
    server.
    $ sheep -r host=localhost,port=7001,s3 /store

Signed-off-by: MORITA Kazutaka <morita.kazutaka at lab.ntt.co.jp>
---
 sheep/http/http.c  | 142 ++++++++++++++++++++++++++++++++++++-----------------
 sheep/http/http.h  |  51 +++++++++++++++++++
 sheep/sheep.c      |  16 +++---
 sheep/sheep_priv.h |   4 +-
 4 files changed, 160 insertions(+), 53 deletions(-)

diff --git a/sheep/http/http.c b/sheep/http/http.c
index e106cec..06b91c5 100644
--- a/sheep/http/http.c
+++ b/sheep/http/http.c
@@ -15,6 +15,13 @@
 
 #include "http.h"
 #include "sheep_priv.h"
+#include "option.h"
+
+static const char *http_host = "localhost";
+static const char *http_port = "8000";
+
+LIST_HEAD(http_drivers);
+static LIST_HEAD(http_enabled_drivers);
 
 static inline const char *stropcode(enum http_opcode opcode)
 {
@@ -187,46 +194,6 @@ void http_response_header(struct http_request *req, enum http_status status)
 	http_request_writes(req, "Content-type: text/plain;\r\n\r\n");
 }
 
-static void http_handle_get(struct http_request *req)
-{
-	http_response_header(req, NOT_IMPLEMENTED);
-	http_request_writes(req, "not implemented\n");
-}
-
-static void http_handle_put(struct http_request *req)
-{
-	http_response_header(req, NOT_IMPLEMENTED);
-	http_request_writes(req, "not implemented\n");
-}
-
-static void http_handle_post(struct http_request *req)
-{
-	http_response_header(req, NOT_IMPLEMENTED);
-	http_request_writes(req, "not implemented\n");
-}
-
-static void http_handle_delete(struct http_request *req)
-{
-	http_response_header(req, NOT_IMPLEMENTED);
-	http_request_writes(req, "not implemented\n");
-}
-
-static void http_handle_head(struct http_request *req)
-{
-	http_response_header(req, NOT_IMPLEMENTED);
-	http_request_writes(req, "not implemented\n");
-}
-
-static void (*const http_request_handlers[])(struct http_request *req) = {
-	[HTTP_GET] = http_handle_get,
-	[HTTP_PUT] = http_handle_put,
-	[HTTP_POST] = http_handle_post,
-	[HTTP_DELETE] = http_handle_delete,
-	[HTTP_HEAD] = http_handle_head,
-};
-
-static const int http_max_request_handlers = ARRAY_SIZE(http_request_handlers);
-
 static void http_end_request(struct http_request *req)
 {
 	FCGX_Finish_r(&req->fcgx);
@@ -238,11 +205,40 @@ static void http_run_request(struct work *work)
 	struct http_work *hw = container_of(work, struct http_work, work);
 	struct http_request *req = hw->request;
 	int op = req->opcode;
+	struct http_driver *hdrv;
+
+	list_for_each_entry(hdrv, &http_enabled_drivers, list) {
+		void (*method)(struct http_request *req);
+
+		switch (op) {
+		case HTTP_HEAD:
+			method = hdrv->head;
+			break;
+		case HTTP_GET:
+			method = hdrv->get;
+			break;
+		case HTTP_PUT:
+			method = hdrv->put;
+			break;
+		case HTTP_POST:
+			method = hdrv->post;
+			break;
+		case HTTP_DELETE:
+			method = hdrv->delete;
+			break;
+		default:
+			break;
+		}
 
-	if (op < http_max_request_handlers && http_request_handlers[op])
-		http_request_handlers[op](req);
-	else
-		panic("unhandled opcode %d", op);
+		if (method != NULL) {
+			method(req);
+			if (req->status != UNKNOWN)
+				goto out;
+		}
+	}
+
+	http_response_header(req, METHOD_NOT_ALLOWED);
+out:
 	http_end_request(req);
 }
 
@@ -300,10 +296,65 @@ out:
 	pthread_exit(NULL);
 }
 
-int http_init(const char *address)
+static int http_opt_host_parser(const char *s)
+{
+	http_host = s;
+	return 0;
+}
+
+static int http_opt_port_parser(const char *s)
+{
+	http_port = s;
+	return 0;
+}
+
+static int http_opt_default_parser(const char *s)
+{
+	struct http_driver *hdrv;
+
+	hdrv = find_hdrv(&http_enabled_drivers, s);
+	if (hdrv != NULL) {
+		sd_err("%s driver is already enabled", hdrv->name);
+		return -1;
+	}
+
+	hdrv = find_hdrv(&http_drivers, s);
+	if (hdrv == NULL) {
+		sd_err("'%s' is not a valid driver name", s);
+		return -1;
+	}
+
+	if (hdrv->init(get_hdrv_option(hdrv, s)) < 0) {
+		sd_err("failed to initialize %s driver", hdrv->name);
+		return -1;
+	}
+
+	list_move_tail(&hdrv->list, &http_enabled_drivers);
+
+	return 0;
+}
+
+static struct option_parser http_opt_parsers[] = {
+	{ "host=", http_opt_host_parser },
+	{ "port=", http_opt_port_parser },
+	{ "", http_opt_default_parser },
+	{ NULL, NULL },
+};
+
+int http_init(const char *options)
 {
 	pthread_t t;
 	int err;
+	char *s, address[HOST_NAME_MAX + 8];
+
+	s = strdup(options);
+	if (s == NULL) {
+		sd_emerg("OOM");
+		return -1;
+	}
+
+	if (option_parse(s, ",", http_opt_parsers) < 0)
+		return -1;
 
 	sys->http_wqueue = create_work_queue("http", WQ_DYNAMIC);
 	if (!sys->http_wqueue)
@@ -312,6 +363,7 @@ int http_init(const char *address)
 	FCGX_Init();
 
 #define LISTEN_QUEUE_DEPTH 1024 /* No rationale */
+	snprintf(address, sizeof(address), "%s:%s", http_host, http_port);
 	http_sockfd = FCGX_OpenSocket(address, LISTEN_QUEUE_DEPTH);
 	if (http_sockfd < 0) {
 		sd_err("open socket failed, address %s", address);
diff --git a/sheep/http/http.h b/sheep/http/http.h
index 0f30ea4..495a66c 100644
--- a/sheep/http/http.h
+++ b/sheep/http/http.h
@@ -49,6 +49,57 @@ struct http_request {
 	size_t data_length;
 };
 
+struct http_driver {
+	const char *name;
+
+	/* Returns zero on success, -1 on error. */
+	int (*init)(const char *option);
+
+	void (*head)(struct http_request *req);
+	void (*get)(struct http_request *req);
+	void (*put)(struct http_request *req);
+	void (*post)(struct http_request *req);
+	void (*delete)(struct http_request *req);
+
+	struct list_node list;
+};
+
+extern struct list_head http_drivers;
+
+#define hdrv_register(driver)						\
+static void __attribute__((constructor)) register_ ## driver(void)	\
+{									\
+	list_add(&driver.list, &http_drivers);				\
+}
+
+static inline struct http_driver *find_hdrv(struct list_head *drivers,
+					    const char *name)
+{
+	struct http_driver *hdrv;
+	int len;
+
+	list_for_each_entry(hdrv, drivers, list) {
+		len = strlen(hdrv->name);
+
+		if (strncmp(hdrv->name, name, len) == 0 &&
+		    (name[len] == ':' || name[len] == '\0'))
+			return hdrv;
+	}
+
+	return NULL;
+}
+
+static inline const char *get_hdrv_option(const struct http_driver *hdrv,
+					  const char *arg)
+{
+	int len = strlen(hdrv->name);
+
+	if (arg[len] == ':')
+		return strdup(arg + len + 1);
+	else
+		return NULL;
+}
+
 const char *str_http_req(const struct http_request *req);
 void http_response_header(struct http_request *req, enum http_status status);
 int http_request_read(struct http_request *req, void *buf, int len);
diff --git a/sheep/sheep.c b/sheep/sheep.c
index 27713d2..306432d 100644
--- a/sheep/sheep.c
+++ b/sheep/sheep.c
@@ -58,9 +58,13 @@ static const char loglevel_help[] =
 "This only allows logs with level smaller than SDOG_WARNING to be logged\n";
 
 static const char http_help[] =
-"Example:\n\t$ sheep -r localhost:7001 ...\n"
-"This tries to enable sheep as http service backend and use localhost:7001 to\n"
-"communicate with http server. Not fully implemented yet.\n";
+"Available arguments:\n"
+"\thost=: specify a host to communicate with http server (default: localhost)\n"
+"\tport=: specify a port to communicate with http server (default: 8000)\n"
+"\tswift: enable swift API\n"
+"Example:\n\t$ sheep -r host=localhost,port=7001,swift ...\n"
+"This tries to enable Swift API and use localhost:7001 to\n"
+"communicate with http server.\n";
 
 static const char myaddr_help[] =
 "Example:\n\t$ sheep -y 192.168.1.1:7000 ...\n"
@@ -548,7 +552,7 @@ int main(int argc, char **argv)
 	int64_t zone = -1;
 	struct cluster_driver *cdrv;
 	struct option *long_options;
-	const char *log_format = "server", *http_address = NULL;
+	const char *log_format = "server", *http_options = NULL;
 	static struct logger_user_info sheep_info;
 
 	install_crash_handler(crash_handler);
@@ -573,7 +577,7 @@ int main(int argc, char **argv)
 			pid_file = optarg;
 			break;
 		case 'r':
-			http_address = optarg;
+			http_options = optarg;
 			break;
 		case 'f':
 			is_daemon = false;
@@ -818,7 +822,7 @@ int main(int argc, char **argv)
 	if (ret)
 		exit(1);
 
-	if (http_address && http_init(http_address) != 0)
+	if (http_options && http_init(http_options) != 0)
 		exit(1);
 
 	if (pid_file && (create_pidfile(pid_file) != 0)) {
diff --git a/sheep/sheep_priv.h b/sheep/sheep_priv.h
index 20ec2be..83b461c 100644
--- a/sheep/sheep_priv.h
+++ b/sheep/sheep_priv.h
@@ -460,9 +460,9 @@ uint64_t md_get_size(uint64_t *used);
 
 /* http.c */
 #ifdef HAVE_HTTP
-int http_init(const char *address);
+int http_init(const char *options);
 #else
-static inline int http_init(const char *address)
+static inline int http_init(const char *options)
 {
 	sd_notice("http service is not complied");
 	return 0;
-- 
1.8.1.2




More information about the sheepdog mailing list