Merge branch 'httpd' into MAX-157
Conflicts: server/core/buffer.c server/include/buffer.h
This commit is contained in:
commit
764ceac105
@ -14,10 +14,15 @@ Source: %{name}-%{version}-%{release}.tar.gz
|
||||
Prefix: /
|
||||
Group: Development/Tools
|
||||
#Requires:
|
||||
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: gcc gcc-c++ ncurses-devel bison glibc-devel cmake libgcc_s1 perl make libtool libopenssl-devel libaio libaio-devel mariadb libedit-devel
|
||||
%else
|
||||
BuildRequires: gcc gcc-c++ ncurses-devel bison glibc-devel cmake libgcc perl make libtool openssl-devel libaio libaio-devel MariaDB-devel MariaDB-server
|
||||
%if 0%{?rhel} == 6
|
||||
BuildRequires: libedit-devel
|
||||
%endif
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%description
|
||||
MaxScale
|
||||
|
@ -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,12 +78,14 @@ 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->hint = NULL;
|
||||
rval->properties = NULL;
|
||||
rval->gwbuf_type = GWBUF_TYPE_UNDEFINED;
|
||||
rval->command = 0;
|
||||
CHK_GWBUF(rval);
|
||||
@ -97,12 +100,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);
|
||||
}
|
||||
|
||||
@ -338,4 +351,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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -39,13 +39,26 @@
|
||||
* 11/07/2013 Mark Riddoch Addition of reference count in the gwbuf
|
||||
* 16/07/2013 Massimiliano Pinto Added command type for the queue
|
||||
* 10/07/2014 Mark Riddoch Addition of hints
|
||||
* 15/07/2014 Mark Riddoch Added buffer properties
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <spinlock.h>
|
||||
#include <skygw_debug.h>
|
||||
#include <hints.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,
|
||||
@ -54,7 +67,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)
|
||||
@ -91,6 +105,8 @@ typedef struct gwbuf {
|
||||
int command;/*< The command type for the queue */
|
||||
gwbuf_type_t gwbuf_type; /*< buffer's data type information */
|
||||
HINT *hint; /*< Hint data for this buffer */
|
||||
SPINLOCK lock;
|
||||
BUF_PROPERTY *properties; /*< Buffer properties */
|
||||
} GWBUF;
|
||||
|
||||
/*<
|
||||
@ -124,4 +140,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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 *);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 <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,
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 $@ ..)
|
||||
|
||||
|
616
server/modules/routing/webserver.c
Normal file
616
server/modules/routing/webserver.c
Normal file
@ -0,0 +1,616 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
/**
|
||||
* The instance structure for this router.
|
||||
*/
|
||||
typedef struct {
|
||||
SERVICE *service;
|
||||
} WEB_INSTANCE;
|
||||
|
||||
/**
|
||||
* The session structure for this router.
|
||||
*/
|
||||
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 *);
|
||||
|
||||
/**
|
||||
* A map of URL to function that implements the URL
|
||||
*/
|
||||
static struct {
|
||||
char *page; /* URL */
|
||||
void (*fcn)(WEB_SESSION *); /* Function to call */
|
||||
} 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML of the index page.
|
||||
*/
|
||||
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>";
|
||||
|
||||
/**
|
||||
* The HTML of the title page
|
||||
*/
|
||||
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>";
|
||||
|
||||
/**
|
||||
* HTML of the main frames, those below the title frame
|
||||
*/
|
||||
static char *frame1_page =
|
||||
"<HTML>"
|
||||
"<FRAMESET COLS=\"20%,80%\">"
|
||||
"<FRAME SRC=\"menu.html\">"
|
||||
"<FRAME SRC=\"blank.html\" NAME=\"darea\">"
|
||||
"</FRAMESET>"
|
||||
"</HTML>";
|
||||
|
||||
/**
|
||||
* The menu page 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>";
|
||||
|
||||
/**
|
||||
* A blank page, contents of the display area when we first connect
|
||||
*/
|
||||
static char *blank_page = "<HTML><BODY> </BODY></HTML>";
|
||||
|
||||
/**
|
||||
* The CSS used for every "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";
|
||||
|
||||
/**
|
||||
* Send the standard HTTP headers for an HTML file
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a static HTML page
|
||||
*
|
||||
* @param dcb The DCB of the connection to the browser
|
||||
* @param html The HTML to send
|
||||
*/
|
||||
static void
|
||||
send_static_html(DCB *dcb, char *html)
|
||||
{
|
||||
dcb_printf(dcb, html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the index page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the CSS
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
static void
|
||||
send_css(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, css);
|
||||
dcb_close(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the title page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the frame1 page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the menu page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a blank page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a table row for a service. This is called using the service
|
||||
* iterator function
|
||||
*
|
||||
* @param service The service to display
|
||||
* @param dcb The DCB to print the HTML to
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the services page. This produces a table by means of the
|
||||
* serviceIterate call.
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a session row for a session. this is called using the session
|
||||
* iterator function
|
||||
*
|
||||
* @param session The session to display
|
||||
* @param dcb The DCB to send the HTML to
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the sessions page. The produces a table of all the current sessions
|
||||
* display. It makes use of the sessionIterate call to call the function
|
||||
* session_row() with each session.
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a table row for a particular server. This is called via the
|
||||
* serverIterate call in send_servers.
|
||||
*
|
||||
* @param server The server to print
|
||||
* @param dcb The DCB to send the HTML to
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the servers page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a table row for the monitors table
|
||||
*
|
||||
* @param monitor The monitor to print
|
||||
* @param dcb The DCB to print to
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the monitors page. This iterates on all the monitors and send
|
||||
* the rows via the monitor_monitor.
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond with an HTTP error
|
||||
*
|
||||
* @param session The router session
|
||||
* @param err The HTTP error code to send
|
||||
* @param msg The message to print
|
||||
*/
|
||||
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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user