Revert "Implementation of HTTPD protocol, gwbuf properties and a dmeo web application"

This reverts commit 6fd5dff34902051f38932947493280e1e62dbeb1.
This commit is contained in:
Mark Riddoch 2014-07-15 17:53:39 +01:00
parent 6fd5dff349
commit 87e66a0ea8
14 changed files with 178 additions and 908 deletions

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 <spinlock.h>
#include <skygw_debug.h>
/**
* 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

View File

@ -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

View File

@ -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

View File

@ -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 *);

View File

@ -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

View File

@ -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;

View File

@ -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 <httpd.h>
#include <gw.h>
#include <modinfo.h>
#include <skygw_utils.h>
#include <log_manager.h>
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");
}
}

View File

@ -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 $@ ..)

View File

@ -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 <stdio.h>
#include <router.h>
#include <modinfo.h>
#include <server.h>
#include <service.h>
#include <session.h>
#include <monitor.h>
#include <string.h>
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 =
"<HTML><HEAD>"
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
"<TITLE>MaxScale</TITLE>"
"</HEAD>"
"<FRAMESET ROWS=\"60,*\">"
"<FRAME SRC=\"title.html\">"
"<FRAME SRC=\"frame1.html\">"
"</FRAMESET>"
"</HTML>";
static char *title_page =
"<HTML><HEAD>"
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
"<TITLE>MaxScale</TITLE>"
"</HEAD><BODY>"
"<H1>MaxScale - Status View</H1>"
"</BODY></HTML>";
static char *frame1_page =
"<HTML>"
"<FRAMESET COLS=\"20%,80%\">"
"<FRAME SRC=\"menu.html\">"
"<FRAME SRC=\"blank.html\" NAME=\"darea\">"
"</FRAMESET>"
"</HTML>";
static char *menu_page =
"<HTML><HEAD>"
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
"</HEAD><BODY>"
"<H2>Options</H2><P>"
"<UL><LI><A HREF=\"monitors.html\" target=\"darea\">Monitors</A>"
"<LI><A HREF=\"services.html\" target=\"darea\">Services</A>"
"<LI><A HREF=\"servers.html\" target=\"darea\">Servers</A>"
"<LI><A HREF=\"sessions.html\" target=\"darea\">Sessions</A>"
"</UL></BODY></HTML>";
static char *blank_page = "<HTML><BODY>&nbsp;</BODY></HTML>";
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, "<TR><TD>%s</TD><TD>%s</TD><TD>%d</TD><TD>%d</TD></TR>\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, "<HTML><HEAD>");
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
dcb_printf(dcb, "<BODY><H2>Services</H2><P>");
dcb_printf(dcb, "<TABLE><TR><TH>Name</TH><TH>Router</TH><TH>");
dcb_printf(dcb, "Current Sessions</TH><TH>Total Sessions</TH></TR>\n");
serviceIterate(service_row, dcb);
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
dcb_close(dcb);
}
static void
session_row(SESSION *session, DCB *dcb)
{
dcb_printf(dcb, "<TR><TD>%-16p</TD><TD>%s</TD><TD>%s</TD><TD>%s</TD></TR>\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, "<HTML><HEAD>");
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
dcb_printf(dcb, "<BODY><H2>Sessions</H2><P>");
dcb_printf(dcb, "<TABLE><TR><TH>Session</TH><TH>Client</TH><TH>");
dcb_printf(dcb, "Service</TH><TH>State</TH></TR>\n");
sessionIterate(session_row, dcb);
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
dcb_close(dcb);
}
static void
server_row(SERVER *server, DCB *dcb)
{
dcb_printf(dcb, "<TR><TD>%s</TD><TD>%s</TD><TD>%d</TD><TD>%s</TD><TD>%d</TD></TR>\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, "<HTML><HEAD>");
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
dcb_printf(dcb, "<BODY><H2>Servers</H2><P>");
dcb_printf(dcb, "<TABLE><TR><TH>Server</TH><TH>Address</TH><TH>");
dcb_printf(dcb, "Port</TH><TH>State</TH><TH>Connections</TH></TR>\n");
serverIterate(server_row, dcb);
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
dcb_close(dcb);
}
static void
monitor_row(MONITOR *monitor, DCB *dcb)
{
dcb_printf(dcb, "<TR><TD>%s</TD><TD>%s</TD></TR>\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, "<HTML><HEAD>");
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
dcb_printf(dcb, "<BODY><H2>Monitors</H2><P>");
dcb_printf(dcb, "<TABLE><TR><TH>Monitor</TH><TH>State</TH></TR>\n");
monitorIterate(monitor_row, dcb);
dcb_printf(dcb, "</TABLE></BODY></HTML>\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, "<HTML><BODY>\n");
dcb_printf(dcb, "MaxScale webserver plugin unable to satisfy request.\n");
dcb_printf(dcb, "<P>Code: %d, %s\n", err, msg);
dcb_printf(dcb, "</BODY></HTML>");
dcb_close(dcb);
}