initial import
This commit is contained in:
Vendored
+237
@@ -0,0 +1,237 @@
|
||||
#ifndef MYSQL_CLIENT_PLUGIN_INCLUDED
|
||||
/* Copyright (c) 2010, 2021, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2.0,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is also distributed with certain software (including
|
||||
but not limited to OpenSSL) that is licensed under separate terms,
|
||||
as designated in a particular file or component or in included license
|
||||
documentation. The authors of MySQL hereby grant you an additional
|
||||
permission to link the program and your derivative works with the
|
||||
separately licensed software that they have included with MySQL.
|
||||
|
||||
Without limiting anything contained in the foregoing, this file,
|
||||
which is part of C Driver for MySQL (Connector/C), is also subject to the
|
||||
Universal FOSS Exception, version 1.0, a copy of which can be found at
|
||||
http://oss.oracle.com/licenses/universal-foss-exception.
|
||||
|
||||
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
/**
|
||||
@file include/mysql/client_plugin.h
|
||||
MySQL Client Plugin API.
|
||||
This file defines the API for plugins that work on the client side
|
||||
*/
|
||||
#define MYSQL_CLIENT_PLUGIN_INCLUDED
|
||||
|
||||
#ifndef MYSQL_ABI_CHECK
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
On Windows, exports from DLL need to be declared.
|
||||
Also, plugin needs to be declared as extern "C" because MSVC
|
||||
unlike other compilers, uses C++ mangling for variables not only
|
||||
for functions.
|
||||
*/
|
||||
|
||||
#undef MYSQL_PLUGIN_EXPORT
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(MYSQL_DYNAMIC_PLUGIN)
|
||||
#ifdef __cplusplus
|
||||
#define MYSQL_PLUGIN_EXPORT extern "C" __declspec(dllexport)
|
||||
#else
|
||||
#define MYSQL_PLUGIN_EXPORT __declspec(dllexport)
|
||||
#endif
|
||||
#else /* MYSQL_DYNAMIC_PLUGIN */
|
||||
#ifdef __cplusplus
|
||||
#define MYSQL_PLUGIN_EXPORT extern "C"
|
||||
#else
|
||||
#define MYSQL_PLUGIN_EXPORT
|
||||
#endif
|
||||
#endif /*MYSQL_DYNAMIC_PLUGIN */
|
||||
#else /*_MSC_VER */
|
||||
|
||||
#if defined(MYSQL_DYNAMIC_PLUGIN)
|
||||
#define MYSQL_PLUGIN_EXPORT MY_ATTRIBUTE((visibility("default")))
|
||||
#else
|
||||
#define MYSQL_PLUGIN_EXPORT
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* known plugin types */
|
||||
#define MYSQL_CLIENT_reserved1 0
|
||||
#define MYSQL_CLIENT_reserved2 1
|
||||
#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2
|
||||
#define MYSQL_CLIENT_TRACE_PLUGIN 3
|
||||
|
||||
#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0200
|
||||
#define MYSQL_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION 0x0200
|
||||
|
||||
#define MYSQL_CLIENT_MAX_PLUGINS 4
|
||||
|
||||
#define MYSQL_CLIENT_PLUGIN_AUTHOR_ORACLE "Oracle Corporation"
|
||||
|
||||
#define mysql_declare_client_plugin(X) \
|
||||
MYSQL_PLUGIN_EXPORT st_mysql_client_plugin_##X \
|
||||
_mysql_client_plugin_declaration_ = { \
|
||||
MYSQL_CLIENT_##X##_PLUGIN, \
|
||||
MYSQL_CLIENT_##X##_PLUGIN_INTERFACE_VERSION,
|
||||
#define mysql_end_client_plugin }
|
||||
|
||||
/* generic plugin header structure */
|
||||
#define MYSQL_CLIENT_PLUGIN_HEADER \
|
||||
int type; \
|
||||
unsigned int interface_version; \
|
||||
const char *name; \
|
||||
const char *author; \
|
||||
const char *desc; \
|
||||
unsigned int version[3]; \
|
||||
const char *license; \
|
||||
void *mysql_api; \
|
||||
int (*init)(char *, size_t, int, va_list); \
|
||||
int (*deinit)(void); \
|
||||
int (*options)(const char *option, const void *); \
|
||||
int (*get_options)(const char *option, void *);
|
||||
|
||||
struct st_mysql_client_plugin {
|
||||
MYSQL_CLIENT_PLUGIN_HEADER
|
||||
};
|
||||
|
||||
struct MYSQL;
|
||||
|
||||
/******** authentication plugin specific declarations *********/
|
||||
#include "plugin_auth_common.h"
|
||||
|
||||
struct auth_plugin_t {
|
||||
MYSQL_CLIENT_PLUGIN_HEADER
|
||||
int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct MYSQL *mysql);
|
||||
enum net_async_status (*authenticate_user_nonblocking)(MYSQL_PLUGIN_VIO *vio,
|
||||
struct MYSQL *mysql,
|
||||
int *result);
|
||||
};
|
||||
|
||||
// Needed for the mysql_declare_client_plugin() macro. Do not use elsewhere.
|
||||
typedef struct auth_plugin_t st_mysql_client_plugin_AUTHENTICATION;
|
||||
|
||||
/******** using plugins ************/
|
||||
|
||||
/**
|
||||
loads a plugin and initializes it
|
||||
|
||||
@param mysql MYSQL structure.
|
||||
@param name a name of the plugin to load
|
||||
@param type type of plugin that should be loaded, -1 to disable type check
|
||||
@param argc number of arguments to pass to the plugin initialization
|
||||
function
|
||||
@param ... arguments for the plugin initialization function
|
||||
|
||||
@retval
|
||||
a pointer to the loaded plugin, or NULL in case of a failure
|
||||
*/
|
||||
struct st_mysql_client_plugin *mysql_load_plugin(struct MYSQL *mysql,
|
||||
const char *name, int type,
|
||||
int argc, ...);
|
||||
|
||||
/**
|
||||
loads a plugin and initializes it, taking va_list as an argument
|
||||
|
||||
This is the same as mysql_load_plugin, but take va_list instead of
|
||||
a list of arguments.
|
||||
|
||||
@param mysql MYSQL structure.
|
||||
@param name a name of the plugin to load
|
||||
@param type type of plugin that should be loaded, -1 to disable type check
|
||||
@param argc number of arguments to pass to the plugin initialization
|
||||
function
|
||||
@param args arguments for the plugin initialization function
|
||||
|
||||
@retval
|
||||
a pointer to the loaded plugin, or NULL in case of a failure
|
||||
*/
|
||||
struct st_mysql_client_plugin *mysql_load_plugin_v(struct MYSQL *mysql,
|
||||
const char *name, int type,
|
||||
int argc, va_list args);
|
||||
|
||||
/**
|
||||
finds an already loaded plugin by name, or loads it, if necessary
|
||||
|
||||
@param mysql MYSQL structure.
|
||||
@param name a name of the plugin to load
|
||||
@param type type of plugin that should be loaded
|
||||
|
||||
@retval
|
||||
a pointer to the plugin, or NULL in case of a failure
|
||||
*/
|
||||
struct st_mysql_client_plugin *mysql_client_find_plugin(struct MYSQL *mysql,
|
||||
const char *name,
|
||||
int type);
|
||||
|
||||
/**
|
||||
adds a plugin structure to the list of loaded plugins
|
||||
|
||||
This is useful if an application has the necessary functionality
|
||||
(for example, a special load data handler) statically linked into
|
||||
the application binary. It can use this function to register the plugin
|
||||
directly, avoiding the need to factor it out into a shared object.
|
||||
|
||||
@param mysql MYSQL structure. It is only used for error reporting
|
||||
@param plugin an st_mysql_client_plugin structure to register
|
||||
|
||||
@retval
|
||||
a pointer to the plugin, or NULL in case of a failure
|
||||
*/
|
||||
struct st_mysql_client_plugin *mysql_client_register_plugin(
|
||||
struct MYSQL *mysql, struct st_mysql_client_plugin *plugin);
|
||||
|
||||
/**
|
||||
set plugin options
|
||||
|
||||
Can be used to set extra options and affect behavior for a plugin.
|
||||
This function may be called multiple times to set several options
|
||||
|
||||
@param plugin an st_mysql_client_plugin structure
|
||||
@param option a string which specifies the option to set
|
||||
@param value value for the option.
|
||||
|
||||
@retval 0 on success, 1 in case of failure
|
||||
**/
|
||||
int mysql_plugin_options(struct st_mysql_client_plugin *plugin,
|
||||
const char *option, const void *value);
|
||||
|
||||
/**
|
||||
get plugin options
|
||||
|
||||
Can be used to get options from a plugin.
|
||||
This function may be called multiple times to get several options
|
||||
|
||||
@param plugin an st_mysql_client_plugin structure
|
||||
@param option a string which specifies the option to get
|
||||
@param[out] value value for the option.
|
||||
|
||||
@retval 0 on success, 1 in case of failure
|
||||
**/
|
||||
int mysql_plugin_get_option(struct st_mysql_client_plugin *plugin,
|
||||
const char *option, void *value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+144
@@ -0,0 +1,144 @@
|
||||
#ifndef ERRMSG_INCLUDED
|
||||
#define ERRMSG_INCLUDED
|
||||
|
||||
/* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2.0,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is also distributed with certain software (including
|
||||
but not limited to OpenSSL) that is licensed under separate terms,
|
||||
as designated in a particular file or component or in included license
|
||||
documentation. The authors of MySQL hereby grant you an additional
|
||||
permission to link the program and your derivative works with the
|
||||
separately licensed software that they have included with MySQL.
|
||||
|
||||
Without limiting anything contained in the foregoing, this file,
|
||||
which is part of C Driver for MySQL (Connector/C), is also subject to the
|
||||
Universal FOSS Exception, version 1.0, a copy of which can be found at
|
||||
http://oss.oracle.com/licenses/universal-foss-exception.
|
||||
|
||||
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
/**
|
||||
@file include/errmsg.h
|
||||
|
||||
Error messages for MySQL clients.
|
||||
These are constant and use the CR_ prefix.
|
||||
<mysqlclient_ername.h> will contain auto-generated mappings
|
||||
containing the symbolic name and the number from this file,
|
||||
and the english error messages in libmysql/errmsg.c.
|
||||
|
||||
Dynamic error messages for the daemon are in share/language/errmsg.sys.
|
||||
The server equivalent to <errmsg.h> is <mysqld_error.h>.
|
||||
The server equivalent to <mysqlclient_ername.h> is <mysqld_ername.h>.
|
||||
|
||||
Note that the auth subsystem also uses codes with a CR_ prefix.
|
||||
*/
|
||||
|
||||
void init_client_errs(void);
|
||||
void finish_client_errs(void);
|
||||
extern const char *client_errors[]; /* Error messages */
|
||||
|
||||
#define CR_MIN_ERROR 2000 /* For easier client code */
|
||||
#define CR_MAX_ERROR 2999
|
||||
#define CLIENT_ERRMAP 2 /* Errormap used by my_error() */
|
||||
|
||||
/* Do not add error numbers before CR_ERROR_FIRST. */
|
||||
/* If necessary to add lower numbers, change CR_ERROR_FIRST accordingly. */
|
||||
#define CR_ERROR_FIRST 2000 /*Copy first error nr.*/
|
||||
#define CR_UNKNOWN_ERROR 2000
|
||||
#define CR_SOCKET_CREATE_ERROR 2001
|
||||
#define CR_CONNECTION_ERROR 2002
|
||||
#define CR_CONN_HOST_ERROR 2003
|
||||
#define CR_IPSOCK_ERROR 2004
|
||||
#define CR_UNKNOWN_HOST 2005
|
||||
#define CR_SERVER_GONE_ERROR 2006
|
||||
#define CR_VERSION_ERROR 2007
|
||||
#define CR_OUT_OF_MEMORY 2008
|
||||
#define CR_WRONG_HOST_INFO 2009
|
||||
#define CR_LOCALHOST_CONNECTION 2010
|
||||
#define CR_TCP_CONNECTION 2011
|
||||
#define CR_SERVER_HANDSHAKE_ERR 2012
|
||||
#define CR_SERVER_LOST 2013
|
||||
#define CR_COMMANDS_OUT_OF_SYNC 2014
|
||||
#define CR_NAMEDPIPE_CONNECTION 2015
|
||||
#define CR_NAMEDPIPEWAIT_ERROR 2016
|
||||
#define CR_NAMEDPIPEOPEN_ERROR 2017
|
||||
#define CR_NAMEDPIPESETSTATE_ERROR 2018
|
||||
#define CR_CANT_READ_CHARSET 2019
|
||||
#define CR_NET_PACKET_TOO_LARGE 2020
|
||||
#define CR_EMBEDDED_CONNECTION 2021
|
||||
#define CR_PROBE_SLAVE_STATUS 2022
|
||||
#define CR_PROBE_SLAVE_HOSTS 2023
|
||||
#define CR_PROBE_SLAVE_CONNECT 2024
|
||||
#define CR_PROBE_MASTER_CONNECT 2025
|
||||
#define CR_SSL_CONNECTION_ERROR 2026
|
||||
#define CR_MALFORMED_PACKET 2027
|
||||
#define CR_WRONG_LICENSE 2028
|
||||
|
||||
/* new 4.1 error codes */
|
||||
#define CR_NULL_POINTER 2029
|
||||
#define CR_NO_PREPARE_STMT 2030
|
||||
#define CR_PARAMS_NOT_BOUND 2031
|
||||
#define CR_DATA_TRUNCATED 2032
|
||||
#define CR_NO_PARAMETERS_EXISTS 2033
|
||||
#define CR_INVALID_PARAMETER_NO 2034
|
||||
#define CR_INVALID_BUFFER_USE 2035
|
||||
#define CR_UNSUPPORTED_PARAM_TYPE 2036
|
||||
|
||||
#define CR_SHARED_MEMORY_CONNECTION 2037
|
||||
#define CR_SHARED_MEMORY_CONNECT_REQUEST_ERROR 2038
|
||||
#define CR_SHARED_MEMORY_CONNECT_ANSWER_ERROR 2039
|
||||
#define CR_SHARED_MEMORY_CONNECT_FILE_MAP_ERROR 2040
|
||||
#define CR_SHARED_MEMORY_CONNECT_MAP_ERROR 2041
|
||||
#define CR_SHARED_MEMORY_FILE_MAP_ERROR 2042
|
||||
#define CR_SHARED_MEMORY_MAP_ERROR 2043
|
||||
#define CR_SHARED_MEMORY_EVENT_ERROR 2044
|
||||
#define CR_SHARED_MEMORY_CONNECT_ABANDONED_ERROR 2045
|
||||
#define CR_SHARED_MEMORY_CONNECT_SET_ERROR 2046
|
||||
#define CR_CONN_UNKNOW_PROTOCOL 2047
|
||||
#define CR_INVALID_CONN_HANDLE 2048
|
||||
#define CR_UNUSED_1 2049
|
||||
#define CR_FETCH_CANCELED 2050
|
||||
#define CR_NO_DATA 2051
|
||||
#define CR_NO_STMT_METADATA 2052
|
||||
#define CR_NO_RESULT_SET 2053
|
||||
#define CR_NOT_IMPLEMENTED 2054
|
||||
#define CR_SERVER_LOST_EXTENDED 2055
|
||||
#define CR_STMT_CLOSED 2056
|
||||
#define CR_NEW_STMT_METADATA 2057
|
||||
#define CR_ALREADY_CONNECTED 2058
|
||||
#define CR_AUTH_PLUGIN_CANNOT_LOAD 2059
|
||||
#define CR_DUPLICATE_CONNECTION_ATTR 2060
|
||||
#define CR_AUTH_PLUGIN_ERR 2061
|
||||
#define CR_INSECURE_API_ERR 2062
|
||||
#define CR_FILE_NAME_TOO_LONG 2063
|
||||
#define CR_SSL_FIPS_MODE_ERR 2064
|
||||
#define CR_DEPRECATED_COMPRESSION_NOT_SUPPORTED 2065
|
||||
#define CR_COMPRESSION_WRONGLY_CONFIGURED 2066
|
||||
#define CR_KERBEROS_USER_NOT_FOUND 2067
|
||||
#define CR_LOAD_DATA_LOCAL_INFILE_REJECTED 2068
|
||||
#define CR_LOAD_DATA_LOCAL_INFILE_REALPATH_FAIL 2069
|
||||
#define CR_DNS_SRV_LOOKUP_FAILED 2070
|
||||
#define CR_MANDATORY_TRACKER_NOT_FOUND 2071
|
||||
#define CR_INVALID_FACTOR_NO 2072
|
||||
#define CR_ERROR_LAST /*Copy last error nr:*/ 2072
|
||||
/* Add error numbers before CR_ERROR_LAST and change it accordingly. */
|
||||
|
||||
/* Visual Studio requires '__inline' for C code */
|
||||
static inline const char *ER_CLIENT(int client_errno) {
|
||||
if (client_errno >= CR_ERROR_FIRST && client_errno <= CR_ERROR_LAST)
|
||||
return client_errors[client_errno - CR_ERROR_FIRST];
|
||||
return client_errors[CR_UNKNOWN_ERROR - CR_ERROR_FIRST];
|
||||
}
|
||||
|
||||
#endif /* ERRMSG_INCLUDED */
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
/* Copyright (c) 2014, 2021, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2.0,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is also distributed with certain software (including
|
||||
but not limited to OpenSSL) that is licensed under separate terms,
|
||||
as designated in a particular file or component or in included license
|
||||
documentation. The authors of MySQL hereby grant you an additional
|
||||
permission to link the program and your derivative works with the
|
||||
separately licensed software that they have included with MySQL.
|
||||
|
||||
Without limiting anything contained in the foregoing, this file,
|
||||
which is part of C Driver for MySQL (Connector/C), is also subject to the
|
||||
Universal FOSS Exception, version 1.0, a copy of which can be found at
|
||||
http://oss.oracle.com/licenses/universal-foss-exception.
|
||||
|
||||
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
/**
|
||||
@file field_types.h
|
||||
|
||||
@brief This file contains the field type.
|
||||
|
||||
|
||||
@note This file can be imported both from C and C++ code, so the
|
||||
definitions have to be constructed to support this.
|
||||
*/
|
||||
|
||||
#ifndef FIELD_TYPES_INCLUDED
|
||||
#define FIELD_TYPES_INCLUDED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/*
|
||||
* Constants exported from this package.
|
||||
*/
|
||||
|
||||
/**
|
||||
Column types for MySQL
|
||||
*/
|
||||
enum enum_field_types
|
||||
#if defined(__cplusplus) && __cplusplus > 201103L
|
||||
// N2764: Forward enum declarations, added in C++11
|
||||
: int
|
||||
#endif /* __cplusplus */
|
||||
{ MYSQL_TYPE_DECIMAL,
|
||||
MYSQL_TYPE_TINY,
|
||||
MYSQL_TYPE_SHORT,
|
||||
MYSQL_TYPE_LONG,
|
||||
MYSQL_TYPE_FLOAT,
|
||||
MYSQL_TYPE_DOUBLE,
|
||||
MYSQL_TYPE_NULL,
|
||||
MYSQL_TYPE_TIMESTAMP,
|
||||
MYSQL_TYPE_LONGLONG,
|
||||
MYSQL_TYPE_INT24,
|
||||
MYSQL_TYPE_DATE,
|
||||
MYSQL_TYPE_TIME,
|
||||
MYSQL_TYPE_DATETIME,
|
||||
MYSQL_TYPE_YEAR,
|
||||
MYSQL_TYPE_NEWDATE, /**< Internal to MySQL. Not used in protocol */
|
||||
MYSQL_TYPE_VARCHAR,
|
||||
MYSQL_TYPE_BIT,
|
||||
MYSQL_TYPE_TIMESTAMP2,
|
||||
MYSQL_TYPE_DATETIME2, /**< Internal to MySQL. Not used in protocol */
|
||||
MYSQL_TYPE_TIME2, /**< Internal to MySQL. Not used in protocol */
|
||||
MYSQL_TYPE_TYPED_ARRAY, /**< Used for replication only */
|
||||
MYSQL_TYPE_INVALID = 243,
|
||||
MYSQL_TYPE_BOOL = 244, /**< Currently just a placeholder */
|
||||
MYSQL_TYPE_JSON = 245,
|
||||
MYSQL_TYPE_NEWDECIMAL = 246,
|
||||
MYSQL_TYPE_ENUM = 247,
|
||||
MYSQL_TYPE_SET = 248,
|
||||
MYSQL_TYPE_TINY_BLOB = 249,
|
||||
MYSQL_TYPE_MEDIUM_BLOB = 250,
|
||||
MYSQL_TYPE_LONG_BLOB = 251,
|
||||
MYSQL_TYPE_BLOB = 252,
|
||||
MYSQL_TYPE_VAR_STRING = 253,
|
||||
MYSQL_TYPE_STRING = 254,
|
||||
MYSQL_TYPE_GEOMETRY = 255 };
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#else
|
||||
typedef enum enum_field_types enum_field_types;
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* FIELD_TYPES_INCLUDED */
|
||||
Vendored
+103
@@ -0,0 +1,103 @@
|
||||
/* Copyright (c) 2015, 2021, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2.0,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is also distributed with certain software (including
|
||||
but not limited to OpenSSL) that is licensed under separate terms,
|
||||
as designated in a particular file or component or in included license
|
||||
documentation. The authors of MySQL hereby grant you an additional
|
||||
permission to link the program and your derivative works with the
|
||||
separately licensed software that they have included with MySQL.
|
||||
|
||||
Without limiting anything contained in the foregoing, this file,
|
||||
which is part of C Driver for MySQL (Connector/C), is also subject to the
|
||||
Universal FOSS Exception, version 1.0, a copy of which can be found at
|
||||
http://oss.oracle.com/licenses/universal-foss-exception.
|
||||
|
||||
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#ifndef _mysql_command_h
|
||||
#define _mysql_command_h
|
||||
|
||||
/**
|
||||
@file include/my_command.h
|
||||
*/
|
||||
|
||||
/**
|
||||
@enum enum_server_command
|
||||
|
||||
@brief A list of all MySQL protocol commands.
|
||||
|
||||
These are the top level commands the server can receive
|
||||
while it listens for a new command in ::dispatch_command
|
||||
|
||||
@par Warning
|
||||
Add new commands to the end of this list, otherwise old
|
||||
servers won't be able to handle them as 'unsupported'.
|
||||
*/
|
||||
enum enum_server_command {
|
||||
/**
|
||||
Currently refused by the server. See ::dispatch_command.
|
||||
Also used internally to mark the start of a session.
|
||||
*/
|
||||
COM_SLEEP,
|
||||
COM_QUIT, /**< See @ref page_protocol_com_quit */
|
||||
COM_INIT_DB, /**< See @ref page_protocol_com_init_db */
|
||||
COM_QUERY, /**< See @ref page_protocol_com_query */
|
||||
COM_FIELD_LIST, /**< Deprecated. See @ref page_protocol_com_field_list */
|
||||
COM_CREATE_DB, /**< Currently refused by the server. See ::dispatch_command */
|
||||
COM_DROP_DB, /**< Currently refused by the server. See ::dispatch_command */
|
||||
COM_REFRESH, /**< Deprecated. See @ref page_protocol_com_refresh */
|
||||
COM_DEPRECATED_1, /**< Deprecated, used to be COM_SHUTDOWN */
|
||||
COM_STATISTICS, /**< See @ref page_protocol_com_statistics */
|
||||
COM_PROCESS_INFO, /**< Deprecated. See @ref page_protocol_com_process_info */
|
||||
COM_CONNECT, /**< Currently refused by the server. */
|
||||
COM_PROCESS_KILL, /**< Deprecated. See @ref page_protocol_com_process_kill */
|
||||
COM_DEBUG, /**< See @ref page_protocol_com_debug */
|
||||
COM_PING, /**< See @ref page_protocol_com_ping */
|
||||
COM_TIME, /**< Currently refused by the server. */
|
||||
COM_DELAYED_INSERT, /**< Functionality removed. */
|
||||
COM_CHANGE_USER, /**< See @ref page_protocol_com_change_user */
|
||||
COM_BINLOG_DUMP, /**< See @ref page_protocol_com_binlog_dump */
|
||||
COM_TABLE_DUMP,
|
||||
COM_CONNECT_OUT,
|
||||
COM_REGISTER_SLAVE,
|
||||
COM_STMT_PREPARE, /**< See @ref page_protocol_com_stmt_prepare */
|
||||
COM_STMT_EXECUTE, /**< See @ref page_protocol_com_stmt_execute */
|
||||
/** See @ref page_protocol_com_stmt_send_long_data */
|
||||
COM_STMT_SEND_LONG_DATA,
|
||||
COM_STMT_CLOSE, /**< See @ref page_protocol_com_stmt_close */
|
||||
COM_STMT_RESET, /**< See @ref page_protocol_com_stmt_reset */
|
||||
COM_SET_OPTION, /**< See @ref page_protocol_com_set_option */
|
||||
COM_STMT_FETCH, /**< See @ref page_protocol_com_stmt_fetch */
|
||||
/**
|
||||
Currently refused by the server. See ::dispatch_command.
|
||||
Also used internally to mark the session as a "daemon",
|
||||
i.e. non-client THD. Currently the scheduler and the GTID
|
||||
code does use this state.
|
||||
These threads won't be killed by `KILL`
|
||||
|
||||
@sa Event_scheduler::start, ::init_thd, ::kill_one_thread,
|
||||
::Find_thd_with_id
|
||||
*/
|
||||
COM_DAEMON,
|
||||
COM_BINLOG_DUMP_GTID,
|
||||
COM_RESET_CONNECTION, /**< See @ref page_protocol_com_reset_connection */
|
||||
COM_CLONE,
|
||||
COM_SUBSCRIBE_GROUP_REPLICATION_STREAM,
|
||||
/* don't forget to update const char *command_name[] in sql_parse.cc */
|
||||
|
||||
/* Must be last */
|
||||
COM_END /**< Not a real command. Refused. */
|
||||
};
|
||||
|
||||
#endif /* _mysql_command_h */
|
||||
Vendored
+114
@@ -0,0 +1,114 @@
|
||||
/* Copyright (c) 2019, 2021, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2.0,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is also distributed with certain software (including
|
||||
but not limited to OpenSSL) that is licensed under separate terms,
|
||||
as designated in a particular file or component or in included license
|
||||
documentation. The authors of MySQL hereby grant you an additional
|
||||
permission to link the program and your derivative works with the
|
||||
separately licensed software that they have included with MySQL.
|
||||
|
||||
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#ifndef MY_COMPRESS_INCLUDED
|
||||
#define MY_COMPRESS_INCLUDED
|
||||
|
||||
/* List of valid values for compression_algorithm */
|
||||
enum enum_compression_algorithm {
|
||||
MYSQL_UNCOMPRESSED = 1,
|
||||
MYSQL_ZLIB,
|
||||
MYSQL_ZSTD,
|
||||
MYSQL_INVALID
|
||||
};
|
||||
|
||||
/**
|
||||
Compress context information. relating to zlib compression.
|
||||
*/
|
||||
|
||||
typedef struct mysql_zlib_compress_context {
|
||||
/**
|
||||
Compression level to use in zlib compression.
|
||||
*/
|
||||
unsigned int compression_level;
|
||||
} mysql_zlib_compress_context;
|
||||
|
||||
typedef struct ZSTD_CCtx_s ZSTD_CCtx;
|
||||
typedef struct ZSTD_DCtx_s ZSTD_DCtx;
|
||||
|
||||
/**
|
||||
Compress context information relating to zstd compression.
|
||||
*/
|
||||
|
||||
typedef struct mysql_zstd_compress_context {
|
||||
/**
|
||||
Pointer to compressor context.
|
||||
*/
|
||||
ZSTD_CCtx *cctx;
|
||||
/**
|
||||
Pointer to decompressor context.
|
||||
*/
|
||||
ZSTD_DCtx *dctx;
|
||||
/**
|
||||
Compression level to use in zstd compression.
|
||||
*/
|
||||
unsigned int compression_level;
|
||||
} mysql_zstd_compress_context;
|
||||
|
||||
/**
|
||||
Compression context information.
|
||||
It encapsulate the context information based on compression method and
|
||||
presents a generic struct.
|
||||
*/
|
||||
|
||||
typedef struct mysql_compress_context {
|
||||
enum enum_compression_algorithm algorithm; ///< Compression algorithm name.
|
||||
union {
|
||||
mysql_zlib_compress_context zlib_ctx; ///< Context information of zlib.
|
||||
mysql_zstd_compress_context zstd_ctx; ///< Context information of zstd.
|
||||
} u;
|
||||
} mysql_compress_context;
|
||||
|
||||
/**
|
||||
Get default compression level corresponding to a given compression method.
|
||||
|
||||
@param algorithm Compression Method. Possible values are zlib or zstd.
|
||||
|
||||
@return an unsigned int representing default compression level.
|
||||
6 is the default compression level for zlib and 3 is the
|
||||
default compression level for zstd.
|
||||
*/
|
||||
|
||||
unsigned int mysql_default_compression_level(
|
||||
enum enum_compression_algorithm algorithm);
|
||||
|
||||
/**
|
||||
Initialize a compress context object to be associated with a NET object.
|
||||
|
||||
@param cmp_ctx Pointer to compression context.
|
||||
@param algorithm Compression algorithm.
|
||||
@param compression_level Compression level corresponding to the compression
|
||||
algorithm.
|
||||
*/
|
||||
|
||||
void mysql_compress_context_init(mysql_compress_context *cmp_ctx,
|
||||
enum enum_compression_algorithm algorithm,
|
||||
unsigned int compression_level);
|
||||
/**
|
||||
Deinitialize the compression context allocated.
|
||||
|
||||
@param mysql_compress_ctx Pointer to Compression context.
|
||||
*/
|
||||
|
||||
void mysql_compress_context_deinit(mysql_compress_context *mysql_compress_ctx);
|
||||
|
||||
#endif // MY_COMPRESS_INCLUDED
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
/* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2.0,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is also distributed with certain software (including
|
||||
but not limited to OpenSSL) that is licensed under separate terms,
|
||||
as designated in a particular file or component or in included license
|
||||
documentation. The authors of MySQL hereby grant you an additional
|
||||
permission to link the program and your derivative works with the
|
||||
separately licensed software that they have included with MySQL.
|
||||
|
||||
Without limiting anything contained in the foregoing, this file,
|
||||
which is part of C Driver for MySQL (Connector/C), is also subject to the
|
||||
Universal FOSS Exception, version 1.0, a copy of which can be found at
|
||||
http://oss.oracle.com/licenses/universal-foss-exception.
|
||||
|
||||
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#ifndef _list_h_
|
||||
#define _list_h_
|
||||
|
||||
/**
|
||||
@file include/my_list.h
|
||||
*/
|
||||
|
||||
typedef struct LIST {
|
||||
#if defined(__cplusplus) && __cplusplus >= 201103L
|
||||
struct LIST *prev{nullptr}, *next{nullptr};
|
||||
void *data{nullptr};
|
||||
#else
|
||||
struct LIST *prev, *next;
|
||||
void *data;
|
||||
#endif
|
||||
} LIST;
|
||||
|
||||
typedef int (*list_walk_action)(void *, void *);
|
||||
|
||||
extern LIST *list_add(LIST *root, LIST *element);
|
||||
extern LIST *list_delete(LIST *root, LIST *element);
|
||||
extern LIST *list_cons(void *data, LIST *root);
|
||||
extern LIST *list_reverse(LIST *root);
|
||||
extern void list_free(LIST *root, unsigned int free_data);
|
||||
extern unsigned int list_length(LIST *);
|
||||
extern int list_walk(LIST *, list_walk_action action, unsigned char *argument);
|
||||
|
||||
#define list_rest(a) ((a)->next)
|
||||
|
||||
#endif
|
||||
Vendored
+803
@@ -0,0 +1,803 @@
|
||||
/* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2.0,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is also distributed with certain software (including
|
||||
but not limited to OpenSSL) that is licensed under separate terms,
|
||||
as designated in a particular file or component or in included license
|
||||
documentation. The authors of MySQL hereby grant you an additional
|
||||
permission to link the program and your derivative works with the
|
||||
separately licensed software that they have included with MySQL.
|
||||
|
||||
Without limiting anything contained in the foregoing, this file,
|
||||
which is part of C Driver for MySQL (Connector/C), is also subject to the
|
||||
Universal FOSS Exception, version 1.0, a copy of which can be found at
|
||||
http://oss.oracle.com/licenses/universal-foss-exception.
|
||||
|
||||
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
/**
|
||||
@file include/mysql.h
|
||||
This file defines the client API to MySQL and also the ABI of the
|
||||
dynamically linked libmysqlclient.
|
||||
|
||||
The ABI should never be changed in a released product of MySQL,
|
||||
thus you need to take great care when changing the file. In case
|
||||
the file is changed so the ABI is broken, you must also update
|
||||
the SHARED_LIB_MAJOR_VERSION in cmake/mysql_version.cmake
|
||||
*/
|
||||
|
||||
#ifndef _mysql_h
|
||||
#define _mysql_h
|
||||
|
||||
#ifndef MYSQL_ABI_CHECK
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
// Legacy definition for the benefit of old code. Use uint64_t in new code.
|
||||
// If you get warnings from printf, use the PRIu64 macro, or, if you need
|
||||
// compatibility with older versions of the client library, cast
|
||||
// before printing.
|
||||
typedef uint64_t my_ulonglong;
|
||||
|
||||
#ifndef my_socket_defined
|
||||
#define my_socket_defined
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#ifdef WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#define my_socket SOCKET
|
||||
#else
|
||||
typedef int my_socket;
|
||||
#endif /* _WIN32 */
|
||||
#endif /* my_socket_defined */
|
||||
|
||||
// Small extra definition to avoid pulling in my_compiler.h in client code.
|
||||
// IWYU pragma: no_include "my_compiler.h"
|
||||
#ifndef MY_COMPILER_INCLUDED
|
||||
#if !defined(_WIN32)
|
||||
#define STDCALL
|
||||
#else
|
||||
#define STDCALL __stdcall
|
||||
#endif
|
||||
#endif /* MY_COMPILER_INCLUDED */
|
||||
|
||||
#include "field_types.h"
|
||||
#include "my_list.h"
|
||||
#include "mysql_com.h"
|
||||
|
||||
/* Include declarations of plug-in API */
|
||||
#include "mysql/client_plugin.h" // IWYU pragma: keep
|
||||
|
||||
/*
|
||||
The client should be able to know which version it is compiled against,
|
||||
even if mysql.h doesn't use this information directly.
|
||||
*/
|
||||
#include "mysql_version.h" // IWYU pragma: keep
|
||||
|
||||
// MYSQL_TIME is part of our public API.
|
||||
#include "mysql_time.h" // IWYU pragma: keep
|
||||
|
||||
// The error messages are part of our public API.
|
||||
#include "errmsg.h" // IWYU pragma: keep
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern unsigned int mysql_port;
|
||||
extern char *mysql_unix_port;
|
||||
|
||||
#define CLIENT_NET_RETRY_COUNT 1 /* Retry count */
|
||||
#define CLIENT_NET_READ_TIMEOUT 365 * 24 * 3600 /* Timeout on read */
|
||||
#define CLIENT_NET_WRITE_TIMEOUT 365 * 24 * 3600 /* Timeout on write */
|
||||
|
||||
#define IS_PRI_KEY(n) ((n)&PRI_KEY_FLAG)
|
||||
#define IS_NOT_NULL(n) ((n)&NOT_NULL_FLAG)
|
||||
#define IS_BLOB(n) ((n)&BLOB_FLAG)
|
||||
/**
|
||||
Returns true if the value is a number which does not need quotes for
|
||||
the sql_lex.cc parser to parse correctly.
|
||||
*/
|
||||
#define IS_NUM(t) \
|
||||
(((t) <= MYSQL_TYPE_INT24 && (t) != MYSQL_TYPE_TIMESTAMP) || \
|
||||
(t) == MYSQL_TYPE_YEAR || (t) == MYSQL_TYPE_NEWDECIMAL)
|
||||
#define IS_LONGDATA(t) ((t) >= MYSQL_TYPE_TINY_BLOB && (t) <= MYSQL_TYPE_STRING)
|
||||
|
||||
typedef struct MYSQL_FIELD {
|
||||
char *name; /* Name of column */
|
||||
char *org_name; /* Original column name, if an alias */
|
||||
char *table; /* Table of column if column was a field */
|
||||
char *org_table; /* Org table name, if table was an alias */
|
||||
char *db; /* Database for table */
|
||||
char *catalog; /* Catalog for table */
|
||||
char *def; /* Default value (set by mysql_list_fields) */
|
||||
unsigned long length; /* Width of column (create length) */
|
||||
unsigned long max_length; /* Max width for selected set */
|
||||
unsigned int name_length;
|
||||
unsigned int org_name_length;
|
||||
unsigned int table_length;
|
||||
unsigned int org_table_length;
|
||||
unsigned int db_length;
|
||||
unsigned int catalog_length;
|
||||
unsigned int def_length;
|
||||
unsigned int flags; /* Div flags */
|
||||
unsigned int decimals; /* Number of decimals in field */
|
||||
unsigned int charsetnr; /* Character set */
|
||||
enum enum_field_types type; /* Type of field. See mysql_com.h for types */
|
||||
void *extension;
|
||||
} MYSQL_FIELD;
|
||||
|
||||
typedef char **MYSQL_ROW; /* return data as array of strings */
|
||||
typedef unsigned int MYSQL_FIELD_OFFSET; /* offset to current field */
|
||||
|
||||
#define MYSQL_COUNT_ERROR (~(uint64_t)0)
|
||||
|
||||
/* backward compatibility define - to be removed eventually */
|
||||
#define ER_WARN_DATA_TRUNCATED WARN_DATA_TRUNCATED
|
||||
|
||||
typedef struct MYSQL_ROWS {
|
||||
struct MYSQL_ROWS *next; /* list of rows */
|
||||
MYSQL_ROW data;
|
||||
unsigned long length;
|
||||
} MYSQL_ROWS;
|
||||
|
||||
typedef MYSQL_ROWS *MYSQL_ROW_OFFSET; /* offset to current row */
|
||||
|
||||
struct MEM_ROOT;
|
||||
|
||||
typedef struct MYSQL_DATA {
|
||||
MYSQL_ROWS *data;
|
||||
struct MEM_ROOT *alloc;
|
||||
uint64_t rows;
|
||||
unsigned int fields;
|
||||
} MYSQL_DATA;
|
||||
|
||||
enum mysql_option {
|
||||
MYSQL_OPT_CONNECT_TIMEOUT,
|
||||
MYSQL_OPT_COMPRESS,
|
||||
MYSQL_OPT_NAMED_PIPE,
|
||||
MYSQL_INIT_COMMAND,
|
||||
MYSQL_READ_DEFAULT_FILE,
|
||||
MYSQL_READ_DEFAULT_GROUP,
|
||||
MYSQL_SET_CHARSET_DIR,
|
||||
MYSQL_SET_CHARSET_NAME,
|
||||
MYSQL_OPT_LOCAL_INFILE,
|
||||
MYSQL_OPT_PROTOCOL,
|
||||
MYSQL_SHARED_MEMORY_BASE_NAME,
|
||||
MYSQL_OPT_READ_TIMEOUT,
|
||||
MYSQL_OPT_WRITE_TIMEOUT,
|
||||
MYSQL_OPT_USE_RESULT,
|
||||
MYSQL_REPORT_DATA_TRUNCATION,
|
||||
MYSQL_OPT_RECONNECT,
|
||||
MYSQL_PLUGIN_DIR,
|
||||
MYSQL_DEFAULT_AUTH,
|
||||
MYSQL_OPT_BIND,
|
||||
MYSQL_OPT_SSL_KEY,
|
||||
MYSQL_OPT_SSL_CERT,
|
||||
MYSQL_OPT_SSL_CA,
|
||||
MYSQL_OPT_SSL_CAPATH,
|
||||
MYSQL_OPT_SSL_CIPHER,
|
||||
MYSQL_OPT_SSL_CRL,
|
||||
MYSQL_OPT_SSL_CRLPATH,
|
||||
MYSQL_OPT_CONNECT_ATTR_RESET,
|
||||
MYSQL_OPT_CONNECT_ATTR_ADD,
|
||||
MYSQL_OPT_CONNECT_ATTR_DELETE,
|
||||
MYSQL_SERVER_PUBLIC_KEY,
|
||||
MYSQL_ENABLE_CLEARTEXT_PLUGIN,
|
||||
MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS,
|
||||
MYSQL_OPT_MAX_ALLOWED_PACKET,
|
||||
MYSQL_OPT_NET_BUFFER_LENGTH,
|
||||
MYSQL_OPT_TLS_VERSION,
|
||||
MYSQL_OPT_SSL_MODE,
|
||||
MYSQL_OPT_GET_SERVER_PUBLIC_KEY,
|
||||
MYSQL_OPT_RETRY_COUNT,
|
||||
MYSQL_OPT_OPTIONAL_RESULTSET_METADATA,
|
||||
MYSQL_OPT_SSL_FIPS_MODE,
|
||||
MYSQL_OPT_TLS_CIPHERSUITES,
|
||||
MYSQL_OPT_COMPRESSION_ALGORITHMS,
|
||||
MYSQL_OPT_ZSTD_COMPRESSION_LEVEL,
|
||||
MYSQL_OPT_LOAD_DATA_LOCAL_DIR,
|
||||
MYSQL_OPT_USER_PASSWORD,
|
||||
};
|
||||
|
||||
/**
|
||||
@todo remove the "extension", move st_mysql_options completely
|
||||
out of mysql.h
|
||||
*/
|
||||
struct st_mysql_options_extention;
|
||||
|
||||
struct st_mysql_options {
|
||||
unsigned int connect_timeout, read_timeout, write_timeout;
|
||||
unsigned int port, protocol;
|
||||
unsigned long client_flag;
|
||||
char *host, *user, *password, *unix_socket, *db;
|
||||
struct Init_commands_array *init_commands;
|
||||
char *my_cnf_file, *my_cnf_group, *charset_dir, *charset_name;
|
||||
char *ssl_key; /* PEM key file */
|
||||
char *ssl_cert; /* PEM cert file */
|
||||
char *ssl_ca; /* PEM CA file */
|
||||
char *ssl_capath; /* PEM directory of CA-s? */
|
||||
char *ssl_cipher; /* cipher to use */
|
||||
char *shared_memory_base_name;
|
||||
unsigned long max_allowed_packet;
|
||||
bool compress, named_pipe;
|
||||
/**
|
||||
The local address to bind when connecting to remote server.
|
||||
*/
|
||||
char *bind_address;
|
||||
/* 0 - never report, 1 - always report (default) */
|
||||
bool report_data_truncation;
|
||||
|
||||
/* function pointers for local infile support */
|
||||
int (*local_infile_init)(void **, const char *, void *);
|
||||
int (*local_infile_read)(void *, char *, unsigned int);
|
||||
void (*local_infile_end)(void *);
|
||||
int (*local_infile_error)(void *, char *, unsigned int);
|
||||
void *local_infile_userdata;
|
||||
struct st_mysql_options_extention *extension;
|
||||
};
|
||||
|
||||
enum mysql_status {
|
||||
MYSQL_STATUS_READY,
|
||||
MYSQL_STATUS_GET_RESULT,
|
||||
MYSQL_STATUS_USE_RESULT,
|
||||
MYSQL_STATUS_STATEMENT_GET_RESULT
|
||||
};
|
||||
|
||||
enum mysql_protocol_type {
|
||||
MYSQL_PROTOCOL_DEFAULT,
|
||||
MYSQL_PROTOCOL_TCP,
|
||||
MYSQL_PROTOCOL_SOCKET,
|
||||
MYSQL_PROTOCOL_PIPE,
|
||||
MYSQL_PROTOCOL_MEMORY
|
||||
};
|
||||
|
||||
enum mysql_ssl_mode {
|
||||
SSL_MODE_DISABLED = 1,
|
||||
SSL_MODE_PREFERRED,
|
||||
SSL_MODE_REQUIRED,
|
||||
SSL_MODE_VERIFY_CA,
|
||||
SSL_MODE_VERIFY_IDENTITY
|
||||
};
|
||||
|
||||
enum mysql_ssl_fips_mode {
|
||||
SSL_FIPS_MODE_OFF = 0,
|
||||
SSL_FIPS_MODE_ON = 1,
|
||||
SSL_FIPS_MODE_STRICT
|
||||
};
|
||||
|
||||
typedef struct character_set {
|
||||
unsigned int number; /* character set number */
|
||||
unsigned int state; /* character set state */
|
||||
const char *csname; /* collation name */
|
||||
const char *name; /* character set name */
|
||||
const char *comment; /* comment */
|
||||
const char *dir; /* character set directory */
|
||||
unsigned int mbminlen; /* min. length for multibyte strings */
|
||||
unsigned int mbmaxlen; /* max. length for multibyte strings */
|
||||
} MY_CHARSET_INFO;
|
||||
|
||||
struct MYSQL_METHODS;
|
||||
struct MYSQL_STMT;
|
||||
|
||||
typedef struct MYSQL {
|
||||
NET net; /* Communication parameters */
|
||||
unsigned char *connector_fd; /* ConnectorFd for SSL */
|
||||
char *host, *user, *passwd, *unix_socket, *server_version, *host_info;
|
||||
char *info, *db;
|
||||
struct CHARSET_INFO *charset;
|
||||
MYSQL_FIELD *fields;
|
||||
struct MEM_ROOT *field_alloc;
|
||||
uint64_t affected_rows;
|
||||
uint64_t insert_id; /* id if insert on table with NEXTNR */
|
||||
uint64_t extra_info; /* Not used */
|
||||
unsigned long thread_id; /* Id for connection in server */
|
||||
unsigned long packet_length;
|
||||
unsigned int port;
|
||||
unsigned long client_flag, server_capabilities;
|
||||
unsigned int protocol_version;
|
||||
unsigned int field_count;
|
||||
unsigned int server_status;
|
||||
unsigned int server_language;
|
||||
unsigned int warning_count;
|
||||
struct st_mysql_options options;
|
||||
enum mysql_status status;
|
||||
enum enum_resultset_metadata resultset_metadata;
|
||||
bool free_me; /* If free in mysql_close */
|
||||
bool reconnect; /* set to 1 if automatic reconnect */
|
||||
|
||||
/* session-wide random string */
|
||||
char scramble[SCRAMBLE_LENGTH + 1];
|
||||
|
||||
LIST *stmts; /* list of all statements */
|
||||
const struct MYSQL_METHODS *methods;
|
||||
void *thd;
|
||||
/*
|
||||
Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag
|
||||
from mysql_stmt_close if close had to cancel result set of this object.
|
||||
*/
|
||||
bool *unbuffered_fetch_owner;
|
||||
void *extension;
|
||||
} MYSQL;
|
||||
|
||||
typedef struct MYSQL_RES {
|
||||
uint64_t row_count;
|
||||
MYSQL_FIELD *fields;
|
||||
struct MYSQL_DATA *data;
|
||||
MYSQL_ROWS *data_cursor;
|
||||
unsigned long *lengths; /* column lengths of current row */
|
||||
MYSQL *handle; /* for unbuffered reads */
|
||||
const struct MYSQL_METHODS *methods;
|
||||
MYSQL_ROW row; /* If unbuffered read */
|
||||
MYSQL_ROW current_row; /* buffer to current row */
|
||||
struct MEM_ROOT *field_alloc;
|
||||
unsigned int field_count, current_field;
|
||||
bool eof; /* Used by mysql_fetch_row */
|
||||
/* mysql_stmt_close() had to cancel this result */
|
||||
bool unbuffered_fetch_cancelled;
|
||||
enum enum_resultset_metadata metadata;
|
||||
void *extension;
|
||||
} MYSQL_RES;
|
||||
|
||||
/**
|
||||
Flag to indicate that COM_BINLOG_DUMP_GTID should
|
||||
be used rather than COM_BINLOG_DUMP in the @sa mysql_binlog_open().
|
||||
*/
|
||||
#define MYSQL_RPL_GTID (1 << 16)
|
||||
/**
|
||||
Skip HEARBEAT events in the @sa mysql_binlog_fetch().
|
||||
*/
|
||||
#define MYSQL_RPL_SKIP_HEARTBEAT (1 << 17)
|
||||
|
||||
/**
|
||||
Struct for information about a replication stream.
|
||||
|
||||
@sa mysql_binlog_open()
|
||||
@sa mysql_binlog_fetch()
|
||||
@sa mysql_binlog_close()
|
||||
*/
|
||||
typedef struct MYSQL_RPL {
|
||||
size_t file_name_length; /** Length of the 'file_name' or 0 */
|
||||
const char *file_name; /** Filename of the binary log to read */
|
||||
uint64_t start_position; /** Position in the binary log to */
|
||||
/* start reading from */
|
||||
unsigned int server_id; /** Server ID to use when identifying */
|
||||
/* with the master */
|
||||
unsigned int flags; /** Flags, e.g. MYSQL_RPL_GTID */
|
||||
|
||||
/** Size of gtid set data */
|
||||
size_t gtid_set_encoded_size;
|
||||
/** Callback function which is called */
|
||||
/* from @sa mysql_binlog_open() to */
|
||||
/* fill command packet gtid set */
|
||||
void (*fix_gtid_set)(struct MYSQL_RPL *rpl, unsigned char *packet_gtid_set);
|
||||
void *gtid_set_arg; /** GTID set data or an argument for */
|
||||
/* fix_gtid_set() callback function */
|
||||
|
||||
unsigned long size; /** Size of the packet returned by */
|
||||
/* mysql_binlog_fetch() */
|
||||
const unsigned char *buffer; /** Pointer to returned data */
|
||||
} MYSQL_RPL;
|
||||
|
||||
/*
|
||||
Set up and bring down the server; to ensure that applications will
|
||||
work when linked against either the standard client library or the
|
||||
embedded server library, these functions should be called.
|
||||
*/
|
||||
int STDCALL mysql_server_init(int argc, char **argv, char **groups);
|
||||
void STDCALL mysql_server_end(void);
|
||||
|
||||
/*
|
||||
mysql_server_init/end need to be called when using libmysqld or
|
||||
libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so
|
||||
you don't need to call it explicitely; but you need to call
|
||||
mysql_server_end() to free memory). The names are a bit misleading
|
||||
(mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general
|
||||
names which suit well whether you're using libmysqld or libmysqlclient. We
|
||||
intend to promote these aliases over the mysql_server* ones.
|
||||
*/
|
||||
#define mysql_library_init mysql_server_init
|
||||
#define mysql_library_end mysql_server_end
|
||||
|
||||
/*
|
||||
Set up and bring down a thread; these function should be called
|
||||
for each thread in an application which opens at least one MySQL
|
||||
connection. All uses of the connection(s) should be between these
|
||||
function calls.
|
||||
*/
|
||||
bool STDCALL mysql_thread_init(void);
|
||||
void STDCALL mysql_thread_end(void);
|
||||
|
||||
/*
|
||||
Functions to get information from the MYSQL and MYSQL_RES structures
|
||||
Should definitely be used if one uses shared libraries.
|
||||
*/
|
||||
|
||||
uint64_t STDCALL mysql_num_rows(MYSQL_RES *res);
|
||||
unsigned int STDCALL mysql_num_fields(MYSQL_RES *res);
|
||||
bool STDCALL mysql_eof(MYSQL_RES *res);
|
||||
MYSQL_FIELD *STDCALL mysql_fetch_field_direct(MYSQL_RES *res,
|
||||
unsigned int fieldnr);
|
||||
MYSQL_FIELD *STDCALL mysql_fetch_fields(MYSQL_RES *res);
|
||||
MYSQL_ROW_OFFSET STDCALL mysql_row_tell(MYSQL_RES *res);
|
||||
MYSQL_FIELD_OFFSET STDCALL mysql_field_tell(MYSQL_RES *res);
|
||||
enum enum_resultset_metadata STDCALL mysql_result_metadata(MYSQL_RES *result);
|
||||
|
||||
unsigned int STDCALL mysql_field_count(MYSQL *mysql);
|
||||
uint64_t STDCALL mysql_affected_rows(MYSQL *mysql);
|
||||
uint64_t STDCALL mysql_insert_id(MYSQL *mysql);
|
||||
unsigned int STDCALL mysql_errno(MYSQL *mysql);
|
||||
const char *STDCALL mysql_error(MYSQL *mysql);
|
||||
const char *STDCALL mysql_sqlstate(MYSQL *mysql);
|
||||
unsigned int STDCALL mysql_warning_count(MYSQL *mysql);
|
||||
const char *STDCALL mysql_info(MYSQL *mysql);
|
||||
unsigned long STDCALL mysql_thread_id(MYSQL *mysql);
|
||||
const char *STDCALL mysql_character_set_name(MYSQL *mysql);
|
||||
int STDCALL mysql_set_character_set(MYSQL *mysql, const char *csname);
|
||||
|
||||
MYSQL *STDCALL mysql_init(MYSQL *mysql);
|
||||
bool STDCALL mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert,
|
||||
const char *ca, const char *capath,
|
||||
const char *cipher);
|
||||
const char *STDCALL mysql_get_ssl_cipher(MYSQL *mysql);
|
||||
bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
|
||||
const char *passwd, const char *db);
|
||||
MYSQL *STDCALL mysql_real_connect(MYSQL *mysql, const char *host,
|
||||
const char *user, const char *passwd,
|
||||
const char *db, unsigned int port,
|
||||
const char *unix_socket,
|
||||
unsigned long clientflag);
|
||||
int STDCALL mysql_select_db(MYSQL *mysql, const char *db);
|
||||
int STDCALL mysql_query(MYSQL *mysql, const char *q);
|
||||
int STDCALL mysql_send_query(MYSQL *mysql, const char *q, unsigned long length);
|
||||
int STDCALL mysql_real_query(MYSQL *mysql, const char *q, unsigned long length);
|
||||
MYSQL_RES *STDCALL mysql_store_result(MYSQL *mysql);
|
||||
MYSQL_RES *STDCALL mysql_use_result(MYSQL *mysql);
|
||||
|
||||
enum net_async_status STDCALL mysql_real_connect_nonblocking(
|
||||
MYSQL *mysql, const char *host, const char *user, const char *passwd,
|
||||
const char *db, unsigned int port, const char *unix_socket,
|
||||
unsigned long clientflag);
|
||||
enum net_async_status STDCALL mysql_send_query_nonblocking(
|
||||
MYSQL *mysql, const char *query, unsigned long length);
|
||||
enum net_async_status STDCALL mysql_real_query_nonblocking(
|
||||
MYSQL *mysql, const char *query, unsigned long length);
|
||||
enum net_async_status STDCALL
|
||||
mysql_store_result_nonblocking(MYSQL *mysql, MYSQL_RES **result);
|
||||
enum net_async_status STDCALL mysql_next_result_nonblocking(MYSQL *mysql);
|
||||
enum net_async_status STDCALL mysql_select_db_nonblocking(MYSQL *mysql,
|
||||
const char *db,
|
||||
bool *error);
|
||||
void STDCALL mysql_get_character_set_info(MYSQL *mysql,
|
||||
MY_CHARSET_INFO *charset);
|
||||
|
||||
int STDCALL mysql_session_track_get_first(MYSQL *mysql,
|
||||
enum enum_session_state_type type,
|
||||
const char **data, size_t *length);
|
||||
int STDCALL mysql_session_track_get_next(MYSQL *mysql,
|
||||
enum enum_session_state_type type,
|
||||
const char **data, size_t *length);
|
||||
/* local infile support */
|
||||
|
||||
#define LOCAL_INFILE_ERROR_LEN 512
|
||||
|
||||
void mysql_set_local_infile_handler(
|
||||
MYSQL *mysql, int (*local_infile_init)(void **, const char *, void *),
|
||||
int (*local_infile_read)(void *, char *, unsigned int),
|
||||
void (*local_infile_end)(void *),
|
||||
int (*local_infile_error)(void *, char *, unsigned int), void *);
|
||||
|
||||
void mysql_set_local_infile_default(MYSQL *mysql);
|
||||
int STDCALL mysql_shutdown(MYSQL *mysql,
|
||||
enum mysql_enum_shutdown_level shutdown_level);
|
||||
int STDCALL mysql_dump_debug_info(MYSQL *mysql);
|
||||
int STDCALL mysql_refresh(MYSQL *mysql, unsigned int refresh_options);
|
||||
int STDCALL mysql_kill(MYSQL *mysql, unsigned long pid);
|
||||
int STDCALL mysql_set_server_option(MYSQL *mysql,
|
||||
enum enum_mysql_set_option option);
|
||||
int STDCALL mysql_ping(MYSQL *mysql);
|
||||
const char *STDCALL mysql_stat(MYSQL *mysql);
|
||||
const char *STDCALL mysql_get_server_info(MYSQL *mysql);
|
||||
const char *STDCALL mysql_get_client_info(void);
|
||||
unsigned long STDCALL mysql_get_client_version(void);
|
||||
const char *STDCALL mysql_get_host_info(MYSQL *mysql);
|
||||
unsigned long STDCALL mysql_get_server_version(MYSQL *mysql);
|
||||
unsigned int STDCALL mysql_get_proto_info(MYSQL *mysql);
|
||||
MYSQL_RES *STDCALL mysql_list_dbs(MYSQL *mysql, const char *wild);
|
||||
MYSQL_RES *STDCALL mysql_list_tables(MYSQL *mysql, const char *wild);
|
||||
MYSQL_RES *STDCALL mysql_list_processes(MYSQL *mysql);
|
||||
int STDCALL mysql_options(MYSQL *mysql, enum mysql_option option,
|
||||
const void *arg);
|
||||
int STDCALL mysql_options4(MYSQL *mysql, enum mysql_option option,
|
||||
const void *arg1, const void *arg2);
|
||||
int STDCALL mysql_get_option(MYSQL *mysql, enum mysql_option option,
|
||||
const void *arg);
|
||||
void STDCALL mysql_free_result(MYSQL_RES *result);
|
||||
enum net_async_status STDCALL mysql_free_result_nonblocking(MYSQL_RES *result);
|
||||
void STDCALL mysql_data_seek(MYSQL_RES *result, uint64_t offset);
|
||||
MYSQL_ROW_OFFSET STDCALL mysql_row_seek(MYSQL_RES *result,
|
||||
MYSQL_ROW_OFFSET offset);
|
||||
MYSQL_FIELD_OFFSET STDCALL mysql_field_seek(MYSQL_RES *result,
|
||||
MYSQL_FIELD_OFFSET offset);
|
||||
MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result);
|
||||
enum net_async_status STDCALL mysql_fetch_row_nonblocking(MYSQL_RES *res,
|
||||
MYSQL_ROW *row);
|
||||
|
||||
unsigned long *STDCALL mysql_fetch_lengths(MYSQL_RES *result);
|
||||
MYSQL_FIELD *STDCALL mysql_fetch_field(MYSQL_RES *result);
|
||||
MYSQL_RES *STDCALL mysql_list_fields(MYSQL *mysql, const char *table,
|
||||
const char *wild);
|
||||
unsigned long STDCALL mysql_escape_string(char *to, const char *from,
|
||||
unsigned long from_length);
|
||||
unsigned long STDCALL mysql_hex_string(char *to, const char *from,
|
||||
unsigned long from_length);
|
||||
unsigned long STDCALL mysql_real_escape_string(MYSQL *mysql, char *to,
|
||||
const char *from,
|
||||
unsigned long length);
|
||||
unsigned long STDCALL mysql_real_escape_string_quote(MYSQL *mysql, char *to,
|
||||
const char *from,
|
||||
unsigned long length,
|
||||
char quote);
|
||||
void STDCALL mysql_debug(const char *debug);
|
||||
void STDCALL myodbc_remove_escape(MYSQL *mysql, char *name);
|
||||
unsigned int STDCALL mysql_thread_safe(void);
|
||||
bool STDCALL mysql_read_query_result(MYSQL *mysql);
|
||||
int STDCALL mysql_reset_connection(MYSQL *mysql);
|
||||
|
||||
int STDCALL mysql_binlog_open(MYSQL *mysql, MYSQL_RPL *rpl);
|
||||
int STDCALL mysql_binlog_fetch(MYSQL *mysql, MYSQL_RPL *rpl);
|
||||
void STDCALL mysql_binlog_close(MYSQL *mysql, MYSQL_RPL *rpl);
|
||||
|
||||
/*
|
||||
The following definitions are added for the enhanced
|
||||
client-server protocol
|
||||
*/
|
||||
|
||||
/* statement state */
|
||||
enum enum_mysql_stmt_state {
|
||||
MYSQL_STMT_INIT_DONE = 1,
|
||||
MYSQL_STMT_PREPARE_DONE,
|
||||
MYSQL_STMT_EXECUTE_DONE,
|
||||
MYSQL_STMT_FETCH_DONE
|
||||
};
|
||||
|
||||
/*
|
||||
This structure is used to define bind information, and
|
||||
internally by the client library.
|
||||
Public members with their descriptions are listed below
|
||||
(conventionally `On input' refers to the binds given to
|
||||
mysql_stmt_bind_param, `On output' refers to the binds given
|
||||
to mysql_stmt_bind_result):
|
||||
|
||||
buffer_type - One of the MYSQL_* types, used to describe
|
||||
the host language type of buffer.
|
||||
On output: if column type is different from
|
||||
buffer_type, column value is automatically converted
|
||||
to buffer_type before it is stored in the buffer.
|
||||
buffer - On input: points to the buffer with input data.
|
||||
On output: points to the buffer capable to store
|
||||
output data.
|
||||
The type of memory pointed by buffer must correspond
|
||||
to buffer_type. See the correspondence table in
|
||||
the comment to mysql_stmt_bind_param.
|
||||
|
||||
The two above members are mandatory for any kind of bind.
|
||||
|
||||
buffer_length - the length of the buffer. You don't have to set
|
||||
it for any fixed length buffer: float, double,
|
||||
int, etc. It must be set however for variable-length
|
||||
types, such as BLOBs or STRINGs.
|
||||
|
||||
length - On input: in case when lengths of input values
|
||||
are different for each execute, you can set this to
|
||||
point at a variable containining value length. This
|
||||
way the value length can be different in each execute.
|
||||
If length is not NULL, buffer_length is not used.
|
||||
Note, length can even point at buffer_length if
|
||||
you keep bind structures around while fetching:
|
||||
this way you can change buffer_length before
|
||||
each execution, everything will work ok.
|
||||
On output: if length is set, mysql_stmt_fetch will
|
||||
write column length into it.
|
||||
|
||||
is_null - On input: points to a boolean variable that should
|
||||
be set to TRUE for NULL values.
|
||||
This member is useful only if your data may be
|
||||
NULL in some but not all cases.
|
||||
If your data is never NULL, is_null should be set to 0.
|
||||
If your data is always NULL, set buffer_type
|
||||
to MYSQL_TYPE_NULL, and is_null will not be used.
|
||||
|
||||
is_unsigned - On input: used to signify that values provided for one
|
||||
of numeric types are unsigned.
|
||||
On output describes signedness of the output buffer.
|
||||
If, taking into account is_unsigned flag, column data
|
||||
is out of range of the output buffer, data for this column
|
||||
is regarded truncated. Note that this has no correspondence
|
||||
to the sign of result set column, if you need to find it out
|
||||
use mysql_stmt_result_metadata.
|
||||
error - where to write a truncation error if it is present.
|
||||
possible error value is:
|
||||
0 no truncation
|
||||
1 value is out of range or buffer is too small
|
||||
|
||||
Please note that MYSQL_BIND also has internals members.
|
||||
*/
|
||||
|
||||
typedef struct MYSQL_BIND {
|
||||
unsigned long *length; /* output length pointer */
|
||||
bool *is_null; /* Pointer to null indicator */
|
||||
void *buffer; /* buffer to get/put data */
|
||||
/* set this if you want to track data truncations happened during fetch */
|
||||
bool *error;
|
||||
unsigned char *row_ptr; /* for the current data position */
|
||||
void (*store_param_func)(NET *net, struct MYSQL_BIND *param);
|
||||
void (*fetch_result)(struct MYSQL_BIND *, MYSQL_FIELD *, unsigned char **row);
|
||||
void (*skip_result)(struct MYSQL_BIND *, MYSQL_FIELD *, unsigned char **row);
|
||||
/* output buffer length, must be set when fetching str/binary */
|
||||
unsigned long buffer_length;
|
||||
unsigned long offset; /* offset position for char/binary fetch */
|
||||
unsigned long length_value; /* Used if length is 0 */
|
||||
unsigned int param_number; /* For null count and error messages */
|
||||
unsigned int pack_length; /* Internal length for packed data */
|
||||
enum enum_field_types buffer_type; /* buffer type */
|
||||
bool error_value; /* used if error is 0 */
|
||||
bool is_unsigned; /* set if integer type is unsigned */
|
||||
bool long_data_used; /* If used with mysql_send_long_data */
|
||||
bool is_null_value; /* Used if is_null is 0 */
|
||||
void *extension;
|
||||
} MYSQL_BIND;
|
||||
|
||||
struct MYSQL_STMT_EXT;
|
||||
|
||||
/* statement handler */
|
||||
typedef struct MYSQL_STMT {
|
||||
struct MEM_ROOT *mem_root; /* root allocations */
|
||||
LIST list; /* list to keep track of all stmts */
|
||||
MYSQL *mysql; /* connection handle */
|
||||
MYSQL_BIND *params; /* input parameters */
|
||||
MYSQL_BIND *bind; /* output parameters */
|
||||
MYSQL_FIELD *fields; /* result set metadata */
|
||||
MYSQL_DATA result; /* cached result set */
|
||||
MYSQL_ROWS *data_cursor; /* current row in cached result */
|
||||
/*
|
||||
mysql_stmt_fetch() calls this function to fetch one row (it's different
|
||||
for buffered, unbuffered and cursor fetch).
|
||||
*/
|
||||
int (*read_row_func)(struct MYSQL_STMT *stmt, unsigned char **row);
|
||||
/* copy of mysql->affected_rows after statement execution */
|
||||
uint64_t affected_rows;
|
||||
uint64_t insert_id; /* copy of mysql->insert_id */
|
||||
unsigned long stmt_id; /* Id for prepared statement */
|
||||
unsigned long flags; /* i.e. type of cursor to open */
|
||||
unsigned long prefetch_rows; /* number of rows per one COM_FETCH */
|
||||
/*
|
||||
Copied from mysql->server_status after execute/fetch to know
|
||||
server-side cursor status for this statement.
|
||||
*/
|
||||
unsigned int server_status;
|
||||
unsigned int last_errno; /* error code */
|
||||
unsigned int param_count; /* input parameter count */
|
||||
unsigned int field_count; /* number of columns in result set */
|
||||
enum enum_mysql_stmt_state state; /* statement state */
|
||||
char last_error[MYSQL_ERRMSG_SIZE]; /* error message */
|
||||
char sqlstate[SQLSTATE_LENGTH + 1];
|
||||
/* Types of input parameters should be sent to server */
|
||||
bool send_types_to_server;
|
||||
bool bind_param_done; /* input buffers were supplied */
|
||||
unsigned char bind_result_done; /* output buffers were supplied */
|
||||
/* mysql_stmt_close() had to cancel this result */
|
||||
bool unbuffered_fetch_cancelled;
|
||||
/*
|
||||
Is set to true if we need to calculate field->max_length for
|
||||
metadata fields when doing mysql_stmt_store_result.
|
||||
*/
|
||||
bool update_max_length;
|
||||
struct MYSQL_STMT_EXT *extension;
|
||||
} MYSQL_STMT;
|
||||
|
||||
enum enum_stmt_attr_type {
|
||||
/*
|
||||
When doing mysql_stmt_store_result calculate max_length attribute
|
||||
of statement metadata. This is to be consistent with the old API,
|
||||
where this was done automatically.
|
||||
In the new API we do that only by request because it slows down
|
||||
mysql_stmt_store_result sufficiently.
|
||||
*/
|
||||
STMT_ATTR_UPDATE_MAX_LENGTH,
|
||||
/*
|
||||
unsigned long with combination of cursor flags (read only, for update,
|
||||
etc)
|
||||
*/
|
||||
STMT_ATTR_CURSOR_TYPE,
|
||||
/*
|
||||
Amount of rows to retrieve from server per one fetch if using cursors.
|
||||
Accepts unsigned long attribute in the range 1 - ulong_max
|
||||
*/
|
||||
STMT_ATTR_PREFETCH_ROWS
|
||||
};
|
||||
|
||||
bool STDCALL mysql_bind_param(MYSQL *mysql, unsigned n_params,
|
||||
MYSQL_BIND *binds, const char **names);
|
||||
|
||||
MYSQL_STMT *STDCALL mysql_stmt_init(MYSQL *mysql);
|
||||
int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query,
|
||||
unsigned long length);
|
||||
int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt);
|
||||
int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt);
|
||||
int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind_arg,
|
||||
unsigned int column, unsigned long offset);
|
||||
int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt);
|
||||
unsigned long STDCALL mysql_stmt_param_count(MYSQL_STMT *stmt);
|
||||
bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt,
|
||||
enum enum_stmt_attr_type attr_type,
|
||||
const void *attr);
|
||||
bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt,
|
||||
enum enum_stmt_attr_type attr_type,
|
||||
void *attr);
|
||||
bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bnd);
|
||||
bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bnd);
|
||||
bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt);
|
||||
bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt);
|
||||
bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt);
|
||||
bool STDCALL mysql_stmt_send_long_data(MYSQL_STMT *stmt,
|
||||
unsigned int param_number,
|
||||
const char *data, unsigned long length);
|
||||
MYSQL_RES *STDCALL mysql_stmt_result_metadata(MYSQL_STMT *stmt);
|
||||
MYSQL_RES *STDCALL mysql_stmt_param_metadata(MYSQL_STMT *stmt);
|
||||
unsigned int STDCALL mysql_stmt_errno(MYSQL_STMT *stmt);
|
||||
const char *STDCALL mysql_stmt_error(MYSQL_STMT *stmt);
|
||||
const char *STDCALL mysql_stmt_sqlstate(MYSQL_STMT *stmt);
|
||||
MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_seek(MYSQL_STMT *stmt,
|
||||
MYSQL_ROW_OFFSET offset);
|
||||
MYSQL_ROW_OFFSET STDCALL mysql_stmt_row_tell(MYSQL_STMT *stmt);
|
||||
void STDCALL mysql_stmt_data_seek(MYSQL_STMT *stmt, uint64_t offset);
|
||||
uint64_t STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt);
|
||||
uint64_t STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt);
|
||||
uint64_t STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt);
|
||||
unsigned int STDCALL mysql_stmt_field_count(MYSQL_STMT *stmt);
|
||||
|
||||
bool STDCALL mysql_commit(MYSQL *mysql);
|
||||
bool STDCALL mysql_rollback(MYSQL *mysql);
|
||||
bool STDCALL mysql_autocommit(MYSQL *mysql, bool auto_mode);
|
||||
bool STDCALL mysql_more_results(MYSQL *mysql);
|
||||
int STDCALL mysql_next_result(MYSQL *mysql);
|
||||
int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt);
|
||||
void STDCALL mysql_close(MYSQL *sock);
|
||||
|
||||
/* Public key reset */
|
||||
void STDCALL mysql_reset_server_public_key(void);
|
||||
|
||||
/* status return codes */
|
||||
#define MYSQL_NO_DATA 100
|
||||
#define MYSQL_DATA_TRUNCATED 101
|
||||
|
||||
#define mysql_reload(mysql) mysql_refresh((mysql), REFRESH_GRANT)
|
||||
|
||||
#define HAVE_MYSQL_REAL_CONNECT
|
||||
|
||||
MYSQL *STDCALL mysql_real_connect_dns_srv(MYSQL *mysql,
|
||||
const char *dns_srv_name,
|
||||
const char *user, const char *passwd,
|
||||
const char *db,
|
||||
unsigned long client_flag);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _mysql_h */
|
||||
Vendored
+1193
File diff suppressed because it is too large
Load Diff
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
/* Copyright (c) 2004, 2021, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2.0,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is also distributed with certain software (including
|
||||
but not limited to OpenSSL) that is licensed under separate terms,
|
||||
as designated in a particular file or component or in included license
|
||||
documentation. The authors of MySQL hereby grant you an additional
|
||||
permission to link the program and your derivative works with the
|
||||
separately licensed software that they have included with MySQL.
|
||||
|
||||
Without limiting anything contained in the foregoing, this file,
|
||||
which is part of C Driver for MySQL (Connector/C), is also subject to the
|
||||
Universal FOSS Exception, version 1.0, a copy of which can be found at
|
||||
http://oss.oracle.com/licenses/universal-foss-exception.
|
||||
|
||||
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#ifndef _mysql_time_h_
|
||||
#define _mysql_time_h_
|
||||
|
||||
/**
|
||||
@file include/mysql_time.h
|
||||
Time declarations shared between the server and client API:
|
||||
you should not add anything to this header unless it's used
|
||||
(and hence should be visible) in mysql.h.
|
||||
If you're looking for a place to add new time-related declaration,
|
||||
it's most likely my_time.h. See also "C API Handling of Date
|
||||
and Time Values" chapter in documentation.
|
||||
*/
|
||||
|
||||
// Do not not pull in the server header "my_inttypes.h" from client code.
|
||||
// IWYU pragma: no_include "my_inttypes.h"
|
||||
|
||||
enum enum_mysql_timestamp_type {
|
||||
MYSQL_TIMESTAMP_NONE = -2,
|
||||
MYSQL_TIMESTAMP_ERROR = -1,
|
||||
|
||||
/// Stores year, month and day components.
|
||||
MYSQL_TIMESTAMP_DATE = 0,
|
||||
|
||||
/**
|
||||
Stores all date and time components.
|
||||
Value is in UTC for `TIMESTAMP` type.
|
||||
Value is in local time zone for `DATETIME` type.
|
||||
*/
|
||||
MYSQL_TIMESTAMP_DATETIME = 1,
|
||||
|
||||
/// Stores hour, minute, second and microsecond.
|
||||
MYSQL_TIMESTAMP_TIME = 2,
|
||||
|
||||
/**
|
||||
A temporary type for `DATETIME` or `TIMESTAMP` types equipped with time
|
||||
zone information. After the time zone information is reconciled, the type is
|
||||
converted to MYSQL_TIMESTAMP_DATETIME.
|
||||
*/
|
||||
MYSQL_TIMESTAMP_DATETIME_TZ = 3
|
||||
};
|
||||
|
||||
/*
|
||||
Structure which is used to represent datetime values inside MySQL.
|
||||
|
||||
We assume that values in this structure are normalized, i.e. year <= 9999,
|
||||
month <= 12, day <= 31, hour <= 23, hour <= 59, hour <= 59. Many functions
|
||||
in server such as my_system_gmt_sec() or make_time() family of functions
|
||||
rely on this (actually now usage of make_*() family relies on a bit weaker
|
||||
restriction). Also functions that produce MYSQL_TIME as result ensure this.
|
||||
There is one exception to this rule though if this structure holds time
|
||||
value (time_type == MYSQL_TIMESTAMP_TIME) days and hour member can hold
|
||||
bigger values.
|
||||
*/
|
||||
typedef struct MYSQL_TIME {
|
||||
unsigned int year, month, day, hour, minute, second;
|
||||
unsigned long second_part; /**< microseconds */
|
||||
bool neg;
|
||||
enum enum_mysql_timestamp_type time_type;
|
||||
/// The time zone displacement, specified in seconds.
|
||||
int time_zone_displacement;
|
||||
} MYSQL_TIME;
|
||||
|
||||
#endif /* _mysql_time_h_ */
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
/* Copyright Abandoned 1996,1999 TCX DataKonsult AB & Monty Program KB
|
||||
& Detron HB, 1996, 1999-2004, 2007 MySQL AB.
|
||||
This file is public domain and comes with NO WARRANTY of any kind
|
||||
*/
|
||||
|
||||
/* Version numbers for protocol & mysqld */
|
||||
|
||||
#ifndef _mysql_version_h
|
||||
#define _mysql_version_h
|
||||
|
||||
#define PROTOCOL_VERSION 10
|
||||
#define MYSQL_SERVER_VERSION "8.0.27"
|
||||
#define MYSQL_BASE_VERSION "mysqld-8.0"
|
||||
#define MYSQL_SERVER_SUFFIX_DEF "-0ubuntu0.21.10.1"
|
||||
#define MYSQL_VERSION_ID 80027
|
||||
#define MYSQL_PORT 3306
|
||||
#define MYSQL_ADMIN_PORT 33062
|
||||
#define MYSQL_PORT_DEFAULT 0
|
||||
#define MYSQL_UNIX_ADDR "/var/run/mysqld/mysqld.sock"
|
||||
#define MYSQL_CONFIG_NAME "my"
|
||||
#define MYSQL_PERSIST_CONFIG_NAME "mysqld-auto"
|
||||
#define MYSQL_COMPILATION_COMMENT "(Ubuntu)"
|
||||
#define MYSQL_COMPILATION_COMMENT_SERVER "(Ubuntu)"
|
||||
#define LIBMYSQL_VERSION "8.0.27"
|
||||
#define LIBMYSQL_VERSION_ID 80027
|
||||
|
||||
#ifndef LICENSE
|
||||
#define LICENSE GPL
|
||||
#endif /* LICENSE */
|
||||
|
||||
#endif /* _mysql_version_h */
|
||||
Vendored
+5562
File diff suppressed because it is too large
Load Diff
Vendored
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2021, Oracle and/or its affiliates.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.0,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is also distributed with certain software (including
|
||||
* but not limited to OpenSSL) that is licensed under separate terms,
|
||||
* as designated in a particular file or component or in included license
|
||||
* documentation. The authors of MySQL hereby grant you an additional
|
||||
* permission to link the program and your derivative works with the
|
||||
* separately licensed software that they have included with MySQL.
|
||||
*
|
||||
* 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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/* Autogenerated file, please don't edit */
|
||||
|
||||
#include "mysqlx_error.h"
|
||||
|
||||
{"ER_X_BAD_MESSAGE", ER_X_BAD_MESSAGE, "", NULL, NULL, 0 },
|
||||
{"ER_X_CAPABILITIES_PREPARE_FAILED", ER_X_CAPABILITIES_PREPARE_FAILED, "", NULL, NULL, 0 },
|
||||
{"ER_X_CAPABILITY_NOT_FOUND", ER_X_CAPABILITY_NOT_FOUND, "", NULL, NULL, 0 },
|
||||
{"ER_X_INVALID_PROTOCOL_DATA", ER_X_INVALID_PROTOCOL_DATA, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_VALUE_LENGTH", ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_VALUE_LENGTH, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_KEY_LENGTH", ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_KEY_LENGTH, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_EMPTY_KEY", ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_EMPTY_KEY, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_LENGTH", ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_LENGTH, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_TYPE", ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_TYPE, "", NULL, NULL, 0 },
|
||||
{"ER_X_CAPABILITY_SET_NOT_ALLOWED", ER_X_CAPABILITY_SET_NOT_ALLOWED, "", NULL, NULL, 0 },
|
||||
{"ER_X_SERVICE_ERROR", ER_X_SERVICE_ERROR, "", NULL, NULL, 0 },
|
||||
{"ER_X_SESSION", ER_X_SESSION, "", NULL, NULL, 0 },
|
||||
{"ER_X_INVALID_ARGUMENT", ER_X_INVALID_ARGUMENT, "", NULL, NULL, 0 },
|
||||
{"ER_X_MISSING_ARGUMENT", ER_X_MISSING_ARGUMENT, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_INSERT_DATA", ER_X_BAD_INSERT_DATA, "", NULL, NULL, 0 },
|
||||
{"ER_X_CMD_NUM_ARGUMENTS", ER_X_CMD_NUM_ARGUMENTS, "", NULL, NULL, 0 },
|
||||
{"ER_X_CMD_ARGUMENT_TYPE", ER_X_CMD_ARGUMENT_TYPE, "", NULL, NULL, 0 },
|
||||
{"ER_X_CMD_ARGUMENT_VALUE", ER_X_CMD_ARGUMENT_VALUE, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_UPSERT_DATA", ER_X_BAD_UPSERT_DATA, "", NULL, NULL, 0 },
|
||||
{"ER_X_DUPLICATED_CAPABILITIES", ER_X_DUPLICATED_CAPABILITIES, "", NULL, NULL, 0 },
|
||||
{"ER_X_CMD_ARGUMENT_OBJECT_EMPTY", ER_X_CMD_ARGUMENT_OBJECT_EMPTY, "", NULL, NULL, 0 },
|
||||
{"ER_X_CMD_INVALID_ARGUMENT", ER_X_CMD_INVALID_ARGUMENT, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_UPDATE_DATA", ER_X_BAD_UPDATE_DATA, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_TYPE_OF_UPDATE", ER_X_BAD_TYPE_OF_UPDATE, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_COLUMN_TO_UPDATE", ER_X_BAD_COLUMN_TO_UPDATE, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_MEMBER_TO_UPDATE", ER_X_BAD_MEMBER_TO_UPDATE, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_STATEMENT_ID", ER_X_BAD_STATEMENT_ID, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_CURSOR_ID", ER_X_BAD_CURSOR_ID, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_SCHEMA", ER_X_BAD_SCHEMA, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_TABLE", ER_X_BAD_TABLE, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_PROJECTION", ER_X_BAD_PROJECTION, "", NULL, NULL, 0 },
|
||||
{"ER_X_DOC_ID_MISSING", ER_X_DOC_ID_MISSING, "", NULL, NULL, 0 },
|
||||
{"ER_X_DUPLICATE_ENTRY", ER_X_DUPLICATE_ENTRY, "", NULL, NULL, 0 },
|
||||
{"ER_X_DOC_REQUIRED_FIELD_MISSING", ER_X_DOC_REQUIRED_FIELD_MISSING, "", NULL, NULL, 0 },
|
||||
{"ER_X_PROJ_BAD_KEY_NAME", ER_X_PROJ_BAD_KEY_NAME, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_DOC_PATH", ER_X_BAD_DOC_PATH, "", NULL, NULL, 0 },
|
||||
{"ER_X_CURSOR_EXISTS", ER_X_CURSOR_EXISTS, "", NULL, NULL, 0 },
|
||||
{"ER_X_CURSOR_REACHED_EOF", ER_X_CURSOR_REACHED_EOF, "", NULL, NULL, 0 },
|
||||
{"ER_X_PREPARED_STATMENT_CAN_HAVE_ONE_CURSOR", ER_X_PREPARED_STATMENT_CAN_HAVE_ONE_CURSOR, "", NULL, NULL, 0 },
|
||||
{"ER_X_PREPARED_EXECUTE_ARGUMENT_NOT_SUPPORTED", ER_X_PREPARED_EXECUTE_ARGUMENT_NOT_SUPPORTED, "", NULL, NULL, 0 },
|
||||
{"ER_X_PREPARED_EXECUTE_ARGUMENT_CONSISTENCY", ER_X_PREPARED_EXECUTE_ARGUMENT_CONSISTENCY, "", NULL, NULL, 0 },
|
||||
{"ER_X_EXPR_BAD_OPERATOR", ER_X_EXPR_BAD_OPERATOR, "", NULL, NULL, 0 },
|
||||
{"ER_X_EXPR_BAD_NUM_ARGS", ER_X_EXPR_BAD_NUM_ARGS, "", NULL, NULL, 0 },
|
||||
{"ER_X_EXPR_MISSING_ARG", ER_X_EXPR_MISSING_ARG, "", NULL, NULL, 0 },
|
||||
{"ER_X_EXPR_BAD_TYPE_VALUE", ER_X_EXPR_BAD_TYPE_VALUE, "", NULL, NULL, 0 },
|
||||
{"ER_X_EXPR_BAD_VALUE", ER_X_EXPR_BAD_VALUE, "", NULL, NULL, 0 },
|
||||
{"ER_X_INVALID_COLLECTION", ER_X_INVALID_COLLECTION, "", NULL, NULL, 0 },
|
||||
{"ER_X_INVALID_ADMIN_COMMAND", ER_X_INVALID_ADMIN_COMMAND, "", NULL, NULL, 0 },
|
||||
{"ER_X_EXPECT_NOT_OPEN", ER_X_EXPECT_NOT_OPEN, "", NULL, NULL, 0 },
|
||||
{"ER_X_EXPECT_NO_ERROR_FAILED", ER_X_EXPECT_NO_ERROR_FAILED, "", NULL, NULL, 0 },
|
||||
{"ER_X_EXPECT_BAD_CONDITION", ER_X_EXPECT_BAD_CONDITION, "", NULL, NULL, 0 },
|
||||
{"ER_X_EXPECT_BAD_CONDITION_VALUE", ER_X_EXPECT_BAD_CONDITION_VALUE, "", NULL, NULL, 0 },
|
||||
{"ER_X_INVALID_NAMESPACE", ER_X_INVALID_NAMESPACE, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_NOTICE", ER_X_BAD_NOTICE, "", NULL, NULL, 0 },
|
||||
{"ER_X_CANNOT_DISABLE_NOTICE", ER_X_CANNOT_DISABLE_NOTICE, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_CONFIGURATION", ER_X_BAD_CONFIGURATION, "", NULL, NULL, 0 },
|
||||
{"ER_X_MYSQLX_ACCOUNT_MISSING_PERMISSIONS", ER_X_MYSQLX_ACCOUNT_MISSING_PERMISSIONS, "", NULL, NULL, 0 },
|
||||
{"ER_X_EXPECT_FIELD_EXISTS_FAILED", ER_X_EXPECT_FIELD_EXISTS_FAILED, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_LOCKING", ER_X_BAD_LOCKING, "", NULL, NULL, 0 },
|
||||
{"ER_X_FRAME_COMPRESSION_DISABLED", ER_X_FRAME_COMPRESSION_DISABLED, "", NULL, NULL, 0 },
|
||||
{"ER_X_DECOMPRESSION_FAILED", ER_X_DECOMPRESSION_FAILED, "", NULL, NULL, 0 },
|
||||
{"ER_X_BAD_COMPRESSED_FRAME", ER_X_BAD_COMPRESSED_FRAME, "", NULL, NULL, 0 },
|
||||
{"ER_X_CAPABILITY_COMPRESSION_INVALID_ALGORITHM", ER_X_CAPABILITY_COMPRESSION_INVALID_ALGORITHM, "", NULL, NULL, 0 },
|
||||
{"ER_X_CAPABILITY_COMPRESSION_INVALID_SERVER_STYLE", ER_X_CAPABILITY_COMPRESSION_INVALID_SERVER_STYLE, "", NULL, NULL, 0 },
|
||||
{"ER_X_CAPABILITY_COMPRESSION_INVALID_CLIENT_STYLE", ER_X_CAPABILITY_COMPRESSION_INVALID_CLIENT_STYLE, "", NULL, NULL, 0 },
|
||||
{"ER_X_CAPABILITY_COMPRESSION_INVALID_OPTION", ER_X_CAPABILITY_COMPRESSION_INVALID_OPTION, "", NULL, NULL, 0 },
|
||||
{"ER_X_CAPABILITY_COMPRESSION_MISSING_REQUIRED_FIELDS", ER_X_CAPABILITY_COMPRESSION_MISSING_REQUIRED_FIELDS, "", NULL, NULL, 0 },
|
||||
{"ER_X_DOCUMENT_DOESNT_MATCH_EXPECTED_SCHEMA", ER_X_DOCUMENT_DOESNT_MATCH_EXPECTED_SCHEMA, "", NULL, NULL, 0 },
|
||||
{"ER_X_COLLECTION_OPTION_DOESNT_EXISTS", ER_X_COLLECTION_OPTION_DOESNT_EXISTS, "", NULL, NULL, 0 },
|
||||
{"ER_X_INVALID_VALIDATION_SCHEMA", ER_X_INVALID_VALIDATION_SCHEMA, "", NULL, NULL, 0 },
|
||||
|
||||
Vendored
+99
@@ -0,0 +1,99 @@
|
||||
/* Copyright (c) 2015, 2021, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2.0,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is also distributed with certain software (including
|
||||
but not limited to OpenSSL) that is licensed under separate terms,
|
||||
as designated in a particular file or component or in included license
|
||||
documentation. The authors of MySQL hereby grant you an additional
|
||||
permission to link the program and your derivative works with the
|
||||
separately licensed software that they have included with MySQL.
|
||||
|
||||
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
|
||||
#ifndef _MYSQLX_ERROR_H_
|
||||
#define _MYSQLX_ERROR_H_
|
||||
|
||||
#define ER_X_BAD_MESSAGE 5000
|
||||
#define ER_X_CAPABILITIES_PREPARE_FAILED 5001
|
||||
#define ER_X_CAPABILITY_NOT_FOUND 5002
|
||||
#define ER_X_INVALID_PROTOCOL_DATA 5003
|
||||
#define ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_VALUE_LENGTH 5004
|
||||
#define ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_KEY_LENGTH 5005
|
||||
#define ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_EMPTY_KEY 5006
|
||||
#define ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_LENGTH 5007
|
||||
#define ER_X_BAD_CONNECTION_SESSION_ATTRIBUTE_TYPE 5008
|
||||
#define ER_X_CAPABILITY_SET_NOT_ALLOWED 5009
|
||||
#define ER_X_SERVICE_ERROR 5010
|
||||
#define ER_X_SESSION 5011
|
||||
#define ER_X_INVALID_ARGUMENT 5012
|
||||
#define ER_X_MISSING_ARGUMENT 5013
|
||||
#define ER_X_BAD_INSERT_DATA 5014
|
||||
#define ER_X_CMD_NUM_ARGUMENTS 5015
|
||||
#define ER_X_CMD_ARGUMENT_TYPE 5016
|
||||
#define ER_X_CMD_ARGUMENT_VALUE 5017
|
||||
#define ER_X_BAD_UPSERT_DATA 5018
|
||||
#define ER_X_DUPLICATED_CAPABILITIES 5019
|
||||
#define ER_X_CMD_ARGUMENT_OBJECT_EMPTY 5020
|
||||
#define ER_X_CMD_INVALID_ARGUMENT 5021
|
||||
#define ER_X_BAD_UPDATE_DATA 5050
|
||||
#define ER_X_BAD_TYPE_OF_UPDATE 5051
|
||||
#define ER_X_BAD_COLUMN_TO_UPDATE 5052
|
||||
#define ER_X_BAD_MEMBER_TO_UPDATE 5053
|
||||
#define ER_X_BAD_STATEMENT_ID 5110
|
||||
#define ER_X_BAD_CURSOR_ID 5111
|
||||
#define ER_X_BAD_SCHEMA 5112
|
||||
#define ER_X_BAD_TABLE 5113
|
||||
#define ER_X_BAD_PROJECTION 5114
|
||||
#define ER_X_DOC_ID_MISSING 5115
|
||||
#define ER_X_DUPLICATE_ENTRY 5116
|
||||
#define ER_X_DOC_REQUIRED_FIELD_MISSING 5117
|
||||
#define ER_X_PROJ_BAD_KEY_NAME 5120
|
||||
#define ER_X_BAD_DOC_PATH 5121
|
||||
#define ER_X_CURSOR_EXISTS 5122
|
||||
#define ER_X_CURSOR_REACHED_EOF 5123
|
||||
#define ER_X_PREPARED_STATMENT_CAN_HAVE_ONE_CURSOR 5131
|
||||
#define ER_X_PREPARED_EXECUTE_ARGUMENT_NOT_SUPPORTED 5133
|
||||
#define ER_X_PREPARED_EXECUTE_ARGUMENT_CONSISTENCY 5134
|
||||
#define ER_X_EXPR_BAD_OPERATOR 5150
|
||||
#define ER_X_EXPR_BAD_NUM_ARGS 5151
|
||||
#define ER_X_EXPR_MISSING_ARG 5152
|
||||
#define ER_X_EXPR_BAD_TYPE_VALUE 5153
|
||||
#define ER_X_EXPR_BAD_VALUE 5154
|
||||
#define ER_X_INVALID_COLLECTION 5156
|
||||
#define ER_X_INVALID_ADMIN_COMMAND 5157
|
||||
#define ER_X_EXPECT_NOT_OPEN 5158
|
||||
#define ER_X_EXPECT_NO_ERROR_FAILED 5159
|
||||
#define ER_X_EXPECT_BAD_CONDITION 5160
|
||||
#define ER_X_EXPECT_BAD_CONDITION_VALUE 5161
|
||||
#define ER_X_INVALID_NAMESPACE 5162
|
||||
#define ER_X_BAD_NOTICE 5163
|
||||
#define ER_X_CANNOT_DISABLE_NOTICE 5164
|
||||
#define ER_X_BAD_CONFIGURATION 5165
|
||||
#define ER_X_MYSQLX_ACCOUNT_MISSING_PERMISSIONS 5167
|
||||
#define ER_X_EXPECT_FIELD_EXISTS_FAILED 5168
|
||||
#define ER_X_BAD_LOCKING 5169
|
||||
#define ER_X_FRAME_COMPRESSION_DISABLED 5170
|
||||
#define ER_X_DECOMPRESSION_FAILED 5171
|
||||
#define ER_X_BAD_COMPRESSED_FRAME 5174
|
||||
#define ER_X_CAPABILITY_COMPRESSION_INVALID_ALGORITHM 5175
|
||||
#define ER_X_CAPABILITY_COMPRESSION_INVALID_SERVER_STYLE 5176
|
||||
#define ER_X_CAPABILITY_COMPRESSION_INVALID_CLIENT_STYLE 5177
|
||||
#define ER_X_CAPABILITY_COMPRESSION_INVALID_OPTION 5178
|
||||
#define ER_X_CAPABILITY_COMPRESSION_MISSING_REQUIRED_FIELDS 5179
|
||||
#define ER_X_DOCUMENT_DOESNT_MATCH_EXPECTED_SCHEMA 5180
|
||||
#define ER_X_COLLECTION_OPTION_DOESNT_EXISTS 5181
|
||||
#define ER_X_INVALID_VALIDATION_SCHEMA 5182
|
||||
|
||||
|
||||
#endif // _MYSQLX_ERROR_H_
|
||||
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2021, Oracle and/or its affiliates.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.0,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is also distributed with certain software (including
|
||||
* but not limited to OpenSSL) that is licensed under separate terms,
|
||||
* as designated in a particular file or component or in included license
|
||||
* documentation. The authors of MySQL hereby grant you an additional
|
||||
* permission to link the program and your derivative works with the
|
||||
* separately licensed software that they have included with MySQL.
|
||||
*
|
||||
* 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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/* Version numbers for X Plugin */
|
||||
|
||||
#ifndef _MYSQLX_VERSION_H_
|
||||
#define _MYSQLX_VERSION_H_
|
||||
|
||||
#define MYSQLX_PLUGIN_VERSION_MAJOR 1
|
||||
#define MYSQLX_PLUGIN_VERSION_MINOR 0
|
||||
#define MYSQLX_PLUGIN_VERSION_PATCH 2
|
||||
|
||||
#define MYSQLX_PLUGIN_NAME "mysqlx"
|
||||
#define MYSQLX_STATUS_VARIABLE_PREFIX(NAME) "Mysqlx_" NAME
|
||||
#define MYSQLX_SYSTEM_VARIABLE_PREFIX(NAME) "mysqlx_" NAME
|
||||
|
||||
#define MYSQLX_TCP_PORT 33060U
|
||||
#define MYSQLX_UNIX_ADDR "/var/run/mysqld/mysqlx.sock"
|
||||
|
||||
#define MYSQLX_PLUGIN_VERSION ( (MYSQLX_PLUGIN_VERSION_MAJOR << 8) | MYSQLX_PLUGIN_VERSION_MINOR )
|
||||
#define MYSQLX_PLUGIN_VERSION_STRING "1.0.2"
|
||||
|
||||
#endif // _MYSQLX_VERSION_H_
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
#ifndef MYSQL_PLUGIN_AUTH_COMMON_INCLUDED
|
||||
/* Copyright (c) 2010, 2021, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2.0,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is also distributed with certain software (including
|
||||
but not limited to OpenSSL) that is licensed under separate terms,
|
||||
as designated in a particular file or component or in included license
|
||||
documentation. The authors of MySQL hereby grant you an additional
|
||||
permission to link the program and your derivative works with the
|
||||
separately licensed software that they have included with MySQL.
|
||||
|
||||
Without limiting anything contained in the foregoing, this file,
|
||||
which is part of C Driver for MySQL (Connector/C), is also subject to the
|
||||
Universal FOSS Exception, version 1.0, a copy of which can be found at
|
||||
http://oss.oracle.com/licenses/universal-foss-exception.
|
||||
|
||||
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
/**
|
||||
@file include/mysql/plugin_auth_common.h
|
||||
|
||||
This file defines constants and data structures that are the same for
|
||||
both client- and server-side authentication plugins.
|
||||
*/
|
||||
#define MYSQL_PLUGIN_AUTH_COMMON_INCLUDED
|
||||
|
||||
/** the max allowed length for a user name */
|
||||
#define MYSQL_USERNAME_LENGTH 96
|
||||
|
||||
/**
|
||||
return values of the plugin authenticate_user() method.
|
||||
*/
|
||||
|
||||
/**
|
||||
Authentication failed, plugin internal error.
|
||||
An error occurred in the authentication plugin itself.
|
||||
These errors are reported in table performance_schema.host_cache,
|
||||
column COUNT_AUTH_PLUGIN_ERRORS.
|
||||
*/
|
||||
#define CR_AUTH_PLUGIN_ERROR 3
|
||||
/**
|
||||
Authentication failed, client server handshake.
|
||||
An error occurred during the client server handshake.
|
||||
These errors are reported in table performance_schema.host_cache,
|
||||
column COUNT_HANDSHAKE_ERRORS.
|
||||
*/
|
||||
#define CR_AUTH_HANDSHAKE 2
|
||||
/**
|
||||
Authentication failed, user credentials.
|
||||
For example, wrong passwords.
|
||||
These errors are reported in table performance_schema.host_cache,
|
||||
column COUNT_AUTHENTICATION_ERRORS.
|
||||
*/
|
||||
#define CR_AUTH_USER_CREDENTIALS 1
|
||||
/**
|
||||
Authentication failed. Additionally, all other CR_xxx values
|
||||
(libmysql error code) can be used too.
|
||||
|
||||
The client plugin may set the error code and the error message directly
|
||||
in the MYSQL structure and return CR_ERROR. If a CR_xxx specific error
|
||||
code was returned, an error message in the MYSQL structure will be
|
||||
overwritten. If CR_ERROR is returned without setting the error in MYSQL,
|
||||
CR_UNKNOWN_ERROR will be user.
|
||||
*/
|
||||
#define CR_ERROR 0
|
||||
/**
|
||||
Authentication (client part) was successful. It does not mean that the
|
||||
authentication as a whole was successful, usually it only means
|
||||
that the client was able to send the user name and the password to the
|
||||
server. If CR_OK is returned, the libmysql reads the next packet expecting
|
||||
it to be one of OK, ERROR, or CHANGE_PLUGIN packets.
|
||||
*/
|
||||
#define CR_OK -1
|
||||
/**
|
||||
Authentication was successful.
|
||||
It means that the client has done its part successfully and also that
|
||||
a plugin has read the last packet (one of OK, ERROR, CHANGE_PLUGIN).
|
||||
In this case, libmysql will not read a packet from the server,
|
||||
but it will use the data at mysql->net.read_pos.
|
||||
|
||||
A plugin may return this value if the number of roundtrips in the
|
||||
authentication protocol is not known in advance, and the client plugin
|
||||
needs to read one packet more to determine if the authentication is finished
|
||||
or not.
|
||||
*/
|
||||
#define CR_OK_HANDSHAKE_COMPLETE -2
|
||||
/**
|
||||
Authentication was successful with limited operations.
|
||||
It means that the both client and server side plugins decided to allow
|
||||
authentication with very limited operations ALTER USER to do registration.
|
||||
*/
|
||||
#define CR_OK_AUTH_IN_SANDBOX_MODE -3
|
||||
/**
|
||||
Flag to be passed back to server from authentication plugins via
|
||||
authenticated_as when proxy mapping should be done by the server.
|
||||
*/
|
||||
#define PROXY_FLAG 0
|
||||
|
||||
/*
|
||||
We need HANDLE definition if on Windows. Define WIN32_LEAN_AND_MEAN (if
|
||||
not already done) to minimize amount of imported declarations.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
struct MYSQL_PLUGIN_VIO_INFO {
|
||||
enum {
|
||||
MYSQL_VIO_INVALID,
|
||||
MYSQL_VIO_TCP,
|
||||
MYSQL_VIO_SOCKET,
|
||||
MYSQL_VIO_PIPE,
|
||||
MYSQL_VIO_MEMORY
|
||||
} protocol;
|
||||
int socket; /**< it's set, if the protocol is SOCKET or TCP */
|
||||
#ifdef _WIN32
|
||||
HANDLE handle; /**< it's set, if the protocol is PIPE or MEMORY */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* state of an asynchronous operation */
|
||||
enum net_async_status {
|
||||
NET_ASYNC_COMPLETE = 0,
|
||||
NET_ASYNC_NOT_READY,
|
||||
NET_ASYNC_ERROR,
|
||||
NET_ASYNC_COMPLETE_NO_MORE_RESULTS
|
||||
};
|
||||
|
||||
/**
|
||||
Provides plugin access to communication channel
|
||||
*/
|
||||
typedef struct MYSQL_PLUGIN_VIO {
|
||||
/**
|
||||
Plugin provides a pointer reference and this function sets it to the
|
||||
contents of any incoming packet. Returns the packet length, or -1 if
|
||||
the plugin should terminate.
|
||||
*/
|
||||
int (*read_packet)(struct MYSQL_PLUGIN_VIO *vio, unsigned char **buf);
|
||||
|
||||
/**
|
||||
Plugin provides a buffer with data and the length and this
|
||||
function sends it as a packet. Returns 0 on success, 1 on failure.
|
||||
*/
|
||||
int (*write_packet)(struct MYSQL_PLUGIN_VIO *vio, const unsigned char *packet,
|
||||
int packet_len);
|
||||
|
||||
/**
|
||||
Fills in a MYSQL_PLUGIN_VIO_INFO structure, providing the information
|
||||
about the connection.
|
||||
*/
|
||||
void (*info)(struct MYSQL_PLUGIN_VIO *vio,
|
||||
struct MYSQL_PLUGIN_VIO_INFO *info);
|
||||
|
||||
/**
|
||||
Non blocking version of read_packet. This function points buf to starting
|
||||
position of incoming packet. When this function returns NET_ASYNC_NOT_READY
|
||||
plugin should call this function again until all incoming packets are read.
|
||||
If return code is NET_ASYNC_COMPLETE, plugin can do further processing of
|
||||
read packets.
|
||||
*/
|
||||
enum net_async_status (*read_packet_nonblocking)(struct MYSQL_PLUGIN_VIO *vio,
|
||||
unsigned char **buf,
|
||||
int *result);
|
||||
/**
|
||||
Non blocking version of write_packet. Sends data available in pkt of length
|
||||
pkt_len to server in asynchrnous way.
|
||||
*/
|
||||
enum net_async_status (*write_packet_nonblocking)(
|
||||
struct MYSQL_PLUGIN_VIO *vio, const unsigned char *pkt, int pkt_len,
|
||||
int *result);
|
||||
|
||||
} MYSQL_PLUGIN_VIO;
|
||||
|
||||
#endif
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/* Copyright (c) 2017, 2021, Oracle and/or its affiliates.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License, version 2.0,
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is also distributed with certain software (including
|
||||
but not limited to OpenSSL) that is licensed under separate terms,
|
||||
as designated in a particular file or component or in included license
|
||||
documentation. The authors of MySQL hereby grant you an additional
|
||||
permission to link the program and your derivative works with the
|
||||
separately licensed software that they have included with MySQL.
|
||||
|
||||
Without limiting anything contained in the foregoing, this file,
|
||||
which is part of C Driver for MySQL (Connector/C), is also subject to the
|
||||
Universal FOSS Exception, version 1.0, a copy of which can be found at
|
||||
http://oss.oracle.com/licenses/universal-foss-exception.
|
||||
|
||||
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, version 2.0, 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 St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#ifndef UDF_REGISTRATION_TYPES_H
|
||||
#define UDF_REGISTRATION_TYPES_H
|
||||
|
||||
#ifndef MYSQL_ABI_CHECK
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
Type of the user defined function return slot and arguments
|
||||
*/
|
||||
enum Item_result {
|
||||
INVALID_RESULT = -1, /** not valid for UDFs */
|
||||
STRING_RESULT = 0, /** char * */
|
||||
REAL_RESULT, /** double */
|
||||
INT_RESULT, /** long long */
|
||||
ROW_RESULT, /** not valid for UDFs */
|
||||
DECIMAL_RESULT /** char *, to be converted to/from a decimal */
|
||||
};
|
||||
|
||||
typedef struct UDF_ARGS {
|
||||
unsigned int arg_count; /**< Number of arguments */
|
||||
enum Item_result *arg_type; /**< Pointer to item_results */
|
||||
char **args; /**< Pointer to argument */
|
||||
unsigned long *lengths; /**< Length of string arguments */
|
||||
char *maybe_null; /**< Set to 1 for all maybe_null args */
|
||||
char **attributes; /**< Pointer to attribute name */
|
||||
unsigned long *attribute_lengths; /**< Length of attribute arguments */
|
||||
void *extension;
|
||||
} UDF_ARGS;
|
||||
|
||||
/**
|
||||
Information about the result of a user defined function
|
||||
|
||||
@todo add a notion for determinism of the UDF.
|
||||
|
||||
@sa Item_udf_func::update_used_tables()
|
||||
*/
|
||||
typedef struct UDF_INIT {
|
||||
bool maybe_null; /** 1 if function can return NULL */
|
||||
unsigned int decimals; /** for real functions */
|
||||
unsigned long max_length; /** For string functions */
|
||||
char *ptr; /** free pointer for function data */
|
||||
bool const_item; /** 1 if function always returns the same value */
|
||||
void *extension;
|
||||
} UDF_INIT;
|
||||
|
||||
enum Item_udftype { UDFTYPE_FUNCTION = 1, UDFTYPE_AGGREGATE };
|
||||
|
||||
typedef void (*Udf_func_clear)(UDF_INIT *, unsigned char *, unsigned char *);
|
||||
typedef void (*Udf_func_add)(UDF_INIT *, UDF_ARGS *, unsigned char *,
|
||||
unsigned char *);
|
||||
typedef void (*Udf_func_deinit)(UDF_INIT *);
|
||||
typedef bool (*Udf_func_init)(UDF_INIT *, UDF_ARGS *, char *);
|
||||
typedef void (*Udf_func_any)(void);
|
||||
typedef double (*Udf_func_double)(UDF_INIT *, UDF_ARGS *, unsigned char *,
|
||||
unsigned char *);
|
||||
typedef long long (*Udf_func_longlong)(UDF_INIT *, UDF_ARGS *, unsigned char *,
|
||||
unsigned char *);
|
||||
typedef char *(*Udf_func_string)(UDF_INIT *, UDF_ARGS *, char *,
|
||||
unsigned long *, unsigned char *,
|
||||
unsigned char *);
|
||||
|
||||
#endif /* UDF_REGISTRATION_TYPES_H */
|
||||
@@ -0,0 +1,8 @@
|
||||
Version 0.1.3 on 2013-02-10:
|
||||
* Included required C header files.
|
||||
|
||||
Version 0.1.2 on 2011-11-30:
|
||||
* Renamed stdin, stdout, stderr to in, out, err.
|
||||
|
||||
Version 0.1.1 on 2009-08-12:
|
||||
* First release.
|
||||
@@ -0,0 +1,34 @@
|
||||
CMAKE_MINIMUM_REQUIRED( VERSION 2.6 )
|
||||
|
||||
PROJECT( fcgicc CXX )
|
||||
SET( PROJECT_VERSION 0.1.3 )
|
||||
SET( CMAKE_BUILD_TYPE RELEASE )
|
||||
SET( CMAKE_INSTALL_PREFIX ${PREFIX} )
|
||||
|
||||
FIND_PATH( FCGI_INCLUDE_DIR fastcgi.h )
|
||||
IF( NOT FCGI_INCLUDE_DIR )
|
||||
FIND_PATH( FCGI_INCLUDE_DIR fastcgi.h ${PROJECT_SOURCE_DIR}/fastcgi_devkit )
|
||||
ENDIF()
|
||||
INCLUDE_DIRECTORIES( ${FCGI_INCLUDE_DIR} )
|
||||
|
||||
ADD_SUBDIRECTORY( src )
|
||||
ADD_SUBDIRECTORY( test EXCLUDE_FROM_ALL )
|
||||
|
||||
INSTALL( FILES LICENSE.txt README.txt DESTINATION share/doc/${PROJECT_NAME} )
|
||||
|
||||
SET( DIST_FILE ${PROJECT_NAME}-${PROJECT_VERSION} )
|
||||
ADD_CUSTOM_TARGET( dist ln -sf ${PROJECT_SOURCE_DIR} ${DIST_FILE} &&
|
||||
tar cjf ${DIST_FILE}.tar.bz2
|
||||
${DIST_FILE}/LICENSE.txt
|
||||
${DIST_FILE}/README.txt
|
||||
${DIST_FILE}/CHANGES.txt
|
||||
${DIST_FILE}/CMakeLists.txt
|
||||
${DIST_FILE}/fastcgi_devkit/LICENSE.TERMS
|
||||
${DIST_FILE}/fastcgi_devkit/fastcgi.h
|
||||
${DIST_FILE}/src/fcgicc.cc
|
||||
${DIST_FILE}/src/fcgicc.h
|
||||
${DIST_FILE}/src/CMakeLists.txt
|
||||
${DIST_FILE}/test/test1.cc
|
||||
${DIST_FILE}/test/test2.cc
|
||||
${DIST_FILE}/test/lighttpd.conf
|
||||
${DIST_FILE}/test/CMakeLists.txt )
|
||||
@@ -0,0 +1,25 @@
|
||||
Copyright 2008, 2009 Andrey Zholos. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. Neither the names of the copyright holders nor the names of contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,154 @@
|
||||
FastCGI C++ Class library (fcgicc)
|
||||
|
||||
1. Introduction
|
||||
|
||||
This is a simple C++ class library that provides FastCGI server functionality.
|
||||
FastCGI is a protocol for connecting web servers with programs that generate
|
||||
content. The protocol is described in more detail at http://www.fastcgi.com.
|
||||
This library provides a single class which handles FastCGI connections on TCP/IP
|
||||
or local domain sockets. Multiple connections are handled in a single thread
|
||||
using select(). When a request is ready, it is passed to an application-
|
||||
supplied callback for processing, after which the generated response is sent
|
||||
back to the client.
|
||||
|
||||
|
||||
2. Version information
|
||||
|
||||
This is the first release of fcgicc, version 0.1.2. It provides a full
|
||||
implementation of the responder mode of FastCGI, but has undergone only limited
|
||||
testing. Use with care!
|
||||
|
||||
|
||||
3. Licensing
|
||||
|
||||
fcgicc is free software, available under a BSD-style license. There is no
|
||||
warranty; not even for merchantability or fitness for a particular purpose. See
|
||||
the file LICENSE.txt for complete information.
|
||||
|
||||
|
||||
4. Installing
|
||||
|
||||
This library depends on the FastCGI Development Kit from http://www.fastcgi.com.
|
||||
For convenience the required part of it is included in this distribution and
|
||||
will be used if the development kit is not found on your system.
|
||||
|
||||
To build and install fcgicc as a standalone library you will need CMake, which
|
||||
is available from http://cmake.org.
|
||||
|
||||
cd fcgicc
|
||||
|
||||
cmake .
|
||||
make install
|
||||
|
||||
- or -
|
||||
|
||||
cmake -DPREFIX=$HOME/local .
|
||||
make install
|
||||
|
||||
Alternatively, it may be simpler to import the two source files into your
|
||||
project and build them as part of it.
|
||||
|
||||
|
||||
5. Using
|
||||
|
||||
Here is how it works:
|
||||
|
||||
Client ------------> Web server ------> FastCGIServer ----------------.
|
||||
HTTP request params FastCGIRequest |
|
||||
in '
|
||||
Application
|
||||
.
|
||||
|
|
||||
Client <------------ Web server <------ FastCGIServer <---------------'
|
||||
HTTP response out FastCGIRequest
|
||||
err
|
||||
|
||||
The web server, which is a client to the FastCGI server, forwards a request as a
|
||||
set of key-value parameter pairs and a standard input stream. The parameter
|
||||
pairs are the environment variables from plain CGI, and they include such
|
||||
important variables as REQUEST_URI. The standard input stream contains data
|
||||
from POST requests.
|
||||
|
||||
An instance of the FastCGIServer class listens for requests from the web server,
|
||||
builds a FastCGIRequest instance for each one, calls event handlers defined by
|
||||
the application to process them, and responds to the web server.
|
||||
|
||||
The application processes requests using event handlers like this:
|
||||
|
||||
...
|
||||
|
||||
#include <fcgicc.h>
|
||||
|
||||
...
|
||||
|
||||
int handle_request(FastCGIRequest& request) {
|
||||
// This is always the first event to occur. It occurs when the
|
||||
// server receives all parameters. There may be more data coming on the
|
||||
// standard input stream.
|
||||
|
||||
if (request.params.count("REQUEST_URI"))
|
||||
return 0; // OK, continue processing
|
||||
else
|
||||
return 1; // Stop processing and return error code
|
||||
}
|
||||
|
||||
int handle_data(FastCGIRequest& request) {
|
||||
// This event occurs when data is received on the standard input stream.
|
||||
// A simple string is used to hold the input stream, so it is the
|
||||
// responsibility of the application to remember which data it has
|
||||
// processed. The application may modify it; new data will be appended
|
||||
// to it by the server. The same goes for the output and error streams:
|
||||
// the application should append data to them; the server will remove
|
||||
// all sent data from them.
|
||||
|
||||
std::transform(request.stdin.begin(), request.stdin.end(),
|
||||
std::back_inserter(request.stderr),
|
||||
std::bind1st(std::plus<char>(), 1));
|
||||
request.stdin.clear(); // don't process it again
|
||||
return 0; // still OK
|
||||
}
|
||||
|
||||
class Application {
|
||||
public:
|
||||
int handle_complete(FastCGIRequest& request) {
|
||||
// The event handler can also be a class member function. This
|
||||
// event occurs when the parameters and standard input streams are
|
||||
// both closed, and thus the request is complete.
|
||||
|
||||
request.out.append("Content-Type: text/plain\r\n\r\n");
|
||||
request.out.append("You requested: ");
|
||||
request.out.append(request.params[std::string("REQUEST_URI")]);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
The application sets up the FastCGI server like this:
|
||||
|
||||
...
|
||||
|
||||
FastCGIServer server; // Instantiate a server
|
||||
|
||||
// Set up our request handlers
|
||||
server.request_handler(&handle_request);
|
||||
server.data_handler(&handle_data);
|
||||
server.complete_handler(application, &Application::handle_complete);
|
||||
|
||||
server.listen(7000); // Listen on a TCP port
|
||||
server.listen(7001); // ... or on two
|
||||
server.listen("./socket"); // ... and also on a local doman socket
|
||||
|
||||
server.process(100); // Process some data, but don't wait more
|
||||
// than 100 ms for it to arrive.
|
||||
server.process(); // Process some data with no timeout
|
||||
server.process_forever(); // Process everything
|
||||
|
||||
...
|
||||
|
||||
|
||||
6. Updates and feedback
|
||||
|
||||
This library is hosted at http://althenia.net/fcgicc. It is programmed by
|
||||
Andrey Zholos <aaz@althenia.net>. Comments, bug reports and testing results are
|
||||
welcome and will be appreciated.
|
||||
@@ -0,0 +1,28 @@
|
||||
This FastCGI application library source and object code (the
|
||||
"Software") and its documentation (the "Documentation") are
|
||||
copyrighted by Open Market, Inc ("Open Market"). The following terms
|
||||
apply to all files associated with the Software and Documentation
|
||||
unless explicitly disclaimed in individual files.
|
||||
|
||||
Open Market permits you to use, copy, modify, distribute, and license
|
||||
this Software and the Documentation for any purpose, provided that
|
||||
existing copyright notices are retained in all copies and that this
|
||||
notice is included verbatim in any distributions. No written
|
||||
agreement, license, or royalty fee is required for any of the
|
||||
authorized uses. Modifications to this Software and Documentation may
|
||||
be copyrighted by their authors and need not follow the licensing
|
||||
terms described here. If modifications to this Software and
|
||||
Documentation have new licensing terms, the new terms must be clearly
|
||||
indicated on the first page of each file where they apply.
|
||||
|
||||
OPEN MARKET MAKES NO EXPRESS OR IMPLIED WARRANTY WITH RESPECT TO THE
|
||||
SOFTWARE OR THE DOCUMENTATION, INCLUDING WITHOUT LIMITATION ANY
|
||||
WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN
|
||||
NO EVENT SHALL OPEN MARKET BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY
|
||||
DAMAGES ARISING FROM OR RELATING TO THIS SOFTWARE OR THE
|
||||
DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY INDIRECT, SPECIAL OR
|
||||
CONSEQUENTIAL DAMAGES OR SIMILAR DAMAGES, INCLUDING LOST PROFITS OR
|
||||
LOST DATA, EVEN IF OPEN MARKET HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS".
|
||||
OPEN MARKET HAS NO LIABILITY IN CONTRACT, TORT, NEGLIGENCE OR
|
||||
OTHERWISE ARISING OUT OF THIS SOFTWARE OR THE DOCUMENTATION.
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* fastcgi.h --
|
||||
*
|
||||
* Defines for the FastCGI protocol.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1995-1996 Open Market, Inc.
|
||||
*
|
||||
* See the file "LICENSE.TERMS" for information on usage and redistribution
|
||||
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
*
|
||||
* $Id: fastcgi.h,v 1.1.1.1 1997/09/16 15:36:32 stanleyg Exp $
|
||||
*/
|
||||
|
||||
#ifndef _FASTCGI_H
|
||||
#define _FASTCGI_H
|
||||
|
||||
/*
|
||||
* Listening socket file number
|
||||
*/
|
||||
#define FCGI_LISTENSOCK_FILENO 0
|
||||
|
||||
typedef struct {
|
||||
unsigned char version;
|
||||
unsigned char type;
|
||||
unsigned char requestIdB1;
|
||||
unsigned char requestIdB0;
|
||||
unsigned char contentLengthB1;
|
||||
unsigned char contentLengthB0;
|
||||
unsigned char paddingLength;
|
||||
unsigned char reserved;
|
||||
} FCGI_Header;
|
||||
|
||||
#define FCGI_MAX_LENGTH 0xffff
|
||||
|
||||
/*
|
||||
* Number of bytes in a FCGI_Header. Future versions of the protocol
|
||||
* will not reduce this number.
|
||||
*/
|
||||
#define FCGI_HEADER_LEN 8
|
||||
|
||||
/*
|
||||
* Value for version component of FCGI_Header
|
||||
*/
|
||||
#define FCGI_VERSION_1 1
|
||||
|
||||
/*
|
||||
* Values for type component of FCGI_Header
|
||||
*/
|
||||
#define FCGI_BEGIN_REQUEST 1
|
||||
#define FCGI_ABORT_REQUEST 2
|
||||
#define FCGI_END_REQUEST 3
|
||||
#define FCGI_PARAMS 4
|
||||
#define FCGI_STDIN 5
|
||||
#define FCGI_STDOUT 6
|
||||
#define FCGI_STDERR 7
|
||||
#define FCGI_DATA 8
|
||||
#define FCGI_GET_VALUES 9
|
||||
#define FCGI_GET_VALUES_RESULT 10
|
||||
#define FCGI_UNKNOWN_TYPE 11
|
||||
#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
|
||||
|
||||
/*
|
||||
* Value for requestId component of FCGI_Header
|
||||
*/
|
||||
#define FCGI_NULL_REQUEST_ID 0
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned char roleB1;
|
||||
unsigned char roleB0;
|
||||
unsigned char flags;
|
||||
unsigned char reserved[5];
|
||||
} FCGI_BeginRequestBody;
|
||||
|
||||
typedef struct {
|
||||
FCGI_Header header;
|
||||
FCGI_BeginRequestBody body;
|
||||
} FCGI_BeginRequestRecord;
|
||||
|
||||
/*
|
||||
* Mask for flags component of FCGI_BeginRequestBody
|
||||
*/
|
||||
#define FCGI_KEEP_CONN 1
|
||||
|
||||
/*
|
||||
* Values for role component of FCGI_BeginRequestBody
|
||||
*/
|
||||
#define FCGI_RESPONDER 1
|
||||
#define FCGI_AUTHORIZER 2
|
||||
#define FCGI_FILTER 3
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned char appStatusB3;
|
||||
unsigned char appStatusB2;
|
||||
unsigned char appStatusB1;
|
||||
unsigned char appStatusB0;
|
||||
unsigned char protocolStatus;
|
||||
unsigned char reserved[3];
|
||||
} FCGI_EndRequestBody;
|
||||
|
||||
typedef struct {
|
||||
FCGI_Header header;
|
||||
FCGI_EndRequestBody body;
|
||||
} FCGI_EndRequestRecord;
|
||||
|
||||
/*
|
||||
* Values for protocolStatus component of FCGI_EndRequestBody
|
||||
*/
|
||||
#define FCGI_REQUEST_COMPLETE 0
|
||||
#define FCGI_CANT_MPX_CONN 1
|
||||
#define FCGI_OVERLOADED 2
|
||||
#define FCGI_UNKNOWN_ROLE 3
|
||||
|
||||
|
||||
/*
|
||||
* Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records
|
||||
*/
|
||||
#define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
|
||||
#define FCGI_MAX_REQS "FCGI_MAX_REQS"
|
||||
#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned char type;
|
||||
unsigned char reserved[7];
|
||||
} FCGI_UnknownTypeBody;
|
||||
|
||||
typedef struct {
|
||||
FCGI_Header header;
|
||||
FCGI_UnknownTypeBody body;
|
||||
} FCGI_UnknownTypeRecord;
|
||||
|
||||
#endif /* _FASTCGI_H */
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ADD_LIBRARY( fcgicc fcgicc.cc fcgicc.h )
|
||||
INSTALL( FILES fcgicc.h DESTINATION include )
|
||||
INSTALL( TARGETS fcgicc LIBRARY DESTINATION lib ARCHIVE DESTINATION lib )
|
||||
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Andrey Zholos. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of the FastCGI C++ Class library (fcgicc) version 0.1,
|
||||
* available at http://althenia.net/fcgicc
|
||||
*/
|
||||
|
||||
|
||||
#include "fcgicc.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <errno.h> // E*
|
||||
#include <unistd.h> // read, write, close, unlink
|
||||
#include <arpa/inet.h> // hton*
|
||||
#include <netinet/in.h> // sockaddr_in, INADDR_*
|
||||
#include <sys/select.h> // select, fd_set, FD_*, timeval
|
||||
#include <sys/socket.h> // socket, bind, accept, listen, sockaddr, AF_*, SOCK_*
|
||||
#include <sys/un.h> // sockaddr_un
|
||||
|
||||
#include "../fastcgi_devkit/fastcgi.h"
|
||||
|
||||
|
||||
FastCGIServer::RequestInfo::RequestInfo() :
|
||||
params_closed(false),
|
||||
in_closed(false),
|
||||
output_closed(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
FastCGIServer::Connection::Connection() :
|
||||
close_responsibility(false),
|
||||
close_socket(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FastCGIServer::FastCGIServer()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FastCGIServer::~FastCGIServer()
|
||||
{
|
||||
|
||||
if(my_pid != parent_pid) // if we're a child process, we must not close the handles
|
||||
return;
|
||||
|
||||
for (std::vector<int>::iterator it = listen_sockets.begin();
|
||||
it != listen_sockets.end(); ++it)
|
||||
close(*it);
|
||||
|
||||
for (std::vector<std::string>::iterator it = listen_unlink.begin();
|
||||
it != listen_unlink.end(); ++it)
|
||||
unlink(it->c_str());
|
||||
|
||||
for (std::map<int, Connection*>::iterator it = read_sockets.begin();
|
||||
it != read_sockets.end(); ++it) {
|
||||
close(it->first);
|
||||
for (RequestList::iterator req_it = it->second->requests.begin();
|
||||
req_it != it->second->requests.end(); ++req_it)
|
||||
delete req_it->second;
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::listen(unsigned tcp_port)
|
||||
{
|
||||
int listen_socket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (listen_socket == -1)
|
||||
throw std::runtime_error("socket() failed");
|
||||
|
||||
try {
|
||||
struct sockaddr_in sa;
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(tcp_port);
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (bind(listen_socket, (struct sockaddr*)&sa, sizeof(sa)) == -1)
|
||||
throw std::runtime_error("bind() failed");
|
||||
|
||||
if (::listen(listen_socket, 100))
|
||||
throw std::runtime_error("listen() failed");
|
||||
|
||||
listen_sockets.push_back(listen_socket);
|
||||
|
||||
} catch (...) {
|
||||
close(listen_socket);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::listen(const std::string& local_path)
|
||||
{
|
||||
int listen_socket = socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (listen_socket == -1)
|
||||
throw std::runtime_error("socket() failed");
|
||||
|
||||
try {
|
||||
struct sockaddr_un sa;
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sun_family = AF_LOCAL;
|
||||
|
||||
std::string::size_type size = local_path.size();
|
||||
if (size >= sizeof(sa.sun_path))
|
||||
throw std::runtime_error("path too long");
|
||||
if (local_path.find_first_of('\0') != std::string::npos)
|
||||
throw std::runtime_error("null character in path");
|
||||
|
||||
std::memcpy(sa.sun_path, local_path.data(), size);
|
||||
|
||||
unlink(local_path.c_str());
|
||||
try {
|
||||
if (bind(listen_socket, (struct sockaddr*)&sa,
|
||||
sizeof(sa) - (sizeof(sa.sun_path) - size - 1)) == -1)
|
||||
throw std::runtime_error("bind() failed");
|
||||
|
||||
if (::listen(listen_socket, 100))
|
||||
throw std::runtime_error("listen() failed");
|
||||
|
||||
listen_sockets.push_back(listen_socket);
|
||||
listen_unlink.push_back(local_path);
|
||||
|
||||
} catch (...) {
|
||||
unlink(local_path.c_str());
|
||||
throw;
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
close(listen_socket);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::abandon_files()
|
||||
{
|
||||
listen_unlink.clear();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::process(int timeout_ms)
|
||||
{
|
||||
char buffer[4096];
|
||||
fd_set fs_read;
|
||||
fd_set fs_write;
|
||||
int nfd = 0;
|
||||
struct timeval tv = { timeout_ms / 1000, (timeout_ms % 1000) * 1000 };
|
||||
|
||||
FD_ZERO(&fs_read);
|
||||
FD_ZERO(&fs_write);
|
||||
|
||||
for(auto socket_handle : listen_sockets)
|
||||
{
|
||||
FD_SET(socket_handle, &fs_read);
|
||||
nfd = std::max(nfd, socket_handle);
|
||||
}
|
||||
|
||||
for(auto con : read_sockets)
|
||||
{
|
||||
FD_SET(con.first, &fs_read);
|
||||
if (!con.second->output_buffer.empty())
|
||||
FD_SET(con.first, &fs_write);
|
||||
nfd = std::max(nfd, con.first);
|
||||
}
|
||||
|
||||
int select_result = select(nfd + 1, &fs_read, &fs_write, NULL,
|
||||
timeout_ms < 0 ? NULL : &tv);
|
||||
if (select_result == -1)
|
||||
if (errno == EINTR)
|
||||
return;
|
||||
else
|
||||
throw std::runtime_error("select() failed");
|
||||
|
||||
for(auto socket_handle : listen_sockets)
|
||||
if (FD_ISSET(socket_handle, &fs_read))
|
||||
{
|
||||
int posix_con = accept(socket_handle, NULL, NULL);
|
||||
if (posix_con == -1)
|
||||
throw std::runtime_error("accept() failed");
|
||||
read_sockets[posix_con] = new Connection();
|
||||
read_sockets[posix_con]->posix_con = posix_con;
|
||||
}
|
||||
|
||||
for (std::map<int, Connection*>::iterator it = read_sockets.begin();
|
||||
it != read_sockets.end();)
|
||||
{
|
||||
int read_socket = it->first;
|
||||
|
||||
if (FD_ISSET(read_socket, &fs_read))
|
||||
{
|
||||
int read_result = read(read_socket, buffer, sizeof(buffer));
|
||||
if (read_result == -1)
|
||||
if (errno == ECONNRESET)
|
||||
goto close_socket;
|
||||
else
|
||||
throw std::runtime_error("read() on socket failed");
|
||||
if (read_result == 0)
|
||||
it->second->close_socket = true;
|
||||
else {
|
||||
it->second->input_buffer.append(buffer, read_result);
|
||||
process_connection_read(*it->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (!it->second->output_buffer.empty() &&
|
||||
FD_ISSET(read_socket, &fs_write))
|
||||
{
|
||||
process_connection_write(*it->second);
|
||||
int write_result = write(read_socket,
|
||||
it->second->output_buffer.data(),
|
||||
it->second->output_buffer.size());
|
||||
if (write_result == -1)
|
||||
throw std::runtime_error("write() failed");
|
||||
it->second->output_buffer.erase(0, write_result);
|
||||
}
|
||||
|
||||
if (it->second->close_socket && it->second->output_buffer.empty())
|
||||
{
|
||||
close_socket:
|
||||
int close_result = close(it->first);
|
||||
if (close_result == -1 && errno != ECONNRESET)
|
||||
throw std::runtime_error("close() failed");
|
||||
Connection* connection = it->second;
|
||||
read_sockets.erase(it++);
|
||||
delete connection;
|
||||
} else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::process_forever()
|
||||
{
|
||||
for (;;)
|
||||
process();
|
||||
}
|
||||
|
||||
int
|
||||
FastCGIServer::call_completion_handler(FastCGIRequest& request)
|
||||
{
|
||||
//printf("- request complete\n");
|
||||
switch_to_arena(request.mem);
|
||||
auto result = on_complete(request);
|
||||
switch_to_system_alloc();
|
||||
return(result);
|
||||
}
|
||||
|
||||
void
|
||||
FastCGIServer::process_connection_read(Connection& connection)
|
||||
{
|
||||
std::string::size_type n = 0;
|
||||
while (connection.input_buffer.size() - n >= FCGI_HEADER_LEN) {
|
||||
const FCGI_Header& header = *reinterpret_cast<const FCGI_Header*>(
|
||||
connection.input_buffer.data() + n);
|
||||
if (header.version != FCGI_VERSION_1) {
|
||||
connection.close_socket = true;
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned content_length =
|
||||
(header.contentLengthB1 << 8) + header.contentLengthB0;
|
||||
if (connection.input_buffer.size() - n <
|
||||
FCGI_HEADER_LEN + content_length + header.paddingLength)
|
||||
break;
|
||||
const char* content =
|
||||
connection.input_buffer.data() + n + FCGI_HEADER_LEN;
|
||||
|
||||
RequestID request_id = (header.requestIdB1 << 8) + header.requestIdB0;
|
||||
|
||||
switch (header.type)
|
||||
{
|
||||
case FCGI_GET_VALUES:
|
||||
{
|
||||
Pairs pairs = parse_pairs(content, content_length);
|
||||
|
||||
std::string::size_type base = connection.output_buffer.size();
|
||||
connection.output_buffer.push_back(FCGI_VERSION_1);
|
||||
connection.output_buffer.push_back(FCGI_GET_VALUES_RESULT);
|
||||
connection.output_buffer.append(FCGI_HEADER_LEN - 2, 0);
|
||||
|
||||
for (Pairs::iterator it = pairs.begin(); it != pairs.end(); ++it)
|
||||
if (it->first == FCGI_MAX_CONNS)
|
||||
write_pair(connection.output_buffer,
|
||||
it->first, std::string("100"));
|
||||
else if (it->first == FCGI_MAX_REQS)
|
||||
write_pair(connection.output_buffer,
|
||||
it->first, std::string("1000"));
|
||||
else if (it->first == FCGI_MPXS_CONNS)
|
||||
write_pair(connection.output_buffer,
|
||||
it->first, std::string("1"));
|
||||
|
||||
std::string::size_type len = connection.output_buffer.size() - base;
|
||||
connection.output_buffer[base + 4] = (len >> 8) & 0xff;
|
||||
connection.output_buffer[base + 5] = len & 0xff;
|
||||
break;
|
||||
}
|
||||
case FCGI_BEGIN_REQUEST:
|
||||
{
|
||||
if (content_length < sizeof(FCGI_BeginRequestBody))
|
||||
break;
|
||||
const FCGI_BeginRequestBody& body =
|
||||
*reinterpret_cast<const FCGI_BeginRequestBody*>(content);
|
||||
|
||||
if (!(body.flags & FCGI_KEEP_CONN))
|
||||
connection.close_responsibility = true;
|
||||
|
||||
unsigned role = (body.roleB1 << 8) + body.roleB0;
|
||||
if (role != FCGI_RESPONDER)
|
||||
{
|
||||
FCGI_EndRequestRecord unknown;
|
||||
bzero(&unknown, sizeof(unknown));
|
||||
unknown.header.version = FCGI_VERSION_1;
|
||||
unknown.header.type = FCGI_END_REQUEST;
|
||||
unknown.header.contentLengthB0 = sizeof(unknown.body);
|
||||
unknown.body.protocolStatus = FCGI_UNKNOWN_ROLE;
|
||||
connection.output_buffer.append(
|
||||
reinterpret_cast<const char*>(&unknown), sizeof(unknown));
|
||||
if (connection.close_responsibility)
|
||||
connection.close_socket = true;
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
RequestList::iterator it = connection.requests.find(request_id);
|
||||
if (it != connection.requests.end())
|
||||
{
|
||||
//printf("- delete request object\n");
|
||||
switch_to_arena(it->second->mem);
|
||||
delete it->second;
|
||||
switch_to_system_alloc();
|
||||
connection.requests.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
if(connection.requests.size() > 1)
|
||||
{
|
||||
printf("(!) %i requests in flight at the same time!\n", connection.requests.size());
|
||||
}
|
||||
|
||||
//auto arena = new MemoryArena(server_state.config.MAX_MEMORY, "request");
|
||||
request_arena->clear();
|
||||
switch_to_arena(request_arena);
|
||||
RequestInfo* new_request = new RequestInfo();
|
||||
new_request->resources.fcgi_socket = connection.posix_con;
|
||||
new_request->mem = request_arena;
|
||||
new_request->stats.time_init = microtime();
|
||||
switch_to_system_alloc();
|
||||
connection.requests[request_id] = new_request;
|
||||
|
||||
break;
|
||||
}
|
||||
case FCGI_ABORT_REQUEST:
|
||||
{
|
||||
RequestList::iterator it = connection.requests.find(request_id);
|
||||
if (it == connection.requests.end())
|
||||
break;
|
||||
|
||||
FCGI_EndRequestRecord aborted;
|
||||
bzero(&aborted, sizeof(aborted));
|
||||
aborted.header.version = FCGI_VERSION_1;
|
||||
aborted.header.type = FCGI_END_REQUEST;
|
||||
aborted.header.contentLengthB0 = sizeof(aborted.body);
|
||||
aborted.body.appStatusB0 = 1;
|
||||
aborted.body.protocolStatus = FCGI_REQUEST_COMPLETE;
|
||||
connection.output_buffer.append(
|
||||
reinterpret_cast<const char*>(&aborted), sizeof(aborted));
|
||||
if (connection.close_responsibility)
|
||||
connection.close_socket = true;
|
||||
|
||||
delete it->second;
|
||||
connection.requests.erase(it);
|
||||
break;
|
||||
}
|
||||
case FCGI_PARAMS:
|
||||
{
|
||||
RequestList::iterator it = connection.requests.find(request_id);
|
||||
if (it == connection.requests.end())
|
||||
break;
|
||||
|
||||
RequestInfo& request = *it->second;
|
||||
switch_to_arena(it->second->mem);
|
||||
if (!request.params_closed)
|
||||
if (content_length != 0)
|
||||
request.params_buffer.append(content, content_length);
|
||||
else {
|
||||
request.params = parse_pairs(request.params_buffer.data(),
|
||||
request.params_buffer.size());
|
||||
request.params_buffer.clear();
|
||||
request.params_closed = true;
|
||||
|
||||
request.status = on_request(request);
|
||||
if (request.status == 0 && !request.in.empty())
|
||||
{
|
||||
request.status = on_data(request);
|
||||
if (request.status == 0 && request.in_closed)
|
||||
request.status = call_completion_handler(request);
|
||||
}
|
||||
process_write_request(connection, request_id, request);
|
||||
}
|
||||
switch_to_system_alloc();
|
||||
break;
|
||||
}
|
||||
case FCGI_STDIN:
|
||||
{
|
||||
RequestList::iterator it = connection.requests.find(request_id);
|
||||
if (it == connection.requests.end())
|
||||
break;
|
||||
|
||||
RequestInfo& request = *it->second;
|
||||
switch_to_arena(it->second->mem);
|
||||
if (!request.in_closed)
|
||||
if (content_length != 0) {
|
||||
request.in.append(content, content_length);
|
||||
if (request.params_closed && request.status == 0)
|
||||
{
|
||||
request.status = on_data(request);
|
||||
process_write_request(connection, request_id, request);
|
||||
}
|
||||
} else {
|
||||
request.in_closed = true;
|
||||
if (request.params_closed && request.status == 0) {
|
||||
request.status = call_completion_handler(request);
|
||||
process_write_request(connection, request_id, request);
|
||||
}
|
||||
}
|
||||
switch_to_system_alloc();
|
||||
break;
|
||||
}
|
||||
case FCGI_DATA:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
FCGI_UnknownTypeRecord unknown;
|
||||
bzero(&unknown, sizeof(unknown));
|
||||
unknown.header.version = FCGI_VERSION_1;
|
||||
unknown.header.type = FCGI_UNKNOWN_TYPE;
|
||||
unknown.header.contentLengthB0 = sizeof(unknown.body);
|
||||
unknown.body.type = header.type;
|
||||
connection.output_buffer.append(
|
||||
reinterpret_cast<const char*>(&unknown), sizeof(unknown));
|
||||
}
|
||||
}
|
||||
|
||||
n += FCGI_HEADER_LEN + content_length + header.paddingLength;
|
||||
}
|
||||
|
||||
connection.input_buffer.erase(0, n);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::process_write_request(Connection& connection, RequestID id,
|
||||
RequestInfo& request)
|
||||
{
|
||||
if (!request.out.empty())
|
||||
{
|
||||
write_data(connection.output_buffer, id, request.out, FCGI_STDOUT);
|
||||
switch_to_arena(request.mem);
|
||||
request.out.clear();
|
||||
switch_to_system_alloc();
|
||||
}
|
||||
if (!request.err.empty())
|
||||
{
|
||||
write_data(connection.output_buffer, id, request.err, FCGI_STDERR);
|
||||
switch_to_arena(request.mem);
|
||||
request.err.clear();
|
||||
switch_to_system_alloc();
|
||||
}
|
||||
if ((request.in_closed || request.status != 0) &&
|
||||
!request.output_closed)
|
||||
{
|
||||
switch_to_arena(request.mem);
|
||||
request.out =
|
||||
var_dump(request.header, "", "\r\n") +
|
||||
var_dump(request.set_cookies, "", "\r\n") +
|
||||
"\r\n";
|
||||
|
||||
for(auto obs : request.ob_stack)
|
||||
{
|
||||
request.out += obs->str();
|
||||
delete obs;
|
||||
}
|
||||
request.ob_stack.clear();
|
||||
|
||||
switch_to_system_alloc();
|
||||
write_data(connection.output_buffer, id, request.out, FCGI_STDOUT);
|
||||
write_data(connection.output_buffer, id, request.err, FCGI_STDERR);
|
||||
|
||||
request.stats.time_end = microtime();
|
||||
if(request.flags.log_request)
|
||||
printf("(r) pid:%i\t%s\t%0.6fs\tfps:%0.0f\tout:%0.1fkB\tmem:%0.0f/%0.0fkB\n",
|
||||
my_pid,
|
||||
request.params["REQUEST_URI"].c_str(),
|
||||
request.stats.time_end - request.stats.time_start,
|
||||
1.0 / (request.stats.time_end - request.stats.time_start),
|
||||
(f32)(request.out.length()/1024),
|
||||
(f32)(request.mem->size/1024),
|
||||
(f32)(request.mem->capacity/1024)
|
||||
);
|
||||
|
||||
FCGI_EndRequestRecord complete;
|
||||
bzero(&complete, sizeof(complete));
|
||||
complete.header.version = FCGI_VERSION_1;
|
||||
complete.header.type = FCGI_END_REQUEST;
|
||||
complete.header.requestIdB1 = (id >> 8) & 0xff;
|
||||
complete.header.requestIdB0 = id & 0xff;
|
||||
complete.header.contentLengthB0 = sizeof(complete.body);
|
||||
complete.body.appStatusB3 = (request.status >> 24) & 0xff;
|
||||
complete.body.appStatusB2 = (request.status >> 16) & 0xff;
|
||||
complete.body.appStatusB1 = (request.status >> 8) & 0xff;
|
||||
complete.body.appStatusB0 = request.status & 0xff;
|
||||
complete.body.protocolStatus = FCGI_REQUEST_COMPLETE;
|
||||
connection.output_buffer.append(
|
||||
reinterpret_cast<const char*>(&complete), sizeof(complete));
|
||||
if (connection.close_responsibility)
|
||||
connection.close_socket = true;
|
||||
|
||||
request.output_closed = true;
|
||||
//printf("- output done\n");
|
||||
}
|
||||
switch_to_system_alloc();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::process_connection_write(Connection& connection)
|
||||
{
|
||||
for (RequestList::iterator it = connection.requests.begin();
|
||||
it != connection.requests.end();)
|
||||
{
|
||||
process_write_request(connection, it->first, *it->second);
|
||||
if (it->second->params_closed && it->second->in_closed)
|
||||
{
|
||||
switch_to_arena(it->second->mem);
|
||||
//printf("- process_connection_write close\n");
|
||||
delete it->second;
|
||||
switch_to_system_alloc();
|
||||
connection.requests.erase(it++);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FastCGIServer::Pairs
|
||||
FastCGIServer::parse_pairs(const char* data, std::string::size_type n)
|
||||
{
|
||||
Pairs pairs;
|
||||
|
||||
const unsigned char* u = reinterpret_cast<const unsigned char*>(data);
|
||||
|
||||
for (std::string::size_type m = 0; m < n;) {
|
||||
std::string::size_type name_length, value_length;
|
||||
|
||||
if (u[m] >> 7) {
|
||||
if (n - m < 4)
|
||||
break;
|
||||
name_length = ((u[m] & 0x7f) << 24) + (u[m + 1] << 16) +
|
||||
(u[m + 2] << 8) + u[m + 3];
|
||||
m += 4;
|
||||
} else
|
||||
name_length = u[m++];
|
||||
if (m >= n)
|
||||
break;
|
||||
|
||||
if (u[m] >> 7) {
|
||||
if (n - m < 4)
|
||||
break;
|
||||
value_length = ((u[m] & 0x7f) << 24) + (u[m + 1] << 16) +
|
||||
(u[m + 2] << 8) + u[m + 3];
|
||||
m += 4;
|
||||
} else
|
||||
value_length = u[m++];
|
||||
|
||||
if (n - m < name_length)
|
||||
break;
|
||||
std::string key(data + m, name_length);
|
||||
m += name_length;
|
||||
|
||||
if (n - m < value_length)
|
||||
break;
|
||||
pairs.insert(Pairs::value_type(
|
||||
key, std::string(data + m, value_length)));
|
||||
m += value_length;
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::write_pair(std::string& buffer,
|
||||
const std::string& key, const std::string& value)
|
||||
{
|
||||
if (key.size() > 0x7f) {
|
||||
buffer.push_back(0x80 + ((key.size() >> 24) & 0x7f));
|
||||
buffer.push_back((key.size() >> 16) & 0xff);
|
||||
buffer.push_back((key.size() >> 8) & 0xff);
|
||||
buffer.push_back(key.size() & 0xff);
|
||||
} else
|
||||
buffer.push_back(key.size());
|
||||
|
||||
if (value.size() > 0x7f) {
|
||||
buffer.push_back(0x80 + ((value.size() >> 24) & 0x7f));
|
||||
buffer.push_back((value.size() >> 16) & 0xff);
|
||||
buffer.push_back((value.size() >> 8) & 0xff);
|
||||
buffer.push_back(value.size() & 0xff);
|
||||
} else
|
||||
buffer.push_back(value.size());
|
||||
|
||||
buffer.append(key);
|
||||
buffer.append(value);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FastCGIServer::write_data(std::string& buffer, RequestID id,
|
||||
const std::string& input, unsigned char type)
|
||||
{
|
||||
FCGI_Header header;
|
||||
bzero(&header, sizeof(header));
|
||||
header.version = FCGI_VERSION_1;
|
||||
header.type = type;
|
||||
header.requestIdB1 = (id >> 8) & 0xff;
|
||||
header.requestIdB0 = id & 0xff;
|
||||
|
||||
for (std::string::size_type n = 0;;) {
|
||||
std::string::size_type written = std::min(input.size() - n,
|
||||
(std::string::size_type)0xffffu);
|
||||
|
||||
header.contentLengthB1 = written >> 8;
|
||||
header.contentLengthB0 = written & 0xff;
|
||||
header.paddingLength = (8 - (written % 8)) % 8;
|
||||
buffer.append(
|
||||
reinterpret_cast<const char*>(&header), sizeof(header));
|
||||
buffer.append(input.data() + n, written);
|
||||
buffer.append(header.paddingLength, 0);
|
||||
|
||||
n += written;
|
||||
if (n == input.size())
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Andrey Zholos. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of the FastCGI C++ Class library (fcgicc) version 0.1,
|
||||
* available at http://althenia.net/fcgicc
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FCGICC_H
|
||||
#define FCGICC_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class FastCGIServer {
|
||||
public:
|
||||
FastCGIServer();
|
||||
~FastCGIServer();
|
||||
|
||||
// called when the parameters and standard input have been receieved
|
||||
std::function<int(FastCGIRequest&)> on_request = 0;
|
||||
std::function<int(FastCGIRequest&)> on_data = 0;
|
||||
std::function<int(FastCGIRequest&)> on_complete = 0;
|
||||
|
||||
void listen(unsigned tcp_port);
|
||||
void listen(const std::string& local_path);
|
||||
void abandon_files();
|
||||
|
||||
void process(int timeout_ms = -1); // timeout_ms<0 blocks forever
|
||||
void process_forever();
|
||||
|
||||
int call_completion_handler(FastCGIRequest& request);
|
||||
|
||||
protected:
|
||||
struct RequestInfo : FastCGIRequest {
|
||||
RequestInfo();
|
||||
|
||||
std::string params_buffer;
|
||||
bool params_closed;
|
||||
bool in_closed;
|
||||
int status;
|
||||
bool output_closed;
|
||||
|
||||
friend class FastCGIServer;
|
||||
};
|
||||
|
||||
typedef unsigned RequestID;
|
||||
typedef std::map<RequestID, RequestInfo*> RequestList;
|
||||
struct Connection {
|
||||
Connection();
|
||||
|
||||
RequestList requests;
|
||||
u64 posix_con = 0;
|
||||
std::string input_buffer;
|
||||
std::string output_buffer;
|
||||
bool close_responsibility;
|
||||
bool close_socket;
|
||||
};
|
||||
|
||||
typedef StringMap Pairs;
|
||||
|
||||
std::vector<int> listen_sockets;
|
||||
std::vector<std::string> listen_unlink;
|
||||
|
||||
std::map<int, Connection*> read_sockets;
|
||||
|
||||
void process_connection_read(Connection&);
|
||||
static void process_write_request(Connection&, RequestID, RequestInfo&);
|
||||
static void process_connection_write(Connection&);
|
||||
static Pairs parse_pairs(const char*, std::string::size_type);
|
||||
static void write_pair(std::string& buffer,
|
||||
const std::string& key, const std::string&);
|
||||
static void write_data(std::string& buffer, RequestID id,
|
||||
const std::string& input, unsigned char type);
|
||||
|
||||
};
|
||||
|
||||
#endif // !FCGICC_H
|
||||
@@ -0,0 +1,5 @@
|
||||
ADD_EXECUTABLE( test1 test1.cc )
|
||||
TARGET_LINK_LIBRARIES( test1 fcgicc )
|
||||
ADD_EXECUTABLE( test2 test2.cc )
|
||||
TARGET_LINK_LIBRARIES( test2 fcgicc )
|
||||
INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/src )
|
||||
@@ -0,0 +1,12 @@
|
||||
server.bind = "127.0.0.1"
|
||||
server.port = 8080
|
||||
server.modules = ( "mod_fastcgi" )
|
||||
server.document-root = "."
|
||||
|
||||
fastcgi.server = (
|
||||
"/" => ((
|
||||
"host" => "127.0.0.1",
|
||||
"port" => 7000,
|
||||
"check-local" => "disable"
|
||||
))
|
||||
)
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Andrey Zholos. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of the FastCGI C++ Class library (fcgicc) version 0.1,
|
||||
* available at http://althenia.net/fcgicc
|
||||
*/
|
||||
|
||||
|
||||
#include <fcgicc.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
|
||||
int handle_request(FastCGIRequest& request) {
|
||||
// This is always the first event to occur. It occurs when the
|
||||
// server receives all parameters. There may be more data coming on the
|
||||
// standard input stream.
|
||||
|
||||
if (request.params.count("REQUEST_URI"))
|
||||
return 0; // OK, continue processing
|
||||
else
|
||||
return 1; // Stop processing and return error code
|
||||
}
|
||||
|
||||
int handle_data(FastCGIRequest& request) {
|
||||
// This event occurs when data is received on the standard input stream.
|
||||
// A simple string is used to hold the input stream, so it is the
|
||||
// responsibility of the application to remember which data it has
|
||||
// processed. The application may modify it; new data will be appended
|
||||
// to it by the server. The same goes for the output and error streams:
|
||||
// the application should append data to them; the server will remove
|
||||
// all sent data from them.
|
||||
|
||||
std::transform(request.in.begin(), request.in.end(),
|
||||
std::back_inserter(request.err),
|
||||
std::bind1st(std::plus<char>(), 1));
|
||||
request.in.clear(); // don't process it again
|
||||
return 0; // still OK
|
||||
}
|
||||
|
||||
class Application {
|
||||
public:
|
||||
int handle_complete(FastCGIRequest& request) {
|
||||
// The event handler can also be a class member function. This
|
||||
// event occurs when the parameters and standard input streams are
|
||||
// both closed, and thus the request is complete.
|
||||
|
||||
request.out.append("Content-Type: text/plain\r\n\r\n");
|
||||
request.out.append("You requested: ");
|
||||
request.out.append(request.params[std::string("REQUEST_URI")]);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
Application application;
|
||||
|
||||
FastCGIServer server; // Instantiate a server
|
||||
|
||||
// Set up our request handlers
|
||||
server.request_handler(&handle_request);
|
||||
server.data_handler(&handle_data);
|
||||
server.complete_handler(application, &Application::handle_complete);
|
||||
|
||||
server.listen(7000); // Listen on a TCP port
|
||||
server.listen(7001); // ... or on two
|
||||
server.listen("./socket"); // ... and also on a local doman socket
|
||||
|
||||
server.process(100); // Process some data, but don't wait more
|
||||
// than 100 ms for it to arrive.
|
||||
server.process(); // Process some data with no timeout
|
||||
server.process_forever(); // Process everything
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
* Copyright 2008, 2009 Andrey Zholos. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file is part of the FastCGI C++ Class library (fcgicc) version 0.1,
|
||||
* available at http://althenia.net/fcgicc
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
$ ./test2
|
||||
|
||||
This starts a simple FastCGI server on ports 7000-7009. It responds to HTTP
|
||||
requests from a server such as lighttpd with the provided lighttpd.conf. It
|
||||
also responds with a particular transformation of standard input.
|
||||
|
||||
$ ./test2 -c
|
||||
|
||||
This acts as a client by sending multiple concurrent data requests to the
|
||||
handler on ports 7000 through 7009 and validates the responses.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#include <fcgicc.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <fastcgi.h>
|
||||
|
||||
|
||||
static const int base_port = 7000;
|
||||
static const int processes = 25;
|
||||
static const int requests = 1000;
|
||||
|
||||
static const std::string param_rot13("ROT13");
|
||||
|
||||
|
||||
struct Rot13 : public std::unary_function<char, char> {
|
||||
char operator() (char c) const {
|
||||
if (c >= 'a' && c <= 'm' || c >= 'A' && c <= 'M')
|
||||
return c + 13;
|
||||
if (c >= 'n' && c <= 'z' || c >= 'N' && c <= 'Z')
|
||||
return c - 13;
|
||||
return c;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct RandomChar {
|
||||
char operator()() const {
|
||||
return rand() % 256;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Handler {
|
||||
int handle_request(FastCGIRequest& request)
|
||||
{
|
||||
static const std::string request_uri("REQUEST_URI");
|
||||
FastCGIRequest::Params::const_iterator it =
|
||||
request.params.find(request_uri);
|
||||
if (it != request.params.end()) {
|
||||
request.out.append("Content-Type: text/html\r\n\r\n"
|
||||
"<html><body><h3>FastCGI C++ Class (fcgicc) test</h3>"
|
||||
"<p>Request: ");
|
||||
request.out.append(it->second);
|
||||
request.out.append("</p></body></html>\n");
|
||||
} else {
|
||||
it = request.params.find(param_rot13);
|
||||
if (it != request.params.end())
|
||||
std::transform(it->second.begin(), it->second.end(),
|
||||
std::back_inserter(request.out), Rot13());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int handle_data(FastCGIRequest& request)
|
||||
{
|
||||
std::transform(request.in.begin(), request.in.end(),
|
||||
std::back_inserter(request.out), Rot13());
|
||||
request.in.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void server()
|
||||
{
|
||||
Handler handler;
|
||||
|
||||
FastCGIServer server;
|
||||
server.request_handler(handler, &Handler::handle_request);
|
||||
server.data_handler(&handle_data);
|
||||
for (int i = 0; i < 10; i++)
|
||||
server.listen(base_port + i);
|
||||
server.process_forever();
|
||||
}
|
||||
|
||||
|
||||
struct Querier {
|
||||
int socket;
|
||||
|
||||
void write(const char* data, size_t length) {
|
||||
for (;;) {
|
||||
int result = ::write(socket, data, length);
|
||||
if (result == length)
|
||||
return;
|
||||
if (result <= 0)
|
||||
throw std::runtime_error("write() failed in data client");
|
||||
data += result;
|
||||
length -= result;
|
||||
}
|
||||
}
|
||||
|
||||
bool read(std::string& output) {
|
||||
char buf[4096];
|
||||
int result = ::read(socket, buf, sizeof(buf));
|
||||
if (result == -1)
|
||||
throw std::runtime_error("read() failed in data client");
|
||||
if (result == 0)
|
||||
return false;
|
||||
output.append(buf, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
void write_stream(int type, const std::string& stream, size_t& i) {
|
||||
size_t n = std::min(stream.size() - i, (std::string::size_type)65535);
|
||||
if (n >= 256 || n > 1 && rand() % 3 != 0)
|
||||
n = rand() % (n - 1) + 1;
|
||||
|
||||
size_t m = i == stream.size() || rand() % 3 != 0 ? 0 :
|
||||
rand() % std::min(stream.size() - i, (std::string::size_type)63);
|
||||
char padding[m];
|
||||
bzero(padding, m);
|
||||
|
||||
FCGI_Header header;
|
||||
bzero(&header, sizeof(header));
|
||||
header.version = FCGI_VERSION_1;
|
||||
header.type = type;
|
||||
header.contentLengthB1 = n / 256;
|
||||
header.contentLengthB0 = n % 256;
|
||||
header.paddingLength = m;
|
||||
|
||||
write(reinterpret_cast<const char*>(&header), sizeof(header));
|
||||
write(stream.data() + i, n);
|
||||
write(padding, m);
|
||||
i += n;
|
||||
}
|
||||
|
||||
void encode_size(std::string& params, size_t n) {
|
||||
if (n >> 7 == 0)
|
||||
params.push_back(static_cast<char>(n));
|
||||
else {
|
||||
char c[4];
|
||||
c[0] = static_cast<char>(n >> 24 | 0x80);
|
||||
c[1] = static_cast<char>(n >> 16);
|
||||
c[2] = static_cast<char>(n >> 8);
|
||||
c[3] = static_cast<char>(n);
|
||||
params.append(c, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void random_params(std::string& params) {
|
||||
int i = rand() % 15;
|
||||
if (i > 10)
|
||||
i = 0;
|
||||
for (; i >= 0; i--) {
|
||||
size_t m = rand() % 300, n = rand() % 700;
|
||||
encode_size(params, m + 1);
|
||||
encode_size(params, n);
|
||||
params.push_back('_');
|
||||
std::generate_n(std::back_inserter(params), m + n, RandomChar());
|
||||
}
|
||||
}
|
||||
|
||||
void process() {
|
||||
for (int i = 0; i < requests; i++) {
|
||||
// Generate random request and send it either as a special parameter
|
||||
// or as the standard input stream. Pad with random parameters.
|
||||
std::string params, in, request;
|
||||
|
||||
std::generate_n(std::back_inserter(request),
|
||||
rand() % 10000, RandomChar());
|
||||
|
||||
random_params(params);
|
||||
if (rand() % 3 == 0)
|
||||
in.append(request);
|
||||
else {
|
||||
encode_size(params, param_rot13.size());
|
||||
encode_size(params, request.size());
|
||||
params.append(param_rot13);
|
||||
params.append(request);
|
||||
}
|
||||
random_params(params);
|
||||
|
||||
// Connect to the server
|
||||
socket = ::socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (socket == -1)
|
||||
throw std::runtime_error("socket() failed in data client");
|
||||
|
||||
struct sockaddr_in sa;
|
||||
bzero(&sa, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_port = htons(base_port + rand() % 10);
|
||||
sa.sin_addr.s_addr = htonl(0x7f000001);
|
||||
if (::connect(socket, (struct sockaddr*)&sa, sizeof(sa)) == -1)
|
||||
throw std::runtime_error("connect() failed in data client");
|
||||
|
||||
static FCGI_BeginRequestRecord begin;
|
||||
bzero(&begin, sizeof(begin));
|
||||
begin.header.version = FCGI_VERSION_1;
|
||||
begin.header.type = FCGI_BEGIN_REQUEST;
|
||||
begin.header.contentLengthB0 = sizeof(begin.body);
|
||||
begin.body.roleB0 = FCGI_RESPONDER;
|
||||
if (rand() % 3 == 0)
|
||||
begin.body.flags = FCGI_KEEP_CONN;
|
||||
write(reinterpret_cast<const char*>(&begin), sizeof(begin));
|
||||
|
||||
// Send streams in random chunks
|
||||
size_t in_i = 0, params_i = 0;
|
||||
while (in_i < in.size() || params_i < params.size()) {
|
||||
if (in_i == in.size() ||
|
||||
params_i < params.size() && rand() % 3 == 0)
|
||||
write_stream(FCGI_PARAMS, params, params_i);
|
||||
else
|
||||
write_stream(FCGI_STDIN, in, in_i);
|
||||
}
|
||||
if (rand() % 3 == 0) {
|
||||
write_stream(FCGI_PARAMS, params, params_i);
|
||||
write_stream(FCGI_STDIN, in, in_i);
|
||||
} else {
|
||||
write_stream(FCGI_STDIN, in, in_i);
|
||||
write_stream(FCGI_PARAMS, params, params_i);
|
||||
}
|
||||
|
||||
// Sometimes close our end of the socket
|
||||
if (rand() % 5 == 0)
|
||||
if (::shutdown(socket, SHUT_WR) == -1)
|
||||
throw std::runtime_error("shutdown() failed "
|
||||
"in data client");
|
||||
|
||||
// Receive and verify results
|
||||
std::string output;
|
||||
std::string out, err;
|
||||
bool closed_out = false, closed_err = false;
|
||||
while (read(output)) {
|
||||
another_record:
|
||||
if (output.size() < sizeof(FCGI_Header))
|
||||
continue;
|
||||
const FCGI_Header& header =
|
||||
*reinterpret_cast<const FCGI_Header*>(output.data());
|
||||
|
||||
if (header.version != FCGI_VERSION_1)
|
||||
throw std::runtime_error("received: incorrect version");
|
||||
|
||||
size_t content = (header.contentLengthB1 << 8) +
|
||||
header.contentLengthB0;
|
||||
size_t padding = header.paddingLength;
|
||||
if (output.size() - sizeof(FCGI_Header) < content + padding)
|
||||
continue;
|
||||
|
||||
switch (header.type) {
|
||||
case FCGI_STDOUT:
|
||||
if (closed_out)
|
||||
throw std::runtime_error("received: "
|
||||
"data on closed stream");
|
||||
out.append(output.data() + sizeof(FCGI_Header), content);
|
||||
if (content == 0)
|
||||
closed_out = true;
|
||||
break;
|
||||
case FCGI_STDERR:
|
||||
if (closed_err)
|
||||
throw std::runtime_error("received: "
|
||||
"data on closed stream");
|
||||
err.append(output.data() + sizeof(FCGI_Header), content);
|
||||
if (content == 0)
|
||||
closed_err = true;
|
||||
break;
|
||||
case FCGI_END_REQUEST: {
|
||||
if (!closed_out || !closed_err)
|
||||
throw std::runtime_error("received: "
|
||||
"streams not closed");
|
||||
if (output.size() - sizeof(FCGI_Header) <
|
||||
sizeof(FCGI_EndRequestBody))
|
||||
continue;
|
||||
const FCGI_EndRequestBody& body =
|
||||
*reinterpret_cast<const FCGI_EndRequestBody*>(
|
||||
output.data() + sizeof(FCGI_Header));
|
||||
if ((body.appStatusB3 | body.appStatusB2 |
|
||||
body.appStatusB1 | body.appStatusB0) != 0)
|
||||
throw std::runtime_error("received: bad exit code");
|
||||
if (body.protocolStatus != FCGI_REQUEST_COMPLETE)
|
||||
throw std::runtime_error("received: bad status");
|
||||
if (content + padding !=
|
||||
output.size() - sizeof(FCGI_Header))
|
||||
throw std::runtime_error("received: extra data");
|
||||
goto request_complete;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("received: unexpected type");
|
||||
}
|
||||
|
||||
output.erase(0, sizeof(FCGI_Header) + content + padding);
|
||||
goto another_record;
|
||||
}
|
||||
throw std::runtime_error("received: not enough data");
|
||||
|
||||
request_complete:
|
||||
if (::close(socket) == -1 && errno != ECONNRESET)
|
||||
throw std::runtime_error("close() failed in data client");
|
||||
|
||||
if (!err.empty())
|
||||
throw std::runtime_error("received: incorrect stderr");
|
||||
std::string result;
|
||||
std::transform(out.begin(), out.end(),
|
||||
std::back_inserter(result), Rot13());
|
||||
if (result != request)
|
||||
throw std::runtime_error("received: incorrect stdout");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void client()
|
||||
{
|
||||
for (int i = 0; i < processes; i++) {
|
||||
switch (::fork()) {
|
||||
case -1:
|
||||
throw std::runtime_error("fork() failed");
|
||||
case 0:
|
||||
::srand(i);
|
||||
Querier querier;
|
||||
querier.process();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < processes; i++) {
|
||||
int status;
|
||||
if (::wait(&status) == -1)
|
||||
throw std::runtime_error("wait() failed");
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
||||
throw std::runtime_error("data client failed");
|
||||
}
|
||||
std::cout << "Success!\n";
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
try {
|
||||
static const std::string arg_client("-c");
|
||||
for (int i = 1; i < argc; i++)
|
||||
if (argv[i] == arg_client) {
|
||||
client();
|
||||
return 0;
|
||||
}
|
||||
|
||||
server();
|
||||
return 0;
|
||||
|
||||
} catch (std::exception& e) {
|
||||
std::cerr << "Error: " << e.what() << ".\n";
|
||||
} catch (...) {
|
||||
std::cerr << "Error.\n";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
#include "compiler.h"
|
||||
#include <sys/file.h>
|
||||
|
||||
String process_html_literal(Request* context, SharedUnit* su, String content)
|
||||
{
|
||||
String pc;
|
||||
String HT_START = "print(R\"(";
|
||||
String HT_END = ")\");";
|
||||
|
||||
u8 mode = 0;
|
||||
char quote_char;
|
||||
bool inside_quote = false;
|
||||
String code_buffer = "";
|
||||
bool is_field = false;
|
||||
|
||||
for(u32 i = 0; i < content.length(); i++)
|
||||
{
|
||||
char c = content[i];
|
||||
|
||||
switch(mode)
|
||||
{
|
||||
case(0):
|
||||
if(c == '<' && content[i+1] == '?')
|
||||
{
|
||||
code_buffer = "";
|
||||
if(content[i+2] == '=')
|
||||
{
|
||||
is_field = true;
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
is_field = false;
|
||||
i += 1;
|
||||
}
|
||||
mode = 1; // code-parsing mode
|
||||
}
|
||||
else
|
||||
{
|
||||
pc.append(1, c);
|
||||
}
|
||||
break;
|
||||
case(1):
|
||||
if(inside_quote)
|
||||
{
|
||||
if(quote_char == c && content[i-1] != '\\')
|
||||
inside_quote = false;
|
||||
code_buffer.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(c == '\"' || c == '\'')
|
||||
{
|
||||
inside_quote = true;
|
||||
quote_char = c;
|
||||
code_buffer.append(1, c);
|
||||
}
|
||||
else if(c == '?' && content[i+1] == '>')
|
||||
{
|
||||
mode = 0;
|
||||
i += 1;
|
||||
if(is_field)
|
||||
{
|
||||
pc.append(
|
||||
HT_END +
|
||||
"print(html_escape( " +
|
||||
code_buffer +
|
||||
" )); " +
|
||||
HT_START
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
pc.append(HT_END + code_buffer + HT_START);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
code_buffer.append(1, c);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return(HT_START + pc + HT_END);
|
||||
}
|
||||
|
||||
String preprocess_shared_unit(Request* context, SharedUnit* su)
|
||||
{
|
||||
String content = file_get_contents(su->file_name);
|
||||
printf("(c) compiling with root dir %s\n", context->server->config.COMPILER_SYS_PATH.c_str());
|
||||
String pc = ("#include \"")+context->server->config.COMPILER_SYS_PATH +"/src/lib/uce_lib.h\" \n";
|
||||
String token = "";
|
||||
String html_buffer = "";
|
||||
u8 mode = 0;
|
||||
bool inside_quote = false;
|
||||
for(u32 i = 0; i < content.length(); i++)
|
||||
{
|
||||
char c = content[i];
|
||||
if(mode == 2)
|
||||
{
|
||||
auto end_pos = content.find(String("</")+token+">", i);
|
||||
if(end_pos != String::npos)
|
||||
{
|
||||
u32 len = token.length() + 3 + end_pos - i;
|
||||
html_buffer.append(content.substr(i, len - (token.length() == 0 ? 3 : 0)));
|
||||
i += len - 1;
|
||||
pc.append(process_html_literal(context, su, html_buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("(!) unterminated HTML literal <%s> in %s", token.c_str(), su->file_name.c_str());
|
||||
}
|
||||
mode = 0;
|
||||
}
|
||||
else if(mode == 1)
|
||||
{
|
||||
if(isspace(c) || c == '>')
|
||||
{
|
||||
mode = 2;
|
||||
if(token.length() > 0)
|
||||
html_buffer.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
token.append(1, c);
|
||||
html_buffer.append(1, c);
|
||||
}
|
||||
}
|
||||
else if(!inside_quote && c == '<' && (content[i+1] == '>'/* || isalpha(content[i+1])*/))
|
||||
{
|
||||
mode = 1;
|
||||
token = "";
|
||||
html_buffer = "";
|
||||
if(content[i+1] != '>')
|
||||
html_buffer.append(1, c);
|
||||
}
|
||||
else if(c == '\"')
|
||||
{
|
||||
inside_quote = !inside_quote;
|
||||
pc.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
pc.append(1, c);
|
||||
}
|
||||
}
|
||||
return(pc);
|
||||
}
|
||||
|
||||
void setup_unit_paths(Request* context, SharedUnit* su, String file_name)
|
||||
{
|
||||
su->file_name = file_name;
|
||||
|
||||
if(su->src_path.length() > 0) // we did this already
|
||||
return;
|
||||
|
||||
su->src_path = dirname(file_name);
|
||||
su->bin_path = context->server->config.BIN_DIRECTORY + su->src_path;
|
||||
su->pre_path = context->server->config.BIN_DIRECTORY + su->src_path;
|
||||
|
||||
su->src_file_name = basename(file_name);
|
||||
su->bin_file_name = su->src_file_name + ".so";
|
||||
su->pre_file_name = su->src_file_name + ".cpp";
|
||||
|
||||
su->so_name = su->bin_path + "/" + su->bin_file_name;
|
||||
}
|
||||
|
||||
void load_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
{
|
||||
//setup_unit_paths(context, su, file_name);
|
||||
|
||||
su->on_render = 0;
|
||||
su->on_setup = 0;
|
||||
su->compiler_messages = "";
|
||||
|
||||
if(!file_exists(su->so_name))
|
||||
{
|
||||
printf("(i) unit file not found: %s\n", su->so_name.c_str());
|
||||
su->compiler_messages = "unit file not found";
|
||||
return;
|
||||
}
|
||||
|
||||
su->so_handle = dlopen(su->so_name.c_str(), RTLD_NOW);
|
||||
if(su->so_handle)
|
||||
{
|
||||
su->last_compiled = file_mtime(su->so_name);
|
||||
char *error;
|
||||
su->on_setup = (request_handler)dlsym(su->so_handle, "set_current_request");
|
||||
su->on_render = (call_handler)dlsym(su->so_handle, "render");
|
||||
if ((error = dlerror()) != NULL)
|
||||
printf("Error - %s in %s\n", error, su->file_name.c_str());
|
||||
else
|
||||
printf("(i) loaded unit %s\n", su->file_name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error loading unit %s, could not open %s\n", su->file_name.c_str(), su->so_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void compile_shared_unit(Request* context, SharedUnit* su, String file_name)
|
||||
{
|
||||
//setup_unit_paths(context, su, file_name);
|
||||
|
||||
if(!file_exists(su->file_name))
|
||||
{
|
||||
su->compiler_messages = "source file not found (" + su->file_name + ")";
|
||||
return;
|
||||
}
|
||||
|
||||
shell_exec("mkdir -p " + shell_escape(su->pre_path));
|
||||
file_put_contents(su->pre_path + "/" + su->pre_file_name, preprocess_shared_unit(context, su));
|
||||
|
||||
printf("Config.COMPILE_SCRIPT %s\n", String(su->pre_path + "/" + su->pre_file_name).c_str());
|
||||
|
||||
su->compiler_messages = trim(shell_exec(context->server->config.COMPILE_SCRIPT+" "+
|
||||
shell_escape(su->src_path)+" "+
|
||||
shell_escape(su->bin_path)+" "+
|
||||
shell_escape(su->file_name)+" "+
|
||||
shell_escape(su->pre_file_name)+" "+
|
||||
shell_escape(su->bin_file_name)
|
||||
));
|
||||
|
||||
if(su->compiler_messages.length() > 0)
|
||||
{
|
||||
printf("%s \n", su->compiler_messages.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
load_shared_unit(context, su, file_name);
|
||||
printf("(i) compiled unit %s\n", file_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
SharedUnit* get_shared_unit(Request* context, String file_name)
|
||||
{
|
||||
SharedUnit* su = context->server->units[file_name];
|
||||
auto mod_time = file_mtime(file_name);
|
||||
bool do_recompile = false;
|
||||
if(su && (su->last_compiled < mod_time || mod_time == 0))
|
||||
{
|
||||
delete su;
|
||||
su = 0;
|
||||
do_recompile = true;
|
||||
}
|
||||
if(!su)
|
||||
{
|
||||
su = new SharedUnit();
|
||||
setup_unit_paths(context, su, file_name);
|
||||
if(!do_recompile)
|
||||
{
|
||||
// if we didn't decide to force recompile yet, we need to check
|
||||
// (this case should only happen if the SU cache for that entry is cold
|
||||
// but the SO itself exists _AND_ is stale)
|
||||
su->last_compiled = file_mtime(su->so_name);
|
||||
if(su->last_compiled < mod_time || mod_time == 0)
|
||||
do_recompile = true;
|
||||
}
|
||||
|
||||
int fdlock = open((su->so_name+".lock").c_str(), O_RDWR | O_CREAT, 0666 );
|
||||
int fl_excl = flock(fdlock, LOCK_EX);
|
||||
|
||||
if(do_recompile)
|
||||
{
|
||||
compile_shared_unit(context, su, file_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
load_shared_unit(context, su, file_name);
|
||||
if(!su->so_handle)
|
||||
compile_shared_unit(context, su, file_name);
|
||||
}
|
||||
|
||||
flock(fdlock, LOCK_UN);
|
||||
close(fdlock);
|
||||
remove((su->so_name+".lock").c_str());
|
||||
|
||||
context->server->units[file_name] = su;
|
||||
}
|
||||
return(su);
|
||||
}
|
||||
|
||||
void compiler_invoke(Request* context, String file_name, DTree& call_param)
|
||||
{
|
||||
|
||||
if(file_name[0] != '/')
|
||||
{
|
||||
file_name = expand_path(file_name);
|
||||
}
|
||||
//printf("(i) invoke %s\n", file_name.c_str());
|
||||
|
||||
//printf("(i) invoke(%s)\n", file_name.c_str());
|
||||
switch_to_system_alloc();
|
||||
auto su = get_shared_unit(context, file_name);
|
||||
switch_to_arena(context->mem);
|
||||
if(!su)
|
||||
{
|
||||
printf("Error loading unit %s\n", file_name.c_str());
|
||||
print("Error loading unit: "+file_name);
|
||||
}
|
||||
else if(!su->on_render)
|
||||
{
|
||||
context->header["Content-Type"] = "text/plain";
|
||||
print("Compiler error: "+su->compiler_messages);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(su->compiler_messages.length() > 0)
|
||||
print(su->compiler_messages);
|
||||
else
|
||||
{
|
||||
String prev_wd = get_cwd();
|
||||
set_cwd(su->src_path);
|
||||
su->on_setup(context);
|
||||
su->on_render(call_param);
|
||||
set_cwd(prev_wd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#define RENDER() extern "C" void render(DTree& call)
|
||||
|
||||
String process_html_literal(Request* context, SharedUnit* su, String content);
|
||||
String preprocess_shared_unit(Request* context, SharedUnit* su);
|
||||
void setup_unit_paths(Request* context, SharedUnit* su, String file_name);
|
||||
void load_shared_unit(Request* context, SharedUnit* su, String file_name);
|
||||
void compile_shared_unit(Request* context, SharedUnit* su, String file_name);
|
||||
SharedUnit* get_shared_unit(Request* context, String file_name);
|
||||
void compiler_invoke(Request* context, String file_name, DTree& call_param);
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
|
||||
void DTree::each(std::function <void (DTree t, String key)> f)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case('M'):
|
||||
for (auto it = _map.begin(); it != _map.end(); ++it)
|
||||
{
|
||||
f(it->second, it->first);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
f(*this, "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool DTree::is_array()
|
||||
{
|
||||
return(type == 'M');
|
||||
}
|
||||
|
||||
String DTree::to_string()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case('S'):
|
||||
return(_String);
|
||||
break;
|
||||
case('F'):
|
||||
return(std::to_string(_float));
|
||||
break;
|
||||
case('B'):
|
||||
return(_bool ? "(true)" : "(false)");
|
||||
break;
|
||||
case('M'):
|
||||
return("");
|
||||
break;
|
||||
case('P'):
|
||||
return(std::to_string((u64)_ptr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String DTree::to_json()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case('S'):
|
||||
return(json_escape(_String));
|
||||
break;
|
||||
case('F'):
|
||||
return(std::to_string(_float));
|
||||
break;
|
||||
case('B'):
|
||||
return(_bool ? "true" : "false");
|
||||
break;
|
||||
case('M'):
|
||||
return("\"(array)\"");
|
||||
break;
|
||||
case('P'):
|
||||
return("\"(pointer)\"");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String DTree::get_type_name()
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case('S'):
|
||||
return("String");
|
||||
break;
|
||||
case('F'):
|
||||
return("f64");
|
||||
break;
|
||||
case('B'):
|
||||
return("bool");
|
||||
break;
|
||||
case('M'):
|
||||
return("array");
|
||||
break;
|
||||
case('P'):
|
||||
return("pointer");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DTree::set_type(char t)
|
||||
{
|
||||
if(type != t)
|
||||
{
|
||||
type = t;
|
||||
switch(type)
|
||||
{
|
||||
case('M'):
|
||||
_map.clear();
|
||||
_array_index = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DTree::set(String s)
|
||||
{
|
||||
set_type('S');
|
||||
_String = s;
|
||||
}
|
||||
|
||||
void DTree::set(void* p)
|
||||
{
|
||||
set_type('P');
|
||||
_ptr = p;
|
||||
}
|
||||
|
||||
void DTree::set(f64 f)
|
||||
{
|
||||
set_type('F');
|
||||
_float = f;
|
||||
}
|
||||
|
||||
void DTree::set_bool(bool b)
|
||||
{
|
||||
set_type('B');
|
||||
_bool = b;
|
||||
}
|
||||
|
||||
void DTree::set(DTree source)
|
||||
{
|
||||
set_type(source.type);
|
||||
switch(type)
|
||||
{
|
||||
case('S'):
|
||||
_String = source._String;
|
||||
break;
|
||||
case('F'):
|
||||
_float = source._float;
|
||||
break;
|
||||
case('B'):
|
||||
_bool = source._bool;
|
||||
break;
|
||||
case('M'):
|
||||
_map = source._map;
|
||||
break;
|
||||
case('P'):
|
||||
_ptr = source._ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DTree::set(StringMap source)
|
||||
{
|
||||
set_type('M');
|
||||
for (auto it = source.begin(); it != source.end(); ++it)
|
||||
{
|
||||
_map[it->first] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
DTree* DTree::key(String s)
|
||||
{
|
||||
set_type('M');
|
||||
return(&_map[s]);
|
||||
}
|
||||
|
||||
DTree& DTree::operator [] (String s) {
|
||||
set_type('M');
|
||||
return(_map[s]);
|
||||
}
|
||||
|
||||
void DTree::operator = (String v) { set(v); }
|
||||
void DTree::operator = (f64 v) { set(v); }
|
||||
void DTree::operator = (void* v) { set(v); }
|
||||
void DTree::operator = (DTree v) { set(v); }
|
||||
void DTree::operator = (StringMap v) { set(v); }
|
||||
|
||||
void DTree::push(DTree& child)
|
||||
{
|
||||
set_type('M');
|
||||
_map[std::to_string(_array_index)] = child;
|
||||
_array_index += 1;
|
||||
}
|
||||
|
||||
DTree DTree::pop()
|
||||
{
|
||||
set_type('M');
|
||||
auto last = _map.rbegin();
|
||||
DTree result = last->second;
|
||||
_map.erase(last->first);
|
||||
return(result);
|
||||
}
|
||||
|
||||
void DTree::remove(String s)
|
||||
{
|
||||
set_type('M');
|
||||
_map.erase(s);
|
||||
}
|
||||
|
||||
void DTree::clear()
|
||||
{
|
||||
set_type('M');
|
||||
_map.clear();
|
||||
}
|
||||
|
||||
String to_String(DTree t)
|
||||
{
|
||||
return(t.to_string());
|
||||
}
|
||||
|
||||
String var_dump(DTree map, String prefix, String postfix)
|
||||
{
|
||||
String result = "";
|
||||
if(!map.is_array())
|
||||
return(map.to_string());
|
||||
map.each([&] (DTree item, String key) {
|
||||
result += prefix + key + ": " + item.to_string() + postfix;
|
||||
if(item.is_array())
|
||||
result += var_dump(item, prefix + "\t");
|
||||
});
|
||||
return(result);
|
||||
}
|
||||
|
||||
String json_escape(String s)
|
||||
{
|
||||
//return(String("\"")+s+"\"");
|
||||
String result;
|
||||
u32 i = 0;
|
||||
result.append(1, '"');
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
switch(c)
|
||||
{
|
||||
case('\t'):
|
||||
result.append("\\t");
|
||||
break;
|
||||
case('\n'):
|
||||
result.append("\\n");
|
||||
break;
|
||||
case('"'):
|
||||
result.append("\\\"");
|
||||
break;
|
||||
case('\r'):
|
||||
result.append("\\r");
|
||||
break;
|
||||
case('\\'):
|
||||
result.append("\\\\");
|
||||
break;
|
||||
case('\b'):
|
||||
result.append("\\b");
|
||||
break;
|
||||
case('\f'):
|
||||
result.append("\\f");
|
||||
break;
|
||||
default:
|
||||
result.append(1, c);
|
||||
break;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
result.append(1, '"');
|
||||
return(result);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
String json_escape(String s);
|
||||
|
||||
struct DTree {
|
||||
|
||||
char type = 'S';
|
||||
|
||||
String _String;
|
||||
f64 _float;
|
||||
s64 _array_index;
|
||||
bool _bool;
|
||||
void* _ptr;
|
||||
std::map<String, DTree> _map;
|
||||
|
||||
void each(std::function <void (DTree t, String key)> f);
|
||||
bool is_array();
|
||||
String to_string();
|
||||
String to_json();
|
||||
String get_type_name();
|
||||
void set_type(char t);
|
||||
void set(String s);
|
||||
void set(void* p);
|
||||
void set(f64 f);
|
||||
void set_bool(bool b);
|
||||
void set(DTree source);
|
||||
void set(StringMap source);
|
||||
DTree* key(String s);
|
||||
DTree& operator [] (String s);
|
||||
void operator = (String v);
|
||||
void operator = (f64 v);
|
||||
void operator = (void* v);
|
||||
void operator = (DTree v);
|
||||
void operator = (StringMap v);
|
||||
|
||||
void push(DTree& child);
|
||||
DTree pop();
|
||||
void remove(String s);
|
||||
void clear();
|
||||
};
|
||||
|
||||
String to_String(DTree t);
|
||||
String var_dump(DTree map, String prefix = "", String postfix = "\n");
|
||||
@@ -0,0 +1,412 @@
|
||||
#include "functionlib.h"
|
||||
|
||||
String var_dump(StringMap map, String prefix, String postfix)
|
||||
{
|
||||
String result = "";
|
||||
|
||||
for (auto it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
result.append(prefix + it->first + ": " + it->second + postfix);
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
String var_dump(StringList slist, String prefix, String postfix)
|
||||
{
|
||||
String result = "";
|
||||
|
||||
for (auto& s : slist)
|
||||
{
|
||||
result.append(prefix + s + postfix);
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
u8 char_to_u8(char input)
|
||||
{
|
||||
if(input >= '0' && input <= '9')
|
||||
return input - '0';
|
||||
if(input >= 'A' && input <= 'F')
|
||||
return input - 'A' + 10;
|
||||
if(input >= 'a' && input <= 'f')
|
||||
return input - 'a' + 10;
|
||||
return(0);
|
||||
}
|
||||
|
||||
u8 hex_to_u8(String src)
|
||||
{
|
||||
return(char_to_u8(src[0])*16 + char_to_u8(src[1]));
|
||||
}
|
||||
|
||||
String str_to_lower(String s)
|
||||
{
|
||||
String result = "";
|
||||
for(auto c : s)
|
||||
{
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
c = tolower(c);
|
||||
result.append(1, c);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String str_to_upper(String s)
|
||||
{
|
||||
String result = "";
|
||||
for(auto c : s)
|
||||
{
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
c = toupper(c);
|
||||
result.append(1, c);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String trim(String raw)
|
||||
{
|
||||
u32 len = raw.length();
|
||||
u32 start_pos = 0;
|
||||
u32 end_pos = len - 1;
|
||||
if(len == 0 || (len == 1 && isspace(raw[0])))
|
||||
return("");
|
||||
while(start_pos < len && isspace(raw[start_pos]))
|
||||
start_pos++;
|
||||
while(end_pos >= 0 && isspace(raw[end_pos]))
|
||||
end_pos--;
|
||||
if(end_pos < start_pos)
|
||||
return("");
|
||||
return(raw.substr(start_pos, 1 + end_pos - start_pos));
|
||||
}
|
||||
|
||||
StringList split(String str, String delim)
|
||||
{
|
||||
StringList result;
|
||||
int start = 0;
|
||||
int end = str.find(delim);
|
||||
while (end != String::npos)
|
||||
{
|
||||
result.push_back(str.substr(start, end - start));
|
||||
start = end + delim.size();
|
||||
end = str.find(delim, start);
|
||||
}
|
||||
result.push_back(str.substr(start, end - start));
|
||||
return(result);
|
||||
}
|
||||
|
||||
String join(StringList l, String delim)
|
||||
{
|
||||
String result;
|
||||
u32 i = 0;
|
||||
for(auto& s : l)
|
||||
{
|
||||
if(i > 0)
|
||||
result.append(delim);
|
||||
result.append(s);
|
||||
i += 1;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String html_escape(String s)
|
||||
{
|
||||
String result;
|
||||
|
||||
for(u32 i = 0; i < s.length(); i++)
|
||||
{
|
||||
char c = s[i];
|
||||
switch(c)
|
||||
{
|
||||
case('&'):
|
||||
result.append("&");
|
||||
break;
|
||||
case('<'):
|
||||
result.append("<");
|
||||
break;
|
||||
case('>'):
|
||||
result.append(">");
|
||||
break;
|
||||
case('"'):
|
||||
result.append(""");
|
||||
break;
|
||||
default:
|
||||
result.append(1, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
String html_escape(u64 a)
|
||||
{
|
||||
return(std::to_string(a));
|
||||
}
|
||||
|
||||
String html_escape(f64 a)
|
||||
{
|
||||
return(std::to_string(a));
|
||||
}
|
||||
|
||||
u64 int_val(String s, u32 base)
|
||||
{
|
||||
return(strtoll(s.c_str(), 0, base));
|
||||
}
|
||||
|
||||
String nibble(String& haystack, String delim)
|
||||
{
|
||||
auto idx = haystack.find(delim);
|
||||
if(idx == String::npos)
|
||||
{
|
||||
String result = haystack;
|
||||
haystack = "";
|
||||
return(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
String result = haystack.substr(0, idx);
|
||||
haystack = haystack.substr(idx+delim.length());
|
||||
return(result);
|
||||
}
|
||||
}
|
||||
|
||||
String json_encode(DTree t)
|
||||
{
|
||||
String result = "";
|
||||
if(t.is_array())
|
||||
{
|
||||
result += "{";
|
||||
u32 count = 0;
|
||||
t.each([&] (DTree item, String key) {
|
||||
if(count > 0)
|
||||
result += ", ";
|
||||
count += 1;
|
||||
result += json_escape(key) + ": " + json_encode(item);
|
||||
});
|
||||
result += "}";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = t.to_json();
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
// https://i.stack.imgur.com/SHLOB.gif
|
||||
String json_decode_String(String s, u32& i, char termination_char)
|
||||
{
|
||||
String result;
|
||||
//print("json_decode_String " + s.substr(i) + "\n");
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
if(c == termination_char)
|
||||
{
|
||||
i += 1;
|
||||
//print("json_decode_String = " + result + "\n");
|
||||
return(result);
|
||||
}
|
||||
else if(c == '\\')
|
||||
{
|
||||
i += 1;
|
||||
c = s[i];
|
||||
switch(c)
|
||||
{
|
||||
case('t'):
|
||||
result.append(1, '\t');
|
||||
break;
|
||||
case('n'):
|
||||
result.append(1, '\n');
|
||||
break;
|
||||
case('r'):
|
||||
result.append(1, '\r');
|
||||
break;
|
||||
case('\\'):
|
||||
result.append(1, '\\');
|
||||
break;
|
||||
case('b'):
|
||||
result.append(1, '\b');
|
||||
break;
|
||||
case('f'):
|
||||
result.append(1, '\f');
|
||||
break;
|
||||
case('u'):
|
||||
// todo decode
|
||||
break;
|
||||
default:
|
||||
result.append(1, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.append(1, c);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree json_decode_map(String s, u32& i);
|
||||
|
||||
void json_consume_space(String s, u32& i)
|
||||
{
|
||||
while(i < s.length() && isspace(s[i]))
|
||||
i += 1;
|
||||
}
|
||||
|
||||
String json_decode_keyword(String s, u32& i)
|
||||
{
|
||||
String result;
|
||||
json_consume_space(s, i);
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
if(isalnum(c))
|
||||
{
|
||||
result.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(result);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String json_decode_number(String s, u32& i)
|
||||
{
|
||||
String result;
|
||||
json_consume_space(s, i);
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
if(isdigit(c) || c == '.')
|
||||
{
|
||||
result.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(result);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree json_decode_value(String s, u32& i)
|
||||
{
|
||||
DTree result;
|
||||
String value = "";
|
||||
json_consume_space(s, i);
|
||||
char c = s[i];
|
||||
//print("json_decode_value " + s.substr(i) + "\n");
|
||||
if(c == '"' || c == '\'') // String value
|
||||
{
|
||||
result.type = 'S';
|
||||
i += 1;
|
||||
result._String = json_decode_String(s, i, s[i-1]);
|
||||
return(result);
|
||||
}
|
||||
else if(isdigit(c))
|
||||
{
|
||||
result.type = 'S';
|
||||
result._String = json_decode_number(s, i);
|
||||
//result._float = stod(json_decode_number(s, i));
|
||||
return(result);
|
||||
}
|
||||
else if(c == '{')
|
||||
{
|
||||
i += 1;
|
||||
return(json_decode_map(s, i));
|
||||
}
|
||||
else
|
||||
{
|
||||
value = json_decode_keyword(s, i);
|
||||
if(value == "true")
|
||||
result.set_bool(true);
|
||||
else if(value == "false")
|
||||
result.set_bool(false);
|
||||
else if(value == "null")
|
||||
result.set("");
|
||||
return(result);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree json_decode_map(String s, u32& i)
|
||||
{
|
||||
DTree result;
|
||||
result.type = 'M';
|
||||
String key = "";
|
||||
json_consume_space(s, i);
|
||||
//print("json_decode_map " + s.substr(i) + "\n");
|
||||
while(i < s.length())
|
||||
{
|
||||
char c = s[i];
|
||||
if(c == '}')
|
||||
{
|
||||
i += 1;
|
||||
return(result);
|
||||
}
|
||||
else if(c == ',')
|
||||
{
|
||||
i += 1;
|
||||
}
|
||||
else if(c == '"' || c == '\'')
|
||||
{
|
||||
i += 1;
|
||||
key = json_decode_String(s, i, s[i-1]);
|
||||
json_consume_space(s, i);
|
||||
if(s[i] != ':')
|
||||
return(result); // malformed
|
||||
i += 1;
|
||||
DTree v = json_decode_value(s, i);
|
||||
//result._map[key] = json_decode_value(s, i);
|
||||
//print("KV " + key + " = " + to_String(v) + "\n");
|
||||
//printf("map add %s (%c) \n", key.c_str(), s[i]);
|
||||
result._map[key] = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
// malformed
|
||||
return(result);
|
||||
}
|
||||
json_consume_space(s, i);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree json_decode(String s)
|
||||
{
|
||||
u32 i = 0;
|
||||
return(json_decode_value(s, i));
|
||||
}
|
||||
|
||||
void ob_start()
|
||||
{
|
||||
context->ob_start();
|
||||
}
|
||||
|
||||
void ob_close()
|
||||
{
|
||||
delete context->ob;
|
||||
context->ob_stack.pop_back();
|
||||
if(context->ob_stack.size() == 0)
|
||||
ob_start();
|
||||
}
|
||||
|
||||
String ob_get()
|
||||
{
|
||||
return(context->ob->str());
|
||||
}
|
||||
|
||||
String ob_get_close()
|
||||
{
|
||||
String result = context->ob->str();
|
||||
ob_close();
|
||||
return(result);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
u8 char_to_u8(char input);
|
||||
u8 hex_to_u8(String src);
|
||||
u64 int_val(String s, u32 base = 10);
|
||||
String str_to_lower(String s);
|
||||
String str_to_upper(String s);
|
||||
|
||||
String trim(String raw);
|
||||
StringList split(String str, String delim = "\n");
|
||||
String join(StringList l, String delim = "\n");
|
||||
String nibble(String& haystack, String delim);
|
||||
void json_consume_space(String s, u32& i);
|
||||
|
||||
template<typename T>
|
||||
std::vector<T> filter(std::vector<T> items, std::function<bool (T)> f)
|
||||
{
|
||||
std::vector<T> new_items;
|
||||
for(auto item : items)
|
||||
{
|
||||
if(f(item))
|
||||
new_items.push_back(item);
|
||||
}
|
||||
return(new_items);
|
||||
}
|
||||
|
||||
template <class ...Args>
|
||||
String first(Args... args)
|
||||
{
|
||||
std::vector<String> vec = {args...};
|
||||
for(auto s : vec)
|
||||
if(trim(s) != "")
|
||||
return(s);
|
||||
return("");
|
||||
}
|
||||
|
||||
String html_escape(String s);
|
||||
String html_escape(u64 a);
|
||||
String html_escape(f64 a);
|
||||
|
||||
String json_encode(DTree t);
|
||||
DTree json_decode(String s);
|
||||
|
||||
String var_dump(StringMap map, String prefix = "", String postfix = "\n");
|
||||
String var_dump(StringList slist, String prefix = "", String postfix = "\n");
|
||||
|
||||
void ob_start();
|
||||
void ob_clear();
|
||||
String ob_get_clear();
|
||||
String ob_get();
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
|
||||
/* from valgrind tests */
|
||||
|
||||
/* ================ sha1.c ================ */
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
|
||||
Test Vectors (from FIPS PUB 180-1)
|
||||
"abc"
|
||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
A million repetitions of "a"
|
||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
|
||||
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
|
||||
/* #define SHA1HANDSOFF * Copies data before messing with it. */
|
||||
|
||||
typedef struct {
|
||||
u_int32_t state[5];
|
||||
u_int32_t count[2];
|
||||
unsigned char buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]);
|
||||
void SHA1Init(SHA1_CTX* context);
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len);
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
|
||||
|
||||
#define SHA1HANDSOFF
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h> /* for u_int*_t */
|
||||
#include "hash.h"
|
||||
|
||||
#ifndef BYTE_ORDER
|
||||
#if (BSD >= 199103)
|
||||
# include <machine/endian.h>
|
||||
#else
|
||||
#if defined(linux) || defined(__linux__)
|
||||
# include <endian.h>
|
||||
#else
|
||||
#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */
|
||||
#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
|
||||
#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp)*/
|
||||
|
||||
#if defined(vax) || defined(ns32000) || defined(sun386) || defined(__i386__) || \
|
||||
defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \
|
||||
defined(__alpha__) || defined(__alpha)
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \
|
||||
defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \
|
||||
defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\
|
||||
defined(apollo) || defined(__convex__) || defined(_CRAY) || \
|
||||
defined(__hppa) || defined(__hp9000) || \
|
||||
defined(__hp9000s300) || defined(__hp9000s700) || \
|
||||
defined (BIT_ZERO_ON_LEFT) || defined(m68k) || defined(__sparc)
|
||||
#define BYTE_ORDER BIG_ENDIAN
|
||||
#endif
|
||||
#endif /* linux */
|
||||
#endif /* BSD */
|
||||
#endif /* BYTE_ORDER */
|
||||
|
||||
#if defined(__BYTE_ORDER) && !defined(BYTE_ORDER)
|
||||
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
#else
|
||||
#define BYTE_ORDER BIG_ENDIAN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(BYTE_ORDER) || \
|
||||
(BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN && \
|
||||
BYTE_ORDER != PDP_ENDIAN)
|
||||
/* you must determine what the correct bit order is for
|
||||
* your compiler - the next line is an intentional error
|
||||
* which will force your compiles to bomb until you fix
|
||||
* the above macros.
|
||||
*/
|
||||
#error "Undefined or invalid BYTE_ORDER"
|
||||
#endif
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/* blk0() and blk() perform the initial expand. */
|
||||
/* I got the idea of expanding during the round function from SSLeay */
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
||||
|(rol(block->l[i],8)&0x00FF00FF))
|
||||
#elif BYTE_ORDER == BIG_ENDIAN
|
||||
#define blk0(i) block->l[i]
|
||||
#else
|
||||
#error "Endianness not defined!"
|
||||
#endif
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
|
||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
|
||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||
|
||||
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
|
||||
{
|
||||
u_int32_t a, b, c, d, e;
|
||||
typedef union {
|
||||
unsigned char c[64];
|
||||
u_int32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
#ifdef SHA1HANDSOFF
|
||||
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
||||
memcpy(block, buffer, 64);
|
||||
#else
|
||||
/* The following had better never be used because it causes the
|
||||
* pointer-to-const buffer to be cast into a pointer to non-const.
|
||||
* And the result is written through. I threw a "const" in, hoping
|
||||
* this will cause a diagnostic.
|
||||
*/
|
||||
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
|
||||
#endif
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
/* Wipe variables */
|
||||
a = b = c = d = e = 0;
|
||||
#ifdef SHA1HANDSOFF
|
||||
memset(block, '\0', sizeof(block));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* SHA1Init - Initialize new context */
|
||||
|
||||
void SHA1Init(SHA1_CTX* context)
|
||||
{
|
||||
/* SHA1 initialization constants */
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Run your data through this. */
|
||||
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
|
||||
{
|
||||
u_int32_t i;
|
||||
u_int32_t j;
|
||||
|
||||
j = context->count[0];
|
||||
if ((context->count[0] += len << 3) < j)
|
||||
context->count[1]++;
|
||||
context->count[1] += (len>>29);
|
||||
j = (j >> 3) & 63;
|
||||
if ((j + len) > 63) {
|
||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1Transform(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64) {
|
||||
SHA1Transform(context->state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else i = 0;
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned char finalcount[8];
|
||||
unsigned char c;
|
||||
|
||||
#if 0 /* untested "improvement" by DHR */
|
||||
/* Convert context->count to a sequence of bytes
|
||||
* in finalcount. Second element first, but
|
||||
* big-endian order within element.
|
||||
* But we do it all backwards.
|
||||
*/
|
||||
unsigned char *fcp = &finalcount[8];
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
u_int32_t t = context->count[i];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 4; t >>= 8, j++)
|
||||
*--fcp = (unsigned char) t
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < 8; i++) {
|
||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||
}
|
||||
#endif
|
||||
c = 0200;
|
||||
SHA1Update(context, &c, 1);
|
||||
while ((context->count[0] & 504) != 448) {
|
||||
c = 0000;
|
||||
SHA1Update(context, &c, 1);
|
||||
}
|
||||
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
|
||||
for (i = 0; i < 20; i++) {
|
||||
digest[i] = (unsigned char)
|
||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
/* Wipe variables */
|
||||
memset(context, '\0', sizeof(*context));
|
||||
memset(&finalcount, '\0', sizeof(finalcount));
|
||||
}
|
||||
/* ================ end of sha1.c ================ */
|
||||
|
||||
String
|
||||
gen_sha1(String s, bool as_binary)
|
||||
{
|
||||
unsigned char v[20];
|
||||
SHA1_CTX ctx;
|
||||
SHA1Init(&ctx);
|
||||
SHA1Update(&ctx, (const unsigned char *)s.data(), s.length());
|
||||
SHA1Final(v, &ctx);
|
||||
String result;
|
||||
if(as_binary)
|
||||
for(int i=0; i<20; i++)
|
||||
result.append(1, v[i]);
|
||||
else
|
||||
for(int i=0; i<20; i++)
|
||||
result += to_hex(v[i], 2);
|
||||
return(result);
|
||||
}
|
||||
|
||||
#define BIT_NOISE1 0xB5297A4D
|
||||
#define BIT_NOISE2 0x68E31DA4
|
||||
#define BIT_NOISE3 0x1B56C4E9
|
||||
|
||||
// based on Squirrel3 https://www.youtube.com/watch?v=LWFzPP8ZbdU&t=2666s
|
||||
u32 gen_noise32(u32 index, u32 seed)
|
||||
{
|
||||
u32 r = index;
|
||||
r *= BIT_NOISE1;
|
||||
r += seed;
|
||||
r ^= (r >> 8);
|
||||
r += BIT_NOISE2;
|
||||
r ^= (r << 8);
|
||||
r *= BIT_NOISE3;
|
||||
r ^= (r >> 8);
|
||||
return(r);
|
||||
}
|
||||
|
||||
#define BIT_NOISE61 0x5134811636f8cc8a
|
||||
#define BIT_NOISE62 0xb8E31DA41B56C4E9
|
||||
#define BIT_NOISE63 0x18cd227aaa1168c1
|
||||
|
||||
u64 gen_noise64(u64 index, u64 seed)
|
||||
{
|
||||
u64 r = index;
|
||||
r *= BIT_NOISE61;
|
||||
r += seed;
|
||||
r ^= (r >> 8);
|
||||
r += BIT_NOISE62;
|
||||
r ^= (r << 8);
|
||||
r *= BIT_NOISE63;
|
||||
r ^= (r >> 8);
|
||||
return(r);
|
||||
}
|
||||
|
||||
#define MAX_64 0xffffffffffffffff
|
||||
|
||||
f64 gen_noise01(u64 index, u64 seed)
|
||||
{
|
||||
return((float)gen_noise64(index, seed)/(float)MAX_64);
|
||||
}
|
||||
|
||||
u64 gen_int(u64 from, u64 to, u64 index, u64 seed)
|
||||
{
|
||||
u64 b = 1 + to - from;
|
||||
return(from + (gen_noise64(index, seed) % b));
|
||||
}
|
||||
|
||||
#include <tgmath.h>
|
||||
f64 gen_float(f64 from, f64 to, u64 index, u64 seed, f64 decimal_precision)
|
||||
{
|
||||
f64 b = to - from;
|
||||
return(from + fmod( decimal_precision*(f64)gen_noise64(index, seed), b));
|
||||
}
|
||||
|
||||
u64 draw_int(u64 from, u64 to)
|
||||
{
|
||||
return(gen_int(from, to, context->random_index++, context->random_seed));
|
||||
}
|
||||
|
||||
f64 draw_float(f64 from, f64 to, f64 decimal_precision)
|
||||
{
|
||||
return(gen_float(from, to, context->random_index++, context->random_seed, decimal_precision));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/* ================ sha1.h ================ */
|
||||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <steve@edmweb.com>
|
||||
100% Public Domain
|
||||
*/
|
||||
|
||||
|
||||
String gen_sha1(String s, bool as_binary = false);
|
||||
|
||||
u32 gen_noise32(u32 index, u32 seed = 0);
|
||||
u64 gen_noise64(u64 index, u64 seed = 0);
|
||||
f64 gen_noise01(u64 index, u64 seed = 0);
|
||||
|
||||
u64 gen_int(u64 from, u64 to, u64 index, u64 seed = 0);
|
||||
f64 gen_float(f64 from, f64 to, u64 index, u64 seed = 0, f64 decimal_precision = 0.000000000001);
|
||||
|
||||
u64 draw_int(u64 from, u64 to);
|
||||
f64 draw_float(f64 from, f64 to, f64 decimal_precision = 0.000000000001);
|
||||
@@ -0,0 +1,304 @@
|
||||
#include "../3rdparty/mysql/mysql.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "mysql-connector.h"
|
||||
|
||||
bool MySQL::connect(String host, String username, String password)
|
||||
{
|
||||
switch_to_system_alloc();
|
||||
connection = mysql_init(NULL);
|
||||
if (connection == NULL)
|
||||
{
|
||||
auto e = mysql_error((MYSQL*)connection);
|
||||
fprintf(stderr, "%s\n", e);
|
||||
switch_to_arena(context->mem);
|
||||
statement_info.assign(e);
|
||||
return(false);
|
||||
}
|
||||
|
||||
if (mysql_real_connect((MYSQL*)connection, host.c_str(), username.c_str(), password.c_str(),
|
||||
NULL, 0, NULL, 0) == NULL)
|
||||
{
|
||||
auto e = mysql_error((MYSQL*)connection);
|
||||
fprintf(stderr, "%s\n", e);
|
||||
mysql_close((MYSQL*)connection);
|
||||
switch_to_arena(context->mem);
|
||||
statement_info.assign(e);
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
if (mysql_query(con, "CREATE DATABASE testdb"))
|
||||
{
|
||||
fprintf(stderr, "%s\n", mysql_error(con));
|
||||
mysql_close(con);
|
||||
exit(1);
|
||||
}
|
||||
*/
|
||||
switch_to_arena(context->mem);
|
||||
statement_info = String("connected");
|
||||
context->resources.mysql_connections.push_back(connection);
|
||||
return(true);
|
||||
}
|
||||
|
||||
String MySQL::escape(String raw, char quote_char)
|
||||
{
|
||||
return(mysql_escape(raw, quote_char));
|
||||
}
|
||||
|
||||
String mysql_escape(String raw, char quote_char)
|
||||
{
|
||||
String result;
|
||||
if(quote_char > 0)
|
||||
result.append(1, quote_char);
|
||||
|
||||
for(u32 i = 0; i < raw.length(); i++)
|
||||
{
|
||||
char c = raw[i];
|
||||
switch(c)
|
||||
{
|
||||
case('\n'):
|
||||
result.append("\\n");
|
||||
break;
|
||||
case('\r'):
|
||||
result.append("\\r");
|
||||
break;
|
||||
case('\t'):
|
||||
result.append("\\t");
|
||||
break;
|
||||
case('\\'):
|
||||
case('\''):
|
||||
case('"'):
|
||||
result.append(1, '\\');
|
||||
result.append(1, c);
|
||||
break;
|
||||
default:
|
||||
result.append(1, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(quote_char > 0)
|
||||
result.append(1, quote_char);
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree field_to_dtree_node(char* data_ptr, MySQLFieldInfo field_info, u32 len)
|
||||
{
|
||||
DTree result;
|
||||
switch(field_info.type)
|
||||
{
|
||||
case(MYSQL_TYPE_TINY):
|
||||
result.set(atoll(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_SHORT):
|
||||
result.set(atoll(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_LONG):
|
||||
result.set(atoll(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_INT24):
|
||||
result.set(atoll(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_LONGLONG):
|
||||
result.set(atoll(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_FLOAT):
|
||||
result.set(atof(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_DOUBLE):
|
||||
result.set(atof(data_ptr));
|
||||
break;
|
||||
case(MYSQL_TYPE_NULL):
|
||||
break;
|
||||
default:
|
||||
String s;
|
||||
s.assign(data_ptr);
|
||||
result.set(s);
|
||||
break;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree MySQL::get_pending_result()
|
||||
{
|
||||
DTree result_data;
|
||||
// based on: https://dev.mysql.com/doc/c-api/5.7/en/mysql-field-count.html
|
||||
MYSQL_RES *result;
|
||||
result = mysql_store_result((MYSQL*)connection);
|
||||
insert_id = mysql_insert_id((MYSQL*)connection);
|
||||
//statement_info.assign(mysql_info((MYSQL*)connection));
|
||||
if (result) // there are rows
|
||||
{
|
||||
field_count = mysql_num_fields(result);
|
||||
row_count = mysql_num_rows(result);
|
||||
|
||||
field_info.clear();
|
||||
unsigned int i;
|
||||
MYSQL_FIELD *fields;
|
||||
fields = mysql_fetch_fields(result);
|
||||
for(i = 0; i < field_count; i++)
|
||||
{
|
||||
MySQLFieldInfo fi;
|
||||
if(fields[i].name) fi.name.assign(fields[i].name);
|
||||
if(fields[i].table) fi.table.assign(fields[i].table);
|
||||
if(fields[i].db) fi.db.assign(fields[i].db);
|
||||
fi.length = (fields[i].length);
|
||||
if(fields[i].def) fi.def.assign(fields[i].def);
|
||||
fi.max_length = (fields[i].max_length);
|
||||
fi.flags = (fields[i].flags);
|
||||
fi.type = (fields[i].type);
|
||||
field_info.push_back(fi);
|
||||
}
|
||||
|
||||
MYSQL_ROW row;
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
DTree row_data;
|
||||
auto lengths = mysql_fetch_lengths(result);
|
||||
for(i = 0; i < field_count; i++)
|
||||
{
|
||||
row_data[field_info[i].name] = field_to_dtree_node(row[i], field_info[i], lengths[i]);
|
||||
}
|
||||
result_data.push(row_data);
|
||||
}
|
||||
|
||||
mysql_free_result(result);
|
||||
}
|
||||
else // mysql_store_result() returned nothing; should it have?
|
||||
{
|
||||
if(mysql_field_count((MYSQL*)connection) == 0)
|
||||
{
|
||||
// query does not return data
|
||||
// (it was not a SELECT)
|
||||
affected_rows = mysql_affected_rows((MYSQL*)connection);
|
||||
}
|
||||
else // mysql_store_result() should have returned data
|
||||
{
|
||||
// error
|
||||
}
|
||||
}
|
||||
return(result_data);
|
||||
}
|
||||
|
||||
DTree MySQL::query(String q)
|
||||
{
|
||||
_preload_next_error_code = mysql_query((MYSQL*)connection, q.c_str());
|
||||
DTree result;
|
||||
if(_preload_next_error_code == 0)
|
||||
result = get_pending_result();
|
||||
return(result);
|
||||
}
|
||||
|
||||
DTree MySQL::query(String q, StringMap params)
|
||||
{
|
||||
return(query(
|
||||
parse_query_parameters(q, params).c_str()
|
||||
));
|
||||
}
|
||||
|
||||
String MySQL::parse_query_parameters(String query, StringMap map)
|
||||
{
|
||||
String result;
|
||||
query.append(1, ' ');
|
||||
|
||||
u8 mode = 0;
|
||||
char quote;
|
||||
String identifier;
|
||||
for(u32 i = 0; i < query.length(); i++)
|
||||
{
|
||||
char c = query[i];
|
||||
if(mode == 0) // normal, unquoted mode
|
||||
{
|
||||
if(c == ':')
|
||||
{
|
||||
mode = 1;
|
||||
identifier = "";
|
||||
}
|
||||
else if(c == '"' || c == '\'')
|
||||
{
|
||||
result.append(1, c);
|
||||
mode = 2;
|
||||
quote = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.append(1, c);
|
||||
}
|
||||
}
|
||||
else if(mode == 1) // identifier mode
|
||||
{
|
||||
if(isalnum(c))
|
||||
{
|
||||
identifier.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.append(escape(map[identifier]));
|
||||
result.append(1, c);
|
||||
mode = 0;
|
||||
}
|
||||
}
|
||||
else if(mode == 2) // quoted mode
|
||||
{
|
||||
if(c == quote)
|
||||
mode = 0;
|
||||
result.append(1, c);
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
void MySQL::disconnect()
|
||||
{
|
||||
if(connection)
|
||||
mysql_close((MYSQL*)connection);
|
||||
connection = NULL;
|
||||
}
|
||||
|
||||
String MySQL::error()
|
||||
{
|
||||
if(_preload_next_error_code)
|
||||
{
|
||||
String p = "Unknown error";
|
||||
switch(_preload_next_error_code)
|
||||
{
|
||||
case(CR_COMMANDS_OUT_OF_SYNC):
|
||||
p = "Commands out of sync";
|
||||
break;
|
||||
case(CR_SERVER_GONE_ERROR):
|
||||
p = "Server connection error";
|
||||
break;
|
||||
case(CR_SERVER_LOST):
|
||||
p = "Server hung up";
|
||||
break;
|
||||
case(CR_OUT_OF_MEMORY):
|
||||
p = "Out of memory";
|
||||
break;
|
||||
default:
|
||||
case(CR_UNKNOWN_ERROR):
|
||||
p = "Unknown server error";
|
||||
break;
|
||||
}
|
||||
_preload_next_error_code = 0;
|
||||
return(p);
|
||||
}
|
||||
const char* res = mysql_error((MYSQL*)connection);
|
||||
if(res)
|
||||
{
|
||||
return(String(res));
|
||||
}
|
||||
else
|
||||
{
|
||||
return("");
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup_mysql_connections()
|
||||
{
|
||||
switch_to_system_alloc();
|
||||
for(auto& con : context->resources.mysql_connections)
|
||||
mysql_close((MYSQL*)con);
|
||||
switch_to_arena(context->mem);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
struct MySQLFieldInfo {
|
||||
String name;
|
||||
String table;
|
||||
String db;
|
||||
u64 length;
|
||||
String def;
|
||||
u64 max_length;
|
||||
u64 flags;
|
||||
u32 type;
|
||||
};
|
||||
|
||||
struct MySQL {
|
||||
|
||||
void* connection = 0;
|
||||
u32 _preload_next_error_code = 0;
|
||||
u32 affected_rows = 0;
|
||||
u32 field_count = 0;
|
||||
u32 row_count = 0;
|
||||
u64 insert_id = 0;
|
||||
String statement_info = ""; //
|
||||
|
||||
std::vector<MySQLFieldInfo> field_info;
|
||||
|
||||
bool connect(String host = "localhost", String username = "root", String password = "");
|
||||
void disconnect();
|
||||
String error();
|
||||
String escape(String raw, char quote_char = '\'');
|
||||
String parse_query_parameters(String query, StringMap m);
|
||||
DTree query(String q);
|
||||
DTree query(String q, StringMap params);
|
||||
DTree get_pending_result();
|
||||
|
||||
};
|
||||
|
||||
MySQL* mysql_connect(String host = "localhost", String username = "root", String password = "")
|
||||
{
|
||||
MySQL* m = new MySQL();
|
||||
m->connect(host, username, password);
|
||||
return(m);
|
||||
}
|
||||
|
||||
void mysql_disconnect(MySQL* m)
|
||||
{
|
||||
m->disconnect();
|
||||
}
|
||||
|
||||
String mysql_error(MySQL* m)
|
||||
{
|
||||
return(m->error());
|
||||
}
|
||||
|
||||
String mysql_escape(String raw, char quote_char);
|
||||
|
||||
DTree mysql_query(MySQL* m, String q)
|
||||
{
|
||||
return(m->query(q));
|
||||
}
|
||||
|
||||
DTree mysql_query(MySQL* m, String q, StringMap params)
|
||||
{
|
||||
return(m->query(q, params));
|
||||
}
|
||||
|
||||
u64 mysql_insert_id(MySQL* m)
|
||||
{
|
||||
return(m->insert_id);
|
||||
}
|
||||
+497
@@ -0,0 +1,497 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <execinfo.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
#include "sys.h"
|
||||
|
||||
String shell_exec(String cmd)
|
||||
{
|
||||
printf("(i) shell_exec(%s)\n", cmd.c_str());
|
||||
String data;
|
||||
FILE * stream;
|
||||
const int max_buffer = 256;
|
||||
char buffer[max_buffer];
|
||||
cmd.append(" 2>&1");
|
||||
|
||||
stream = popen(cmd.c_str(), "r");
|
||||
|
||||
if (stream) {
|
||||
while (!feof(stream))
|
||||
if (fgets(buffer, max_buffer, stream) != NULL) data.append(buffer);
|
||||
pclose(stream);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
String shell_escape(String raw)
|
||||
{
|
||||
// FIXME
|
||||
return("\"" + raw + "\"");
|
||||
/*
|
||||
` U+0060 (Grave Accent) Backtick Command substitution
|
||||
~ U+007E Tilde Tilde expansion
|
||||
! U+0021 Exclamation mark History expansion
|
||||
# U+0023 Number sign Hash Comments
|
||||
$ U+0024 Dollar sign Parameter expansion
|
||||
& U+0026 Ampersand Background commands
|
||||
* U+002A Asterisk Filename expansion and globbing
|
||||
( U+0028 Left Parenthesis Subshells
|
||||
) U+0029 Right Parenthesis Subshells
|
||||
U+0009 Tab (⇥) Word splitting (whitespace)
|
||||
{ U+007B Left Curly Bracket Left brace Brace expansion
|
||||
[ U+005B Left Square Bracket Filename expansion and globbing
|
||||
| U+007C Vertical Line Vertical bar Pipelines
|
||||
\ U+005C Reverse Solidus Backslash Escape character
|
||||
; U+003B Semicolon Separating commands
|
||||
' U+0027 Apostrophe Single quote String quoting
|
||||
" U+0022 Quotation Mark Double quote String quoting with interpolation
|
||||
↩ U+000A Line Feed Newline Line break
|
||||
< U+003C Less than Input redirection
|
||||
> U+003E Greater than Output redirection
|
||||
? U+003F Question mark Filename expansion and globbing
|
||||
U+0020 Space Word splitting1 (whitespace)
|
||||
*/
|
||||
}
|
||||
|
||||
String basename(String fn)
|
||||
{
|
||||
String result;
|
||||
while(fn.length() > 0)
|
||||
result = nibble("/", fn);
|
||||
//printf("basename(%s) %s\n", fn.c_str(), result.c_str());
|
||||
return(result);
|
||||
}
|
||||
|
||||
String dirname(String fn)
|
||||
{
|
||||
String result;
|
||||
auto seg = split(fn, "/");
|
||||
seg.pop_back();
|
||||
result = join(seg, "/");
|
||||
//printf("dirname(%s) %s seg#%i\n", fn.c_str(), result.c_str(), seg.size());
|
||||
return(result);
|
||||
}
|
||||
|
||||
bool mkdir(String path)
|
||||
{
|
||||
shell_exec(String("mkdir -p ")+" "+shell_escape(path));
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool file_exists(String path)
|
||||
{
|
||||
std::filesystem::path fp{ path };
|
||||
return(std::filesystem::exists(fp));
|
||||
}
|
||||
|
||||
String file_get_contents(String file_name)
|
||||
{
|
||||
/*std::ifstream ifs(file_name);
|
||||
printf("stream file desc %i\n", ifs.filedesc());
|
||||
String content(
|
||||
(std::istreambuf_iterator<char>(ifs) ),
|
||||
(std::istreambuf_iterator<char>() ) );*/
|
||||
char buf[512];
|
||||
String content;
|
||||
s32 fd = open(file_name.c_str(), O_RDONLY);
|
||||
if(fd == -1)
|
||||
{
|
||||
printf("(!) Could not read %s\n", file_name.c_str());
|
||||
return("");
|
||||
}
|
||||
flock(fd, LOCK_SH);
|
||||
s64 bytes_read = 0;
|
||||
//s64 size = lseek(fd, 0, SEEK_END);
|
||||
//lseek(fd, 0, SEEK_SET);
|
||||
//content.reserve(size+1);
|
||||
|
||||
while((bytes_read = read(fd, buf, 512)) > 0)
|
||||
{
|
||||
content.append(buf, bytes_read);
|
||||
}
|
||||
|
||||
flock(fd, LOCK_UN);
|
||||
close(fd);
|
||||
return(content);
|
||||
}
|
||||
|
||||
bool file_put_contents(String file_name, String content)
|
||||
{
|
||||
s32 fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC);
|
||||
if(fd == -1)
|
||||
{
|
||||
printf("(!) Could not write %s\n", file_name.c_str());
|
||||
return(false);
|
||||
}
|
||||
flock(fd, LOCK_EX);
|
||||
write(fd, content.data(), content.length());
|
||||
flock(fd, LOCK_UN);
|
||||
close(fd);
|
||||
return(true);
|
||||
}
|
||||
|
||||
String get_cwd()
|
||||
{
|
||||
return(std::filesystem::current_path());
|
||||
}
|
||||
|
||||
void set_cwd(String path)
|
||||
{
|
||||
chdir(path.c_str());
|
||||
}
|
||||
|
||||
time_t file_mtime(String file_name)
|
||||
{
|
||||
struct stat info;
|
||||
if (stat(file_name.c_str(), &info) != 0)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return(info.st_mtime);
|
||||
}
|
||||
}
|
||||
|
||||
void unlink(String file_name)
|
||||
{
|
||||
remove(file_name.c_str());
|
||||
}
|
||||
|
||||
String expand_path(String path)
|
||||
{
|
||||
String result;
|
||||
|
||||
auto base_path = split(get_cwd(), "/");
|
||||
auto rel_path = split(path, "/");
|
||||
|
||||
for(auto& s : rel_path)
|
||||
{
|
||||
if(s == "..")
|
||||
{
|
||||
base_path.pop_back();
|
||||
}
|
||||
else if(s == ".")
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
base_path.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
return(join(base_path, "/"));
|
||||
}
|
||||
|
||||
f64 microtime()
|
||||
{
|
||||
return ((f64)std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch()).count()) / 1000000;
|
||||
}
|
||||
|
||||
u64 time()
|
||||
{
|
||||
return(std::time(0));
|
||||
}
|
||||
|
||||
String date(String format, u64 timestamp)
|
||||
{
|
||||
String ts;
|
||||
String fmt;
|
||||
if(timestamp > 0)
|
||||
ts = String("-d '@")+std::to_string(timestamp)+"'";
|
||||
if(format != "")
|
||||
fmt = String("+'"+format+"'");
|
||||
return(trim(shell_exec("date "+ts+" "+fmt)));
|
||||
}
|
||||
|
||||
String gmdate(String format, u64 timestamp)
|
||||
{
|
||||
String ts;
|
||||
String fmt;
|
||||
if(timestamp > 0)
|
||||
ts = String("-d '@")+std::to_string(timestamp)+"'";
|
||||
if(format == "RFC1123")
|
||||
format = "%a, %d %b %Y %T GMT";
|
||||
if(format != "")
|
||||
fmt = String("+'"+format+"'");
|
||||
return(trim(shell_exec("date -u "+ts+" "+fmt)));
|
||||
}
|
||||
|
||||
u64 parse_time(String time_String)
|
||||
{
|
||||
return(int_val(trim(shell_exec("date -u -d '"+time_String+"' +'%s'"))));
|
||||
}
|
||||
|
||||
u64 socket_connect(String host, short port)
|
||||
{
|
||||
|
||||
/*String addrinfo {
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
socklen_t ai_addrlen;
|
||||
String sockaddr *ai_addr;
|
||||
char *ai_canonname;
|
||||
String addrinfo *ai_next;
|
||||
};*/
|
||||
|
||||
auto sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if(sockfd < 0)
|
||||
{
|
||||
print("SOCKET ERROR (could not open socket)\n");
|
||||
perror("SOCKET ERROR ");
|
||||
return(0);
|
||||
}
|
||||
|
||||
struct sockaddr_in addr = {0};
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = inet_addr(host.c_str());
|
||||
|
||||
if(connect(sockfd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
|
||||
{
|
||||
print("SOCKET ERROR (could not connect to address " + String(host) + ":" + std::to_string(port) + ")\n");
|
||||
perror("SOCKET ERROR ");
|
||||
return(0);
|
||||
}
|
||||
context->resources.sockets.push_back(sockfd);
|
||||
return(sockfd);
|
||||
}
|
||||
|
||||
void socket_close(u64 sockfd)
|
||||
{
|
||||
close(sockfd);
|
||||
}
|
||||
|
||||
bool socket_write(u64 sockfd, String data)
|
||||
{
|
||||
return(write(sockfd, data.c_str(), data.length()) >= 0);
|
||||
}
|
||||
|
||||
String socket_read(u64 sockfd, u32 max_length, u32 timeout)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
|
||||
char buf[max_length+1];
|
||||
auto byte_count = recv(sockfd, buf, sizeof(buf), 0);
|
||||
if(byte_count > 0)
|
||||
{
|
||||
buf[byte_count] = 0;
|
||||
String result(buf, byte_count+1);
|
||||
return(result);
|
||||
}
|
||||
return("");
|
||||
}
|
||||
|
||||
String memcache_escape_key(String key)
|
||||
{
|
||||
String result;
|
||||
for(auto c : key)
|
||||
{
|
||||
if(isspace(c))
|
||||
c = '_';
|
||||
result.append(1, c);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
StringList memcache_escape_keys(StringList keys)
|
||||
{
|
||||
StringList result;
|
||||
for(auto s : keys)
|
||||
{
|
||||
result.push_back(memcache_escape_key(s));
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
u64 memcache_connect(String host, short port)
|
||||
{
|
||||
return(socket_connect(host, port));
|
||||
}
|
||||
|
||||
String memcache_command(u64 connection, String command)
|
||||
{
|
||||
socket_write(connection, command+"\r\n");
|
||||
return(socket_read(connection)); // FIXME: do multi-chunk until END line is received!
|
||||
}
|
||||
|
||||
bool memcache_set(u64 connection, String key, String value, u64 expires_in)
|
||||
{
|
||||
socket_write(connection,
|
||||
// set KEY META_DATA EXPIRY_TIME LENGTH_IN_BYTES
|
||||
String("set ") + memcache_escape_key(key) + " 0 " + std::to_string(expires_in) + " " + std::to_string(value.length()) + "\r\n" +
|
||||
value + "\r\n");
|
||||
return("STORED" == trim(socket_read(connection)));
|
||||
}
|
||||
|
||||
bool memcache_delete(u64 connection, String key)
|
||||
{
|
||||
socket_write(connection,
|
||||
// set KEY META_DATA EXPIRY_TIME LENGTH_IN_BYTES
|
||||
String("delete ") + memcache_escape_key(key) + "\r\n"
|
||||
);
|
||||
return("DELETED" == trim(socket_read(connection)));
|
||||
}
|
||||
|
||||
String memcache_get(u64 connection, String key, String default_value)
|
||||
{
|
||||
auto res = memcache_command(connection, String("get ")+memcache_escape_key(key));
|
||||
String t = nibble(res, " ");
|
||||
if(t == "VALUE")
|
||||
{
|
||||
String key = nibble(res, " ");
|
||||
String meta = nibble(res, " ");
|
||||
u32 length = stoi(nibble(res, "\r\n"));
|
||||
return(res.substr(0, length));
|
||||
}
|
||||
return(default_value);
|
||||
}
|
||||
|
||||
StringMap memcache_get_multiple(u64 connection, StringList keys)
|
||||
{
|
||||
StringMap result;
|
||||
// to do: escape key String
|
||||
auto res = memcache_command(connection, String("get ")+join(memcache_escape_keys(keys), " "));
|
||||
while(res.length() > 0)
|
||||
{
|
||||
String t = nibble(res, " ");
|
||||
if(t == "VALUE")
|
||||
{
|
||||
String key = nibble(res, " ");
|
||||
String meta = nibble(res, " ");
|
||||
u32 length = stoi(nibble(res, "\r\n"));
|
||||
result[key] = res.substr(0, length);
|
||||
res = res.substr(length+2);
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
void on_segfault(int sig)
|
||||
{
|
||||
void *array[10];
|
||||
size_t size;
|
||||
|
||||
// get void*'s for all entries on the stack
|
||||
size = backtrace(array, 10);
|
||||
|
||||
// print out all the frames to stderr
|
||||
fprintf(stderr, "SEG FAULT: %d:\n", sig);
|
||||
backtrace_symbols_fd(array, size, STDERR_FILENO);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
std::map<pid_t, Worker> workers;
|
||||
#include <sys/wait.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
void spawn_subprocess(std::function<void()> exec_after_spawn)
|
||||
{
|
||||
parent_pid = getpid();
|
||||
pid_t p;
|
||||
p = fork();
|
||||
if(p == 0)
|
||||
{
|
||||
my_pid = getpid();
|
||||
//printf("(C) child procress started, PID:%i\n", my_pid);
|
||||
prctl(PR_SET_PDEATHSIG, SIGHUP);
|
||||
exec_after_spawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
Worker w;
|
||||
w.pid = p;
|
||||
workers[w.pid] = w;
|
||||
printf("(P) child procress spawned: PID %i\n", p);
|
||||
}
|
||||
}
|
||||
|
||||
pid_t task_pid(String key)
|
||||
{
|
||||
String status_file_name = context->server->config.BIN_DIRECTORY + "/task-" + key;
|
||||
String status_file = file_get_contents(status_file_name);
|
||||
pid_t p = 0;
|
||||
if(status_file != "")
|
||||
{
|
||||
p = int_val(status_file);
|
||||
if(kill(p, 0) == 0) // process is still running
|
||||
return(p);
|
||||
unlink(status_file_name);
|
||||
}
|
||||
return(p);
|
||||
}
|
||||
|
||||
pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
|
||||
{
|
||||
String status_file_name = context->server->config.BIN_DIRECTORY + "/task-" + key;
|
||||
String status_file = file_get_contents(status_file_name);
|
||||
pid_t p;
|
||||
if(status_file != "")
|
||||
{
|
||||
p = int_val(status_file);
|
||||
if(kill(p, 0) == 0) // process is still running
|
||||
{
|
||||
printf("(P) worker process '%s' already running: PID %i\n", key.c_str(), p);
|
||||
return(p);
|
||||
}
|
||||
//printf("(P) worker process '%s' had crashed: PID %i\n", key.c_str(), p);
|
||||
unlink(status_file_name);
|
||||
}
|
||||
p = fork();
|
||||
if(p == 0)
|
||||
{
|
||||
my_pid = getpid();
|
||||
file_put_contents(status_file_name, std::to_string(my_pid));
|
||||
|
||||
close(context->resources.fcgi_socket);
|
||||
context->resources.fcgi_socket = 0;
|
||||
//printf("(C) child procress started, PID:%i\n", my_pid);
|
||||
//prctl(PR_SET_PDEATHSIG, SIGHUP);
|
||||
exec_after_spawn();
|
||||
unlink(status_file_name);
|
||||
printf("(P) worker process '%s' terminated: PID %i\n", key.c_str(), my_pid);
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("(P) worker process '%s' spawned: PID %i\n", key.c_str(), p);
|
||||
return(p);
|
||||
}
|
||||
}
|
||||
|
||||
void on_child_exit(int sig)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
if ((pid = waitpid(-1, &status, WNOHANG)) != -1)
|
||||
{
|
||||
if(workers.count(pid) > 0)
|
||||
{
|
||||
workers.erase(pid);
|
||||
printf("(P) child terminated (PID:%i)\n", pid);
|
||||
//spawn_subprocess();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringList ls(String dir)
|
||||
{
|
||||
return(split(trim(shell_exec("ls -1 "+shell_escape(dir))), "\n"));
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <signal.h>
|
||||
|
||||
String shell_exec(String cmd);
|
||||
String shell_escape(String raw);
|
||||
String basename(String fn);
|
||||
String dirname(String fn);
|
||||
bool mkdir(String path);
|
||||
bool file_exists(String path);
|
||||
String file_get_contents(String file_name);
|
||||
bool file_put_contents(String file_name, String content);
|
||||
#include <fstream>
|
||||
template <typename... Ts>
|
||||
bool file_append(String file_name, Ts... args)
|
||||
{
|
||||
std::ofstream fout;
|
||||
fout.open(file_name.c_str(), std::ios_base::app);
|
||||
((fout << args), ...);
|
||||
fout.close();
|
||||
return(true);
|
||||
}
|
||||
String get_cwd();
|
||||
void set_cwd(String path);
|
||||
time_t file_mtime(String file_name);
|
||||
void unlink(String file_name);
|
||||
String expand_path(String path);
|
||||
StringList ls(String dir);
|
||||
|
||||
f64 microtime();
|
||||
u64 time();
|
||||
String date(String format = "", u64 timestamp = 0);
|
||||
String gmdate(String format = "", u64 timestamp = 0);
|
||||
u64 parse_time(String time_String);
|
||||
|
||||
u64 socket_connect(String host, short port);
|
||||
void socket_close(u64 sockfd);
|
||||
bool socket_write(u64 sockfd, String data);
|
||||
String socket_read(u64 sockfd, u32 max_length = 1024*128, u32 timeout = 1);
|
||||
|
||||
String memcache_escape_key(String key);
|
||||
StringList memcache_escape_keys(StringList keys);
|
||||
u64 memcache_connect(String host = "127.0.0.1", short port = 11211);
|
||||
String memcache_command(u64 connection, String command);
|
||||
bool memcache_set(u64 connection, String key, String value, u64 expires_in = 60*60);
|
||||
bool memcache_delete(u64 connection, String key);
|
||||
String memcache_get(u64 connection, String key, String default_value = "");
|
||||
StringMap memcache_get_multiple(u64 connection, StringList keys);
|
||||
|
||||
pid_t parent_pid = 0;
|
||||
pid_t my_pid = 0;
|
||||
|
||||
void on_segfault(int sig);
|
||||
|
||||
pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout = 60*10);
|
||||
pid_t task_pid(String key);
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <ctype.h>
|
||||
#include <fstream>
|
||||
#include <sys/stat.h>
|
||||
#include <ctime>
|
||||
#include <dlfcn.h>
|
||||
#include <limits.h>
|
||||
#include <algorithm>
|
||||
#include <sys/stat.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
SharedUnit::~SharedUnit()
|
||||
{
|
||||
if(so_handle)
|
||||
{
|
||||
dlclose(so_handle);
|
||||
}
|
||||
}
|
||||
|
||||
String nibble(String div, String& haystack)
|
||||
{
|
||||
auto pos = haystack.find(div);
|
||||
if(pos == String::npos)
|
||||
{
|
||||
auto result = haystack;
|
||||
haystack.clear();
|
||||
return(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto result = haystack.substr(0, pos);
|
||||
haystack.erase(0, pos+div.length());
|
||||
return(result);
|
||||
}
|
||||
}
|
||||
|
||||
void Request::invoke(String file_name)
|
||||
{
|
||||
DTree call_param;
|
||||
compiler_invoke(this, file_name, call_param);
|
||||
}
|
||||
|
||||
void Request::invoke(String file_name, DTree& call_param)
|
||||
{
|
||||
compiler_invoke(this, file_name, call_param);
|
||||
}
|
||||
|
||||
void Request::ob_start()
|
||||
{
|
||||
ob_stack.push_back(new std::ostringstream());
|
||||
ob = ob_stack.back();
|
||||
}
|
||||
|
||||
Request::~Request()
|
||||
{
|
||||
for(auto& sockfd : resources.sockets)
|
||||
close(sockfd);
|
||||
}
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef signed char s8;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short s16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int s32;
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long s64;
|
||||
|
||||
typedef std::string String;
|
||||
|
||||
String operator+(String lhs, u64 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
String operator+(String lhs, u32 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
String operator+(String lhs, s64 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
String operator+(String lhs, s32 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
String operator+(String lhs, f64 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
String operator+(String lhs, f32 rhs) {
|
||||
return(lhs + std::to_string(rhs));
|
||||
}
|
||||
|
||||
#define DEBUG_MEMORY_OFF
|
||||
#define GLOBAL_ARENA_ALLOCATOR
|
||||
|
||||
struct MemoryArena {
|
||||
|
||||
u8* data;
|
||||
u64 size = 0;
|
||||
u64 capacity = 0;
|
||||
String name = "unnamed";
|
||||
|
||||
MemoryArena(u64 cap, String _name = "unnamed")
|
||||
{
|
||||
name = _name;
|
||||
capacity = cap;
|
||||
printf("(i) memory arena '%s' created with capacity of %llu bytes\n", name.c_str(), capacity);
|
||||
data = (u8*)malloc(cap);
|
||||
}
|
||||
|
||||
~MemoryArena()
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
#ifdef DEBUG_MEMORY
|
||||
printf("(i) memory arena '%s' cleared after high mark of %llu bytes\n", name.c_str(), size);
|
||||
#endif
|
||||
size = 0;
|
||||
}
|
||||
|
||||
void* get(u64 size_needed)
|
||||
{
|
||||
u64 size_aligned = 8 + (8 * ((size_needed) / 8));
|
||||
u8* result = data + size;
|
||||
if(size_aligned + size >= capacity)
|
||||
{
|
||||
printf("(!) memory arena '%s' capacity (%llu) exceeded %llu/%llu + %llu >= %llu\n",
|
||||
name.c_str(), capacity, size_needed, size_aligned, size, capacity);
|
||||
return(0);
|
||||
}
|
||||
size += size_aligned;
|
||||
#ifdef DEBUG_MEMORY_DETAILED
|
||||
printf("(i) memory arena '%s' [+%llu]:%p alloc %llu/%llu bytes\n", name.c_str(), size, result, size_needed, size_aligned);
|
||||
#endif
|
||||
return(result);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
MemoryArena* current_memory_arena = 0;
|
||||
|
||||
void switch_to_system_alloc()
|
||||
{
|
||||
#ifdef GLOBAL_ARENA_ALLOCATOR
|
||||
current_memory_arena = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void switch_to_arena(MemoryArena* a)
|
||||
{
|
||||
#ifdef GLOBAL_ARENA_ALLOCATOR
|
||||
current_memory_arena = a;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef GLOBAL_ARENA_ALLOCATOR
|
||||
void * operator new(decltype(sizeof(0)) n) noexcept(false)
|
||||
{
|
||||
if(current_memory_arena)
|
||||
{
|
||||
return(current_memory_arena->get(n));
|
||||
}
|
||||
else
|
||||
{
|
||||
return(malloc(n));
|
||||
}
|
||||
}
|
||||
|
||||
void operator delete(void * p) throw()
|
||||
{
|
||||
if(current_memory_arena)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef std::map<String, String> StringMap;
|
||||
typedef std::vector<String> StringList;
|
||||
|
||||
struct Request;
|
||||
struct DTree;
|
||||
|
||||
typedef void (*call_handler)(DTree& call_param);
|
||||
typedef void (*request_handler)(Request* request);
|
||||
|
||||
String to_string(s64 v) { return(std::to_string(v)); }
|
||||
|
||||
struct ServerSettings {
|
||||
|
||||
String BIN_DIRECTORY = "/tmp/uce/work";
|
||||
String COMPILE_SCRIPT = "scripts/compile";
|
||||
String LIT_ESC = "3d5b5_1";
|
||||
String CONTENT_TYPE = "text/html; charset=utf-8";
|
||||
String SOCKET_PATH = "/run/uce.sock";
|
||||
String TMP_UPLOAD_PATH = "/tmp/uce/uploads";
|
||||
String SESSION_PATH = "/tmp/uce/sessions";
|
||||
String COMPILER_SYS_PATH = ".";
|
||||
u32 LISTEN_PORT = 9993;
|
||||
u64 SESSION_TIME = 60*60*24*30;
|
||||
u32 WORKER_COUNT = 4;
|
||||
u32 MAX_MEMORY = 1024*1024*16;
|
||||
|
||||
};
|
||||
|
||||
struct SharedUnit {
|
||||
|
||||
String file_name;
|
||||
String so_name;
|
||||
|
||||
String src_path;
|
||||
String bin_path;
|
||||
String pre_path;
|
||||
String src_file_name;
|
||||
String bin_file_name;
|
||||
String pre_file_name;
|
||||
|
||||
void* so_handle;
|
||||
|
||||
request_handler on_setup;
|
||||
call_handler on_render;
|
||||
|
||||
String compiler_messages;
|
||||
time_t last_compiled;
|
||||
|
||||
~SharedUnit();
|
||||
};
|
||||
|
||||
struct UploadedFile {
|
||||
String file_name;
|
||||
String tmp_name;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
struct ServerState {
|
||||
|
||||
std::map<String, SharedUnit*> units;
|
||||
ServerSettings config;
|
||||
u32 request_count = 0;
|
||||
|
||||
};
|
||||
|
||||
struct URI {
|
||||
|
||||
StringMap query;
|
||||
StringMap parts;
|
||||
|
||||
};
|
||||
|
||||
String nibble(String div, String& haystack);
|
||||
|
||||
template <typename ITYPE>
|
||||
String to_hex(ITYPE w, size_t hex_len = sizeof(ITYPE)<<1)
|
||||
{
|
||||
static const char* digits = "0123456789ABCDEF";
|
||||
String rc(hex_len,'0');
|
||||
for (size_t i=0, j=(hex_len-1)*4 ; i<hex_len; ++i,j-=4)
|
||||
rc[i] = digits[(w>>j) & 0x0f];
|
||||
return(rc);
|
||||
}
|
||||
|
||||
#include "dtree.h"
|
||||
|
||||
void compiler_invoke(Request* context, String file_name, DTree& call_param);
|
||||
|
||||
struct Request {
|
||||
|
||||
ServerState* server;
|
||||
|
||||
StringMap params;
|
||||
StringMap get;
|
||||
StringMap post;
|
||||
StringMap cookies;
|
||||
StringMap session;
|
||||
|
||||
DTree var;
|
||||
|
||||
String session_id = "";
|
||||
String session_name = "";
|
||||
std::vector<UploadedFile> uploaded_files;
|
||||
|
||||
StringMap header;
|
||||
StringList set_cookies;
|
||||
|
||||
u64 random_seed;
|
||||
u64 random_index;
|
||||
|
||||
MemoryArena* mem;
|
||||
|
||||
String in;
|
||||
std::vector<std::ostringstream*> ob_stack;
|
||||
std::ostringstream* ob;
|
||||
String out;
|
||||
String err;
|
||||
|
||||
bool is_finished = false;
|
||||
|
||||
struct Flags {
|
||||
bool log_request = true;
|
||||
} flags;
|
||||
|
||||
struct Stats {
|
||||
u32 bytes_written;
|
||||
f64 time_init;
|
||||
f64 time_start;
|
||||
f64 time_end;
|
||||
} stats;
|
||||
|
||||
struct Resources {
|
||||
std::vector<u64> sockets;
|
||||
std::vector<void*> mysql_connections;
|
||||
u64 fcgi_socket = 0;
|
||||
} resources;
|
||||
|
||||
void invoke(String file_name);
|
||||
void invoke(String file_name, DTree& call_param);
|
||||
|
||||
void ob_start();
|
||||
|
||||
~Request();
|
||||
|
||||
};
|
||||
|
||||
typedef Request FastCGIRequest;
|
||||
|
||||
Request* context;
|
||||
|
||||
#include <iostream>
|
||||
|
||||
template <typename... Ts>
|
||||
void print(Ts... args)
|
||||
{
|
||||
((*context->ob << args), ...);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
#include "types.cpp"
|
||||
#include "hash.cpp"
|
||||
#include "dtree.cpp"
|
||||
#include "functionlib.cpp"
|
||||
#include "sys.cpp"
|
||||
#include "uri.cpp"
|
||||
#include "compiler.cpp"
|
||||
#include "mysql-connector.cpp"
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
#include "types.h"
|
||||
#include "hash.h"
|
||||
#include "functionlib.h"
|
||||
#include "sys.h"
|
||||
#include "uri.h"
|
||||
#include "compiler.h"
|
||||
#include "mysql-connector.h"
|
||||
|
||||
extern "C" void set_current_request(Request* _request)
|
||||
{
|
||||
context = _request;
|
||||
signal(SIGSEGV, on_segfault);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#include "uce_lib.h"
|
||||
|
||||
|
||||
+321
@@ -0,0 +1,321 @@
|
||||
#include "uri.h"
|
||||
|
||||
String var_dump(URI uri, String prefix, String postfix)
|
||||
{
|
||||
return(
|
||||
prefix + "URI Parts: " + postfix +
|
||||
var_dump(uri.parts, prefix+" ", postfix)+
|
||||
prefix + " Query: " + postfix +
|
||||
var_dump(uri.query, prefix+" ", postfix)
|
||||
);
|
||||
}
|
||||
|
||||
String uri_decode(String q)
|
||||
{
|
||||
String result;
|
||||
for(u32 i = 0; i < q.length(); i++)
|
||||
{
|
||||
char c = q[i];
|
||||
if(c == '%' && q[i+1] != '%')
|
||||
{
|
||||
result.append(1, hex_to_u8(q.substr(i+1, 2)));
|
||||
i += 2;
|
||||
}
|
||||
else if(c == '+')
|
||||
{
|
||||
result.append(1, ' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
result.append(1, c);
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String uri_encode(String q)
|
||||
{
|
||||
String result;
|
||||
for(u32 i = 0; i < q.length(); i++)
|
||||
{
|
||||
char c = q[i];
|
||||
if(isalnum(c) || c == '~' || c == '.' || c == '_' || c == '-')
|
||||
result.append(1, c);
|
||||
else
|
||||
{
|
||||
result.append(1, '%');
|
||||
result.append(to_hex(c));
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
StringMap parse_query(String q)
|
||||
{
|
||||
StringMap result;
|
||||
if(q.length() == 0)
|
||||
return(result);
|
||||
|
||||
bool is_key = true;
|
||||
String key = "";
|
||||
String value = "";
|
||||
for (char &c: q)
|
||||
{
|
||||
if(c == '=')
|
||||
{
|
||||
is_key = !is_key;
|
||||
}
|
||||
else if(c == '&')
|
||||
{
|
||||
result[uri_decode(key)] = uri_decode(value);
|
||||
key = "";
|
||||
value = "";
|
||||
is_key = true;
|
||||
}
|
||||
else if(is_key)
|
||||
{
|
||||
key.append(1, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
value.append(1, c);
|
||||
}
|
||||
}
|
||||
|
||||
result[uri_decode(key)] = uri_decode(value);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
String encode_query(StringMap map)
|
||||
{
|
||||
String result;
|
||||
|
||||
for (auto it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
if(result.length() > 0)
|
||||
result.append(1, '&');
|
||||
result.append(uri_encode(it->first) + "=" + uri_encode(it->second));
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
StringMap parse_multipart(String q, String boundary, std::vector<UploadedFile>& uploaded_files)
|
||||
{
|
||||
StringMap result;
|
||||
|
||||
auto i = boundary.length();
|
||||
while(i < q.length())
|
||||
{
|
||||
auto end_pos = q.find(boundary, i);
|
||||
if(end_pos != String::npos)
|
||||
{
|
||||
String field = q.substr(i, end_pos - i);
|
||||
nibble(":", field);
|
||||
String ftype = trim(nibble(";", field));
|
||||
if(ftype == "form-data")
|
||||
{
|
||||
nibble("=\"", field);
|
||||
String field_name = nibble("\"", field);
|
||||
result[field_name] = field.substr(4, field.length()-6);
|
||||
}
|
||||
else if(ftype == "attachment")
|
||||
{
|
||||
nibble("=\"", field);
|
||||
UploadedFile f;
|
||||
f.tmp_name = context->server->config.TMP_UPLOAD_PATH + std::to_string(rand());
|
||||
f.file_name = nibble("\"", field);
|
||||
String bin = field.substr(4, field.length()-6);
|
||||
f.size = bin.length();
|
||||
file_put_contents(f.tmp_name, bin);
|
||||
uploaded_files.push_back(f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're done
|
||||
end_pos = q.length();
|
||||
}
|
||||
i = end_pos + boundary.length();
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
URI parse_uri(String uri_String)
|
||||
{
|
||||
URI result;
|
||||
|
||||
u8 state = 0;
|
||||
String current = "";
|
||||
char expect = 0;
|
||||
|
||||
result.parts["raw"] = uri_String;
|
||||
|
||||
String part_names[] = {
|
||||
"scheme",
|
||||
"host",
|
||||
"port",
|
||||
"path",
|
||||
"query",
|
||||
"fragment",
|
||||
};
|
||||
|
||||
if(uri_String[0] == '/')
|
||||
state = 3;
|
||||
|
||||
for (char &c: uri_String)
|
||||
{
|
||||
bool append_it = true;
|
||||
|
||||
if(expect && expect != c)
|
||||
{
|
||||
result.parts["error"] = String("\'") + c + String("\' expected");
|
||||
result.parts["error_parsing"] = current;
|
||||
result.parts["error_part"] = part_names[state];
|
||||
return(result);
|
||||
}
|
||||
expect = 0;
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case(0): // scheme
|
||||
if(c == ':')
|
||||
{
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 1;
|
||||
}
|
||||
break;
|
||||
case(1): // host name
|
||||
if(c == '/')
|
||||
{
|
||||
if(current == "")
|
||||
{
|
||||
append_it = false;
|
||||
break;
|
||||
}
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 3;
|
||||
expect = '/';
|
||||
}
|
||||
else if(c == ':')
|
||||
{
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 2;
|
||||
}
|
||||
break;
|
||||
case(2): // port
|
||||
if(c == '/')
|
||||
{
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 3;
|
||||
expect = '/';
|
||||
}
|
||||
break;
|
||||
case(3): // path
|
||||
if(c == '/' && current == "")
|
||||
{
|
||||
append_it = false;
|
||||
break;
|
||||
}
|
||||
if(c == '?')
|
||||
{
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 4;
|
||||
}
|
||||
break;
|
||||
case(4): // query
|
||||
if(c == '#')
|
||||
{
|
||||
result.parts[part_names[state]] = current;
|
||||
append_it = false;
|
||||
current = "";
|
||||
state = 5;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(append_it)
|
||||
current.append(1, c);
|
||||
}
|
||||
|
||||
result.parts[part_names[state]] = current;
|
||||
|
||||
result.query = parse_query(result.parts["query"]);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
void set_cookie(
|
||||
String name, String value,
|
||||
u64 expires, String path, String domain,
|
||||
bool secure, bool http_only)
|
||||
{
|
||||
String cookie = "Set-Cookie: ";
|
||||
cookie.append(uri_encode(name) + "=" + uri_encode(value));
|
||||
if(expires > 0)
|
||||
cookie.append(String("; Expires=") + gmdate("RFC1123", expires));
|
||||
context->set_cookies.push_back(cookie);
|
||||
context->cookies[name] = value;
|
||||
}
|
||||
|
||||
StringMap parse_cookies(String cookie_String)
|
||||
{
|
||||
StringMap result;
|
||||
while(cookie_String.length() > 0)
|
||||
{
|
||||
String key = trim(nibble("=", cookie_String));
|
||||
String value = nibble(";", cookie_String);
|
||||
result[key] = value;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
String make_session_id()
|
||||
{
|
||||
return(to_hex(rand())+to_hex(rand())+to_hex(rand())+to_hex(rand()));
|
||||
}
|
||||
|
||||
StringMap load_session_data(String session_id)
|
||||
{
|
||||
return(parse_query(file_get_contents(context->server->config.SESSION_PATH + "/" + session_id)));
|
||||
}
|
||||
|
||||
void save_session_data(String session_id, StringMap data)
|
||||
{
|
||||
file_put_contents(context->server->config.SESSION_PATH + "/" + session_id, encode_query(data));
|
||||
}
|
||||
|
||||
String session_start(String session_name)
|
||||
{
|
||||
if(context->cookies[session_name].length() == 0)
|
||||
{
|
||||
set_cookie(session_name, make_session_id(), time() + context->server->config.SESSION_TIME);
|
||||
}
|
||||
context->session_id = context->cookies[session_name];
|
||||
context->session_name = session_name;
|
||||
context->session = load_session_data(context->session_id);
|
||||
return(context->session_id);
|
||||
}
|
||||
|
||||
void session_destroy(String session_name)
|
||||
{
|
||||
if(context->cookies[session_name].length() > 0)
|
||||
{
|
||||
set_cookie(session_name, "", time() - context->server->config.SESSION_TIME);
|
||||
context->session.clear();
|
||||
save_session_data(context->session_id, context->session);
|
||||
context->session_id = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
String var_dump(URI uri, String prefix = "", String postfix = "\n");
|
||||
String uri_decode(String q);
|
||||
String uri_encode(String q);
|
||||
StringMap parse_query(String q);
|
||||
String encode_query(StringMap map);
|
||||
StringMap parse_multipart(String q, String boundary, std::vector<UploadedFile>& uploaded_files);
|
||||
URI parse_uri(String uri_String);
|
||||
void set_cookie(
|
||||
String name, String value = "",
|
||||
u64 expires = 0, String path = "/", String domain = "",
|
||||
bool secure = false, bool http_only = true);
|
||||
StringMap parse_cookies(String cookie_String);
|
||||
String make_session_id();
|
||||
StringMap load_session_data(String session_id);
|
||||
void save_session_data(String session_id, StringMap data);
|
||||
String session_start(String session_name = "uce-session");
|
||||
void session_destroy(String session_name = "uce-session");
|
||||
@@ -0,0 +1,157 @@
|
||||
#include "lib/uce_lib.cpp"
|
||||
|
||||
ServerState server_state;
|
||||
MemoryArena* request_arena = new MemoryArena(server_state.config.MAX_MEMORY, "request");
|
||||
|
||||
#include "fastcgi/src/fcgicc.cc"
|
||||
|
||||
FastCGIServer server;
|
||||
|
||||
int handle_request(FastCGIRequest& request) {
|
||||
// This is always the first event to occur. It occurs when the
|
||||
// server receives all parameters. There may be more data coming on the
|
||||
// standard input stream.
|
||||
if (request.params.count("REQUEST_URI"))
|
||||
return 0; // OK, continue processing
|
||||
else
|
||||
return 1; // stop processing and return error code
|
||||
}
|
||||
|
||||
int handle_data(FastCGIRequest& request) {
|
||||
// This event occurs when data is received on the standard input stream.
|
||||
// A simple String is used to hold the input stream, so it is the
|
||||
// responsibility of the application to remember which data it has
|
||||
// processed. The application may modify it; new data will be appended
|
||||
// to it by the server. The same goes for the output and error streams:
|
||||
// the application should append data to them; the server will remove
|
||||
// all sent data from them.
|
||||
return 0; // still OK
|
||||
|
||||
std::transform(request.in.begin(), request.in.end(),
|
||||
std::back_inserter(request.err),
|
||||
std::bind1st(std::plus<char>(), 1));
|
||||
request.in.clear(); // don't process it again
|
||||
return 0; // still OK
|
||||
}
|
||||
|
||||
int handle_complete(FastCGIRequest& request) {
|
||||
// The event handler can also be a class member function. This
|
||||
// event occurs when the parameters and standard input streams are
|
||||
// both closed, and thus the request is complete.
|
||||
// printf("(i) request handle\n");
|
||||
|
||||
context = &request;
|
||||
server_state.request_count += 1;
|
||||
request.server = &server_state;
|
||||
request.stats.time_start = microtime();
|
||||
request.header["Content-Type"] = context->server->config.CONTENT_TYPE;
|
||||
request.get = parse_query(request.params["QUERY_STRING"]);
|
||||
request.random_index = 0;
|
||||
request.random_seed = gen_noise64(*reinterpret_cast<u64*>(&request.stats.time_start));
|
||||
request.ob_start();
|
||||
|
||||
if(request.params["HTTP_COOKIE"].length() > 0)
|
||||
request.cookies = parse_cookies(request.params["HTTP_COOKIE"]);
|
||||
|
||||
String ct_info = request.params["CONTENT_TYPE"];
|
||||
String ct_type = nibble(";", ct_info);
|
||||
|
||||
if(request.params["REQUEST_METHOD"] == "POST")
|
||||
{
|
||||
if(ct_type == "multipart/form-data")
|
||||
{
|
||||
nibble("boundary=", ct_info);
|
||||
request.post = parse_multipart(request.in, String("--")+ct_info, request.uploaded_files);
|
||||
}
|
||||
else
|
||||
{
|
||||
request.post = parse_query(request.in);
|
||||
}
|
||||
}
|
||||
|
||||
// printf("(i) request ready\n");
|
||||
request.invoke(request.params["SCRIPT_FILENAME"]);
|
||||
|
||||
for( auto &f : request.uploaded_files)
|
||||
{
|
||||
unlink(f.tmp_name);
|
||||
}
|
||||
|
||||
if(request.session_id.length() > 0)
|
||||
save_session_data(request.session_id, request.session);
|
||||
|
||||
cleanup_mysql_connections();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void listen_for_connections()
|
||||
{
|
||||
signal(SIGSEGV, on_segfault);
|
||||
server.on_request = &handle_request;
|
||||
server.on_data = &handle_data;
|
||||
server.on_complete = &handle_complete;
|
||||
/*
|
||||
server.request_handler(&handle_request);
|
||||
server.data_handler(&handle_data);
|
||||
server.complete_handler(&handle_complete);
|
||||
*/
|
||||
for(;;)
|
||||
{
|
||||
//if(request_arena) request_arena->clear();
|
||||
//current_memory_arena = request_arena;
|
||||
server.process();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
printf("(P) Starting parent server PID:%i\n", getpid());
|
||||
|
||||
signal(SIGCHLD, on_child_exit);
|
||||
srand(time());
|
||||
|
||||
//if(server_state.config.COMPILER_SYS_PATH == "")
|
||||
server_state.config.COMPILER_SYS_PATH = get_cwd();
|
||||
|
||||
// printf("MySQL client version: %s\n", mysql_get_client_info());
|
||||
|
||||
printf("Compiler base path: %s\n", server_state.config.COMPILER_SYS_PATH.c_str());
|
||||
|
||||
server_state.config.COMPILE_SCRIPT =
|
||||
server_state.config.COMPILER_SYS_PATH + "/" + server_state.config.COMPILE_SCRIPT;
|
||||
if(server_state.config.LISTEN_PORT)
|
||||
server.listen(server_state.config.LISTEN_PORT);
|
||||
if(server_state.config.SOCKET_PATH != "")
|
||||
server.listen(server_state.config.SOCKET_PATH);
|
||||
chmod(server_state.config.SOCKET_PATH.c_str(), S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
|
||||
dirname(server_state.config.COMPILER_SYS_PATH);
|
||||
basename(server_state.config.COMPILER_SYS_PATH);
|
||||
|
||||
mkdir(server_state.config.BIN_DIRECTORY);
|
||||
mkdir(server_state.config.TMP_UPLOAD_PATH);
|
||||
mkdir(server_state.config.SESSION_PATH);
|
||||
|
||||
//server.process(100);
|
||||
//server.process();
|
||||
/*try
|
||||
{
|
||||
server.process_forever();
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
std::cout << e.what();
|
||||
}*/
|
||||
|
||||
for(;;)
|
||||
{
|
||||
while(workers.size() < server_state.config.WORKER_COUNT)
|
||||
{
|
||||
spawn_subprocess(listen_for_connections);
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user