From 6fd5dff34902051f38932947493280e1e62dbeb1 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Tue, 15 Jul 2014 17:39:31 +0100 Subject: [PATCH 1/7] Implementation of HTTPD protocol, gwbuf properties and a dmeo web application interface to test the new httpd. --- server/core/buffer.c | 88 +++++ server/core/monitor.c | 25 ++ server/core/server.c | 25 ++ server/core/service.c | 25 ++ server/core/session.c | 25 ++ server/include/buffer.h | 21 +- server/include/monitor.h | 1 + server/include/server.h | 1 + server/include/service.h | 1 + server/include/session.h | 1 + server/modules/include/httpd.h | 20 +- server/modules/protocol/httpd.c | 332 ++++++++++--------- server/modules/routing/Makefile | 8 +- server/modules/routing/webserver.c | 493 +++++++++++++++++++++++++++++ 14 files changed, 898 insertions(+), 168 deletions(-) create mode 100644 server/modules/routing/webserver.c diff --git a/server/core/buffer.c b/server/core/buffer.c index d3278a505..4d28793fc 100644 --- a/server/core/buffer.c +++ b/server/core/buffer.c @@ -32,6 +32,7 @@ * 11/07/13 Mark Riddoch Add reference count mechanism * 16/07/2013 Massimiliano Pinto Added command type to gwbuf struct * 24/06/2014 Mark Riddoch Addition of gwbuf_trim + * 15/07/2014 Mark Riddoch Addition of properties * * @endverbatim */ @@ -77,11 +78,13 @@ SHARED_BUF *sbuf; free(sbuf); return NULL; } + spinlock_init(&rval->lock); rval->start = sbuf->data; rval->end = rval->start + size; sbuf->refcount = 1; rval->sbuf = sbuf; rval->next = NULL; + rval->properties = NULL; rval->gwbuf_type = GWBUF_TYPE_UNDEFINED; rval->command = 0; CHK_GWBUF(rval); @@ -96,12 +99,22 @@ SHARED_BUF *sbuf; void gwbuf_free(GWBUF *buf) { +BUF_PROPERTY *prop; + CHK_GWBUF(buf); if (atomic_add(&buf->sbuf->refcount, -1) == 1) { free(buf->sbuf->data); free(buf->sbuf); } + while (buf->properties) + { + prop = buf->properties; + buf->properties = prop->next; + free(prop->name); + free(prop->value); + free(prop); + } free(buf); } @@ -337,4 +350,79 @@ void gwbuf_set_type( } +/** + * Add a property to a buffer. + * + * @param buf The buffer to add the property to + * @param name The property name + * @param value The property value + * @return Non-zero on success + */ +int +gwbuf_add_property(GWBUF *buf, char *name, char *value) +{ +BUF_PROPERTY *prop; + if ((prop = malloc(sizeof(BUF_PROPERTY))) == NULL) + return 0; + + prop->name = strdup(name); + prop->value = strdup(value); + spinlock_acquire(&buf->lock); + prop->next = buf->properties; + buf->properties = prop; + spinlock_release(&buf->lock); + return 1; +} + +/** + * Return the value of a buffer property + * @param buf The buffer itself + * @param name The name of the property to return + * @return The property value or NULL if the property was not found. + */ +char * +gwbuf_get_property(GWBUF *buf, char *name) +{ +BUF_PROPERTY *prop; + + spinlock_acquire(&buf->lock); + prop = buf->properties; + while (prop && strcmp(prop->name, name) != 0) + prop = prop->next; + spinlock_release(&buf->lock); + if (prop) + return prop->value; + return NULL; +} + + +/** + * Convert a chain of GWBUF structures into a single GWBUF structure + * + * @param orig The chain to convert + * @return The contiguous buffer + */ +GWBUF * +gwbuf_make_contiguous(GWBUF *orig) +{ +GWBUF *newbuf; +char *ptr; +int len; + + if (orig->next == NULL) + return orig; + + if ((newbuf = gwbuf_alloc(gwbuf_length(orig))) != NULL) + { + ptr = GWBUF_DATA(newbuf); + while (orig) + { + len = GWBUF_LENGTH(orig); + memcpy(ptr, GWBUF_DATA(orig), len); + ptr += len; + orig = gwbuf_consume(orig, len); + } + } + return newbuf; +} diff --git a/server/core/monitor.c b/server/core/monitor.c index d7b53f5b7..8bb62bc2d 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -312,3 +312,28 @@ monitorSetReplicationHeartbeat(MONITOR *mon, int replication_heartbeat) mon->module->replicationHeartbeat(mon->handle, replication_heartbeat); } } + +/** + * Iterate over the monitors, calling a function per call + * + * @param fcn The function to call + * @param data The data to pass to each call + */ +void +monitorIterate(void (*fcn)(MONITOR *, void *), void *data) +{ +MONITOR *monitor, *next; + + spinlock_acquire(&monLock); + monitor = allMonitors; + while (monitor) + { + next = monitor->next; + spinlock_release(&monLock); + (*fcn)(monitor, data); + spinlock_acquire(&monLock); + monitor = next; + } + spinlock_release(&monLock); + +} diff --git a/server/core/server.c b/server/core/server.c index bfe5e4d04..cb3090679 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -561,3 +561,28 @@ SERVER_PARAM *param = server->parameters; } return NULL; } + +/** + * Iterate over the servers, calling a function per call + * + * @param fcn The function to call + * @param data The data to pass to each call + */ +void +serverIterate(void (*fcn)(SERVER *, void *), void *data) +{ +SERVER *server, *next; + + spinlock_acquire(&server_spin); + server = allServers; + while (server) + { + next = server->next; + spinlock_release(&server_spin); + (*fcn)(server, data); + spinlock_acquire(&server_spin); + server = next; + } + spinlock_release(&server_spin); + +} diff --git a/server/core/service.c b/server/core/service.c index 1102dabb4..592d90321 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -1171,3 +1171,28 @@ serviceGetWeightingParameter(SERVICE *service) { return service->weightby; } + +/** + * Iterate over the services, calling a function per call + * + * @param fcn The function to call + * @param data The data to pass to each call + */ +void +serviceIterate(void (*fcn)(SERVICE *, void *), void *data) +{ +SERVICE *service, *next; + + spinlock_acquire(&service_spin); + service = allServices; + while (service) + { + next = service->next; + spinlock_release(&service_spin); + (*fcn)(service, data); + spinlock_acquire(&service_spin); + service = next; + } + spinlock_release(&service_spin); + +} diff --git a/server/core/session.c b/server/core/session.c index 80767f9ff..9f1a55615 100644 --- a/server/core/session.c +++ b/server/core/session.c @@ -756,3 +756,28 @@ session_getUser(SESSION *session) { return (session && session->client) ? session->client->user : NULL; } + +/** + * Iterate over the sessions, calling a function per call + * + * @param fcn The function to call + * @param data The data to pass to each call + */ +void +sessionIterate(void (*fcn)(SESSION *, void *), void *data) +{ +SESSION *session, *next; + + spinlock_acquire(&session_spin); + session = allSessions; + while (session) + { + next = session->next; + spinlock_release(&session_spin); + (*fcn)(session, data); + spinlock_acquire(&session_spin); + session = next; + } + spinlock_release(&session_spin); + +} diff --git a/server/include/buffer.h b/server/include/buffer.h index 9729c538c..cf2f9e8dd 100644 --- a/server/include/buffer.h +++ b/server/include/buffer.h @@ -38,12 +38,25 @@ * 10/06/2013 Mark Riddoch Initial implementation * 11/07/2013 Mark Riddoch Addition of reference count in the gwbuf * 16/07/2013 Massimiliano Pinto Added command type for the queue + * 15/07/2014 Mark Riddoch Added buffer properties * * @endverbatim */ +#include #include +/** + * Buffer properties - used to store properties related to the buffer + * contents. This may be added at any point during the processing of the + * data, especially in the protocol stage of the processing. + */ +typedef struct buf_property { + char *name; + char *value; + struct buf_property *next; +} BUF_PROPERTY; + typedef enum { GWBUF_TYPE_UNDEFINED = 0x00, @@ -52,7 +65,8 @@ typedef enum GWBUF_TYPE_SINGLE_STMT = 0x04, GWBUF_TYPE_SESCMD_RESPONSE = 0x08, GWBUF_TYPE_RESPONSE_END = 0x10, - GWBUF_TYPE_SESCMD = 0x20 + GWBUF_TYPE_SESCMD = 0x20, + GWBUF_TYPE_HTTP = 0x40 } gwbuf_type_t; #define GWBUF_IS_TYPE_UNDEFINED(b) (b->gwbuf_type == 0) @@ -88,6 +102,8 @@ typedef struct gwbuf { SHARED_BUF *sbuf; /*< The shared buffer with the real data */ int command;/*< The command type for the queue */ gwbuf_type_t gwbuf_type; /*< buffer's data type information */ + SPINLOCK lock; + BUF_PROPERTY *properties; /*< Buffer properties */ } GWBUF; /*< @@ -121,4 +137,7 @@ extern unsigned int gwbuf_length(GWBUF *head); extern GWBUF *gwbuf_clone_portion(GWBUF *head, size_t offset, size_t len); extern GWBUF *gwbuf_clone_transform(GWBUF *head, gwbuf_type_t type); extern void gwbuf_set_type(GWBUF *head, gwbuf_type_t type); +extern int gwbuf_add_property(GWBUF *buf, char *name, char *value); +extern char *gwbuf_get_property(GWBUF *buf, char *name); +extern GWBUF *gwbuf_make_contiguous(GWBUF *); #endif diff --git a/server/include/monitor.h b/server/include/monitor.h index d65fd075f..4dee5f49c 100644 --- a/server/include/monitor.h +++ b/server/include/monitor.h @@ -110,4 +110,5 @@ extern void monitorList(DCB *); extern void monitorSetId(MONITOR *, unsigned long); extern void monitorSetInterval (MONITOR *, unsigned long); extern void monitorSetReplicationHeartbeat(MONITOR *, int); +extern void monitorIterate(void (*fcn)(MONITOR *, void *), void *data); #endif diff --git a/server/include/server.h b/server/include/server.h index fdcc32e80..f622b5765 100644 --- a/server/include/server.h +++ b/server/include/server.h @@ -161,4 +161,5 @@ extern void serverAddParameter(SERVER *, char *, char *); extern char *serverGetParameter(SERVER *, char *); extern void server_update(SERVER *, char *, char *, char *); extern void server_set_unique_name(SERVER *, char *); +extern void serverIterate(void (*fcn)(SERVER *, void *), void *data); #endif diff --git a/server/include/service.h b/server/include/service.h index cd13d411b..0d8d32cda 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -161,6 +161,7 @@ extern void serviceSetFilters(SERVICE *, char *); extern int serviceEnableRootUser(SERVICE *, int ); extern void serviceWeightBy(SERVICE *, char *); extern char *serviceGetWeightingParameter(SERVICE *); +extern void serviceIterate(void (*fcn)(SERVICE *, void *), void *data); extern void service_update(SERVICE *, char *, char *, char *); extern int service_refresh_users(SERVICE *); extern void printService(SERVICE *); diff --git a/server/include/session.h b/server/include/session.h index cbd43fe40..0386a5eb6 100644 --- a/server/include/session.h +++ b/server/include/session.h @@ -157,6 +157,7 @@ void dprintAllSessions(struct dcb *); void dprintSession(struct dcb *, SESSION *); void dListSessions(struct dcb *); char *session_state(int); +void sessionIterate(void (*fcn)(SESSION *, void *), void *data); bool session_link_dcb(SESSION *, struct dcb *); SESSION* get_session_by_router_ses(void* rses); #endif diff --git a/server/modules/include/httpd.h b/server/modules/include/httpd.h index 0d5e65604..920e65b2d 100644 --- a/server/modules/include/httpd.h +++ b/server/modules/include/httpd.h @@ -48,18 +48,20 @@ #define HTTPD_FIELD_MAXLEN 8192 #define HTTPD_REQUESTLINE_MAXLEN 8192 +typedef enum { + METHOD_UNKNOWN = 0, + METHOD_POST, + METHOD_PUT, + METHOD_GET, + METHOD_HEAD +} HTTP_METHOD; /** * HTTPD session specific data * */ typedef struct httpd_session { - char user[HTTPD_USER_MAXLEN]; /*< username for authentication*/ - char *cookies; /*< all input cookies */ - char hostname[HTTPD_HOSTNAME_MAXLEN]; /*< The hostname */ - char useragent[HTTPD_USERAGENT_MAXLEN]; /*< The useragent */ - char method[HTTPD_METHOD_MAXLEN]; /*< The HTTPD Method */ - char *url; /*< the URL in the request */ - char *path_info; /*< the Pathinfo, starts with /, is the extra path segments after the document name */ - char *query_string; /*< the Query string, starts with ?, after path_info and document name */ - int headers_received; /*< All the headers has been received, if 1 */ + HTTP_METHOD method; + GWBUF *saved; + int request_len; + char *url; } HTTPD_session; diff --git a/server/modules/protocol/httpd.c b/server/modules/protocol/httpd.c index 7db1366ad..bdb06e560 100644 --- a/server/modules/protocol/httpd.c +++ b/server/modules/protocol/httpd.c @@ -33,6 +33,8 @@ * Date Who Description * 08/07/2013 Massimiliano Pinto Initial version * 09/07/2013 Massimiliano Pinto Added /show?dcb|session for all dcbs|sessions + * 11/07/2014 Mark Riddoch Recoded as more generic protocol module + * removing hardcoded example * * @endverbatim */ @@ -40,6 +42,10 @@ #include #include #include +#include +#include + +extern int lm_enabled_logfiles_bitmask; MODULE_INFO info = { MODULE_API_PROTOCOL, @@ -48,7 +54,6 @@ MODULE_INFO info = { "An experimental HTTPD implementation for use in admnistration" }; -#define ISspace(x) isspace((int)(x)) #define HTTP_SERVER_STRING "Gateway(c) v.1.0.0" static char *version_str = "V1.0.1"; @@ -60,8 +65,8 @@ static int httpd_hangup(DCB *dcb); static int httpd_accept(DCB *dcb); static int httpd_close(DCB *dcb); static int httpd_listen(DCB *dcb, char *config); -static int httpd_get_line(int sock, char *buf, int size); -static void httpd_send_headers(DCB *dcb, int final); +static char *httpd_nextline(GWBUF *buf, char *ptr); +static void httpd_process_header(GWBUF *buf, char *sol, HTTPD_session *client_data); /** * The "module object" for the httpd protocol module. @@ -121,132 +126,99 @@ GetModuleObject() * @return */ static int -httpd_read_event(DCB* dcb) +httpd_read_event(DCB *dcb) { -//SESSION *session = dcb->session; -//ROUTER_OBJECT *router = session->service->router; -//ROUTER *router_instance = session->service->router_instance; -//void *rsession = session->router_session; +SESSION *session = dcb->session; +GWBUF *buf = NULL; +char *ptr, *sol; +HTTPD_session *client_data = NULL; +int n; -int numchars = 1; -char buf[HTTPD_REQUESTLINE_MAXLEN-1] = ""; -char *query_string = NULL; -char method[HTTPD_METHOD_MAXLEN-1] = ""; -char url[HTTPD_SMALL_BUFFER] = ""; -int cgi = 0; -size_t i, j; -int headers_read = 0; -HTTPD_session *client_data = NULL; + // Read all the available data + if ((n = dcb_read(dcb, &buf)) != -1) + { + client_data = dcb->data; - client_data = dcb->data; - - /** - * get the request line - * METHOD URL HTTP_VER\r\n - */ - - numchars = httpd_get_line(dcb->fd, buf, sizeof(buf)); - - i = 0; j = 0; - while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) { - method[i] = buf[j]; - i++; j++; - } - method[i] = '\0'; - - strcpy(client_data->method, method); - - /* check allowed http methods */ - if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) { - //httpd_unimplemented(dcb->fd); - return 0; - } - - if (strcasecmp(method, "POST") == 0) - cgi = 1; - - i = 0; - - while (ISspace(buf[j]) && (j < sizeof(buf))) { - j++; - } - - while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) { - url[i] = buf[j]; - i++; j++; - } - - url[i] = '\0'; - - /** - * Get the query string if availble - */ - - if (strcasecmp(method, "GET") == 0) { - query_string = url; - while ((*query_string != '?') && (*query_string != '\0')) - query_string++; - if (*query_string == '?') { - cgi = 1; - *query_string = '\0'; - query_string++; + if (client_data->saved) + { + buf = gwbuf_append(client_data->saved, buf); + client_data->saved = NULL; } - } + buf = gwbuf_make_contiguous(buf); - /** - * Get the request headers - */ + ptr = GWBUF_DATA(buf); - while ((numchars > 0) && strcmp("\n", buf)) { - char *value = NULL; - char *end = NULL; - numchars = httpd_get_line(dcb->fd, buf, sizeof(buf)); - if ( (value = strchr(buf, ':'))) { - *value = '\0'; - value++; - end = &value[strlen(value) -1]; - *end = '\0'; + if (strncasecmp(ptr, "POST", 4)) + { + client_data->method = METHOD_POST; + gwbuf_add_property(buf, "Method", "POST"); + ptr = ptr + 4; + } + else if (strncasecmp(ptr, "PUT", 3)) + { + client_data->method = METHOD_PUT; + gwbuf_add_property(buf, "Method", "PUT"); + ptr = ptr + 3; + } + else if (strncasecmp(ptr, "GET", 3)) + { + client_data->method = METHOD_GET; + gwbuf_add_property(buf, "Method", "GET"); + ptr = ptr + 3; + } + else if (strncasecmp(ptr, "HEAD", 4)) + { + client_data->method = METHOD_HEAD; + gwbuf_add_property(buf, "Method", "HEAD"); + ptr = ptr + 4; + } + while (ptr < (char *)(buf->end) && isspace(*ptr)) + ptr++; + sol = ptr; + while (ptr < (char *)(buf->end) && isspace(*ptr) == 0) + ptr++; + client_data->url = strndup(sol, ptr - sol); + gwbuf_add_property(buf, "URL", client_data->url); + while ((sol = httpd_nextline(buf, ptr)) != NULL && + *sol != '\n' && *sol != '\r') + { + httpd_process_header(buf, sol, client_data); + ptr = sol; + } - if (strncasecmp(buf, "Hostname", 6) == 0) { - strcpy(client_data->hostname, value); + /* + * We have read all the headers, or run out of data to + * examine. + */ + if (sol == NULL) + { + client_data->saved = buf; + return 0; + } + else + { + if (((char *)(buf->end) - sol) + < client_data->request_len) + { + client_data->saved = buf; } - if (strncasecmp(buf, "useragent", 9) == 0) { - strcpy(client_data->useragent, value); + else + { + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, + "HTTPD: request %s.\n", client_data->url))); + SESSION_ROUTE_QUERY(session, buf); + if (client_data->url) + { + free(client_data->url); + client_data->url = NULL; + } } } + + + } - - if (numchars) { - headers_read = 1; - memcpy(&client_data->headers_received, &headers_read, sizeof(int)); - } - - /** - * Now begins the server reply - */ - - /* send all the basic headers and close with \r\n */ - httpd_send_headers(dcb, 1); - - /** - * ToDO: launch proper content handling based on the requested URI, later REST interface - * - */ - - dcb_printf(dcb, "Welcome to HTTPD Gateway (c) %s\n\n", version_str); - - if (strcmp(url, "/show") == 0) { - if (strlen(query_string)) { - if (strcmp(query_string, "dcb") == 0) - dprintAllDCBs(dcb); - if (strcmp(query_string, "session") == 0) - dprintAllSessions(dcb); - } - } - - /* force the client connecton close */ - dcb_close(dcb); - return 0; } @@ -287,6 +259,18 @@ httpd_write(DCB *dcb, GWBUF *queue) static int httpd_error(DCB *dcb) { +HTTPD_session *client_data = NULL; + if (dcb->data) + { + client_data = dcb->data; + if (client_data->url) + { + free(client_data->url); + client_data->url = NULL; + } + free(dcb->data); + dcb->data = NULL; + } dcb_close(dcb); return 0; } @@ -318,7 +302,7 @@ int n_connect = 0; { int so = -1; struct sockaddr_in addr; - socklen_t addrlen; + socklen_t addrlen = 0; DCB *client = NULL; HTTPD_session *client_data = NULL; @@ -333,10 +317,11 @@ int n_connect = 0; memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL)); /* we don't need the session */ - client->session = NULL; + client->session = session_alloc(dcb->session->service, client); /* create the session data for HTTPD */ client_data = (HTTPD_session *)calloc(1, sizeof(HTTPD_session)); + memset(client_data, 0, sizeof(HTTPD_session)); client->data = client_data; if (poll_add_dcb(client) == -1) @@ -425,51 +410,84 @@ int rc; } /** - * HTTPD get line from client + * Return the start of the next line int the buffer. + * + * @param buf The GWBUF chain + * @param ptr Start point within the buffer + * + * @return the start of the next line or NULL if there are no more lines */ -static int httpd_get_line(int sock, char *buf, int size) { - int i = 0; - char c = '\0'; - int n; +static char * +httpd_nextline(GWBUF *buf, char *ptr) +{ + while (ptr < (char *)(buf->end) && *ptr != '\n' && *ptr != '\r') + ptr++; + if (ptr >= (char *)(buf->end)) + return NULL; - while ((i < size - 1) && (c != '\n')) { - n = recv(sock, &c, 1, 0); - /* DEBUG printf("%02X\n", c); */ - if (n > 0) { - if (c == '\r') { - n = recv(sock, &c, 1, MSG_PEEK); - /* DEBUG printf("%02X\n", c); */ - if ((n > 0) && (c == '\n')) - recv(sock, &c, 1, 0); - else - c = '\n'; - } - buf[i] = c; - i++; - } else - c = '\n'; - } - - buf[i] = '\0'; - - return i; + /* Skip prcisely one CR/LF */ + if (*ptr == '\r') + ptr++; + if (*ptr == '\n') + ptr++; + return ptr; } /** - * HTTPD send basic headers with 200 OK + * The headers to extract from the HTTP request and add as properties to the + * GWBUF structure. */ -static void httpd_send_headers(DCB *dcb, int final) +static char *headers[] = { + "Content-Type", + "User-Agent", + "From", + "Date", + NULL +}; + +/** + * Process a single header line + * + * @param buf The GWBUF that contains the request + * @param sol The current start of line + * @param client_data The client data structure for this request + */ +static void +httpd_process_header(GWBUF *buf, char *sol, HTTPD_session *client_data) { - char date[64] = ""; - const char *fmt = "%a, %d %b %Y %H:%M:%S GMT"; - time_t httpd_current_time = time(NULL); +char *ptr = sol; +char cbuf[300]; +int len, i; - strftime(date, sizeof(date), fmt, localtime(&httpd_current_time)); + /* Find the end of the line */ + while (ptr < (char *)(buf->end) && *ptr != '\n' && *ptr != '\r') + ptr++; - dcb_printf(dcb, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: %s\r\nConnection: close\r\nContent-Type: text/plain\r\n", date, HTTP_SERVER_STRING); - - /* close the headers */ - if (final) { - dcb_printf(dcb, "\r\n"); + if (strncmp(sol, "Content-Length:", strlen("Content-Length:")) == 0) + { + char *p1 = sol + strlen("Content-Length:"); + while (isspace(*p1)) + p1++; + len = ptr - p1; + strncpy(cbuf, p1, len); + cbuf[len] = 0; + client_data->request_len = atoi(cbuf); + gwbuf_add_property(buf, "Content-Length", cbuf); + } + else + { + for (i = 0; headers[i]; i++) + { + if (strncmp(sol, headers[i], strlen(headers[i])) == 0) + { + char *p1 = sol + strlen(headers[i]) + 1; + while (isspace(*p1)) + p1++; + len = ptr - p1; + strncpy(cbuf, p1, len); + cbuf[len] = 0; + gwbuf_add_property(buf, headers[i], cbuf); + } + } } } diff --git a/server/modules/routing/Makefile b/server/modules/routing/Makefile index 4feac68fb..a1095304c 100644 --- a/server/modules/routing/Makefile +++ b/server/modules/routing/Makefile @@ -44,10 +44,13 @@ DEBUGCLISRCS=debugcli.c debugcmd.c DEBUGCLIOBJ=$(DEBUGCLISRCS:.c=.o) CLISRCS=cli.c debugcmd.c CLIOBJ=$(CLISRCS:.c=.o) +WEBSRCS=webserver.o +WEBOBJ=$(WEBSRCS:.c=.o) SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS) cli.c OBJ=$(SRCS:.c=.o) LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager -MODULES= libdebugcli.so libreadconnroute.so libtestroute.so libcli.so +MODULES= libdebugcli.so libreadconnroute.so libtestroute.so libcli.so \ + libwebserver.so all: $(MODULES) @@ -64,6 +67,9 @@ libdebugcli.so: $(DEBUGCLIOBJ) libcli.so: $(CLIOBJ) $(CC) $(LDFLAGS) $(CLIOBJ) $(LIBS) -o $@ +libwebserver.so: $(WEBOBJ) + $(CC) $(LDFLAGS) $(WEBOBJ) $(LIBS) -o $@ + libreadwritesplit.so: # (cd readwritesplit; touch depend.mk ; make; cp $@ ..) diff --git a/server/modules/routing/webserver.c b/server/modules/routing/webserver.c new file mode 100644 index 000000000..ee8516108 --- /dev/null +++ b/server/modules/routing/webserver.c @@ -0,0 +1,493 @@ +/* + * This file is distributed as part of MaxScale. It is free + * software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, + * version 2. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright SkySQL Ab 2014 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + SERVICE *service; +} WEB_INSTANCE; + +typedef struct { + SESSION *session; +} WEB_SESSION; + +static char *version_str = "V1.0.0"; + +MODULE_INFO info = { + MODULE_API_ROUTER, + MODULE_IN_DEVELOPMENT, + ROUTER_VERSION, + "A test router - not for use in real systems" +}; + +static ROUTER *createInstance(SERVICE *service, char **options); +static void *newSession(ROUTER *instance, SESSION *session); +static void closeSession(ROUTER *instance, void *session); +static void freeSession(ROUTER *instance, void *session); +static int routeQuery(ROUTER *instance, void *session, GWBUF *queue); +static void diagnostic(ROUTER *instance, DCB *dcb); +static uint8_t getCapabilities (ROUTER* inst, void* router_session); + + +static ROUTER_OBJECT MyObject = { + createInstance, + newSession, + closeSession, + freeSession, + routeQuery, + diagnostic, + NULL, + NULL, + getCapabilities +}; + + +static void send_index(WEB_SESSION *); +static void send_css(WEB_SESSION *); +static void send_menu(WEB_SESSION *); +static void send_blank(WEB_SESSION *); +static void send_title(WEB_SESSION *); +static void send_frame1(WEB_SESSION *); +static void send_services(WEB_SESSION *); +static void send_sessions(WEB_SESSION *); +static void send_servers(WEB_SESSION *); +static void send_monitors(WEB_SESSION *); +static void respond_error(WEB_SESSION *, int, char *); + +static struct { + char *page; + void (*fcn)(WEB_SESSION *); +} pages[] = { + { "index.html", send_index }, + { "services.html", send_services }, + { "menu.html", send_menu }, + { "sessions.html", send_sessions }, + { "blank.html", send_blank }, + { "title.html", send_title }, + { "frame1.html", send_frame1 }, + { "servers.html", send_servers }, + { "monitors.html", send_monitors }, + { "styles.css", send_css }, + { NULL, NULL } +}; + +/** + * Implementation of the mandatory version entry point + * + * @return version string of the module + */ +char * +version() +{ + return version_str; +} + +/** + * The module initialisation routine, called when the module + * is first loaded. + */ +void +ModuleInit() +{ + +} + +/** + * The module entry point routine. It is this routine that + * must populate the structure that is referred to as the + * "module object", this is a structure with the set of + * external entry points for this module. + * + * @return The module object + */ +ROUTER_OBJECT * +GetModuleObject() +{ + return &MyObject; +} + +/** + * Create an instance of the router for a particular service + * within the gateway. + * + * @param service The service this router is being create for + * @param options The options for this query router + * + * @return The instance data for this new instance + */ +static ROUTER * +createInstance(SERVICE *service, char **options) +{ +WEB_INSTANCE *inst; + + if ((inst = (WEB_INSTANCE *)malloc(sizeof(WEB_INSTANCE))) == NULL) + return NULL; + + inst->service = service; + return (ROUTER *)inst; +} + +/** + * Associate a new session with this instance of the router. + * + * @param instance The router instance data + * @param session The session itself + * @return Session specific data for this session + */ +static void * +newSession(ROUTER *instance, SESSION *session) +{ +WEB_SESSION *wsession; + + if ((wsession = (WEB_SESSION *)malloc(sizeof(WEB_SESSION))) == NULL) + return NULL; + + wsession->session = session; + return wsession; +} + +/** + * Close a session with the router, this is the mechanism + * by which a router may cleanup data structure etc. + * + * @param instance The router instance data + * @param session The session being closed + */ +static void +closeSession(ROUTER *instance, void *session) +{ + free(session); +} + +static void freeSession( + ROUTER* router_instance, + void* router_client_session) +{ + return; +} + +static int +routeQuery(ROUTER *instance, void *session, GWBUF *queue) +{ +WEB_SESSION *wsession = (WEB_SESSION *)session; +char *ptr; +int i, found = 0; +char *url; + + if ((url = gwbuf_get_property(queue, "URL")) == NULL) + { + respond_error(wsession, 404, "No URL available"); + } + + ptr = strrchr(url, '/'); + if (ptr) + ptr++; + else + ptr = url; + for (i = 0; pages[i].page; i++) + { + if (!strcmp(ptr, pages[i].page)) + { + (pages[i].fcn)(wsession); + found = 1; + } + } + if (!found) + respond_error(wsession, 404, "Unrecognised URL received"); + gwbuf_free(queue); + return 0; +} + +/** + * Diagnostics routine + * + * @param instance The router instance + * @param dcb The DCB for diagnostic output + */ +static void +diagnostic(ROUTER *instance, DCB *dcb) +{ +} + +/** + * Return the router capabilities bitmask + * + * @param inst The router instance + * @param router_session The router session + * @return Router capabilities bitmask + */ +static uint8_t +getCapabilities(ROUTER *inst, void *router_session) +{ + return 0; +} + +static char *index_page = +"" +"" +"MaxScale" +"" +"" +"" +"" +"" +""; + +static char *title_page = +"" +"" +"MaxScale" +"" +"

MaxScale - Status View

" +""; + +static char *frame1_page = +"" +"" +"" +"" +"" +""; + +static char *menu_page = +"" +"" +"" +"

Options

" +"

"; + +static char *blank_page = " "; + +static char *css = +"table, td, th { border: 1px solid blue; }\n" +"th { background-color: blue; color: white; padding: 5px }\n" +"td { padding: 5px; }\n" +"table { border-collapse: collapse; }\n" +"a:link { color: #0000FF; }\n" +"a:visted { color: #0000FF; }\n" +"a:hover { color: #FF0000; }\n" +"a:active { color: #0000FF; }\n" +"h1 { color: blue; font-family: serif }\n" +"h2 { color: blue; font-family: serif }\n" +"p { font-family: serif }\n" +"li { font-family: serif }\n"; + + +static void +send_html_header(DCB *dcb) +{ +char date[64] = ""; +const char *fmt = "%a, %d %b %Y %H:%M:%S GMT"; + + time_t httpd_current_time = time(NULL); + + strftime(date, sizeof(date), fmt, localtime(&httpd_current_time)); + + dcb_printf(dcb, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: %s\r\nConnection: close\r\nContent-Type: text/html\r\n", date, "MaxScale"); + + dcb_printf(dcb, "\r\n"); +} + +static void +send_static_html(DCB *dcb, char *html) +{ + dcb_printf(dcb, html); +} + +static void +send_index(WEB_SESSION *session) +{ +DCB *dcb = session->session->client; + + send_html_header(dcb); + send_static_html(dcb, index_page); + dcb_close(dcb); +} + +static void +send_css(WEB_SESSION *session) +{ +DCB *dcb = session->session->client; + + send_html_header(dcb); + send_static_html(dcb, css); + dcb_close(dcb); +} + +static void +send_title(WEB_SESSION *session) +{ +DCB *dcb = session->session->client; + + send_html_header(dcb); + send_static_html(dcb, title_page); + dcb_close(dcb); +} + +static void +send_frame1(WEB_SESSION *session) +{ +DCB *dcb = session->session->client; + + send_html_header(dcb); + send_static_html(dcb, frame1_page); + dcb_close(dcb); +} + +static void +send_menu(WEB_SESSION *session) +{ +DCB *dcb = session->session->client; + + send_html_header(dcb); + send_static_html(dcb, menu_page); + dcb_close(dcb); +} + +static void +send_blank(WEB_SESSION *session) +{ +DCB *dcb = session->session->client; + + send_html_header(dcb); + send_static_html(dcb, blank_page); + dcb_close(dcb); +} + +static void +service_row(SERVICE *service, DCB *dcb) +{ + dcb_printf(dcb, "%s%s%d%d\n", + service->name, service->routerModule, + service->stats.n_current, service->stats.n_sessions); +} + +static void +send_services(WEB_SESSION *session) +{ +DCB *dcb = session->session->client; + + send_html_header(dcb); + dcb_printf(dcb, ""); + dcb_printf(dcb, ""); + dcb_printf(dcb, "

Services

"); + dcb_printf(dcb, "\n"); + serviceIterate(service_row, dcb); + dcb_printf(dcb, "
NameRouter"); + dcb_printf(dcb, "Current SessionsTotal Sessions
\n"); + dcb_close(dcb); +} + +static void +session_row(SESSION *session, DCB *dcb) +{ + dcb_printf(dcb, "%-16p%s%s%s\n", + session, ((session->client && session->client->remote) + ? session->client->remote : ""), + (session->service && session->service->name + ? session->service->name : ""), + session_state(session->state)); +} + +static void +send_sessions(WEB_SESSION *session) +{ +DCB *dcb = session->session->client; + + send_html_header(dcb); + dcb_printf(dcb, ""); + dcb_printf(dcb, ""); + dcb_printf(dcb, "

Sessions

"); + dcb_printf(dcb, "\n"); + sessionIterate(session_row, dcb); + dcb_printf(dcb, "
SessionClient"); + dcb_printf(dcb, "ServiceState
\n"); + dcb_close(dcb); +} + +static void +server_row(SERVER *server, DCB *dcb) +{ + dcb_printf(dcb, "%s%s%d%s%d\n", + server->unique_name, server->name, server->port, + server_status(server), server->stats.n_current); +} + +static void +send_servers(WEB_SESSION *session) +{ +DCB *dcb = session->session->client; + + send_html_header(dcb); + dcb_printf(dcb, ""); + dcb_printf(dcb, ""); + dcb_printf(dcb, "

Servers

"); + dcb_printf(dcb, "\n"); + serverIterate(server_row, dcb); + dcb_printf(dcb, "
ServerAddress"); + dcb_printf(dcb, "PortStateConnections
\n"); + dcb_close(dcb); +} + +static void +monitor_row(MONITOR *monitor, DCB *dcb) +{ + dcb_printf(dcb, "%s%s\n", + monitor->name, monitor->state & MONITOR_STATE_RUNNING + ? "Running" : "Stopped"); +} + +static void +send_monitors(WEB_SESSION *session) +{ +DCB *dcb = session->session->client; + + send_html_header(dcb); + dcb_printf(dcb, ""); + dcb_printf(dcb, ""); + dcb_printf(dcb, "

Monitors

"); + dcb_printf(dcb, "\n"); + monitorIterate(monitor_row, dcb); + dcb_printf(dcb, "
MonitorState
\n"); + dcb_close(dcb); +} + +static void +respond_error(WEB_SESSION *session, int err, char *msg) +{ +DCB *dcb = session->session->client; + + dcb_printf(dcb, "HTTP/1.1 %d %s\n", err, msg); + dcb_printf(dcb, "Content-Type: text/html\n"); + dcb_printf(dcb, "\n"); + dcb_printf(dcb, "\n"); + dcb_printf(dcb, "MaxScale webserver plugin unable to satisfy request.\n"); + dcb_printf(dcb, "

Code: %d, %s\n", err, msg); + dcb_printf(dcb, ""); + dcb_close(dcb); +} From 87e66a0ea80f03d8a2451ab400779b6fe7c8b733 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Tue, 15 Jul 2014 17:53:39 +0100 Subject: [PATCH 2/7] Revert "Implementation of HTTPD protocol, gwbuf properties and a dmeo web application" This reverts commit 6fd5dff34902051f38932947493280e1e62dbeb1. --- server/core/buffer.c | 88 ----- server/core/monitor.c | 25 -- server/core/server.c | 25 -- server/core/service.c | 25 -- server/core/session.c | 25 -- server/include/buffer.h | 21 +- server/include/monitor.h | 1 - server/include/server.h | 1 - server/include/service.h | 1 - server/include/session.h | 1 - server/modules/include/httpd.h | 20 +- server/modules/protocol/httpd.c | 352 ++++++++++---------- server/modules/routing/Makefile | 8 +- server/modules/routing/webserver.c | 493 ----------------------------- 14 files changed, 178 insertions(+), 908 deletions(-) delete mode 100644 server/modules/routing/webserver.c diff --git a/server/core/buffer.c b/server/core/buffer.c index 4d28793fc..d3278a505 100644 --- a/server/core/buffer.c +++ b/server/core/buffer.c @@ -32,7 +32,6 @@ * 11/07/13 Mark Riddoch Add reference count mechanism * 16/07/2013 Massimiliano Pinto Added command type to gwbuf struct * 24/06/2014 Mark Riddoch Addition of gwbuf_trim - * 15/07/2014 Mark Riddoch Addition of properties * * @endverbatim */ @@ -78,13 +77,11 @@ SHARED_BUF *sbuf; free(sbuf); return NULL; } - spinlock_init(&rval->lock); rval->start = sbuf->data; rval->end = rval->start + size; sbuf->refcount = 1; rval->sbuf = sbuf; rval->next = NULL; - rval->properties = NULL; rval->gwbuf_type = GWBUF_TYPE_UNDEFINED; rval->command = 0; CHK_GWBUF(rval); @@ -99,22 +96,12 @@ SHARED_BUF *sbuf; void gwbuf_free(GWBUF *buf) { -BUF_PROPERTY *prop; - CHK_GWBUF(buf); if (atomic_add(&buf->sbuf->refcount, -1) == 1) { free(buf->sbuf->data); free(buf->sbuf); } - while (buf->properties) - { - prop = buf->properties; - buf->properties = prop->next; - free(prop->name); - free(prop->value); - free(prop); - } free(buf); } @@ -350,79 +337,4 @@ void gwbuf_set_type( } -/** - * Add a property to a buffer. - * - * @param buf The buffer to add the property to - * @param name The property name - * @param value The property value - * @return Non-zero on success - */ -int -gwbuf_add_property(GWBUF *buf, char *name, char *value) -{ -BUF_PROPERTY *prop; - if ((prop = malloc(sizeof(BUF_PROPERTY))) == NULL) - return 0; - - prop->name = strdup(name); - prop->value = strdup(value); - spinlock_acquire(&buf->lock); - prop->next = buf->properties; - buf->properties = prop; - spinlock_release(&buf->lock); - return 1; -} - -/** - * Return the value of a buffer property - * @param buf The buffer itself - * @param name The name of the property to return - * @return The property value or NULL if the property was not found. - */ -char * -gwbuf_get_property(GWBUF *buf, char *name) -{ -BUF_PROPERTY *prop; - - spinlock_acquire(&buf->lock); - prop = buf->properties; - while (prop && strcmp(prop->name, name) != 0) - prop = prop->next; - spinlock_release(&buf->lock); - if (prop) - return prop->value; - return NULL; -} - - -/** - * Convert a chain of GWBUF structures into a single GWBUF structure - * - * @param orig The chain to convert - * @return The contiguous buffer - */ -GWBUF * -gwbuf_make_contiguous(GWBUF *orig) -{ -GWBUF *newbuf; -char *ptr; -int len; - - if (orig->next == NULL) - return orig; - - if ((newbuf = gwbuf_alloc(gwbuf_length(orig))) != NULL) - { - ptr = GWBUF_DATA(newbuf); - while (orig) - { - len = GWBUF_LENGTH(orig); - memcpy(ptr, GWBUF_DATA(orig), len); - ptr += len; - orig = gwbuf_consume(orig, len); - } - } - return newbuf; -} diff --git a/server/core/monitor.c b/server/core/monitor.c index 8bb62bc2d..d7b53f5b7 100644 --- a/server/core/monitor.c +++ b/server/core/monitor.c @@ -312,28 +312,3 @@ monitorSetReplicationHeartbeat(MONITOR *mon, int replication_heartbeat) mon->module->replicationHeartbeat(mon->handle, replication_heartbeat); } } - -/** - * Iterate over the monitors, calling a function per call - * - * @param fcn The function to call - * @param data The data to pass to each call - */ -void -monitorIterate(void (*fcn)(MONITOR *, void *), void *data) -{ -MONITOR *monitor, *next; - - spinlock_acquire(&monLock); - monitor = allMonitors; - while (monitor) - { - next = monitor->next; - spinlock_release(&monLock); - (*fcn)(monitor, data); - spinlock_acquire(&monLock); - monitor = next; - } - spinlock_release(&monLock); - -} diff --git a/server/core/server.c b/server/core/server.c index cb3090679..bfe5e4d04 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -561,28 +561,3 @@ SERVER_PARAM *param = server->parameters; } return NULL; } - -/** - * Iterate over the servers, calling a function per call - * - * @param fcn The function to call - * @param data The data to pass to each call - */ -void -serverIterate(void (*fcn)(SERVER *, void *), void *data) -{ -SERVER *server, *next; - - spinlock_acquire(&server_spin); - server = allServers; - while (server) - { - next = server->next; - spinlock_release(&server_spin); - (*fcn)(server, data); - spinlock_acquire(&server_spin); - server = next; - } - spinlock_release(&server_spin); - -} diff --git a/server/core/service.c b/server/core/service.c index 592d90321..1102dabb4 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -1171,28 +1171,3 @@ serviceGetWeightingParameter(SERVICE *service) { return service->weightby; } - -/** - * Iterate over the services, calling a function per call - * - * @param fcn The function to call - * @param data The data to pass to each call - */ -void -serviceIterate(void (*fcn)(SERVICE *, void *), void *data) -{ -SERVICE *service, *next; - - spinlock_acquire(&service_spin); - service = allServices; - while (service) - { - next = service->next; - spinlock_release(&service_spin); - (*fcn)(service, data); - spinlock_acquire(&service_spin); - service = next; - } - spinlock_release(&service_spin); - -} diff --git a/server/core/session.c b/server/core/session.c index 9f1a55615..80767f9ff 100644 --- a/server/core/session.c +++ b/server/core/session.c @@ -756,28 +756,3 @@ session_getUser(SESSION *session) { return (session && session->client) ? session->client->user : NULL; } - -/** - * Iterate over the sessions, calling a function per call - * - * @param fcn The function to call - * @param data The data to pass to each call - */ -void -sessionIterate(void (*fcn)(SESSION *, void *), void *data) -{ -SESSION *session, *next; - - spinlock_acquire(&session_spin); - session = allSessions; - while (session) - { - next = session->next; - spinlock_release(&session_spin); - (*fcn)(session, data); - spinlock_acquire(&session_spin); - session = next; - } - spinlock_release(&session_spin); - -} diff --git a/server/include/buffer.h b/server/include/buffer.h index cf2f9e8dd..9729c538c 100644 --- a/server/include/buffer.h +++ b/server/include/buffer.h @@ -38,25 +38,12 @@ * 10/06/2013 Mark Riddoch Initial implementation * 11/07/2013 Mark Riddoch Addition of reference count in the gwbuf * 16/07/2013 Massimiliano Pinto Added command type for the queue - * 15/07/2014 Mark Riddoch Added buffer properties * * @endverbatim */ -#include #include -/** - * Buffer properties - used to store properties related to the buffer - * contents. This may be added at any point during the processing of the - * data, especially in the protocol stage of the processing. - */ -typedef struct buf_property { - char *name; - char *value; - struct buf_property *next; -} BUF_PROPERTY; - typedef enum { GWBUF_TYPE_UNDEFINED = 0x00, @@ -65,8 +52,7 @@ typedef enum GWBUF_TYPE_SINGLE_STMT = 0x04, GWBUF_TYPE_SESCMD_RESPONSE = 0x08, GWBUF_TYPE_RESPONSE_END = 0x10, - GWBUF_TYPE_SESCMD = 0x20, - GWBUF_TYPE_HTTP = 0x40 + GWBUF_TYPE_SESCMD = 0x20 } gwbuf_type_t; #define GWBUF_IS_TYPE_UNDEFINED(b) (b->gwbuf_type == 0) @@ -102,8 +88,6 @@ typedef struct gwbuf { SHARED_BUF *sbuf; /*< The shared buffer with the real data */ int command;/*< The command type for the queue */ gwbuf_type_t gwbuf_type; /*< buffer's data type information */ - SPINLOCK lock; - BUF_PROPERTY *properties; /*< Buffer properties */ } GWBUF; /*< @@ -137,7 +121,4 @@ extern unsigned int gwbuf_length(GWBUF *head); extern GWBUF *gwbuf_clone_portion(GWBUF *head, size_t offset, size_t len); extern GWBUF *gwbuf_clone_transform(GWBUF *head, gwbuf_type_t type); extern void gwbuf_set_type(GWBUF *head, gwbuf_type_t type); -extern int gwbuf_add_property(GWBUF *buf, char *name, char *value); -extern char *gwbuf_get_property(GWBUF *buf, char *name); -extern GWBUF *gwbuf_make_contiguous(GWBUF *); #endif diff --git a/server/include/monitor.h b/server/include/monitor.h index 4dee5f49c..d65fd075f 100644 --- a/server/include/monitor.h +++ b/server/include/monitor.h @@ -110,5 +110,4 @@ extern void monitorList(DCB *); extern void monitorSetId(MONITOR *, unsigned long); extern void monitorSetInterval (MONITOR *, unsigned long); extern void monitorSetReplicationHeartbeat(MONITOR *, int); -extern void monitorIterate(void (*fcn)(MONITOR *, void *), void *data); #endif diff --git a/server/include/server.h b/server/include/server.h index f622b5765..fdcc32e80 100644 --- a/server/include/server.h +++ b/server/include/server.h @@ -161,5 +161,4 @@ extern void serverAddParameter(SERVER *, char *, char *); extern char *serverGetParameter(SERVER *, char *); extern void server_update(SERVER *, char *, char *, char *); extern void server_set_unique_name(SERVER *, char *); -extern void serverIterate(void (*fcn)(SERVER *, void *), void *data); #endif diff --git a/server/include/service.h b/server/include/service.h index 0d8d32cda..cd13d411b 100644 --- a/server/include/service.h +++ b/server/include/service.h @@ -161,7 +161,6 @@ extern void serviceSetFilters(SERVICE *, char *); extern int serviceEnableRootUser(SERVICE *, int ); extern void serviceWeightBy(SERVICE *, char *); extern char *serviceGetWeightingParameter(SERVICE *); -extern void serviceIterate(void (*fcn)(SERVICE *, void *), void *data); extern void service_update(SERVICE *, char *, char *, char *); extern int service_refresh_users(SERVICE *); extern void printService(SERVICE *); diff --git a/server/include/session.h b/server/include/session.h index 0386a5eb6..cbd43fe40 100644 --- a/server/include/session.h +++ b/server/include/session.h @@ -157,7 +157,6 @@ void dprintAllSessions(struct dcb *); void dprintSession(struct dcb *, SESSION *); void dListSessions(struct dcb *); char *session_state(int); -void sessionIterate(void (*fcn)(SESSION *, void *), void *data); bool session_link_dcb(SESSION *, struct dcb *); SESSION* get_session_by_router_ses(void* rses); #endif diff --git a/server/modules/include/httpd.h b/server/modules/include/httpd.h index 920e65b2d..0d5e65604 100644 --- a/server/modules/include/httpd.h +++ b/server/modules/include/httpd.h @@ -48,20 +48,18 @@ #define HTTPD_FIELD_MAXLEN 8192 #define HTTPD_REQUESTLINE_MAXLEN 8192 -typedef enum { - METHOD_UNKNOWN = 0, - METHOD_POST, - METHOD_PUT, - METHOD_GET, - METHOD_HEAD -} HTTP_METHOD; /** * HTTPD session specific data * */ typedef struct httpd_session { - HTTP_METHOD method; - GWBUF *saved; - int request_len; - char *url; + char user[HTTPD_USER_MAXLEN]; /*< username for authentication*/ + char *cookies; /*< all input cookies */ + char hostname[HTTPD_HOSTNAME_MAXLEN]; /*< The hostname */ + char useragent[HTTPD_USERAGENT_MAXLEN]; /*< The useragent */ + char method[HTTPD_METHOD_MAXLEN]; /*< The HTTPD Method */ + char *url; /*< the URL in the request */ + char *path_info; /*< the Pathinfo, starts with /, is the extra path segments after the document name */ + char *query_string; /*< the Query string, starts with ?, after path_info and document name */ + int headers_received; /*< All the headers has been received, if 1 */ } HTTPD_session; diff --git a/server/modules/protocol/httpd.c b/server/modules/protocol/httpd.c index bdb06e560..7db1366ad 100644 --- a/server/modules/protocol/httpd.c +++ b/server/modules/protocol/httpd.c @@ -33,8 +33,6 @@ * Date Who Description * 08/07/2013 Massimiliano Pinto Initial version * 09/07/2013 Massimiliano Pinto Added /show?dcb|session for all dcbs|sessions - * 11/07/2014 Mark Riddoch Recoded as more generic protocol module - * removing hardcoded example * * @endverbatim */ @@ -42,10 +40,6 @@ #include #include #include -#include -#include - -extern int lm_enabled_logfiles_bitmask; MODULE_INFO info = { MODULE_API_PROTOCOL, @@ -54,6 +48,7 @@ MODULE_INFO info = { "An experimental HTTPD implementation for use in admnistration" }; +#define ISspace(x) isspace((int)(x)) #define HTTP_SERVER_STRING "Gateway(c) v.1.0.0" static char *version_str = "V1.0.1"; @@ -65,8 +60,8 @@ static int httpd_hangup(DCB *dcb); static int httpd_accept(DCB *dcb); static int httpd_close(DCB *dcb); static int httpd_listen(DCB *dcb, char *config); -static char *httpd_nextline(GWBUF *buf, char *ptr); -static void httpd_process_header(GWBUF *buf, char *sol, HTTPD_session *client_data); +static int httpd_get_line(int sock, char *buf, int size); +static void httpd_send_headers(DCB *dcb, int final); /** * The "module object" for the httpd protocol module. @@ -126,99 +121,132 @@ GetModuleObject() * @return */ static int -httpd_read_event(DCB *dcb) +httpd_read_event(DCB* dcb) { -SESSION *session = dcb->session; -GWBUF *buf = NULL; -char *ptr, *sol; -HTTPD_session *client_data = NULL; -int n; +//SESSION *session = dcb->session; +//ROUTER_OBJECT *router = session->service->router; +//ROUTER *router_instance = session->service->router_instance; +//void *rsession = session->router_session; - // Read all the available data - if ((n = dcb_read(dcb, &buf)) != -1) - { - client_data = dcb->data; +int numchars = 1; +char buf[HTTPD_REQUESTLINE_MAXLEN-1] = ""; +char *query_string = NULL; +char method[HTTPD_METHOD_MAXLEN-1] = ""; +char url[HTTPD_SMALL_BUFFER] = ""; +int cgi = 0; +size_t i, j; +int headers_read = 0; +HTTPD_session *client_data = NULL; - if (client_data->saved) - { - buf = gwbuf_append(client_data->saved, buf); - client_data->saved = NULL; - } - buf = gwbuf_make_contiguous(buf); - - ptr = GWBUF_DATA(buf); - - if (strncasecmp(ptr, "POST", 4)) - { - client_data->method = METHOD_POST; - gwbuf_add_property(buf, "Method", "POST"); - ptr = ptr + 4; - } - else if (strncasecmp(ptr, "PUT", 3)) - { - client_data->method = METHOD_PUT; - gwbuf_add_property(buf, "Method", "PUT"); - ptr = ptr + 3; - } - else if (strncasecmp(ptr, "GET", 3)) - { - client_data->method = METHOD_GET; - gwbuf_add_property(buf, "Method", "GET"); - ptr = ptr + 3; - } - else if (strncasecmp(ptr, "HEAD", 4)) - { - client_data->method = METHOD_HEAD; - gwbuf_add_property(buf, "Method", "HEAD"); - ptr = ptr + 4; - } - while (ptr < (char *)(buf->end) && isspace(*ptr)) - ptr++; - sol = ptr; - while (ptr < (char *)(buf->end) && isspace(*ptr) == 0) - ptr++; - client_data->url = strndup(sol, ptr - sol); - gwbuf_add_property(buf, "URL", client_data->url); - while ((sol = httpd_nextline(buf, ptr)) != NULL && - *sol != '\n' && *sol != '\r') - { - httpd_process_header(buf, sol, client_data); - ptr = sol; - } - - /* - * We have read all the headers, or run out of data to - * examine. - */ - if (sol == NULL) - { - client_data->saved = buf; - return 0; - } - else - { - if (((char *)(buf->end) - sol) - < client_data->request_len) - { - client_data->saved = buf; - } - else - { - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "HTTPD: request %s.\n", client_data->url))); - SESSION_ROUTE_QUERY(session, buf); - if (client_data->url) - { - free(client_data->url); - client_data->url = NULL; - } - } - } + client_data = dcb->data; + /** + * get the request line + * METHOD URL HTTP_VER\r\n + */ + numchars = httpd_get_line(dcb->fd, buf, sizeof(buf)); + i = 0; j = 0; + while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) { + method[i] = buf[j]; + i++; j++; } + method[i] = '\0'; + + strcpy(client_data->method, method); + + /* check allowed http methods */ + if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) { + //httpd_unimplemented(dcb->fd); + return 0; + } + + if (strcasecmp(method, "POST") == 0) + cgi = 1; + + i = 0; + + while (ISspace(buf[j]) && (j < sizeof(buf))) { + j++; + } + + while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) { + url[i] = buf[j]; + i++; j++; + } + + url[i] = '\0'; + + /** + * Get the query string if availble + */ + + if (strcasecmp(method, "GET") == 0) { + query_string = url; + while ((*query_string != '?') && (*query_string != '\0')) + query_string++; + if (*query_string == '?') { + cgi = 1; + *query_string = '\0'; + query_string++; + } + } + + /** + * Get the request headers + */ + + while ((numchars > 0) && strcmp("\n", buf)) { + char *value = NULL; + char *end = NULL; + numchars = httpd_get_line(dcb->fd, buf, sizeof(buf)); + if ( (value = strchr(buf, ':'))) { + *value = '\0'; + value++; + end = &value[strlen(value) -1]; + *end = '\0'; + + if (strncasecmp(buf, "Hostname", 6) == 0) { + strcpy(client_data->hostname, value); + } + if (strncasecmp(buf, "useragent", 9) == 0) { + strcpy(client_data->useragent, value); + } + } + } + + if (numchars) { + headers_read = 1; + memcpy(&client_data->headers_received, &headers_read, sizeof(int)); + } + + /** + * Now begins the server reply + */ + + /* send all the basic headers and close with \r\n */ + httpd_send_headers(dcb, 1); + + /** + * ToDO: launch proper content handling based on the requested URI, later REST interface + * + */ + + dcb_printf(dcb, "Welcome to HTTPD Gateway (c) %s\n\n", version_str); + + if (strcmp(url, "/show") == 0) { + if (strlen(query_string)) { + if (strcmp(query_string, "dcb") == 0) + dprintAllDCBs(dcb); + if (strcmp(query_string, "session") == 0) + dprintAllSessions(dcb); + } + } + + /* force the client connecton close */ + dcb_close(dcb); + return 0; } @@ -259,18 +287,6 @@ httpd_write(DCB *dcb, GWBUF *queue) static int httpd_error(DCB *dcb) { -HTTPD_session *client_data = NULL; - if (dcb->data) - { - client_data = dcb->data; - if (client_data->url) - { - free(client_data->url); - client_data->url = NULL; - } - free(dcb->data); - dcb->data = NULL; - } dcb_close(dcb); return 0; } @@ -302,7 +318,7 @@ int n_connect = 0; { int so = -1; struct sockaddr_in addr; - socklen_t addrlen = 0; + socklen_t addrlen; DCB *client = NULL; HTTPD_session *client_data = NULL; @@ -317,11 +333,10 @@ int n_connect = 0; memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL)); /* we don't need the session */ - client->session = session_alloc(dcb->session->service, client); + client->session = NULL; /* create the session data for HTTPD */ client_data = (HTTPD_session *)calloc(1, sizeof(HTTPD_session)); - memset(client_data, 0, sizeof(HTTPD_session)); client->data = client_data; if (poll_add_dcb(client) == -1) @@ -410,84 +425,51 @@ int rc; } /** - * Return the start of the next line int the buffer. - * - * @param buf The GWBUF chain - * @param ptr Start point within the buffer - * - * @return the start of the next line or NULL if there are no more lines + * HTTPD get line from client */ -static char * -httpd_nextline(GWBUF *buf, char *ptr) -{ - while (ptr < (char *)(buf->end) && *ptr != '\n' && *ptr != '\r') - ptr++; - if (ptr >= (char *)(buf->end)) - return NULL; +static int httpd_get_line(int sock, char *buf, int size) { + int i = 0; + char c = '\0'; + int n; - /* Skip prcisely one CR/LF */ - if (*ptr == '\r') - ptr++; - if (*ptr == '\n') - ptr++; - return ptr; -} - -/** - * The headers to extract from the HTTP request and add as properties to the - * GWBUF structure. - */ -static char *headers[] = { - "Content-Type", - "User-Agent", - "From", - "Date", - NULL -}; - -/** - * Process a single header line - * - * @param buf The GWBUF that contains the request - * @param sol The current start of line - * @param client_data The client data structure for this request - */ -static void -httpd_process_header(GWBUF *buf, char *sol, HTTPD_session *client_data) -{ -char *ptr = sol; -char cbuf[300]; -int len, i; - - /* Find the end of the line */ - while (ptr < (char *)(buf->end) && *ptr != '\n' && *ptr != '\r') - ptr++; - - if (strncmp(sol, "Content-Length:", strlen("Content-Length:")) == 0) - { - char *p1 = sol + strlen("Content-Length:"); - while (isspace(*p1)) - p1++; - len = ptr - p1; - strncpy(cbuf, p1, len); - cbuf[len] = 0; - client_data->request_len = atoi(cbuf); - gwbuf_add_property(buf, "Content-Length", cbuf); - } - else - { - for (i = 0; headers[i]; i++) - { - if (strncmp(sol, headers[i], strlen(headers[i])) == 0) - { - char *p1 = sol + strlen(headers[i]) + 1; - while (isspace(*p1)) - p1++; - len = ptr - p1; - strncpy(cbuf, p1, len); - cbuf[len] = 0; - gwbuf_add_property(buf, headers[i], cbuf); + while ((i < size - 1) && (c != '\n')) { + n = recv(sock, &c, 1, 0); + /* DEBUG printf("%02X\n", c); */ + if (n > 0) { + if (c == '\r') { + n = recv(sock, &c, 1, MSG_PEEK); + /* DEBUG printf("%02X\n", c); */ + if ((n > 0) && (c == '\n')) + recv(sock, &c, 1, 0); + else + c = '\n'; } - } + buf[i] = c; + i++; + } else + c = '\n'; + } + + buf[i] = '\0'; + + return i; +} + +/** + * HTTPD send basic headers with 200 OK + */ +static void httpd_send_headers(DCB *dcb, int final) +{ + char date[64] = ""; + const char *fmt = "%a, %d %b %Y %H:%M:%S GMT"; + time_t httpd_current_time = time(NULL); + + strftime(date, sizeof(date), fmt, localtime(&httpd_current_time)); + + dcb_printf(dcb, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: %s\r\nConnection: close\r\nContent-Type: text/plain\r\n", date, HTTP_SERVER_STRING); + + /* close the headers */ + if (final) { + dcb_printf(dcb, "\r\n"); } } diff --git a/server/modules/routing/Makefile b/server/modules/routing/Makefile index a1095304c..4feac68fb 100644 --- a/server/modules/routing/Makefile +++ b/server/modules/routing/Makefile @@ -44,13 +44,10 @@ DEBUGCLISRCS=debugcli.c debugcmd.c DEBUGCLIOBJ=$(DEBUGCLISRCS:.c=.o) CLISRCS=cli.c debugcmd.c CLIOBJ=$(CLISRCS:.c=.o) -WEBSRCS=webserver.o -WEBOBJ=$(WEBSRCS:.c=.o) SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS) cli.c OBJ=$(SRCS:.c=.o) LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager -MODULES= libdebugcli.so libreadconnroute.so libtestroute.so libcli.so \ - libwebserver.so +MODULES= libdebugcli.so libreadconnroute.so libtestroute.so libcli.so all: $(MODULES) @@ -67,9 +64,6 @@ libdebugcli.so: $(DEBUGCLIOBJ) libcli.so: $(CLIOBJ) $(CC) $(LDFLAGS) $(CLIOBJ) $(LIBS) -o $@ -libwebserver.so: $(WEBOBJ) - $(CC) $(LDFLAGS) $(WEBOBJ) $(LIBS) -o $@ - libreadwritesplit.so: # (cd readwritesplit; touch depend.mk ; make; cp $@ ..) diff --git a/server/modules/routing/webserver.c b/server/modules/routing/webserver.c deleted file mode 100644 index ee8516108..000000000 --- a/server/modules/routing/webserver.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * This file is distributed as part of MaxScale. It is free - * software: you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation, - * version 2. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright SkySQL Ab 2014 - */ -#include -#include -#include -#include -#include -#include -#include -#include - -typedef struct { - SERVICE *service; -} WEB_INSTANCE; - -typedef struct { - SESSION *session; -} WEB_SESSION; - -static char *version_str = "V1.0.0"; - -MODULE_INFO info = { - MODULE_API_ROUTER, - MODULE_IN_DEVELOPMENT, - ROUTER_VERSION, - "A test router - not for use in real systems" -}; - -static ROUTER *createInstance(SERVICE *service, char **options); -static void *newSession(ROUTER *instance, SESSION *session); -static void closeSession(ROUTER *instance, void *session); -static void freeSession(ROUTER *instance, void *session); -static int routeQuery(ROUTER *instance, void *session, GWBUF *queue); -static void diagnostic(ROUTER *instance, DCB *dcb); -static uint8_t getCapabilities (ROUTER* inst, void* router_session); - - -static ROUTER_OBJECT MyObject = { - createInstance, - newSession, - closeSession, - freeSession, - routeQuery, - diagnostic, - NULL, - NULL, - getCapabilities -}; - - -static void send_index(WEB_SESSION *); -static void send_css(WEB_SESSION *); -static void send_menu(WEB_SESSION *); -static void send_blank(WEB_SESSION *); -static void send_title(WEB_SESSION *); -static void send_frame1(WEB_SESSION *); -static void send_services(WEB_SESSION *); -static void send_sessions(WEB_SESSION *); -static void send_servers(WEB_SESSION *); -static void send_monitors(WEB_SESSION *); -static void respond_error(WEB_SESSION *, int, char *); - -static struct { - char *page; - void (*fcn)(WEB_SESSION *); -} pages[] = { - { "index.html", send_index }, - { "services.html", send_services }, - { "menu.html", send_menu }, - { "sessions.html", send_sessions }, - { "blank.html", send_blank }, - { "title.html", send_title }, - { "frame1.html", send_frame1 }, - { "servers.html", send_servers }, - { "monitors.html", send_monitors }, - { "styles.css", send_css }, - { NULL, NULL } -}; - -/** - * Implementation of the mandatory version entry point - * - * @return version string of the module - */ -char * -version() -{ - return version_str; -} - -/** - * The module initialisation routine, called when the module - * is first loaded. - */ -void -ModuleInit() -{ - -} - -/** - * The module entry point routine. It is this routine that - * must populate the structure that is referred to as the - * "module object", this is a structure with the set of - * external entry points for this module. - * - * @return The module object - */ -ROUTER_OBJECT * -GetModuleObject() -{ - return &MyObject; -} - -/** - * Create an instance of the router for a particular service - * within the gateway. - * - * @param service The service this router is being create for - * @param options The options for this query router - * - * @return The instance data for this new instance - */ -static ROUTER * -createInstance(SERVICE *service, char **options) -{ -WEB_INSTANCE *inst; - - if ((inst = (WEB_INSTANCE *)malloc(sizeof(WEB_INSTANCE))) == NULL) - return NULL; - - inst->service = service; - return (ROUTER *)inst; -} - -/** - * Associate a new session with this instance of the router. - * - * @param instance The router instance data - * @param session The session itself - * @return Session specific data for this session - */ -static void * -newSession(ROUTER *instance, SESSION *session) -{ -WEB_SESSION *wsession; - - if ((wsession = (WEB_SESSION *)malloc(sizeof(WEB_SESSION))) == NULL) - return NULL; - - wsession->session = session; - return wsession; -} - -/** - * Close a session with the router, this is the mechanism - * by which a router may cleanup data structure etc. - * - * @param instance The router instance data - * @param session The session being closed - */ -static void -closeSession(ROUTER *instance, void *session) -{ - free(session); -} - -static void freeSession( - ROUTER* router_instance, - void* router_client_session) -{ - return; -} - -static int -routeQuery(ROUTER *instance, void *session, GWBUF *queue) -{ -WEB_SESSION *wsession = (WEB_SESSION *)session; -char *ptr; -int i, found = 0; -char *url; - - if ((url = gwbuf_get_property(queue, "URL")) == NULL) - { - respond_error(wsession, 404, "No URL available"); - } - - ptr = strrchr(url, '/'); - if (ptr) - ptr++; - else - ptr = url; - for (i = 0; pages[i].page; i++) - { - if (!strcmp(ptr, pages[i].page)) - { - (pages[i].fcn)(wsession); - found = 1; - } - } - if (!found) - respond_error(wsession, 404, "Unrecognised URL received"); - gwbuf_free(queue); - return 0; -} - -/** - * Diagnostics routine - * - * @param instance The router instance - * @param dcb The DCB for diagnostic output - */ -static void -diagnostic(ROUTER *instance, DCB *dcb) -{ -} - -/** - * Return the router capabilities bitmask - * - * @param inst The router instance - * @param router_session The router session - * @return Router capabilities bitmask - */ -static uint8_t -getCapabilities(ROUTER *inst, void *router_session) -{ - return 0; -} - -static char *index_page = -"" -"" -"MaxScale" -"" -"" -"" -"" -"" -""; - -static char *title_page = -"" -"" -"MaxScale" -"" -"

MaxScale - Status View

" -""; - -static char *frame1_page = -"" -"" -"" -"" -"" -""; - -static char *menu_page = -"" -"" -"" -"

Options

" -"

"; - -static char *blank_page = " "; - -static char *css = -"table, td, th { border: 1px solid blue; }\n" -"th { background-color: blue; color: white; padding: 5px }\n" -"td { padding: 5px; }\n" -"table { border-collapse: collapse; }\n" -"a:link { color: #0000FF; }\n" -"a:visted { color: #0000FF; }\n" -"a:hover { color: #FF0000; }\n" -"a:active { color: #0000FF; }\n" -"h1 { color: blue; font-family: serif }\n" -"h2 { color: blue; font-family: serif }\n" -"p { font-family: serif }\n" -"li { font-family: serif }\n"; - - -static void -send_html_header(DCB *dcb) -{ -char date[64] = ""; -const char *fmt = "%a, %d %b %Y %H:%M:%S GMT"; - - time_t httpd_current_time = time(NULL); - - strftime(date, sizeof(date), fmt, localtime(&httpd_current_time)); - - dcb_printf(dcb, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: %s\r\nConnection: close\r\nContent-Type: text/html\r\n", date, "MaxScale"); - - dcb_printf(dcb, "\r\n"); -} - -static void -send_static_html(DCB *dcb, char *html) -{ - dcb_printf(dcb, html); -} - -static void -send_index(WEB_SESSION *session) -{ -DCB *dcb = session->session->client; - - send_html_header(dcb); - send_static_html(dcb, index_page); - dcb_close(dcb); -} - -static void -send_css(WEB_SESSION *session) -{ -DCB *dcb = session->session->client; - - send_html_header(dcb); - send_static_html(dcb, css); - dcb_close(dcb); -} - -static void -send_title(WEB_SESSION *session) -{ -DCB *dcb = session->session->client; - - send_html_header(dcb); - send_static_html(dcb, title_page); - dcb_close(dcb); -} - -static void -send_frame1(WEB_SESSION *session) -{ -DCB *dcb = session->session->client; - - send_html_header(dcb); - send_static_html(dcb, frame1_page); - dcb_close(dcb); -} - -static void -send_menu(WEB_SESSION *session) -{ -DCB *dcb = session->session->client; - - send_html_header(dcb); - send_static_html(dcb, menu_page); - dcb_close(dcb); -} - -static void -send_blank(WEB_SESSION *session) -{ -DCB *dcb = session->session->client; - - send_html_header(dcb); - send_static_html(dcb, blank_page); - dcb_close(dcb); -} - -static void -service_row(SERVICE *service, DCB *dcb) -{ - dcb_printf(dcb, "%s%s%d%d\n", - service->name, service->routerModule, - service->stats.n_current, service->stats.n_sessions); -} - -static void -send_services(WEB_SESSION *session) -{ -DCB *dcb = session->session->client; - - send_html_header(dcb); - dcb_printf(dcb, ""); - dcb_printf(dcb, ""); - dcb_printf(dcb, "

Services

"); - dcb_printf(dcb, "\n"); - serviceIterate(service_row, dcb); - dcb_printf(dcb, "
NameRouter"); - dcb_printf(dcb, "Current SessionsTotal Sessions
\n"); - dcb_close(dcb); -} - -static void -session_row(SESSION *session, DCB *dcb) -{ - dcb_printf(dcb, "%-16p%s%s%s\n", - session, ((session->client && session->client->remote) - ? session->client->remote : ""), - (session->service && session->service->name - ? session->service->name : ""), - session_state(session->state)); -} - -static void -send_sessions(WEB_SESSION *session) -{ -DCB *dcb = session->session->client; - - send_html_header(dcb); - dcb_printf(dcb, ""); - dcb_printf(dcb, ""); - dcb_printf(dcb, "

Sessions

"); - dcb_printf(dcb, "\n"); - sessionIterate(session_row, dcb); - dcb_printf(dcb, "
SessionClient"); - dcb_printf(dcb, "ServiceState
\n"); - dcb_close(dcb); -} - -static void -server_row(SERVER *server, DCB *dcb) -{ - dcb_printf(dcb, "%s%s%d%s%d\n", - server->unique_name, server->name, server->port, - server_status(server), server->stats.n_current); -} - -static void -send_servers(WEB_SESSION *session) -{ -DCB *dcb = session->session->client; - - send_html_header(dcb); - dcb_printf(dcb, ""); - dcb_printf(dcb, ""); - dcb_printf(dcb, "

Servers

"); - dcb_printf(dcb, "\n"); - serverIterate(server_row, dcb); - dcb_printf(dcb, "
ServerAddress"); - dcb_printf(dcb, "PortStateConnections
\n"); - dcb_close(dcb); -} - -static void -monitor_row(MONITOR *monitor, DCB *dcb) -{ - dcb_printf(dcb, "%s%s\n", - monitor->name, monitor->state & MONITOR_STATE_RUNNING - ? "Running" : "Stopped"); -} - -static void -send_monitors(WEB_SESSION *session) -{ -DCB *dcb = session->session->client; - - send_html_header(dcb); - dcb_printf(dcb, ""); - dcb_printf(dcb, ""); - dcb_printf(dcb, "

Monitors

"); - dcb_printf(dcb, "\n"); - monitorIterate(monitor_row, dcb); - dcb_printf(dcb, "
MonitorState
\n"); - dcb_close(dcb); -} - -static void -respond_error(WEB_SESSION *session, int err, char *msg) -{ -DCB *dcb = session->session->client; - - dcb_printf(dcb, "HTTP/1.1 %d %s\n", err, msg); - dcb_printf(dcb, "Content-Type: text/html\n"); - dcb_printf(dcb, "\n"); - dcb_printf(dcb, "\n"); - dcb_printf(dcb, "MaxScale webserver plugin unable to satisfy request.\n"); - dcb_printf(dcb, "

Code: %d, %s\n", err, msg); - dcb_printf(dcb, ""); - dcb_close(dcb); -} From 3585054b35b933a6cee37281a0a3920c6f1a9594 Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Tue, 29 Jul 2014 22:28:44 +0300 Subject: [PATCH 3/7] Bug 462, http://bugs.skysql.com/show_bug.cgi?id=462, fixed path MAXSCALE_HOME path expression in server/test/makefile --- server/test/makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/test/makefile b/server/test/makefile index d812494fc..7f9c36b84 100644 --- a/server/test/makefile +++ b/server/test/makefile @@ -10,7 +10,7 @@ include ../../test.inc TEST_ROOT := $(ROOT_PATH)/test PARENT_DIR := $(ROOT_PATH)/server CUR_DIR := $(PARENT_DIR)/test -export MAXSCALE_HOME=$(CUR_DIR)/MaxScale +export MAXSCALE_HOME=$(CUR_DIR) CC=cc TESTLOG := $(CUR_DIR)/testserver.log From 4ef1df4e9d8ec6e4b120b2f059ecc2f3ab931b93 Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Tue, 5 Aug 2014 13:44:04 +0300 Subject: [PATCH 4/7] Bug #468, http://bugs.skysql.com/show_bug.cgi?id=468, Query classifier accessed freed thread context. If parsing fails thd doesn't need to be freed because it holds correct information about command type. --- query_classifier/query_classifier.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index eccc35a31..889940e00 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -149,17 +149,17 @@ skygw_query_type_t skygw_query_classifier_get_type( thd = get_or_create_thd_for_parsing(mysql, query_str); if (thd == NULL) - { - skygw_query_classifier_free(mysql); - } - /** Create parse_tree inside thd */ - failp = create_parse_tree(thd); - - if (failp) { skygw_query_classifier_free(mysql); *p_mysql = NULL; + goto return_qtype; } + /** + * Create parse_tree inside thd. + * thd and even lex are readable even if parser failed so let it + * continue despite failure. + */ + failp = create_parse_tree(thd); qtype = resolve_query_type(thd); if (p_mysql == NULL) @@ -464,7 +464,7 @@ static skygw_query_type_t resolve_query_type( type |= QUERY_TYPE_DISABLE_AUTOCOMMIT; type |= QUERY_TYPE_BEGIN_TRX; } - /** + /** * REVOKE ALL, ASSIGN_TO_KEYCACHE, * PRELOAD_KEYS, FLUSH, RESET, CREATE|ALTER|DROP SERVER */ From f738b2e0cbd272cf9eee6b1babf63533640234f9 Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Tue, 5 Aug 2014 16:38:00 +0300 Subject: [PATCH 5/7] Fix to http://bugs.skysql.com/show_bug.cgi?id=469, connection counter leaks in master. Removed redundant counter increments. --- .../routing/readwritesplit/readwritesplit.c | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 90132a9f9..9d4b1b8ff 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -381,7 +381,7 @@ static void refreshInstance( * used in slave selection. */ if (!rlag_enabled) - { + { if (rlag_limited) { LOGIF(LE, (skygw_log_write_flush( @@ -1132,6 +1132,8 @@ static int routeQuery( break; case MYSQL_COM_STMT_EXECUTE: + /** Parsing is not needed for this type of packet */ +#if defined(NOT_USED) plainsqlbuf = gwbuf_clone_transform(querybuf, GWBUF_TYPE_PLAINSQL); len = GWBUF_LENGTH(plainsqlbuf); @@ -1140,7 +1142,8 @@ static int routeQuery( memcpy(querystr, startpos, len); memset(&querystr[len], 0, 1); qtype = skygw_query_classifier_get_type(querystr, 0, &mysql); - qtype |= QUERY_TYPE_EXEC_STMT; +#endif + qtype = QUERY_TYPE_EXEC_STMT; break; case MYSQL_COM_SHUTDOWN: /**< 8 where should shutdown be routed ? */ @@ -1923,7 +1926,8 @@ static bool select_connect_backend_servers( } } } - } + } /*< log only */ + /** * Choose at least 1+min_nslaves (master and slave) and at most 1+max_nslaves * servers from the sorted list. First master found is selected. @@ -1981,7 +1985,7 @@ static bool select_connect_backend_servers( backend_ref[i].bref_state = 0; bref_set_state(&backend_ref[i], BREF_IN_USE); - /** + /** * Increase backend connection counter. * Server's stats are _increased_ in * dcb.c:dcb_alloc ! @@ -2036,8 +2040,6 @@ static bool select_connect_backend_servers( bref_set_state(&backend_ref[i], BREF_IN_USE); /** Increase backend connection counters */ - atomic_add(&b->backend_server->stats.n_current, 1); - atomic_add(&b->backend_server->stats.n_connections, 1); atomic_add(&b->backend_conn_count, 1); } else @@ -2666,8 +2668,9 @@ static bool execute_sescmd_in_backend( pthread_self(), dcb->fd, STRPACKETTYPE(cmd)))); + gwbuf_free(tmpbuf); } -#endif +#endif /*< SS_DEBUG */ switch (scur->scmd_cur_cmd->my_sescmd_packet_type) { case MYSQL_COM_CHANGE_USER: rc = dcb->func.auth( @@ -3065,7 +3068,7 @@ static bool router_option_configured( } return succp; } -#endif +#endif /*< NOT_USED */ static void rwsplit_process_router_options( ROUTER_INSTANCE* router, @@ -3346,7 +3349,7 @@ static void print_error_packet( { while ((buf = gwbuf_consume(buf, GWBUF_LENGTH(buf))) != NULL); } -#endif +#endif /*< SS_DEBUG */ } static int router_get_servercount( @@ -3568,10 +3571,10 @@ static prep_stmt_t* prep_stmt_init( if (pstmt != NULL) { - #if defined(SS_DEBUG) +#if defined(SS_DEBUG) pstmt->pstmt_chk_top = CHK_NUM_PREP_STMT; pstmt->pstmt_chk_tail = CHK_NUM_PREP_STMT; - #endif +#endif pstmt->pstmt_state = PREP_STMT_ALLOC; pstmt->pstmt_type = type; From 7f18914d90c169c36a1715c4a27824c688cb528e Mon Sep 17 00:00:00 2001 From: Hartmut Holzgraefe Date: Fri, 15 Aug 2014 02:51:16 +0200 Subject: [PATCH 6/7] fixed wrong server port in README --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 3750fb332..cdc9b35ac 100644 --- a/README +++ b/README @@ -182,7 +182,7 @@ on localhost: * a master on port 3000, with server_id=2 * a slave on port 3001, server_id doesn't matter -* a slave on port 2002, server_id doesn't matter +* a slave on port 3002, server_id doesn't matter On the master full privileges on the databases "test" and "FOO" are needed, on the saves SELECT permissions on test.* should From fb87f683687aebfbf0a197f5d1fe7608867ae048 Mon Sep 17 00:00:00 2001 From: Hartmut Holzgraefe Date: Sun, 17 Aug 2014 21:02:24 +0000 Subject: [PATCH 7/7] consistently use DEL instead of rm in Makefiles --- client/Makefile | 9 ++++++--- log_manager/makefile | 2 +- query_classifier/makefile | 2 +- server/Makefile | 5 ++++- server/core/Makefile | 6 +++--- server/modules/filter/Makefile | 4 ++-- server/modules/monitor/Makefile | 6 ++++-- server/modules/protocol/Makefile | 4 ++-- server/modules/routing/Makefile | 4 ++-- server/modules/routing/readwritesplit/Makefile | 4 ++-- 10 files changed, 27 insertions(+), 19 deletions(-) diff --git a/client/Makefile b/client/Makefile index 216ef23ca..19f38785f 100644 --- a/client/Makefile +++ b/client/Makefile @@ -20,6 +20,9 @@ # client program # 18/06/14 Mark Riddoch Addition of conditional for histedit +include ../build_gateway.inc +include ../makefile.inc + ifeq ($(wildcard /usr/include/histedit.h), ) HISTLIB= HISTFLAG= @@ -63,14 +66,14 @@ maxadmin: $(OBJ) clean: - rm -f $(OBJ) maxadmin - - rm *.so + $(DEL) $(OBJ) maxadmin + $(DEL) *.so tags: ctags $(SRCS) $(HDRS) depend: - @rm -f depend.mk + @$(DEL) depend.mk cc -M $(CFLAGS) $(SRCS) > depend.mk install: maxadmin diff --git a/log_manager/makefile b/log_manager/makefile index b1386bbcc..a020c7932 100644 --- a/log_manager/makefile +++ b/log_manager/makefile @@ -44,7 +44,7 @@ install: liblink install liblog_manager.so.1.0.1 liblog_manager.so $(DEST)/lib depend: - @rm -f depend + @$(DEL) depend $(CPP) -M $(CFLAGS) \ $(MYSQL_HEADERS) \ -I$(UTILS_PATH) -I./ \ diff --git a/query_classifier/makefile b/query_classifier/makefile index efa456a01..b08f0a9bd 100644 --- a/query_classifier/makefile +++ b/query_classifier/makefile @@ -62,7 +62,7 @@ install: liblink install ./libquery_classifier.so.1.0.1 ./libquery_classifier.so $(DEST)/lib depend: - @rm -f depend + @$(DEL) depend $(CPP) -M $(CFLAGS) \ $(MYSQL_HEADERS) \ -I$(LOG_MANAGER_PATH) \ diff --git a/server/Makefile b/server/Makefile index 09120da07..fda9d14c1 100644 --- a/server/Makefile +++ b/server/Makefile @@ -24,6 +24,9 @@ # 08/07/13 Mark Riddoch Addition of monitor modules # 16/07/13 Mark Riddoch Renamed things to match the new naming +include ../build_gateway.inc +include ../makefile.inc + DEST=$(HOME)/usr/local/skysql all: @@ -45,7 +48,7 @@ testall: $(MAKE) -C test HAVE_SRV=$(HAVE_SRV) testall clean: - (cd Documentation; rm -rf html) + (cd Documentation; $(DEL) html) (cd core; touch depend.mk ; make clean) (cd modules/routing; touch depend.mk ; make clean) (cd modules/protocol; touch depend.mk ; make clean) diff --git a/server/core/Makefile b/server/core/Makefile index 9bf650c34..9a807a925 100644 --- a/server/core/Makefile +++ b/server/core/Makefile @@ -108,14 +108,14 @@ maxpasswd: $(POBJS) echo '#define MAXSCALE_VERSION "'`cat ../../VERSION`'"' > ../include/version.h clean: - rm -f $(OBJ) maxscale - - rm *.so + $(DEL) $(OBJ) maxscale + $(DEL) *.so tags: ctags $(SRCS) $(HDRS) depend: ../include/version.h - @rm -f depend.mk + @$(DEL) depend.mk cc -M $(CFLAGS) $(SRCS) > depend.mk install: maxscale maxkeys maxpasswd diff --git a/server/modules/filter/Makefile b/server/modules/filter/Makefile index 931c35ab1..8e6036e91 100644 --- a/server/modules/filter/Makefile +++ b/server/modules/filter/Makefile @@ -69,13 +69,13 @@ libtee.so: $(TEEOBJ) $(CC) $(CFLAGS) $< -o $@ clean: - rm -f $(OBJ) $(MODULES) + $(DEL) $(OBJ) $(MODULES) tags: ctags $(SRCS) $(HDRS) depend: - @rm -f depend.mk + @$(DEL) depend.mk cc -M $(CFLAGS) $(SRCS) > depend.mk install: $(MODULES) diff --git a/server/modules/monitor/Makefile b/server/modules/monitor/Makefile index 7fdbc5843..8b60824ea 100644 --- a/server/modules/monitor/Makefile +++ b/server/modules/monitor/Makefile @@ -19,6 +19,8 @@ # 08/07/13 Mark Riddoch Initial implementation include ../../../build_gateway.inc +include ../../../makefile.inc + LOGPATH := $(ROOT_PATH)/log_manager UTILSPATH := $(ROOT_PATH)/utils @@ -55,13 +57,13 @@ libgaleramon.so: $(GALERAOBJ) $(CC) $(CFLAGS) $< -o $@ clean: - rm -f $(OBJ) $(MODULES) + $(DEL) $(OBJ) $(MODULES) tags: ctags $(SRCS) $(HDRS) depend: - @rm -f depend.mk + @$(DEL) depend.mk cc -M $(CFLAGS) $(SRCS) > depend.mk install: $(MODULES) diff --git a/server/modules/protocol/Makefile b/server/modules/protocol/Makefile index 54a8f8c38..0b41d329e 100644 --- a/server/modules/protocol/Makefile +++ b/server/modules/protocol/Makefile @@ -76,7 +76,7 @@ libmaxscaled.so: $(MAXSCALEDOBJ) $(CC) $(CFLAGS) $< -o $@ clean: - rm -f $(OBJ) $(MODULES) + $(DEL) $(OBJ) $(MODULES) tags: ctags $(SRCS) $(HDRS) @@ -85,7 +85,7 @@ install: $(MODULES) install -D $(MODULES) $(DEST)/modules depend: - rm -f depend.mk + @$(DEL) depend.mk cc -M $(CFLAGS) $(SRCS) > depend.mk include depend.mk diff --git a/server/modules/routing/Makefile b/server/modules/routing/Makefile index 4feac68fb..8287bdaea 100644 --- a/server/modules/routing/Makefile +++ b/server/modules/routing/Makefile @@ -71,7 +71,7 @@ libreadwritesplit.so: $(CC) $(CFLAGS) $< -o $@ clean: - rm -f $(OBJ) $(MODULES) + $(DEL) $(OBJ) $(MODULES) (cd readwritesplit; touch depend.mk; make clean) tags: @@ -79,7 +79,7 @@ tags: (cd readwritesplit; make tags) depend: - @rm -f depend.mk + @$(DEL) depend.mk cc -M $(CFLAGS) $(SRCS) > depend.mk (cd readwritesplit; touch depend.mk ; make depend) diff --git a/server/modules/routing/readwritesplit/Makefile b/server/modules/routing/readwritesplit/Makefile index c60f2ffd1..80f2ec572 100644 --- a/server/modules/routing/readwritesplit/Makefile +++ b/server/modules/routing/readwritesplit/Makefile @@ -50,13 +50,13 @@ libreadwritesplit.so: $(OBJ) $(CC) $(CFLAGS) $< -o $@ clean: - rm -f $(OBJ) $(MODULES) + $(DEL) $(OBJ) $(MODULES) tags: ctags $(SRCS) $(HDRS) depend: - @rm -f depend.mk + @$(DEL) depend.mk cc -M $(CFLAGS) $(SRCS) > depend.mk install: $(MODULES)