diff --git a/plugins/experimental/fastcgi/README.md b/plugins/experimental/fastcgi/README.md deleted file mode 100644 index 8a149206261..00000000000 --- a/plugins/experimental/fastcgi/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# AtsFastcgi -- a FastCGI plugin implementation for Apache Traffic Server. - -This plugin was presented at the ATS Euro-Tour summit in Cork [1]. Its current state -should be considered alpha, the plugin has seen 0 production mileage. - -[1] Slides: https://docs.google.com/presentation/d/1kJ3hxbmflh8z4jsquPskeb1xNrLXHAYI2-OQXwwftZU/edit - -## Install - -```bash -cd src - -ATS_SRC="/home/oschaaf/trafficserver" -ATS_EXEC="/usr/local" -make ATS_SRC="$ATS_SRC" ATS_EXEC="$ATS_EXEC" -sudo make ATS_SRC="$ATS_SRC" ATS_EXEC="$ATS_EXEC" install -``` - - -## CONFIG - -Add to plugin.config: -``` -# For possible settings, see below -ats_mod_fcgi.so /usr/local/etc/trafficserver/fcgi.config -``` - -## Starting CGI - -``` -sudo apt-get install php-cgi -php-cgi -b 60000 -``` - -## Running ATS - -``` -sudo traffic_server -T .*cgi.* -``` - -## Requesting a php page - - -``` -curl 127.0.0.1/test.php -``` - -## Config php: - -Default directory where php looks for scripts is "/var/www/html/ -Simple test page that dump php info: - -``` -oschaaf@ats-fastcgi:/var/www/html⟫ cat test.php - - -``` - -## Settings - -``` -ats_mod_fcgi.config:CONFIG proxy.config.http.fcgi.enabled INT 1 -ats_mod_fcgi.config:CONFIG proxy.config.http.fcgi.host.hostname STRING localhost -ats_mod_fcgi.config:CONFIG proxy.config.http.fcgi.host.server_ip STRING 127.0.0.1 -ats_mod_fcgi.config:CONFIG proxy.config.http.fcgi.host.server_port STRING 60000 -ats_mod_fcgi.config:CONFIG proxy.config.http.fcgi.host.include STRING etc/trafficserver/fastcgi.config -ats_mod_fcgi.config:CONFIG proxy.config.http.fcgi.host.document_root STRING /var/www/ -ats_mod_fcgi.config:CONFIG proxy.config.http.fcgi.host.html STRING index.php -ats_mod_fcgi.config:CONFIG proxy.config.http.fcgi.host.min_connections INT 2 -ats_mod_fcgi.config:CONFIG proxy.config.http.fcgi.host.max_connections INT 16 -ats_mod_fcgi.config:CONFIG proxy.config.http.fcgi.host.max_requests INT 1000 -ats_mod_fcgi.config:CONFIG proxy.config.http.fcgi.host.request_queue_size INT 250 -``` - -### Old/stale docs: https://github.com/We-Amp/AtsFastcgi/wiki/Building-ATS-FCGI-from-Source diff --git a/plugins/experimental/fastcgi/src/Makefile b/plugins/experimental/fastcgi/src/Makefile deleted file mode 100644 index bceb8049630..00000000000 --- a/plugins/experimental/fastcgi/src/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -#Please update below required files/directories inorder to work this make file -# 1. ATS_SRC --either set or export env variable path to traffic server src directory -# 2. ATS_EXEC --either set or export env variable path to traffic server exec directory - - -ifndef ATS_SRC -$(error ATS_SRC is not set) -endif - -ifndef ATS_EXEC -$(error ATS_EXEC is not set) -endif - -MOD_FCGI_DIR=$(CURDIR) -TRAFFIC_SERVER_SRC_DIR=$(ATS_SRC) -TRAFFIC_SERVER_EXEC_DIR=$(ATS_EXEC) - -TSXS?=$(TRAFFIC_SERVER_EXEC_DIR)/bin/tsxs -OBJ_FILES=server_intercept.cc fcgi_config.cc ats_fcgi_client.cc server.cc connection_pool.cc request_queue.cc server_connection.cc - -TRAFFIC_SERVER_EXEC_CONF_DIR=$(TRAFFIC_SERVER_EXEC_DIR)/etc/trafficserver/ -PLUGIN_CONF_FILES=$(MOD_FCGI_DIR)/config/ats_fastcgi.config - -LDLIB_DIR=-L $(TRAFFIC_SERVER_EXEC_DIR)/lib/ -LDLIB=-latscppapi - -INC=-I $(MOD_FCGI_DIR)\ - -I $(TRAFFIC_SERVER_SRC_DIR)/lib\ - -I $(TRAFFIC_SERVER_SRC_DIR)/lib/cppapi/include/ -COPY_CONFIG = $(shell sudo cp -R '$(PLUGIN_CONF_FILES)' '$(TRAFFIC_SERVER_EXEC_CONF_DIR)') - -all: - $(TSXS) -o ats_fastcgi.so -c ats_fastcgi.cc $(LDLIB_DIR) $(LDLIB) $(INC) $(OBJ_FILES) -install: - $(TSXS) -v -i -o ats_fastcgi.so - echo copying config files to ats_fascgi config dir..... $(COPY_CONFIG) -clean: - rm -f *.lo *.so diff --git a/plugins/experimental/fastcgi/src/Profiler.h b/plugins/experimental/fastcgi/src/Profiler.h deleted file mode 100644 index a8f23155da8..00000000000 --- a/plugins/experimental/fastcgi/src/Profiler.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace ats_plugin -{ -// A single profile, stores data of a taken profile -class Profile -{ -public: - // Computes the start time and sets the thread id - // The duration of the profiles is in microseconds - void - ComputeStartTime() - { - this->start_time_ = - std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); - this->thread_id_ = std::hash()(std::this_thread::get_id()); - this->process_id_ = getpid(); - } - - // Computes the end time - void - ComputeEndTime() - { - this->end_time_ = - std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); - } - - // Gettors - std::size_t - start_time() const - { - return this->start_time_; - } - std::size_t - end_time() const - { - return this->end_time_; - } - std::size_t - thread_id() const - { - return this->thread_id_; - } - - std::size_t - process_id() const - { - return this->process_id_; - } - std::size_t - object_id() const - { - return this->object_id_; - } - void - set_object_id(std::size_t objId) - { - object_id_ = objId; - } - const std::string & - task_name() const - { - return this->task_name_; - } - - // Settors - void - set_task_name(const std::string &task_name) - { - this->task_name_ = task_name; - } - - const std::string & - obj_stage() const - { - return this->obj_stage_; - } - void - set_obj_stage(const std::string &obj_stage) - { - this->obj_stage_ = obj_stage; - } - -private: - // The time the profile started - std::chrono::high_resolution_clock::rep start_time_; - - // The time the profile ended - std::chrono::high_resolution_clock::rep end_time_; - - // The id of the thread - std::size_t thread_id_; - std::size_t process_id_; - - std::size_t object_id_; - // The name of the task of the profile - std::string task_name_; - std::string obj_stage_; -}; - -// Keeps tracks of the taken profiles and saves the data to a JSON. -// In order to take profiles use the embedded class profile taker -// The duration is in microseconds -class Profiler -{ -public: - // The storage of profiles - using ProfileContainer = std::vector; - - // Empty constructor - Profiler() : record_enabled_(false) {} - // Submits a new profile - void - SubmitProfile(const Profile &profile) - { - // Ignore if not enabled - if (!this->record_enabled_) { - return; - } - std::unique_lock lock(this->profiles_mutex_); - this->profiles_.push_back(profile); - } - - // Removes all the profiles - void - Clear() - { - std::unique_lock lock(this->profiles_mutex_); - this->profiles_.clear(); - } - - // Gettors - bool - record_enabled() const - { - return this->record_enabled_; - } - const ProfileContainer & - profiles() const - { - return this->profiles_; - } - - void - printProfileLength() - { - std::cout << "Profile Length: " << this->profiles_.size() << std::endl; - } - // Settors - void - set_record_enabled(bool enabled) - { - this->record_enabled_ = enabled; - if (!this->record_enabled_) { - this->Clear(); - } - } - -private: - // The profiles - ProfileContainer profiles_; - - // If true, enabled - bool record_enabled_; - - // The mutex for safe access - mutable std::mutex profiles_mutex_; -}; - -// Takes a profile during its life time -class ProfileTaker -{ -public: - // Initializes a profile - ProfileTaker(Profiler *owner, const std::string &task_name, std::size_t objId, const std::string &phase) : owner_(owner) - { - profile_.ComputeStartTime(); - profile_.set_task_name(task_name); - profile_.set_obj_stage(phase); - profile_.set_object_id(objId); - } - - // Releases a profile and submits it - ~ProfileTaker() - { - // profile_.ComputeEndTime(); - owner_->SubmitProfile(this->profile_); - // profile_.set_task_name(this->profile_.task_name()); - // profile_.set_obj_stage("E"); - Profile endProf; - endProf.ComputeStartTime(); - endProf.set_obj_stage("E"); - endProf.set_task_name(this->profile_.task_name()); - endProf.set_object_id(this->profile_.object_id()); - owner_->SubmitProfile(endProf); - } - -private: - // The profile to take care of - Profile profile_; - - // The owner profiler - Profiler *owner_; -}; -} // namespace ats_plugin diff --git a/plugins/experimental/fastcgi/src/Readme b/plugins/experimental/fastcgi/src/Readme deleted file mode 100644 index a89c676f6ea..00000000000 --- a/plugins/experimental/fastcgi/src/Readme +++ /dev/null @@ -1,167 +0,0 @@ ------------------------------------------------------------------------------ -Coding convention and formatting in VS Code: - Using below config and store it in settings.json : - { - "C_Cpp.clang_format_path": "oathto/trafficserver-7.1.1/.git/fmt/20160415/clang-format/clang-format.linux", - "editor.formatOnSave": true, - "editor.formatOnType": true, - "C_Cpp.intelliSenseEngine": "Default" - } - -Download the ATS clang-format binary ,find the url from path/to/trafficserver/tools/git/pre-commit -Extract the file and set the flag "C_Cpp.clang_format_path" to file path. - -2. Adding pre-commit hook : - Copy the file path/to/trafficserver/tools/git/pre-commit under .git/hook/ directory. - This will does the formatting every time does a commit. - ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ -Build and Install cmd = - -#building CPP ats_fastcgi.so - -```bash -set -e - -# Update the path below for your system -ats_dir="/path/to/trafficserver-source/" - -tsxs -o ats_fastcgi.so \ - -c ats_fastcgi.cc \ - -L "${ats_dir}lib/" \ - -I "${ats_dir}lib" \ - -c ats_fcgi_client.cc \ - -latscppapi - -# Remember the tsxs path here and use it below -# (for the case where the sudo context doesn't know about tsxs) -mtsxs=$(which tsxs) - -sudo $mtsxs -o ats_fastcgi.so -i -``` - -OR Build and install using Makefile utility: - Before doing make Plz set below directories. -# 1. ATS_SRC --either set or export env variable path to traffic server src directory -# 2. ATS_EXEC --either set or export env variable path to traffic server exec directory - - Afterwards command, $> make && sudo -E make install - For example: - make ATS_SRC=/home/oschaaf/code/apache/trafficserver ATS_EXEC=/usr/local/ CPPFLAGS=-std=c++11 - ------------------------------------------------------------------------------- - -ATS (Apache Traffic Server) FastCGI Plugin ------------------------------------------------------------------------------- - -This plugin collapses connections with identical CacheUrl/EffectiveUrl. - -If an entry was created for a given CacheUrl/EffectiveUrl in our global hashTable, -successive GET requests with identical CacheUrl/EffectiveUrl will be blocked in -POST_REMAP hook until the hash entry was removed. (POST_REMAP hook is the last -hook before 'cache lookup') - -For requests going into 'cache lookup' stage, - if CacheLookupStatus is HIT_FRESH, - hash entry will be removed at earliest possible time. - -For requests going into 'read server response header' stage, - if response is not 200 OK, - hash entry will be removed at earliest possible time. - if response is public cacheable, (by checking 'Expires' header and 'Cache-Control' header with 'public' & 'max-age=...' values), - if proxy.config.cache.enable_read_while_writer is enabled, - hash entry will be removed at earliest possible time. - else - hash entry will be removed in TXN_CLOSE hook. - if response is not public cacheable, - we will update hash entry with a special value to let successive requests pass collapsed check, - this special hash entry will be removed in TXN_CLOSE hook if value of keep_pass_record_time is 0, - else it will be added into a list with timeout and removed by later collapsed requests. - -To view full state diagram, please view state.png - - -CONFIGURATION - -To have trafficserver use this plugin, add a line for 'ats_fastcgi.so' in the 'plugin.config' file. -The plugin can be configured with only 1 argument, which is location of its configuration file. - -Example plugin.config: - ats_fastcgi.so conf/ats_fastcgi/ats_fastcgi_XXXNAME.config - -This plugin can be added in remap.config as well. -Thus, it can have different configurations or only be enabled/disabled for specific remap rules. -If the only argument for this plugin is "0" or "1", it means just "disable" or "enable" this plugin with default config value. - -Example remap.config: - map http://no-collapse1.www.example.com/ http://www.example.com/ @plugin=ats_fastcgi.so @pparam=0 - map http://no-collapse2.www.example.com/ http://www.example.com/ @plugin=ats_fastcgi.so @pparam=conf/collapsed_connection/disable.config - -Its configuration file can have 5 configurable options: - CONFIG proxy.config.http.fcgi.enabled INT 1 - CONFIG proxy.config.http.fcgi.host.hostname STRING localhost - CONFIG proxy.config.http.fcgi.host.server_ip STRING 127.0.0.1 - CONFIG proxy.config.http.fcgi.host.server_port STRING 60000 - CONFIG proxy.config.http.fcgi.host.root_directory STRING / - CONFIG proxy.config.http.fcgi.host.min_connections INT 2 - CONFIG proxy.config.http.fcgi.host.max_connections INT 16 - CONFIG proxy.config.http.fcgi.host.max_requests INT 1000 - CONFIG proxy.config.http.fcgi.host.request_queue_size INT 250 - -Meaning of these configurable options: - enabled - enable fcgi Server or not, useful in remap rules if we want to disable/enable specific remap rules. - hostname - hostname of the fcgi server in string format e.g localhost - server_ip - IP of the fcgi server machine in string format e.g 127.0.0.1 - server_port - fcgi server port number in string format e.g 60000 - root_directory - root directory path from where fcgi server will server resources/web_contents - min_connections - min number of connections plugin will open to the php server - max_connections - max number of connections plugin will make to the php server - max_requests - max number of requests per connection plugin will allow to the php server - request_queue_size - max requests queue size limit - - - - PHP REQUIREMENTS - - This section outlines build requirements for different OS - distributions. This may be out of date compared to the on-line - requirements at - - . - - As of ATS v7.0.0 and later, gcc 4.8.1 or later is required, since we now use - and require the C++11 standard. - - a. download libfcgi-dev package :i.e libfcgi-dev_2.4.0-8.4+b1_arm64.deb from https://packages.debian.org/sid/arm64/libfcgi-dev/download - - b. download php7.0-cli php7.0-fpm with : sudo apt-get install php7.0-cli php7.0-fpm - - -Starting the php fastcgi server: - -$ > ./bin/php-fastcgi start //this will start the server on port 60000 - -Note: Place sample scripts from ./examples/* files to /var/www/ directory to access php scripts from browser. As php server uses default location as /var/www/html/*.php - - - - -Using Profiler: - 1. To use profiler, set the ATS_FCGI_PROFILER flag to true inside ats_fastcgi.h file. - - 2. Place below piece of code inside functions to record profiling. - - #if ATS_FCGI_PROFILER - ats_plugin::ProfileTaker profile_taker(&profiler, "functinName", (std::size_t)&plugin, "B"); - #endif diff --git a/plugins/experimental/fastcgi/src/ats_fastcgi.cc b/plugins/experimental/fastcgi/src/ats_fastcgi.cc deleted file mode 100644 index 508bcb6f8f8..00000000000 --- a/plugins/experimental/fastcgi/src/ats_fastcgi.cc +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ats_fastcgi.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "ts/ink_defs.h" -#include "ts/ts.h" -#include "../../../../src/tscpp/api/utils_internal.h" - -#include "fcgi_config.h" -#include "server_intercept.h" -#include "server.h" -#include - -#if ATS_FCGI_PROFILER -#include "Profiler.h" -#define CONFIGURU_IMPLEMENTATION 1 -#include "configuru.hpp" -using namespace configuru; -#endif - -using namespace atscppapi; - -using std::cout; -using std::endl; -using std::string; - -namespace InterceptGlobal -{ -GlobalPlugin *plugin; -ats_plugin::InterceptPluginData *plugin_data; -ats_plugin::Server *gServer; -int reqBegId, reqEndId, respBegId, respEndId, threadCount, phpConnCount; -thread_local pthread_key_t threadKey = 0; -#if ATS_FCGI_PROFILER -ats_plugin::Profiler profiler; -#endif -} // namespace InterceptGlobal -// For experimental purpose to keep stats of plugin request/response -const char reqBegName[] = "plugin." PLUGIN_NAME ".reqCountBeg"; -const char reqEndName[] = "plugin." PLUGIN_NAME ".reqCountEnd"; -const char respBegName[] = "plugin." PLUGIN_NAME ".respCountBeg"; -const char respEndName[] = "plugin." PLUGIN_NAME ".respCountEnd"; -const char threadName[] = "plugin." PLUGIN_NAME ".threadCount"; -const char phpConnName[] = "plugin." PLUGIN_NAME ".phpConnCount"; - -using namespace InterceptGlobal; - -class InterceptGlobalPlugin : public GlobalPlugin -{ -public: - InterceptGlobalPlugin() : GlobalPlugin(true) - { - // GlobalPlugin::registerHook(Plugin::HOOK_READ_REQUEST_HEADERS); - GlobalPlugin::registerHook(Plugin::HOOK_CACHE_LOOKUP_COMPLETE); - } - - void - handleReadCacheLookupComplete(Transaction &transaction) override - { - if (transaction.getCacheStatus() == Transaction::CACHE_LOOKUP_HIT_FRESH) { - transaction.resume(); - Dbg(dbg_ctl, " Cache hit."); - return; - } - if (static_cast(transaction.getAtsHandle()) == nullptr) { - Dbg(dbg_ctl, "Invalid Transaction."); - return; - } - string path = transaction.getClientRequest().getUrl().getPath(); - // TODO: Regex based url selection - std::smatch urlMatch; - // std::regex e(".*.[wp*|php|js|css|scss|png|gif](p{2})?"); - std::regex e(".*"); - while (std::regex_search(path, urlMatch, e)) { - // for (auto &x : urlMatch) - // std::cout << x << " " << std::endl; - // path = urlMatch.suffix().str(); - // std::cout << "Path:" << path << std::endl; - break; - } - - // if (path.find("php") != string::npos) { - if (path.compare(urlMatch.str()) == 0) { - TSStatIntIncrement(reqBegId, 1); - auto intercept = new ats_plugin::ServerIntercept(transaction); - - if (threadKey == 0) { - // setup thread local storage - while (!gServer->setupThreadLocalStorage()) { - // do nothing - } - } - gServer->connect(intercept); - - } else { - transaction.resume(); - } - } -}; - -void -TSPluginInit(int argc, const char *argv[]) -{ - RegisterGlobalPlugin(ATS_MODULE_FCGI_NAME, "apache", "dev@trafficserver.apache.org"); - plugin_data = new ats_plugin::InterceptPluginData(); - ats_plugin::FcgiPluginConfig *config = new ats_plugin::FcgiPluginConfig(); - if (argc > 1) { - plugin_data->setGlobalConfigObj(config->initConfig(argv[1])); - } else { - plugin_data->setGlobalConfigObj(config->initConfig(nullptr)); - } - ats_plugin::FcgiPluginConfig *gConfig = plugin_data->getGlobalConfigObj(); - if (gConfig->getFcgiEnabledStatus()) { - plugin = new InterceptGlobalPlugin(); - gServer = new ats_plugin::Server(); - - if (TSStatFindName(reqBegName, &reqBegId) == TS_ERROR) { - reqBegId = TSStatCreate(reqBegName, TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); - if (reqBegId == TS_ERROR) { - TSError("[%s] failed to register '%s'", PLUGIN_NAME, reqBegName); - return; - } - } - - TSError("[%s] %s registered with id %d", PLUGIN_NAME, reqBegName, reqBegId); - - if (TSStatFindName(reqEndName, &reqEndId) == TS_ERROR) { - reqEndId = TSStatCreate(reqEndName, TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); - if (reqEndId == TS_ERROR) { - TSError("[%s] failed to register '%s'", PLUGIN_NAME, reqEndName); - return; - } - } - - TSError("[%s] %s registered with id %d", PLUGIN_NAME, reqEndName, reqEndId); - - if (TSStatFindName(respBegName, &respBegId) == TS_ERROR) { - respBegId = TSStatCreate(respBegName, TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); - if (respBegId == TS_ERROR) { - TSError("[%s] failed to register '%s'", PLUGIN_NAME, respBegName); - return; - } - } - - TSError("[%s] %s registered with id %d", PLUGIN_NAME, respBegName, respBegId); - - if (TSStatFindName(respEndName, &respEndId) == TS_ERROR) { - respEndId = TSStatCreate(respEndName, TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); - if (respEndId == TS_ERROR) { - TSError("[%s] failed to register '%s'", PLUGIN_NAME, respEndName); - return; - } - } - - TSError("[%s] %s registered with id %d", PLUGIN_NAME, respEndName, respEndId); - - if (TSStatFindName(threadName, &threadCount) == TS_ERROR) { - threadCount = TSStatCreate(threadName, TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); - if (threadCount == TS_ERROR) { - TSError("[%s] failed to register '%s'", PLUGIN_NAME, threadName); - return; - } - } - - TSError("[%s] %s registered with id %d", PLUGIN_NAME, threadName, threadCount); - - if (TSStatFindName(phpConnName, &phpConnCount) == TS_ERROR) { - phpConnCount = TSStatCreate(phpConnName, TS_RECORDDATATYPE_INT, TS_STAT_NON_PERSISTENT, TS_STAT_SYNC_SUM); - if (phpConnCount == TS_ERROR) { - TSError("[%s] failed to register '%s'", PLUGIN_NAME, phpConnName); - return; - } - } - - TSError("[%s] %s registered with id %d", PLUGIN_NAME, phpConnName, phpConnCount); - -#if DEBUG - TSReleaseAssert(reqBegId != TS_ERROR); - TSReleaseAssert(reqEndId != TS_ERROR); - TSReleaseAssert(respBegId != TS_ERROR); - TSReleaseAssert(respEndId != TS_ERROR); - TSReleaseAssert(threadCount != TS_ERROR); - TSReleaseAssert(phpConnCount != TS_ERROR); -#endif - // Set an initial value for our statistic. - TSStatIntSet(reqBegId, 0); - TSStatIntSet(reqEndId, 0); - TSStatIntSet(respBegId, 0); - TSStatIntSet(respEndId, 0); - TSStatIntSet(threadCount, 0); - TSStatIntSet(phpConnCount, 0); - - } else { - Dbg(dbg_ctl, " plugin is disabled."); - } -} diff --git a/plugins/experimental/fastcgi/src/ats_fastcgi.h b/plugins/experimental/fastcgi/src/ats_fastcgi.h deleted file mode 100644 index 67163193277..00000000000 --- a/plugins/experimental/fastcgi/src/ats_fastcgi.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define PLUGIN_VENDOR "Apache Software Foundation" -#define PLUGIN_SUPPORT "dev@trafficserver.apache.org" - -#define ATS_MODULE_FCGI_NAME "ats_fastcgi" -#define ATS_MOD_FCGI_VERSION "ats_fastcgi" -#define ATS_FCGI_PROFILER true - -#include "fcgi_config.h" -#include -#include "server.h" -#if ATS_FCGI_PROFILER -#include "Profiler.h" -#endif - -using namespace atscppapi; - -class FCGIServer; - -namespace InterceptGlobal -{ -extern GlobalPlugin *plugin; -extern ats_plugin::InterceptPluginData *plugin_data; -// TODO (Rakesh): Move to FCGI connect file -extern ats_plugin::Server *gServer; -extern thread_local pthread_key_t threadKey; -extern int reqBegId, reqEndId, respBegId, respEndId, threadCount, phpConnCount; -#ifdef ATS_FCGI_PROFILER -extern ats_plugin::Profiler profiler; -#endif -} // namespace InterceptGlobal diff --git a/plugins/experimental/fastcgi/src/ats_fcgi_client.cc b/plugins/experimental/fastcgi/src/ats_fcgi_client.cc deleted file mode 100644 index b5629e08d17..00000000000 --- a/plugins/experimental/fastcgi/src/ats_fcgi_client.cc +++ /dev/null @@ -1,651 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ats_fcgi_client.h" -#include "ats_fastcgi.h" -#include "fcgi_protocol.h" -#include "ts/ink_defs.h" -#include -#include -#include -#include -#include -#include -#include -#include "../../../../src/tscpp/api/utils_internal.h" -#include -#include - -using namespace atscppapi; -using namespace ats_plugin; -using namespace std; - -struct ats_plugin::FCGIClientState { - FCGI_BeginRequest *request; - FCGI_Header *header, *postHeader; - unsigned char *buff, *pBuffInc; - FCGIRecordList *records = nullptr; - TSHttpTxn txn_; - map requestHeaders; - int request_id_; - - FCGIClientState() - : request(nullptr), header(nullptr), postHeader(nullptr), buff(nullptr), pBuffInc(nullptr), records(nullptr), request_id_(0){}; - - ~FCGIClientState() - { - request_id_ = 0; - free(request); - free(header); - TSfree(postHeader); - TSfree(buff); - TSfree(records); - }; -}; -// input to the constructor will be either unique transaction id or int type -// requestId -FCGIClientRequest::FCGIClientRequest(int request_id, TSHttpTxn txn) -{ - first_chunk = true; - state_ = new FCGIClientState(); - _headerRecord = nullptr; - state_->txn_ = txn; - state_->request_id_ = request_id; - state_->requestHeaders = GenerateFcgiRequestHeaders(); - // TODO Call printFCGIRequestHeaders() to printFCGIHeaders - // printFCGIRequestHeaders(); - string str("POST"), value; - if (str.compare(state_->requestHeaders["REQUEST_METHOD"]) == 0) { - Transaction &transaction = utils::internal::getTransaction(state_->txn_); - Headers &h = transaction.getClientRequest().getHeaders(); - - if (h.isInitialized()) { - string key("Content-Length"); - atscppapi::header_field_iterator it = h.find(key); - if (it != h.end()) { - atscppapi::HeaderField hf(*it); - string value = hf.values(","); // delimiter for header values - state_->requestHeaders["CONTENT_LENGTH"] = value.c_str(); - } - - key = string("Content-type"); - it = h.find(key); - if (it != h.end()) { - HeaderField hf1(*it); - string value = hf1.values(","); // delimiter for header values - state_->requestHeaders["CONTENT_TYPE"] = value.c_str(); - } - } - - int contentLength = 0; - string cl = state_->requestHeaders["CONTENT_LENGTH"]; - stringstream strToInt(cl); - strToInt >> contentLength; - state_->buff = static_cast(TSmalloc(BUF_SIZE + contentLength)); - } else { - state_->buff = static_cast(TSmalloc(BUF_SIZE)); - } - - state_->pBuffInc = state_->buff; -} - -// destructor will reset the client_req_id and delete the recListState_ object -// holding response records received from fcgi server -FCGIClientRequest::~FCGIClientRequest() -{ - if (_headerRecord) { - delete _headerRecord; - } - - delete state_; -} - -bool -endsWith(const std::string &mainStr, const std::string &toMatch) -{ - if (mainStr.size() >= toMatch.size() && mainStr.compare(mainStr.size() - toMatch.size(), toMatch.size(), toMatch) == 0) { - return true; - } else { - return false; - } -} - -map -FCGIClientRequest::GenerateFcgiRequestHeaders() -{ - map fcgiReqHeader; - Transaction &transaction = utils::internal::getTransaction(state_->txn_); - Headers &h = transaction.getClientRequest().getHeaders(); - if (h.isInitialized()) { - for (auto it : h) { - atscppapi::HeaderField hf(it); - std::string str = hf.name().c_str(); - std::string http("HTTP_"); - std::locale loc; - - for (std::string::size_type i = 0; i < str.length(); ++i) { - http += std::toupper(str[i], loc); - } - fcgiReqHeader[http] = hf.values(); - } - } - - // if string ends with '/' char then request global html file to server - string index; - string requestScript = transaction.getClientRequest().getUrl().getPath(); - if (endsWith(requestScript, "/")) { - ats_plugin::FcgiPluginConfig *gConfig = InterceptGlobal::plugin_data->getGlobalConfigObj(); - index = gConfig->getHtml(); - requestScript += index; - } - - fcgiReqHeader["DOCUMENT_ROOT"] = InterceptGlobal::plugin_data->getGlobalConfigObj()->getDocumentRootDir(); - fcgiReqHeader["SCRIPT_FILENAME"] = fcgiReqHeader["DOCUMENT_ROOT"] + requestScript; - fcgiReqHeader["GATEWAY_INTERFACE"] = "FastCGI/1.1"; - fcgiReqHeader["REQUEST_METHOD"] = HTTP_METHOD_STRINGS[transaction.getClientRequest().getMethod()]; - fcgiReqHeader["SCRIPT_NAME"] = "/" + requestScript; - fcgiReqHeader["QUERY_STRING"] = transaction.getClientRequest().getUrl().getQuery(); - fcgiReqHeader["REQUEST_URI"] = "/" + requestScript; - - // TODO map fcgiconfig with request headers. - // atsfcgiconfig::FCGIParams *params = fcgiGlobal::plugin_data->getGlobalConfigObj()->getFcgiParams(); - // atsfcgiconfig::FCGIParams::iterator it = params->begin(); - // for (it = params->begin(); it != params->end(); ++it) - // cout << it->first << " => " << it->second << endl; - fcgiReqHeader["SERVER_SOFTWARE"] = "ATS 7.1.1"; - fcgiReqHeader["REMOTE_ADDR"] = "127.0.0.1"; - fcgiReqHeader["REMOTE_PORT"] = "8090"; - fcgiReqHeader["SERVER_ADDR"] = "127.0.0.1"; - fcgiReqHeader["SERVER_PORT"] = "60000"; - fcgiReqHeader["SERVER_NAME"] = "ATS 7.1.1"; - fcgiReqHeader["SERVER_PROTOCOL"] = "HTTP/1.1"; - fcgiReqHeader["FCGI_ROLE"] = "RESPONDER"; - return fcgiReqHeader; -} - -void -FCGIClientRequest::printFCGIRequestHeaders() -{ - for (const auto &it : state_->requestHeaders) { - cout << it.first << " => " << it.second << endl; - } -} - -void -FCGIClientRequest::emptyParam() -{ - string str("POST"); - state_->pBuffInc = state_->buff; - // if Method is not post, then writing empty FCGI_STDIN to buffer - if (str.compare(state_->requestHeaders["REQUEST_METHOD"]) != 0) { - state_->postHeader = createHeader(FCGI_STDIN); - state_->postHeader->contentLengthB0 = 0; - state_->postHeader->contentLengthB1 = 0; - serialize(state_->pBuffInc, state_->postHeader, sizeof(FCGI_Header)); - state_->pBuffInc += sizeof(FCGI_Header); - return; - } - Dbg(dbg_ctl, "empty Post Header Len: %ld ", state_->pBuffInc - state_->buff); -} - -FCGI_Header * -FCGIClientRequest::createHeader(uchar type) -{ - FCGI_Header *tmp = static_cast(calloc(1, sizeof(FCGI_Header))); - tmp->version = FCGI_VERSION_1; - tmp->type = type; - fcgiHeaderSetRequestId(tmp, state_->request_id_); - return tmp; -} - -FCGI_BeginRequest * -FCGIClientRequest::createBeginRequest() -{ - state_->request = static_cast(TSmalloc(sizeof(FCGI_BeginRequest))); - // TODO send the request id here - state_->request->header = createHeader(FCGI_BEGIN_REQUEST); - state_->request->body = static_cast(calloc(1, sizeof(FCGI_BeginRequestBody))); - state_->request->body->roleB0 = FCGI_RESPONDER; - state_->request->body->flags = FCGI_KEEP_CONN; - state_->request->header->contentLengthB0 = sizeof(FCGI_BeginRequestBody); - - // serialize request header - serialize(state_->pBuffInc, state_->request->header, sizeof(FCGI_Header)); - state_->pBuffInc += sizeof(FCGI_Header); - serialize(state_->pBuffInc, state_->request->body, sizeof(FCGI_BeginRequestBody)); - state_->pBuffInc += sizeof(FCGI_BeginRequestBody); - Dbg(dbg_ctl, "Header Len: %ld ", state_->pBuffInc - state_->buff); - // FCGI Params headers - state_->header = createHeader(FCGI_PARAMS); - int len = 0, nb = 0; - - for (const auto &it : state_->requestHeaders) { - nb = serializeNameValue(state_->pBuffInc, it); - len += nb; - } - - state_->header->contentLengthB0 = BYTE_0(len); - state_->header->contentLengthB1 = BYTE_1(len); - Dbg(dbg_ctl, "ParamsLen: %d ContLenB0: %d ContLenB1: %d", len, state_->header->contentLengthB0, state_->header->contentLengthB1); - serialize(state_->pBuffInc, state_->header, sizeof(FCGI_Header)); - state_->pBuffInc += sizeof(FCGI_Header); - - for (const auto &it : state_->requestHeaders) { - nb = serializeNameValue(state_->pBuffInc, it); - state_->pBuffInc += nb; - } - - state_->header->contentLengthB0 = 0; - state_->header->contentLengthB1 = 0; - serialize(state_->pBuffInc, state_->header, sizeof(FCGI_Header)); - state_->pBuffInc += sizeof(FCGI_Header); - return state_->request; -} - -void -FCGIClientRequest::postBodyChunk() -{ - state_->pBuffInc = state_->buff; - int dataLen = 0; - state_->postHeader = createHeader(FCGI_STDIN); - dataLen = postData.length(); - - state_->postHeader->contentLengthB0 = BYTE_0(dataLen); - state_->postHeader->contentLengthB1 = BYTE_1(dataLen); - serialize(state_->pBuffInc, state_->postHeader, sizeof(FCGI_Header)); - state_->pBuffInc += sizeof(FCGI_Header); - memcpy(state_->pBuffInc, postData.c_str(), dataLen); - state_->pBuffInc += dataLen; - - state_->postHeader->contentLengthB0 = 0; - state_->postHeader->contentLengthB1 = 0; - serialize(state_->pBuffInc, state_->postHeader, sizeof(FCGI_Header)); - state_->pBuffInc += sizeof(FCGI_Header); - Dbg(dbg_ctl, "Serialized Post Data. Post Header Len: %ld ", state_->pBuffInc - state_->buff); -} - -unsigned char * -FCGIClientRequest::addClientRequest(int &dataLen) -{ - dataLen = state_->pBuffInc - state_->buff; - return state_->buff; -} - -void -FCGIClientRequest::serialize(uchar *buffer, void *st, size_t size) -{ - memcpy(buffer, st, size); -} - -uint32_t -FCGIClientRequest::serializeNameValue(uchar *buffer, const std::pair &it) -{ - uchar *p = buffer; - uint32_t nl, vl; - nl = it.first.length(); - vl = it.second.length(); - - if (nl < 128) { - *p++ = BYTE_0(nl); - } else { - *p++ = BYTE_3(nl); - *p++ = BYTE_2(nl); - *p++ = BYTE_1(nl); - *p++ = BYTE_0(nl); - } - - if (vl < 128) { - *p++ = BYTE_0(vl); - } else { - *p++ = BYTE_3(vl); - *p++ = BYTE_2(vl); - *p++ = BYTE_1(vl); - *p++ = BYTE_0(vl); - } - memcpy(p, it.first.c_str(), nl); - p += nl; - memcpy(p, it.second.c_str(), vl); - p += vl; - return p - buffer; -} - -void -FCGIClientRequest::fcgiHeaderSetRequestId(FCGI_Header *h, int request_id) -{ - h->requestIdB0 = BYTE_0(request_id); - h->requestIdB1 = BYTE_1(request_id); -} - -void -FCGIClientRequest::fcgiHeaderSetContentLen(FCGI_Header *h, uint16_t len) -{ - h->contentLengthB0 = BYTE_0(len); - h->contentLengthB1 = BYTE_1(len); -} - -uint32_t -FCGIClientRequest::fcgiHeaderGetContentLen(FCGI_Header *h) -{ - return (h->contentLengthB1 << 8) + h->contentLengthB0; -} - -int -FCGIClientRequest::fcgiProcessHeader(uchar ch, FCGIRecordList *rec) -{ - FCGI_Header *h; - FCGI_State *state = &(rec->state); - h = rec->header; - - switch (*state) { - case fcgi_state_version: - h->version = ch; - *state = fcgi_state_type; - break; - case fcgi_state_type: - h->type = ch; - *state = fcgi_state_request_id_hi; - break; - case fcgi_state_request_id_hi: - h->requestIdB1 = ch; - *state = fcgi_state_request_id_lo; - break; - case fcgi_state_request_id_lo: - h->requestIdB0 = ch; - *state = fcgi_state_content_len_hi; - break; - case fcgi_state_content_len_hi: - h->contentLengthB1 = ch; - *state = fcgi_state_content_len_lo; - break; - case fcgi_state_content_len_lo: - h->contentLengthB0 = ch; - *state = fcgi_state_padding_len; - break; - case fcgi_state_padding_len: - h->paddingLength = ch; - *state = fcgi_state_reserved; - break; - case fcgi_state_reserved: - h->reserved = ch; - *state = fcgi_state_content_begin; - break; - - case fcgi_state_content_begin: - case fcgi_state_content_proc: - case fcgi_state_padding: - case fcgi_state_done: - return FCGI_PROCESS_DONE; - } - return FCGI_PROCESS_AGAIN; -} - -int -FCGIClientRequest::fcgiProcessContent(uchar **beg_buf, uchar *end_buf, FCGIRecordList *rec) -{ - size_t tot_len, con_len, cpy_len, offset, nb = end_buf - *beg_buf; - FCGI_State *state = &(rec->state); - FCGI_Header *h = rec->header; - offset = rec->offset; - - if (*state == fcgi_state_padding) { - *state = fcgi_state_done; - *beg_buf += static_cast(static_cast(rec->length) - static_cast(offset) + static_cast(h->paddingLength)); - return FCGI_PROCESS_DONE; - } - - con_len = rec->length - offset; - tot_len = con_len + h->paddingLength; - - if (con_len <= nb) { - cpy_len = con_len; - } else { - cpy_len = nb; - } - - memcpy(rec->content + offset, *beg_buf, cpy_len); - - if (tot_len <= nb) { - rec->offset += tot_len; - *state = fcgi_state_done; - *beg_buf += tot_len; - return FCGI_PROCESS_DONE; - } else if (con_len <= nb) { - /* Have to still skip all or some of padding */ - *state = fcgi_state_padding; - rec->offset += nb; - *beg_buf += nb; - return FCGI_PROCESS_AGAIN; - } else { - rec->offset += nb; - *beg_buf += nb; - return FCGI_PROCESS_AGAIN; - } - - return 0; -} - -int -FCGIClientRequest::fcgiProcessRecord(uchar **beg_buf, uchar *end_buf, FCGIRecordList *rec) -{ - int rv; - while (rec->state < fcgi_state_content_begin) { - if ((rv = fcgiProcessHeader(**beg_buf, rec)) == FCGI_PROCESS_ERR) { - return FCGI_PROCESS_ERR; - } - (*beg_buf)++; - if (*beg_buf == end_buf) { - return FCGI_PROCESS_AGAIN; - } - } - if (rec->state == fcgi_state_content_begin) { - rec->length = fcgiHeaderGetContentLen(rec->header); - rec->content = static_cast(TSmalloc(rec->length)); - rec->state = static_cast(static_cast(rec->state) + 1); - } - - return fcgiProcessContent(beg_buf, end_buf, rec); -} - -static char * -convert_mime_hdr_to_string(TSMBuffer bufp, TSMLoc hdr_loc) -{ - TSIOBuffer output_buffer; - TSIOBufferReader reader; - int64_t total_avail; - - TSIOBufferBlock block; - const char *block_start; - int64_t block_avail; - - char *output_string; - int output_len; - - output_buffer = TSIOBufferCreate(); - - if (!output_buffer) { - TSError("[InkAPITest] couldn't allocate IOBuffer"); - } - - reader = TSIOBufferReaderAlloc(output_buffer); - - /* This will print just MIMEFields and not - the http request line */ - TSMimeHdrPrint(bufp, hdr_loc, output_buffer); - - /* Find out how the big the complete header is by - seeing the total bytes in the buffer. We need to - look at the buffer rather than the first block to - see the size of the entire header */ - total_avail = TSIOBufferReaderAvail(reader); - - /* Allocate the string with an extra byte for the string - terminator */ - output_string = static_cast(TSmalloc(total_avail + 1)); - output_len = 0; - - /* We need to loop over all the buffer blocks to make - sure we get the complete header since the header can - be in multiple blocks */ - block = TSIOBufferReaderStart(reader); - while (block) { - block_start = TSIOBufferBlockReadStart(block, reader, &block_avail); - - /* We'll get a block pointer back even if there is no data - left to read so check for this condition and break out of - the loop. A block with no data to read means we've exhausted - buffer of data since if there was more data on a later - block in the chain, this block would have been skipped over */ - if (block_avail == 0) { - break; - } - - memcpy(output_string + output_len, block_start, block_avail); - output_len += block_avail; - - /* Consume the data so that we get to the next block */ - TSIOBufferReaderConsume(reader, block_avail); - - /* Get the next block now that we've consumed the - data off the last block */ - block = TSIOBufferReaderStart(reader); - } - - /* Terminate the string */ - output_string[output_len] = '\0'; - output_len++; - - /* Free up the TSIOBuffer that we used to print out the header */ - TSIOBufferReaderFree(reader); - TSIOBufferDestroy(output_buffer); - - return output_string; -} - -bool -FCGIClientRequest::fcgiProcessBuffer(uchar *beg_buf, uchar *end_buf, std::ostringstream &output) -{ - if (!_headerRecord) { - _headerRecord = new FCGIRecordList; - } - - while (true) { - if (_headerRecord->state == fcgi_state_done) { - FCGIRecordList *tmp = _headerRecord; - _headerRecord = new FCGIRecordList(); - delete tmp; - } - - if (fcgiProcessRecord(&beg_buf, end_buf, _headerRecord) == FCGI_PROCESS_DONE) { - if (first_chunk) { - string start = std::string(reinterpret_cast(_headerRecord->content), _headerRecord->length); - string end("\r\n\r\n"); - string headerString; - int foundPos = start.find(end); - if (foundPos != -1) { - headerString = start.substr(0, foundPos + 4); - } - const char *buff = headerString.c_str(); - const char *start1; - const char *endPtr; - start1 = buff; - endPtr = buff + strlen(buff) + 1; - char *temp; - TSMLoc mime_hdr_loc1 = (TSMLoc) nullptr; - TSParseResult retval; - TSMimeParser parser = TSMimeParserCreate(); - TSMBuffer bufp = TSMBufferCreate(); - TSMimeHdrCreate(bufp, &mime_hdr_loc1); - if ((retval = TSMimeHdrParse(parser, bufp, mime_hdr_loc1, &start1, endPtr)) == TS_PARSE_ERROR) { - Dbg(dbg_ctl, "[FCGIClientRequest:%s] Hdr Parse Error.", __FUNCTION__); - } else { - if (retval == TS_PARSE_DONE) { - temp = convert_mime_hdr_to_string(bufp, mime_hdr_loc1); // Implements TSMimeHdrPrint. - if (strcmp(buff, temp) == 0) { - Headers h(bufp, mime_hdr_loc1); - string key("Status"); - if (h.isInitialized()) { - atscppapi::header_field_iterator it = h.find(key); - if (it != h.end()) { - atscppapi::HeaderField hf(*it); - string value = hf.values(","); // delimiter for header values - output << HTTP_VERSION_STRINGS[HTTP_VERSION_1_1] << " "; - output << value.c_str() << "\r\n"; - } else { - output << "HTTP/1.0 200 OK\r\n"; - } - // it = h.begin(); - // for (it = h.begin(); it != h.end(); ++it) { - // atscppapi::HeaderField hf(*it); - // std::cout << "Name => " << hf.name() << "Value => " << hf.values() << std::endl; - // } - } - } else { - Dbg(dbg_ctl, "[FCGIClientRequest:%s] Incorrect Parsing.", __FUNCTION__); - output << "HTTP/1.0 200 OK\r\n"; - } - TSfree(temp); - } - } - - TSMimeHdrDestroy(bufp, mime_hdr_loc1); - TSHandleMLocRelease(bufp, TS_NULL_MLOC, mime_hdr_loc1); - TSMBufferDestroy(bufp); - TSMimeParserDestroy(parser); - // OS(XXX): merge -- check later, currently leaning towards that we do not have - // to do this. - // TSfree(buff); - first_chunk = false; - } - if (_headerRecord->header->type == FCGI_STDOUT) { - output << std::string(reinterpret_cast(_headerRecord->content), _headerRecord->length); - } - if (_headerRecord->header->type == FCGI_STDERR) { - // XXX(oschaaf): we may want to treat this differently, but for now this will do. - output << "HTTP/1.0 500 Server Error\r\n\r\n"; - output << std::string(reinterpret_cast(_headerRecord->content), _headerRecord->length); - Dbg(dbg_ctl, "[ FCGIClientRequest:%s ] Response FCGI_STDERR.*****\n\n", __FUNCTION__); - return true; - } - if (_headerRecord->header->type == FCGI_END_REQUEST) { - Dbg(dbg_ctl, "[ FCGIClientRequest:%s ] Response FCGI_END_REQUEST.*****\n\n", __FUNCTION__); - return true; - } - } - - if (beg_buf == end_buf) { - return false; - } - } -} - -bool -FCGIClientRequest::fcgiDecodeRecordChunk(uchar *beg_buf, size_t remain, std::ostringstream &output) -{ - return fcgiProcessBuffer(beg_buf, beg_buf + remain, output); -} - -void -FCGIClientRequest::print_bytes(uchar *buf, int n) -{ - int i; - printf("{"); - for (i = 0; i < n; i++) { - printf("%02x", buf[i]); - } - printf("}\n"); -} diff --git a/plugins/experimental/fastcgi/src/ats_fcgi_client.h b/plugins/experimental/fastcgi/src/ats_fcgi_client.h deleted file mode 100644 index c44b1ec87ba..00000000000 --- a/plugins/experimental/fastcgi/src/ats_fcgi_client.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "fcgi_protocol.h" -#include -#include -#include -#include -#include -#include //for memcpy - -#define BUF_SIZE 5000 - -/* Bytes from LSB to MSB 0..3 */ - -#define BYTE_0(x) ((x) & 0xff) -#define BYTE_1(x) ((x) >> 8 & 0xff) -#define BYTE_2(x) ((x) >> 16 & 0xff) -#define BYTE_3(x) ((x) >> 24 | 0x80) - -using uchar = unsigned char; - -#define PRINT_OPAQUE_STRUCT(p) print_mem((p), sizeof(*(p))) - -#define FCGI_PROCESS_AGAIN 1 -#define FCGI_PROCESS_DONE 2 -#define FCGI_PROCESS_ERR 3 - -namespace ats_plugin -{ -using namespace atscppapi; - -enum FCGI_State { - fcgi_state_version = 0, - fcgi_state_type, - fcgi_state_request_id_hi, - fcgi_state_request_id_lo, - fcgi_state_content_len_hi, - fcgi_state_content_len_lo, - fcgi_state_padding_len, - fcgi_state_reserved, - fcgi_state_content_begin, - fcgi_state_content_proc, - fcgi_state_padding, - fcgi_state_done -}; - -struct FCGIClientState; - -struct FCGIRecordList { - FCGI_Header *header; - FCGI_EndRequestBody *endBody; - uchar *content; - FCGI_State state; - - size_t length, offset; - - FCGIRecordList() : content(nullptr), state(FCGI_State::fcgi_state_version), length(0), offset(0) - { - header = static_cast(TSmalloc(sizeof(FCGI_Header))); - memset(header, 0, sizeof(FCGI_Header)); - endBody = static_cast(TSmalloc(sizeof(FCGI_EndRequestBody))); - memset(endBody, 0, sizeof(FCGI_EndRequestBody)); - }; - - ~FCGIRecordList() - { - TSfree(header); - TSfree(content); - } -}; - -class FCGIClientRequest -{ -public: - std::string postData; - FCGIClientRequest(int request_id, TSHttpTxn txn); - ~FCGIClientRequest(); - - std::map GenerateFcgiRequestHeaders(); - void printFCGIRequestHeaders(); - - // Request Creation - FCGI_BeginRequest *createBeginRequest(); - FCGI_Header *createHeader(unsigned char type); - void postBodyChunk(); - void emptyParam(); - - void serialize(uchar *buffer, void *st, size_t size); - void fcgiHeaderSetRequestId(FCGI_Header *h, int request_id); - void fcgiHeaderSetContentLen(FCGI_Header *h, uint16_t len); - uint32_t fcgiHeaderGetContentLen(FCGI_Header *h); - uint32_t serializeNameValue(uchar *buffer, const std::pair &it); - unsigned char *addClientRequest(int &); - - // Response Decoding member functions - bool fcgiProcessBuffer(uchar *beg_buf, uchar *end_buf, std::ostringstream &output); - FCGIRecordList *fcgiRecordCreate(); - int fcgiProcessHeader(uchar ch, FCGIRecordList *rec); - int fcgiProcessContent(uchar **beg_buf, uchar *end_buf, FCGIRecordList *rec); - int fcgiProcessRecord(uchar **beg_buf, uchar *end_buf, FCGIRecordList *rec); - - bool fcgiDecodeRecordChunk(uchar *beg_buf, size_t remain, std::ostringstream &output); - - void print_bytes(uchar *buf, int n); - -protected: - struct FCGIClientState *state_; - -private: - bool first_chunk; - FCGIRecordList *_headerRecord; -}; -} // namespace ats_plugin diff --git a/plugins/experimental/fastcgi/src/bin/php-fastcgi.sh b/plugins/experimental/fastcgi/src/bin/php-fastcgi.sh deleted file mode 100644 index 57f0b8d8cc8..00000000000 --- a/plugins/experimental/fastcgi/src/bin/php-fastcgi.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -BIND=127.0.0.1:60000 -PHP_FCGI_CHILDREN=16 -PHP_FCGI_MAX_REQUESTS=1000 - -PHP_CGI=/usr/bin/php-cgi -PHP_CGI_NAME=`basename $PHP_CGI` -PHP_CGI_ARGS="- PATH=/usr/bin PHP_FCGI_CHILDREN=$PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS=$PHP_FCGI_MAX_REQUESTS $PHP_CGI -b $BIND" -RETVAL=0 - -start() { - echo -n "Starting PHP FastCGI: " - start-stop-daemon --quiet --start --background --exec /usr/bin/env -- $PHP_CGI_ARGS - RETVAL=$? - echo "$PHP_CGI_NAME." -} -stop() { - echo -n "Stopping PHP FastCGI: " - killall -q -w -u $USER $PHP_CGI - RETVAL=$? - echo "$PHP_CGI_NAME." -} - -case "$1" in - start) - start - ;; - stop) - stop - ;; - restart) - stop - start - ;; - *) - echo "Usage: php-fastcgi {start|stop|restart}" - exit 1 - ;; -esac -exit $RETVAL diff --git a/plugins/experimental/fastcgi/src/config/ats_mod_fcgi.config b/plugins/experimental/fastcgi/src/config/ats_mod_fcgi.config deleted file mode 100644 index 3953344e191..00000000000 --- a/plugins/experimental/fastcgi/src/config/ats_mod_fcgi.config +++ /dev/null @@ -1,11 +0,0 @@ -CONFIG proxy.config.http.fcgi.enabled INT 1 -CONFIG proxy.config.http.fcgi.host.hostname STRING localhost -CONFIG proxy.config.http.fcgi.host.server_ip STRING 127.0.0.1 -CONFIG proxy.config.http.fcgi.host.server_port STRING 60000 -CONFIG proxy.config.http.fcgi.host.include STRING etc/trafficserver/fastcgi.config -CONFIG proxy.config.http.fcgi.host.document_root STRING /var/www/ -CONFIG proxy.config.http.fcgi.host.html STRING index.php -CONFIG proxy.config.http.fcgi.host.min_connections INT 2 -CONFIG proxy.config.http.fcgi.host.max_connections INT 16 -CONFIG proxy.config.http.fcgi.host.max_requests INT 1000 -CONFIG proxy.config.http.fcgi.host.request_queue_size INT 250 diff --git a/plugins/experimental/fastcgi/src/config/fastcgi.config b/plugins/experimental/fastcgi/src/config/fastcgi.config deleted file mode 100644 index 97fdb5868c0..00000000000 --- a/plugins/experimental/fastcgi/src/config/fastcgi.config +++ /dev/null @@ -1,18 +0,0 @@ -#fastcgi.conf -fastcgi_param GATEWAY_INTERFACE CGI/1.1; -fastcgi_param SERVER_SOFTWARE ATS; -fastcgi_param QUERY_STRING $query_string; -fastcgi_param REQUEST_METHOD $request_method; -fastcgi_param CONTENT_TYPE $content_type; -fastcgi_param CONTENT_LENGTH $content_length; -fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; -fastcgi_param SCRIPT_NAME $fastcgi_script_name; -fastcgi_param REQUEST_URI $request_uri; -fastcgi_param DOCUMENT_URI $document_uri; -fastcgi_param DOCUMENT_ROOT $document_root; -fastcgi_param SERVER_PROTOCOL $server_protocol; -fastcgi_param REMOTE_ADDR $remote_addr; -fastcgi_param REMOTE_PORT $remote_port; -fastcgi_param SERVER_ADDR $server_addr; -fastcgi_param SERVER_PORT $server_port; -fastcgi_param SERVER_NAME $server_name; \ No newline at end of file diff --git a/plugins/experimental/fastcgi/src/configuru.hpp b/plugins/experimental/fastcgi/src/configuru.hpp deleted file mode 100644 index 2c530ae0a68..00000000000 --- a/plugins/experimental/fastcgi/src/configuru.hpp +++ /dev/null @@ -1,3914 +0,0 @@ -/* -www.github.com/emilk/configuru - -# Configuru - Configuru, an experimental config library for C++, by Emil Ernerfeldt. - -# License - This software is in the public domain. Where that dedication is not - recognized, you are granted a perpetual, irrevocable license to copy - and modify this file as you see fit. - - That being said, I would appreciate credit! - If you find this library useful, send a tweet to [@ernerfeldt](https://twitter.com/ernerfeldt) or mail me at -emil.ernerfeldt@gmail.com. - -# Version history - 0.0.0: 2014-07-21 - Initial steps - 0.1.0: 2015-11-08 - First commit as stand-alone library - 0.2.0: 2016-03-25 - check_dangling changes - 0.2.1: 2016-04-11 - mark_accessed in dump_string by default - 0.2.2: 2016-07-27 - optimizations - 0.2.3: 2016-08-09 - optimizations + add Config::emplace(key, value) - 0.2.4: 2016-08-18 - fix compilation error for when CONFIGURU_VALUE_SEMANTICS=0 - 0.3.0: 2016-09-15 - Add option to not align values (object_align_values) - 0.3.1: 2016-09-19 - Fix crashes on some compilers/stdlibs - 0.3.2: 2016-09-22 - Add support for Config::array(some_container) - 0.3.3: 2017-01-10 - Add some missing iterator members - 0.3.4: 2017-01-17 - Add cast conversion to std::array - 0.4.0: 2017-04-17 - Automatic (de)serialization with serialize/deserialize with https://github.com/cbeck88/visit_struct - 0.4.1: 2017-05-21 - Make it compile on VC++ - -# Getting started - For using: - `#include ` - - And in one .cpp file: - - #define CONFIGURU_IMPLEMENTATION 1 - #include - - For more info, please see README.md (at www.github.com/emilk/configuru). -*/ - -// dP""b8 dP"Yb 88b 88 888888 88 dP""b8 88 88 88""Yb 88 88 -// dP `" dP Yb 88Yb88 88__ 88 dP `" 88 88 88__dP 88 88 -// Yb Yb dP 88 Y88 88"" 88 Yb "88 Y8 8P 88"Yb Y8 8P -// YboodP YbodP 88 Y8 88 88 YboodP `YbodP' 88 Yb `YbodP' - -// Disable all warnings from gcc/clang: -#if defined(__clang__) -#pragma clang system_header -#elif defined(__GNUC__) -#pragma GCC system_header -#endif - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef CONFIGURU_ONERROR -#define CONFIGURU_ONERROR(message_str) throw std::runtime_error(message_str) -#endif // CONFIGURU_ONERROR - -#ifndef CONFIGURU_ASSERT -#include -#define CONFIGURU_ASSERT(test) assert(test) -#endif // CONFIGURU_ASSERT - -#ifndef CONFIGURU_ON_DANGLING -/// CONFIGURU_ON_DANGLING(message_str) is called by check_dangling() if there is any unaccessed keys. -#define CONFIGURU_ON_DANGLING(message_str) CONFIGURU_ONERROR(message_str) -#endif // CONFIGURU_ON_DANGLING - -#ifdef __GNUC__ -#define CONFIGURU_NORETURN __attribute__((noreturn)) -#elif __MINGW32__ -#define CONFIGURU_NORETURN __attribute__((noreturn)) -#elif __clang__ -#define CONFIGURU_NORETURN __attribute__((noreturn)) -#elif _MSC_VER -#define CONFIGURU_NORETURN -#endif - -#ifndef CONFIGURU_IMPLICIT_CONVERSIONS -/// Set to 1 to allow `int x = some_cfg,` -#define CONFIGURU_IMPLICIT_CONVERSIONS 0 -#endif - -#ifndef CONFIGURU_VALUE_SEMANTICS -/// If set, all copies are deep clones. -/// If 0, all copies of objects and array are shallow (ref-counted). -#define CONFIGURU_VALUE_SEMANTICS 0 -#endif - -#undef Bool // Needed on Ubuntu 14.04 with GCC 4.8.5 -#undef check // Needed on OSX - -/// The Configuru namespace. -namespace configuru -{ -struct DocInfo; -using DocInfo_SP = std::shared_ptr; - -using Index = unsigned; -const Index BAD_INDEX = static_cast(-1); - -struct Include { - DocInfo_SP doc; - Index line = BAD_INDEX; - - Include() {} - Include(DocInfo_SP d, Index l) : doc(d), line(l) {} -}; - -/// Helper for describing a document. -struct DocInfo { - std::vector includers; - - std::string filename; - - DocInfo(const std::string &fn) : filename(fn) {} - void append_include_info(std::string &ret, const std::string &indent = " ") const; -}; - -struct BadLookupInfo; - -/// Helper: value in an object. -template struct Config_Entry { - Config_T _value; - Index _nr = BAD_INDEX; ///< Size of the object prior to adding this entry - mutable bool _accessed = false; ///< Set to true if accessed. - - Config_Entry() {} - Config_Entry(Config_T value, Index nr) : _value(std::move(value)), _nr(nr) {} -}; - -using Comment = std::string; -using Comments = std::vector; - -/// Captures the comments related to a Config value. -struct ConfigComments { - /// Comments on preceeding lines. - /// Like this. - Comments prefix; - Comments postfix; ///< After the value, on the same line. Like this. - Comments pre_end_brace; /// Before the closing } or ] - - ConfigComments() {} - bool empty() const; - void append(ConfigComments &&other); -}; - -/// A dynamic config variable. -class Config; - -/** Overload this (in cofiguru namespace) for you own types, e.g: - - ``` - namespace configuru { - template<> - inline Vector2f as(const Config& config) - { - auto&& array = config.as_array(); - config.check(array.size() == 2, "Expected Vector2f"); - return {(float)array[0], (float)array[1]}; - } - } - ``` -*/ -template inline T as(const configuru::Config &config); - -/// A dynamic config variable. -/// Acts like something out of Python or Lua. -/// If CONFIGURU_VALUE_SEMANTICS all copies of this will be deep copies. -/// If not, it will use reference-counting for objects and arrays, -/// meaning all copies will be shallow copies. -class Config -{ -public: - enum Type { - Uninitialized, ///< Accessing a Config of this type is always an error. - BadLookupType, ///< We are the result of a key-lookup in a Object with no hit. We are in effect write-only. - Null, - Bool, - Int, - Float, - String, - Array, - Object - }; - - using ObjectEntry = Config_Entry; - - using ConfigArrayImpl = std::vector; - using ConfigObjectImpl = std::map; - struct ConfigArray { -#if !CONFIGURU_VALUE_SEMANTICS - std::atomic _ref_count{1}; -#endif - ConfigArrayImpl _impl; - }; - struct ConfigObject; - - // ---------------------------------------- - // Constructors: - - /// Creates an uninitialized Config. - Config() : _type(Uninitialized) {} - Config(std::nullptr_t) : _type(Null) {} - Config(float f) : _type(Float) { _u.f = f; } - Config(double f) : _type(Float) { _u.f = f; } - Config(bool b) : _type(Bool) { _u.b = b; } - Config(int i) : _type(Int) { _u.i = i; } - Config(unsigned int i) : _type(Int) { _u.i = i; } - Config(long i) : _type(Int) { _u.i = i; } - Config(unsigned long i) : Config(static_cast(i)) {} - Config(long long i) : _type(Int) { _u.i = i; } - Config(unsigned long long i) : _type(Int) - { - if ((i & 0x8000000000000000ull) != 0) { - CONFIGURU_ONERROR("Integer too large to fit into 63 bits"); - } - _u.i = static_cast(i); - } - Config(const char *str); - Config(std::string str); - - /** This constructor is a short-form for Config::object(...). - We have no short-form for Config::array(...), - as that is less common and can lead to ambiguities. - Usage: - - ``` - Config cfg { - { "key", "value" }, - { "empty_array", Config::array() }, - { "array", Config::array({1, 2, 3}) }, - { "empty_object", Config::object() }, - { "object", Config::object({ - { "nested_key", "nested_value" }, - })}, - { "another_object", { - { "nested_key", "nested_value" }, - }}, - }; - ``` - */ - Config(std::initializer_list> values); - - /// Array constructor - template Config(const std::vector &values) : _type(Uninitialized) - { - make_array(); - _u.array->_impl.reserve(values.size()); - for (const auto &v : values) { - push_back(v); - } - } - - /// Array constructor - Config(const std::vector &values) : _type(Uninitialized) - { - make_array(); - _u.array->_impl.reserve(values.size()); - for (const auto v : values) { - push_back(!!v); - } - } - - /// Object constructor - template Config(const std::map &values) : _type(Uninitialized) - { - make_object(); - for (const auto &p : values) { - (*this)[p.first] = p.second; - } - } - - /// Used by the parser - no need to use directly. - void make_object(); - - /// Used by the parser - no need to use directly. - void make_array(); - - /// Used by the parser - no need to use directly. - void tag(const DocInfo_SP &doc, Index line, Index column); - - /// Preferred way to create an empty object. - static Config object(); - - /// Preferred way to create an object. - static Config object(std::initializer_list> values); - - /// Preferred way to create an empty array. - static Config array(); - - /// Preferred way to create an array. - static Config array(std::initializer_list values); - - /// Preferred way to create an array from an STL container. - template - static Config - array(const Container &container) - { - Config ret; - ret.make_array(); - auto &impl = ret._u.array->_impl; - impl.reserve(container.size()); - for (auto &&v : container) { - impl.emplace_back(v); - } - return ret; - } - - // ---------------------------------------- - - ~Config(); - - Config(const Config &o); - Config(Config &&o) noexcept; - Config &operator=(const Config &o); - - /// Will still remember file/line when assigned an object which has no file/line - Config &operator=(Config &&o) noexcept; - - /// Swaps file/line too. - void swap(Config &o) noexcept; - -#ifdef CONFIG_EXTENSION - CONFIG_EXTENSION -#endif - - // ---------------------------------------- - // Inspectors: - - Type - type() const - { - return _type; - } - - bool - is_uninitialized() const - { - return _type == Uninitialized; - } - bool - is_null() const - { - return _type == Null; - } - bool - is_bool() const - { - return _type == Bool; - } - bool - is_int() const - { - return _type == Int; - } - bool - is_float() const - { - return _type == Float; - } - bool - is_string() const - { - return _type == String; - } - bool - is_object() const - { - return _type == Object; - } - bool - is_array() const - { - return _type == Array; - } - bool - is_number() const - { - return is_int() || is_float(); - } - - /// Returns file:line iff available. - std::string where() const; - - /// BAD_INDEX if not set. - Index - line() const - { - return _line; - } - - /// Handle to document. - const DocInfo_SP & - doc() const - { - return _doc; - } - void - set_doc(const DocInfo_SP &doc) - { - _doc = doc; - } - -// ---------------------------------------- -// Converters: - -#if CONFIGURU_IMPLICIT_CONVERSIONS - /// Explicit casting, for overloads of as - template explicit operator T() const { return as(*this); } - inline operator bool() const { return as_bool(); } - inline operator signed char() const { return as_integer(); } - inline operator unsigned char() const { return as_integer(); } - inline operator signed short() const { return as_integer(); } - inline operator unsigned short() const { return as_integer(); } - inline operator signed int() const { return as_integer(); } - inline operator unsigned int() const { return as_integer(); } - inline operator signed long() const { return as_integer(); } - inline operator unsigned long() const { return as_integer(); } - inline operator signed long long() const { return as_integer(); } - inline operator unsigned long long() const { return as_integer(); } - inline operator float() const { return as_float(); } - inline operator double() const { return as_double(); } - inline operator std::string() const { return as_string(); } - inline operator Config::ConfigArrayImpl() const { return as_array(); } - /// Convenience conversion to std::vector - template operator std::vector() const - { - const auto &array = as_array(); - std::vector ret; - ret.reserve(array.size()); - for (auto &&config : array) { - ret.push_back((T)config); - } - return ret; - } - - /// Convenience conversion to std::array - template operator std::array() const - { - const auto &array = as_array(); - check(array.size() == N, "Array size mismatch."); - std::array ret; - std::copy(array.begin(), array.end(), ret.begin()); - return ret; - } - - /// Convenience conversion of an array of length 2 to an std::pair. - /// TODO: generalize for tuples. - template operator std::pair() const - { - const auto &array = as_array(); - check(array.size() == 2u, "Mismatched array length."); - return {(Left)array[0], (Right)array[1]}; - } -#else - /// Explicit casting, since C++ handles implicit casts real badly. - template explicit operator T() const { return as(*this); } - /// Convenience conversion to std::vector - template explicit operator std::vector() const - { - const auto &array = as_array(); - std::vector ret; - ret.reserve(array.size()); - for (auto &&config : array) { - ret.push_back(static_cast(config)); - } - return ret; - } - - /// Convenience conversion to std::array - template explicit operator std::array() const - { - const auto &array = as_array(); - check(array.size() == N, "Array size mismatch."); - std::array ret; - for (size_t i = 0; i < N; ++i) { - ret[i] = static_cast(array[i]); - } - return ret; - } - - /// Convenience conversion of an array of length 2 to an std::pair. - /// TODO: generalize for tuples. - template explicit operator std::pair() const - { - const auto &array = as_array(); - check(array.size() == 2u, "Mismatched array length."); - return {static_cast(array[0]), static_cast(array[1])}; - } -#endif - - const std::string & - as_string() const - { - assert_type(String); - return *_u.str; - } - const char * - c_str() const - { - assert_type(String); - return _u.str->c_str(); - } - - /// The Config must be a boolean. - bool - as_bool() const - { - assert_type(Bool); - return _u.b; - } - - template - IntT - as_integer() const - { - static_assert(std::is_integral::value, "Not an integer."); - assert_type(Int); - check(static_cast(static_cast(_u.i)) == _u.i, "Integer out of range"); - return static_cast(_u.i); - } - - float - as_float() const - { - if (_type == Int) { - return _u.i; - } else { - assert_type(Float); - return static_cast(_u.f); - } - } - - double - as_double() const - { - if (_type == Int) { - return _u.i; - } else { - assert_type(Float); - return _u.f; - } - } - - /// Extract the value of this Config. - template T get() const; - - /// Returns the value or `default_value` if this is the result of a bad lookup. - template - T - get_or(const T &default_value) const - { - if (_type == BadLookupType) { - return default_value; - } else { - return static_cast(*this); - } - } - - // ---------------------------------------- - // Array: - - /// Length of an array - size_t - array_size() const - { - return as_array().size(); - } - - /// Only use this for iterating over an array: `for (Config& e : cfg.as_array()) { ... }` - ConfigArrayImpl & - as_array() - { - assert_type(Array); - return _u.array->_impl; - } - - /// Only use this for iterating over an array: `for (Config& e : cfg.as_array()) { ... }` - const ConfigArrayImpl & - as_array() const - { - assert_type(Array); - return _u.array->_impl; - } - - /// Array indexing - Config &operator[](size_t ix) - { - auto &&array = as_array(); - check(ix < array.size(), "Array index out of range"); - return array[ix]; - } - - /// Array indexing - const Config &operator[](size_t ix) const - { - auto &&array = as_array(); - check(ix < array.size(), "Array index out of range"); - return array[ix]; - } - - /// Append a value to this array. - void - push_back(Config value) - { - as_array().push_back(std::move(value)); - } - - // ---------------------------------------- - // Object: - - /// Number of elementsi n this object - size_t object_size() const; - - /// Only use this for iterating over an object: - /// `for (auto& p : cfg.as_object()) { p.value() = p.key(); }` - ConfigObject & - as_object() - { - assert_type(Object); - return *_u.object; - } - - /// Only use this for iterating over an object: - /// `for (const auto& p : cfg.as_object()) { cout << p.key() << ": " << p.value(); }` - const ConfigObject & - as_object() const - { - assert_type(Object); - return *_u.object; - } - - /// Look up a value in an Object. Returns a BadLookupType Config if the key does not exist. - const Config &operator[](const std::string &key) const; - - /// Prefer `obj.insert_or_assign(key, value);` to `obj[key] = value;` when inserting and performance is important! - Config &operator[](const std::string &key); - - /// For indexing with string literals: - template Config &operator[](const char (&key)[N]) { return operator[](std::string(key)); } - template const Config &operator[](const char (&key)[N]) const { return operator[](std::string(key)); } - /// Check if an object has a specific key. - bool has_key(const std::string &key) const; - - /// Like has_key, but STL compatible. - size_t - count(const std::string &key) const - { - return has_key(key) ? 1 : 0; - } - - /// Returns true iff the value was inserted, false if they key was already there. - bool emplace(std::string key, Config value); - - /// Like `foo[key] = value`, but faster. - void insert_or_assign(const std::string &key, Config &&value); - - /// Erase a key from an object. - bool erase(const std::string &key); - - /// Get the given value in this object. - template - T - get(const std::string &key) const - { - return as((*this)[key]); - } - - /// Look for the given key in this object, and return default_value on failure. - template T get_or(const std::string &key, const T &default_value) const; - - /// Look for the given key in this object, and return default_value on failure. - std::string - get_or(const std::string &key, const char *default_value) const - { - return get_or(key, default_value); - } - - /// obj.get_or({"a", "b". "c"}, 42) - like obj["a"]["b"]["c"], but returns 42 if any of the keys are *missing*. - template T get_or(std::initializer_list keys, const T &default_value) const; - - /// obj.get_or({"a", "b". "c"}, 42) - like obj["a"]["b"]["c"], but returns 42 if any of the keys are *missing*. - std::string - get_or(std::initializer_list keys, const char *default_value) const - { - return get_or(keys, default_value); - } - - // -------------------------------------------------------------------------------- - - /// Compare Config values recursively. - static bool deep_eq(const Config &a, const Config &b); - -#if !CONFIGURU_VALUE_SEMANTICS // No need for a deep_clone method when all copies are deep clones. - /// Copy this Config value recursively. - Config deep_clone() const; -#endif - - // ---------------------------------------- - - /// Visit dangling (unaccessed) object keys recursively. - void visit_dangling(const std::function &visitor) const; - - /// Will check for dangling (unaccessed) object keys recursively and call CONFIGURU_ON_DANGLING on all found. - void check_dangling() const; - - /// Set the 'access' flag recursively, - void mark_accessed(bool v) const; - - // ---------------------------------------- - - /// Was there any comments about this value in the input? - bool - has_comments() const - { - return _comments && !_comments->empty(); - } - - /// Read/write of comments. - ConfigComments & - comments() - { - if (!_comments) { - _comments.reset(new ConfigComments()); - } - return *_comments; - } - - /// Read comments. - const ConfigComments & - comments() const - { - static const ConfigComments s_empty{}; - if (_comments) { - return *_comments; - } else { - return s_empty; - } - } - - /// Returns either "true", "false", the constained string, or the type name. - const char *debug_descr() const; - - /// Human-readable version of the type ("integer", "bool", etc). - static const char *type_str(Type t); - - // ---------------------------------------- - // Helper functions for checking the type is what we expect: - - inline void - check(bool b, const char *msg) const - { - if (!b) { - on_error(msg); - } - } - - void assert_type(Type t) const; - - void on_error(const std::string &msg) const CONFIGURU_NORETURN; - -private: - void free(); - - using ConfigComments_UP = std::unique_ptr; - - union { - bool b; - int64_t i; - double f; - const std::string *str; - ConfigObject *object; - ConfigArray *array; - BadLookupInfo *bad_lookup; - } _u; - - DocInfo_SP _doc; // So we can name the file - ConfigComments_UP _comments; - Index _line = BAD_INDEX; // Where in the source, or BAD_INDEX. Lines are 1-indexed. - Type _type = Uninitialized; -}; - -// ------------------------------------------------------------------------ - -struct Config::ConfigObject { -#if !CONFIGURU_VALUE_SEMANTICS - std::atomic _ref_count{1}; -#endif - ConfigObjectImpl _impl; - - class iterator - { - public: - iterator() = default; - explicit iterator(ConfigObjectImpl::iterator it) : _it(std::move(it)) {} - const iterator &operator*() const - { - _it->second._accessed = true; - return *this; - } - - iterator &operator++() - { - ++_it; - return *this; - } - - friend bool - operator==(const iterator &a, const iterator &b) - { - return a._it == b._it; - } - - friend bool - operator!=(const iterator &a, const iterator &b) - { - return a._it != b._it; - } - - const std::string & - key() const - { - return _it->first; - } - Config & - value() const - { - return _it->second._value; - } - - private: - ConfigObjectImpl::iterator _it; - }; - - class const_iterator - { - public: - const_iterator() = default; - explicit const_iterator(ConfigObjectImpl::const_iterator it) : _it(std::move(it)) {} - const const_iterator &operator*() const - { - _it->second._accessed = true; - return *this; - } - - const_iterator &operator++() - { - ++_it; - return *this; - } - - friend bool - operator==(const const_iterator &a, const const_iterator &b) - { - return a._it == b._it; - } - - friend bool - operator!=(const const_iterator &a, const const_iterator &b) - { - return a._it != b._it; - } - - const std::string & - key() const - { - return _it->first; - } - const Config & - value() const - { - return _it->second._value; - } - - private: - ConfigObjectImpl::const_iterator _it; - }; - - iterator - begin() - { - return iterator{_impl.begin()}; - } - iterator - end() - { - return iterator{_impl.end()}; - } - const_iterator - begin() const - { - return const_iterator{_impl.cbegin()}; - } - const_iterator - end() const - { - return const_iterator{_impl.cend()}; - } - const_iterator - cbegin() const - { - return const_iterator{_impl.cbegin()}; - } - const_iterator - cend() const - { - return const_iterator{_impl.cend()}; - } -}; - -// ------------------------------------------------------------------------ - -inline bool -operator==(const Config &a, const Config &b) -{ - return Config::deep_eq(a, b); -} - -inline bool -operator!=(const Config &a, const Config &b) -{ - return !Config::deep_eq(a, b); -} - -// ------------------------------------------------------------------------ - -template <> -inline bool -Config::get() const -{ - return as_bool(); -} -template <> -inline signed char -Config::get() const -{ - return as_integer(); -} -template <> -inline unsigned char -Config::get() const -{ - return as_integer(); -} -template <> -inline signed short -Config::get() const -{ - return as_integer(); -} -template <> -inline unsigned short -Config::get() const -{ - return as_integer(); -} -template <> -inline signed int -Config::get() const -{ - return as_integer(); -} -template <> -inline unsigned int -Config::get() const -{ - return as_integer(); -} -template <> -inline signed long -Config::get() const -{ - return as_integer(); -} -template <> -inline unsigned long -Config::get() const -{ - return as_integer(); -} -template <> -inline signed long long -Config::get() const -{ - return as_integer(); -} -template <> -inline unsigned long long -Config::get() const -{ - return as_integer(); -} -template <> -inline float -Config::get() const -{ - return as_float(); -} -template <> -inline double -Config::get() const -{ - return as_double(); -} -template <> -inline const std::string & -Config::get() const -{ - return as_string(); -} -template <> -inline std::string -Config::get() const -{ - return as_string(); -} -template <> -inline const Config::ConfigArrayImpl & -Config::get() const -{ - return as_array(); -} -// template<> inline std::vector Config::get() const { return as_vector(); } - -// ------------------------------------------------------------------------ - -template -inline T -as(const configuru::Config &config) -{ - return config.get(); -} - -template -T -Config::get_or(const std::string &key, const T &default_value) const -{ - auto &&object = as_object()._impl; - auto it = object.find(key); - if (it == object.end()) { - return default_value; - } else { - const auto &entry = it->second; - entry._accessed = true; - return as(entry._value); - } -} - -template -T -Config::get_or(std::initializer_list keys, const T &default_value) const -{ - const Config *obj = this; - for (const auto &key : keys) { - if (obj->has_key(key)) { - obj = &(*obj)[key]; - } else { - return default_value; - } - } - return as(*obj); -} - -// ------------------------------------------------------------------------ - -/// Prints in JSON but in a fail-safe manner, allowing uninitialized keys and inf/nan. -std::ostream &operator<<(std::ostream &os, const Config &cfg); - -// ------------------------------------------------------------------------ - -/// Recursively visit all values in a config. -template -void -visit_configs(Config &&config, Visitor &&visitor) -{ - visitor(config); - if (config.is_object()) { - for (auto &&p : config.as_object()) { - visit_configs(p.value(), visitor); - } - } else if (config.is_array()) { - for (auto &&e : config.as_array()) { - visit_configs(e, visitor); - } - } -} - -inline void clear_doc( - Config &root) // TODO: shouldn't be needed. Replace with some info of whether a Config is the root of the document it is in. -{ - visit_configs(root, [&](Config &cfg) { cfg.set_doc(nullptr); }); -} - -/* -inline void replace_doc(Config& root, DocInfo_SP find, DocInfo_SP replacement) -{ - visit_configs(root, [&](Config& config){ - if (config.doc() == find) { - config.set_doc(replacement); - } - }); -} - -// Will try to merge from 'src' do 'dst', replacing with 'src' on any conflict. -inline void merge_replace(Config& dst, const Config& src) -{ - if (dst.is_object() && src.is_object()) { - for (auto&& p : src.as_object()) { - merge_replace(dst[p.key()], p.value()); - } - } else { - dst = src; - } -} - */ - -// ---------------------------------------------------------- - -/// Thrown on a syntax error. -class ParseError : public std::exception -{ -public: - ParseError(const DocInfo_SP &doc, Index line, Index column, const std::string &msg) : _line(line), _column(column) - { - _what = doc->filename + ":" + std::to_string(line) + ":" + std::to_string(column); - doc->append_include_info(_what); - _what += ": " + msg; - } - - /// Will name the file name, line number, column and description. - const char * - what() const noexcept override - { - return _what.c_str(); - } - - Index - line() const noexcept - { - return _line; - } - Index - column() const noexcept - { - return _column; - } - -private: - Index _line, _column; - std::string _what; -}; - -// ---------------------------------------------------------- - -/// This struct basically contain all the way we can tweak the file format. -struct FormatOptions { - /// Indentation should be a single tab, - /// multiple spaces or an empty string. - /// An empty string means the output will be compact. - std::string indentation = "\t"; - bool enforce_indentation = true; ///< Must have correct indentation? - bool end_with_newline = true; ///< End each file with a newline (unless compact). - - // Top file: - bool empty_file = false; ///< If true, an empty file is an empty object. - bool implicit_top_object = true; ///< Ok with key-value pairs top-level? - bool implicit_top_array = true; ///< Ok with several values top-level? - - // Comments: - bool single_line_comments = true; ///< Allow this? - bool block_comments = true; /* Allow this? */ - bool nesting_block_comments = true; ///< /* Allow /* this? */ */ - - // Numbers: - bool inf = true; ///< Allow +inf, -inf - bool nan = true; ///< Allow +NaN - bool hexadecimal_integers = true; ///< Allow 0xff - bool binary_integers = true; ///< Allow 0b1010 - bool unary_plus = true; ///< Allow +42 - bool distinct_floats = true; ///< Print 9.0 as "9.0", not just "9". A must for round-tripping. - - // Arrays - bool array_omit_comma = true; ///< Allow [1 2 3] - bool array_trailing_comma = true; ///< Allow [1, 2, 3,] - - // Objects: - bool identifiers_keys = true; ///< { is_this_ok: true } - bool object_separator_equal = false; ///< { "is_this_ok" = true } - bool allow_space_before_colon = false; ///< { "is_this_ok" : true } - bool omit_colon_before_object = false; ///< { "nested_object" { } } - bool object_omit_comma = true; ///< Allow {a:1 b:2} - bool object_trailing_comma = true; ///< Allow {a:1, b:2,} - bool object_duplicate_keys = false; ///< Allow {"a":1, "a":2} - bool object_align_values = true; ///< Add spaces after keys to align subsequent values. - - // Strings - bool str_csharp_verbatim = true; ///< Allow @"Verbatim\strings" - bool str_python_multiline = true; ///< Allow """ Python\nverbatim strings """ - bool str_32bit_unicode = true; ///< Allow "\U0030dbfd" - bool str_allow_tab = true; ///< Allow unescaped tab in string. - - // Special - bool allow_macro = true; ///< Allow `#include "some_other_file.cfg"` - - // When writing: - bool write_comments = true; - - /// Sort keys lexicographically. If false, sort by order they where added. - bool sort_keys = false; - - /// When printing, write uninitialized values as UNINITIALIZED. Useful for debugging. - bool write_uninitialized = false; - - /// Dumping should mark the json as accessed? - bool mark_accessed = true; - - bool - compact() const - { - return indentation.empty(); - } -}; - -/// Returns FormatOptions that are describe a JSON file format. -inline FormatOptions -make_json_options() -{ - FormatOptions options; - - options.indentation = "\t"; - options.enforce_indentation = false; - - // Top file: - options.empty_file = false; - options.implicit_top_object = false; - options.implicit_top_array = false; - - // Comments: - options.single_line_comments = false; - options.block_comments = false; - options.nesting_block_comments = false; - - // Numbers: - options.inf = false; - options.nan = false; - options.hexadecimal_integers = false; - options.binary_integers = false; - options.unary_plus = false; - options.distinct_floats = true; - - // Arrays - options.array_omit_comma = false; - options.array_trailing_comma = false; - - // Objects: - options.identifiers_keys = false; - options.object_separator_equal = false; - options.allow_space_before_colon = true; - options.omit_colon_before_object = false; - options.object_omit_comma = false; - options.object_trailing_comma = false; - options.object_duplicate_keys = false; // To be 100% JSON compatile, this should be true, but it is error prone. - options.object_align_values = true; // Looks better. - - // Strings - options.str_csharp_verbatim = false; - options.str_python_multiline = false; - options.str_32bit_unicode = false; - options.str_allow_tab = false; - - // Special - options.allow_macro = false; - - // When writing: - options.write_comments = false; - options.sort_keys = false; - - return options; -} - -/// Returns format options that allow us parsing most files. -inline FormatOptions -make_forgiving_options() -{ - FormatOptions options; - - options.indentation = "\t"; - options.enforce_indentation = false; - - // Top file: - options.empty_file = true; - options.implicit_top_object = true; - options.implicit_top_array = true; - - // Comments: - options.single_line_comments = true; - options.block_comments = true; - options.nesting_block_comments = true; - - // Numbers: - options.inf = true; - options.nan = true; - options.hexadecimal_integers = true; - options.binary_integers = true; - options.unary_plus = true; - options.distinct_floats = true; - - // Arrays - options.array_omit_comma = true; - options.array_trailing_comma = true; - - // Objects: - options.identifiers_keys = true; - options.object_separator_equal = true; - options.allow_space_before_colon = true; - options.omit_colon_before_object = true; - options.object_omit_comma = true; - options.object_trailing_comma = true; - options.object_duplicate_keys = true; - - // Strings - options.str_csharp_verbatim = true; - options.str_python_multiline = true; - options.str_32bit_unicode = true; - options.str_allow_tab = true; - - // Special - options.allow_macro = true; - - // When writing: - options.write_comments = false; - options.sort_keys = false; - - return options; -} - -/// The CFG file format. -static const FormatOptions CFG = FormatOptions(); - -/// The JSON file format. -static const FormatOptions JSON = make_json_options(); - -/// A very forgiving file format, when parsing stuff that is not strict. -static const FormatOptions FORGIVING = make_forgiving_options(); - -struct ParseInfo { - std::map parsed_files; // Two #include gives same Config tree. -}; - -/// The parser may throw ParseError. -/// `str` should be a zero-ended Utf-8 encoded string of characters. -/// The `name` should be something akin to a filename. It is only for error reporting. -Config parse_string(const char *str, const FormatOptions &options, const char *name); -Config parse_file(const std::string &path, const FormatOptions &options); - -/// Advanced usage: -Config parse_string(const char *str, const FormatOptions &options, DocInfo _doc, ParseInfo &info); -Config parse_file(const std::string &path, const FormatOptions &options, DocInfo_SP doc, ParseInfo &info); - -// ---------------------------------------------------------- -/// Writes the config as a string in the given format. -/// May call CONFIGURU_ONERROR if the given config is invalid. This can happen if -/// a Config is uninitialized (and options write_uninitialized is not set) or -/// a Config contains inf/nan (and options.inf/options.nan aren't set). -std::string dump_string(const Config &config, const FormatOptions &options); - -/// Writes the config to a file. Like dump_string, but can may also call CONFIGURU_ONERROR -/// if it fails to write to the given path. -void dump_file(const std::string &path, const Config &config, const FormatOptions &options); - -// ---------------------------------------------------------- -// Automatic (de)serialize of most things. -// Include (from https://github.com/cbeck88/visit_struct) -// before including to get this feature. - -#ifdef VISITABLE_STRUCT -template struct is_container : std::false_type { -}; - -// template struct is_container > : std::true_type { }; -template struct is_container> : std::true_type { -}; - -// ---------------------------------------------------------------------------- - -Config serialize(const std::string &some_string); - -template typename std::enable_if::value, Config>::type serialize(const T &some_value); - -template Config serialize(T (&some_array)[N]); - -template typename std::enable_if::value, Config>::type serialize(const T &some_container); - -template -typename std::enable_if::value, Config>::type serialize(const T &some_struct); - -// ---------------------------------------------------------------------------- - -inline Config -serialize(const std::string &some_string) -{ - return Config(some_string); -} - -template -typename std::enable_if::value, Config>::type -serialize(const T &some_value) -{ - return Config(some_value); -} - -template Config serialize(T (&some_array)[N]) -{ - auto config = Config::array(); - for (size_t i = 0; i < N; ++i) { - config.push_back(serialize(some_array[i])); - } - return config; -} - -template -typename std::enable_if::value, Config>::type -serialize(const T &some_container) -{ - auto config = Config::array(); - for (const auto &value : some_container) { - config.push_back(serialize(value)); - } - return config; -} - -template -typename std::enable_if::value, Config>::type -serialize(const T &some_struct) -{ - auto config = Config::object(); - visit_struct::apply_visitor([&config](const std::string &name, const auto &value) { config[name] = serialize(value); }, - some_struct); - return config; -} - -// ---------------------------------------------------------------------------- - -/// Called when there is a problem in deserialize. -using ConversionError = std::function; - -void deserialize(std::string *some_string, const Config &config, const ConversionError &on_error); - -template -typename std::enable_if::value>::type deserialize(T *some_value, const Config &config, - const ConversionError &on_error); - -template -typename std::enable_if::value>::type deserialize(T (*some_array)[N], const Config &config, - const ConversionError &on_error); - -template -typename std::enable_if::value>::type deserialize(T *some_container, const Config &config, - const ConversionError &on_error); - -template -typename std::enable_if::value>::type deserialize(T *some_struct, const Config &config, - const ConversionError &on_error); - -// ---------------------------------------------------------------------------- - -inline void -deserialize(std::string *some_string, const Config &config, const ConversionError &on_error) -{ - *some_string = config.as_string(); -} - -template -typename std::enable_if::value>::type -deserialize(T *some_value, const Config &config, const ConversionError &on_error) -{ - *some_value = as(config); -} - -template -typename std::enable_if::value>::type -deserialize(T (*some_array)[N], const Config &config, const ConversionError &on_error) -{ - if (config.array_size() != N) { - if (on_error) { - on_error(config.where() + "Expected array to be " + std::to_string(N) + " long."); - } - } else { - for (size_t i = 0; i < N; ++i) { - deserialize(&(*some_array)[i], config[i], on_error); - } - } -} - -template -typename std::enable_if::value>::type -deserialize(T *some_container, const Config &config, const ConversionError &on_error) -{ - if (!config.is_array()) { - if (on_error) { - on_error(config.where() + "Failed to deserialize container: config is not an array."); - } - } else { - some_container->clear(); - some_container->reserve(config.array_size()); - for (const auto &value : config.as_array()) { - some_container->push_back({}); - deserialize(&some_container->back(), value, on_error); - } - } -} - -template -typename std::enable_if::value>::type -deserialize(T *some_struct, const Config &config, const ConversionError &on_error) -{ - if (!config.is_object()) { - if (on_error) { - on_error(config.where() + "Failed to deserialize object: config is not an object."); - } - } else { - visit_struct::apply_visitor( - [&config, &on_error](const std::string &name, auto &value) { - if (config.has_key(name)) { - deserialize(&value, config[name], on_error); - } - }, - *some_struct); - } -} -#endif // VISITABLE_STRUCT - -} // namespace configuru - -// ---------------------------------------------------------------------------- -// 88 8b d8 88""Yb 88 888888 8b d8 888888 88b 88 888888 db 888888 88 dP"Yb 88b 88 -// 88 88b d88 88__dP 88 88__ 88b d88 88__ 88Yb88 88 dPYb 88 88 dP Yb 88Yb88 -// 88 88YbdP88 88""" 88 .o 88"" 88YbdP88 88"" 88 Y88 88 dP__Yb 88 88 Yb dP 88 Y88 -// 88 88 YY 88 88 88ood8 888888 88 YY 88 888888 88 Y8 88 dP""""Yb 88 88 YbodP 88 Y8 - -/* In one of your .cpp files you need to do the following: -#define CONFIGURU_IMPLEMENTATION -#include - -This will define all the Configuru functions so that the linker may find them. -*/ - -#include -#include -#include - -// ---------------------------------------------------------------------------- -namespace configuru -{ -void -DocInfo::append_include_info(std::string &ret, const std::string &indent) const -{ - if (!includers.empty()) { - ret += ", included at:\n"; - for (auto &&includer : includers) { - ret += indent + includer.doc->filename + ":" + std::to_string(includer.line); - includer.doc->append_include_info(ret, indent + " "); - ret += "\n"; - } - ret.pop_back(); - } -} - -struct BadLookupInfo { - const DocInfo_SP doc; // Of parent object - const unsigned line; // Of parent object - const std::string key; - -#if !CONFIGURU_VALUE_SEMANTICS - std::atomic _ref_count{1}; -#endif - - BadLookupInfo(DocInfo_SP doc_, Index line_, std::string key_) : doc(std::move(doc_)), line(line_), key(std::move(key_)) {} -}; - -Config::Config(const char *str) : _type(String) -{ - CONFIGURU_ASSERT(str != nullptr); - _u.str = new std::string(str); -} - -Config::Config(std::string str) : _type(String) -{ - _u.str = new std::string(move(str)); -} - -Config::Config(std::initializer_list> values) : _type(Uninitialized) -{ - make_object(); - for (auto &&v : values) { - (*this)[v.first] = std::move(v.second); - } -} - -void -Config::make_object() -{ - assert_type(Uninitialized); - _type = Object; - _u.object = new ConfigObject(); -} - -void -Config::make_array() -{ - assert_type(Uninitialized); - _type = Array; - _u.array = new ConfigArray(); -} - -Config -Config::object() -{ - Config ret; - ret.make_object(); - return ret; -} - -Config -Config::object(std::initializer_list> values) -{ - Config ret; - ret.make_object(); - for (auto &&p : values) { - ret[static_cast(p.first)] = std::move(p.second); - } - return ret; -} - -Config -Config::array() -{ - Config ret; - ret.make_array(); - return ret; -} - -Config -Config::array(std::initializer_list values) -{ - Config ret; - ret.make_array(); - ret._u.array->_impl.reserve(values.size()); - for (auto &&v : values) { - ret.push_back(std::move(v)); - } - return ret; -} - -void -Config::tag(const DocInfo_SP &doc, Index line, Index column) -{ - _doc = doc; - _line = line; - (void)column; // TODO: include this info too. -} - -// ------------------------------------------------------------------------ - -Config::Config(const Config &o) : _type(Uninitialized) -{ - *this = o; -} - -Config::Config(Config &&o) noexcept : _type(Uninitialized) -{ - this->swap(o); -} - -void -Config::swap(Config &o) noexcept -{ - if (&o == this) { - return; - } - std::swap(_type, o._type); - std::swap(_u, o._u); - std::swap(_doc, o._doc); - std::swap(_line, o._line); - std::swap(_comments, o._comments); -} - -Config & -Config::operator=(Config &&o) noexcept -{ - if (&o == this) { - return *this; - } - - std::swap(_type, o._type); - std::swap(_u, o._u); - - // Remember where we come from even when assigned a new value: - if (o._doc || o._line != BAD_INDEX) { - std::swap(_doc, o._doc); - std::swap(_line, o._line); - } - - if (o._comments) { - std::swap(_comments, o._comments); - } - - return *this; -} - -Config & -Config::operator=(const Config &o) -{ - if (&o == this) { - return *this; - } - - free(); - - _type = o._type; - -#if CONFIGURU_VALUE_SEMANTICS - if (_type == String) { - _u.str = new std::string(*o._u.str); - } else if (_type == BadLookupType) { - _u.bad_lookup = new BadLookupInfo(*o._u.bad_lookup); - } else if (_type == Object) { - _u.object = new ConfigObject(*o._u.object); - } else if (_type == Array) { - _u.array = new ConfigArray(*o._u.array); - } else { - memcpy(&_u, &o._u, sizeof(_u)); - } -#else // !CONFIGURU_VALUE_SEMANTICS: - if (_type == String) { - _u.str = new std::string(*o._u.str); - } else { - memcpy(&_u, &o._u, sizeof(_u)); - if (_type == BadLookupType) { - ++_u.bad_lookup->_ref_count; - } - if (_type == Array) { - ++_u.array->_ref_count; - } - if (_type == Object) { - ++_u.object->_ref_count; - } - } -#endif // !CONFIGURU_VALUE_SEMANTICS - - // Remember where we come from even when assigned a new value: - if (o._doc || o._line != BAD_INDEX) { - _doc = o._doc; - _line = o._line; - } - - if (o._comments) { - _comments.reset(new ConfigComments(*o._comments)); - } - -#if CONFIGURU_VALUE_SEMANTICS - o.mark_accessed(true); -#endif - - return *this; -} - -Config::~Config() -{ - free(); -} - -void -Config::free() -{ -#if CONFIGURU_VALUE_SEMANTICS - if (_type == BadLookupType) { - delete _u.bad_lookup; - } else if (_type == Object) { - delete _u.object; - } else if (_type == Array) { - delete _u.array; - } else if (_type == String) { - delete _u.str; - } -#else // !CONFIGURU_VALUE_SEMANTICS: - if (_type == BadLookupType) { - if (--_u.bad_lookup->_ref_count == 0) { - delete _u.bad_lookup; - } - } else if (_type == Object) { - if (--_u.object->_ref_count == 0) { - delete _u.object; - } - } else if (_type == Array) { - if (--_u.array->_ref_count == 0) { - delete _u.array; - } - } else if (_type == String) { - delete _u.str; - } -#endif // !CONFIGURU_VALUE_SEMANTICS - - _type = Uninitialized; - - // Keep _doc, _line, _comments until overwritten/destructor. -} - -// ------------------------------------------------------------------------ - -size_t -Config::object_size() const -{ - return as_object()._impl.size(); -} - -const Config &Config::operator[](const std::string &key) const -{ - auto &&object = as_object()._impl; - auto it = object.find(key); - if (it == object.end()) { - on_error("Key '" + key + "' not in object"); - } else { - const auto &entry = it->second; - entry._accessed = true; - return entry._value; - } -} - -Config &Config::operator[](const std::string &key) -{ - auto &&object = as_object()._impl; - auto &&entry = object[key]; - if (entry._nr == BAD_INDEX) { - // New entry - entry._nr = static_cast(object.size()) - 1; - entry._value._type = BadLookupType; - entry._value._u.bad_lookup = new BadLookupInfo{_doc, _line, key}; - } else { - entry._accessed = true; - } - return entry._value; -} - -bool -Config::has_key(const std::string &key) const -{ - return as_object()._impl.count(key) != 0; -} - -bool -Config::emplace(std::string key, Config value) -{ - auto &&object = as_object()._impl; - return object.emplace(std::move(key), Config::ObjectEntry{std::move(value), (unsigned)object.size()}).second; -} - -void -Config::insert_or_assign(const std::string &key, Config &&config) -{ - auto &&object = as_object()._impl; - auto &&entry = object[key]; - if (entry._nr == BAD_INDEX) { - // New entry - entry._nr = static_cast(object.size()) - 1; - } else { - entry._accessed = true; - } - entry._value = std::move(config); -} - -bool -Config::erase(const std::string &key) -{ - auto &object = as_object()._impl; - auto it = object.find(key); - if (it == object.end()) { - return false; - } else { - object.erase(it); - return true; - } -} - -bool -Config::deep_eq(const Config &a, const Config &b) -{ - if (a._type != b._type) { - return false; - } - if (a._type == Null) { - return true; - } - if (a._type == Bool) { - return a._u.b == b._u.b; - } - if (a._type == Int) { - return a._u.i == b._u.i; - } - if (a._type == Float) { - return a._u.f == b._u.f; - } - if (a._type == String) { - return *a._u.str == *b._u.str; - } - if (a._type == Object) { - if (a._u.object == b._u.object) { - return true; - } - auto &&a_object = a.as_object()._impl; - auto &&b_object = b.as_object()._impl; - if (a_object.size() != b_object.size()) { - return false; - } - for (auto &&p : a_object) { - auto it = b_object.find(p.first); - if (it == b_object.end()) { - return false; - } - if (!deep_eq(p.second._value, it->second._value)) { - return false; - } - } - return true; - } - if (a._type == Array) { - if (a._u.array == b._u.array) { - return true; - } - auto &&a_array = a.as_array(); - auto &&b_array = b.as_array(); - if (a_array.size() != b_array.size()) { - return false; - } - for (size_t i = 0; i < a_array.size(); ++i) { - if (!deep_eq(a_array[i], a_array[i])) { - return false; - } - } - return true; - } - - return false; -} - -#if !CONFIGURU_VALUE_SEMANTICS -Config -Config::deep_clone() const -{ - Config ret = *this; - if (ret._type == Object) { - ret = Config::object(); - for (auto &&p : this->as_object()._impl) { - auto &dst = ret._u.object->_impl[p.first]; - dst._nr = p.second._nr; - dst._value = p.second._value.deep_clone(); - } - } - if (ret._type == Array) { - ret = Config::array(); - for (auto &&value : this->as_array()) { - ret.push_back(value.deep_clone()); - } - } - return ret; -} -#endif - -void -Config::visit_dangling(const std::function &visitor) const -{ - if (is_object()) { - for (auto &&p : as_object()._impl) { - auto &&entry = p.second; - auto &&value = entry._value; - if (entry._accessed) { - value.check_dangling(); - } else { - visitor(p.first, value); - } - } - } else if (is_array()) { - for (auto &&e : as_array()) { - e.check_dangling(); - } - } -} - -void -Config::check_dangling() const -{ - std::string message = ""; - - visit_dangling([&](const std::string &key, const Config &value) { - message += "\n " + value.where() + "Key '" + key + "' never accessed."; - }); - - if (!message.empty()) { - message = "Dangling keys:" + message; - CONFIGURU_ON_DANGLING(message); - } -} - -void -Config::mark_accessed(bool v) const -{ - if (is_object()) { - for (auto &&p : as_object()._impl) { - auto &&entry = p.second; - entry._accessed = v; - entry._value.mark_accessed(v); - } - } else if (is_array()) { - for (auto &&e : as_array()) { - e.mark_accessed(v); - } - } -} - -const char * -Config::debug_descr() const -{ - switch (_type) { - case Bool: - return _u.b ? "true" : "false"; - case String: - return _u.str->c_str(); - default: - return type_str(_type); - } -} - -const char * -Config::type_str(Type t) -{ - switch (t) { - case Uninitialized: - return "uninitialized"; - case BadLookupType: - return "undefined"; - case Null: - return "null"; - case Bool: - return "bool"; - case Int: - return "integer"; - case Float: - return "float"; - case String: - return "string"; - case Array: - return "array"; - case Object: - return "object"; - } - return "BROKEN Config"; -} - -std::string -where_is(const DocInfo_SP &doc, Index line) -{ - if (doc) { - std::string ret = doc->filename; - if (line != BAD_INDEX) { - ret += ":" + std::to_string(line); - } - doc->append_include_info(ret); - ret += ": "; - return ret; - } else if (line != BAD_INDEX) { - return "line " + std::to_string(line) + ": "; - } else { - return ""; - } -} - -std::string -Config::where() const -{ - return where_is(_doc, _line); -} - -void -Config::on_error(const std::string &msg) const -{ - CONFIGURU_ONERROR(where() + msg); - abort(); // We shouldn't get here. -} - -void -Config::assert_type(Type expected) const -{ - if (_type == BadLookupType) { - auto where = where_is(_u.bad_lookup->doc, _u.bad_lookup->line); - CONFIGURU_ONERROR(where + "Failed to find key '" + _u.bad_lookup->key + "'"); - } else if (_type != expected) { - const auto message = where() + "Expected " + type_str(expected) + ", got " + type_str(_type); - if (_type == Uninitialized && expected == Object) { - CONFIGURU_ONERROR(message + ". Did you forget to call Config::object()?"); - } else if (_type == Uninitialized && expected == Array) { - CONFIGURU_ONERROR(message + ". Did you forget to call Config::array()?"); - } else { - CONFIGURU_ONERROR(message); - } - } -} - -std::ostream & -operator<<(std::ostream &os, const Config &cfg) -{ - auto format = JSON; - // Make sure that all config types are serializable: - format.inf = true; - format.nan = true; - format.write_uninitialized = true; - format.end_with_newline = false; - format.mark_accessed = false; - return os << dump_string(cfg, format); -} -} - -// ---------------------------------------------------------------------------- -// 88""Yb db 88""Yb .dP"Y8 888888 88""Yb -// 88__dP dPYb 88__dP `Ybo." 88__ 88__dP -// 88""" dP__Yb 88"Yb o.`Y8b 88"" 88"Yb -// 88 dP""""Yb 88 Yb 8bodP' 888888 88 Yb - -#include -#include - -namespace configuru -{ -void -append(Comments &a, Comments &&b) -{ - for (auto &&entry : b) { - a.emplace_back(std::move(entry)); - } -} - -bool -ConfigComments::empty() const -{ - return prefix.empty() && postfix.empty() && pre_end_brace.empty(); -} - -void -ConfigComments::append(ConfigComments &&other) -{ - configuru::append(this->prefix, std::move(other.prefix)); - configuru::append(this->postfix, std::move(other.postfix)); - configuru::append(this->pre_end_brace, std::move(other.pre_end_brace)); -} - -// Returns the number of bytes written, or 0 on error -size_t -encode_utf8(std::string &dst, uint64_t c) -{ - if (c <= 0x7F) // 0XXX XXXX - one byte - { - dst += static_cast(c); - return 1; - } else if (c <= 0x7FF) // 110X XXXX - two bytes - { - dst += static_cast(0xC0 | (c >> 6)); - dst += static_cast(0x80 | (c & 0x3F)); - return 2; - } else if (c <= 0xFFFF) // 1110 XXXX - three bytes - { - dst += static_cast(0xE0 | (c >> 12)); - dst += static_cast(0x80 | ((c >> 6) & 0x3F)); - dst += static_cast(0x80 | (c & 0x3F)); - return 3; - } else if (c <= 0x1FFFFF) // 1111 0XXX - four bytes - { - dst += static_cast(0xF0 | (c >> 18)); - dst += static_cast(0x80 | ((c >> 12) & 0x3F)); - dst += static_cast(0x80 | ((c >> 6) & 0x3F)); - dst += static_cast(0x80 | (c & 0x3F)); - return 4; - } else if (c <= 0x3FFFFFF) // 1111 10XX - five bytes - { - dst += static_cast(0xF8 | (c >> 24)); - dst += static_cast(0x80 | (c >> 18)); - dst += static_cast(0x80 | ((c >> 12) & 0x3F)); - dst += static_cast(0x80 | ((c >> 6) & 0x3F)); - dst += static_cast(0x80 | (c & 0x3F)); - return 5; - } else if (c <= 0x7FFFFFFF) // 1111 110X - six bytes - { - dst += static_cast(0xFC | (c >> 30)); - dst += static_cast(0x80 | ((c >> 24) & 0x3F)); - dst += static_cast(0x80 | ((c >> 18) & 0x3F)); - dst += static_cast(0x80 | ((c >> 12) & 0x3F)); - dst += static_cast(0x80 | ((c >> 6) & 0x3F)); - dst += static_cast(0x80 | (c & 0x3F)); - return 6; - } else { - return 0; // Error - } -} - -std::string -quote(char c) -{ - if (c == 0) { - return ""; - } - if (c == ' ') { - return ""; - } - if (c == '\n') { - return "'\\n'"; - } - if (c == '\t') { - return "'\\t'"; - } - if (c == '\r') { - return "'\\r'"; - } - if (c == '\b') { - return "'\\b'"; - } - return std::string("'") + c + "'"; -} - -struct State { - const char *ptr; - unsigned line_nr; - const char *line_start; -}; - -struct Parser { - Parser(const char *str, const FormatOptions &options, DocInfo_SP doc, ParseInfo &info); - - bool skip_white(Comments *out_comments, int &out_indentation, bool break_on_newline); - - bool - skip_white_ignore_comments() - { - int indentation; - return skip_white(nullptr, indentation, false); - } - - bool - skip_pre_white(Config *config, int &out_indentation) - { - if (!MAYBE_WHITE[static_cast(_ptr[0])]) { - // Early out - out_indentation = -1; - return false; - } - - Comments comments; - bool did_skip = skip_white(&comments, out_indentation, false); - if (!comments.empty()) { - append(config->comments().prefix, std::move(comments)); - } - return did_skip; - } - - bool - skip_post_white(Config *config) - { - if (!MAYBE_WHITE[static_cast(_ptr[0])]) { - // Early out - return false; - } - - Comments comments; - int indentation; - bool did_skip = skip_white(&comments, indentation, true); - if (!comments.empty()) { - append(config->comments().postfix, std::move(comments)); - } - return did_skip; - } - - Config top_level(); - void parse_value(Config &out, bool *out_did_skip_postwhites); - void parse_array(Config &dst); - void parse_array_contents(Config &dst); - void parse_object(Config &dst); - void parse_object_contents(Config &dst); - void parse_int(Config &out); - void parse_float(Config &out); - void parse_finite_number(Config &dst); - std::string parse_string(); - std::string parse_c_sharp_string(); - uint64_t parse_hex(int count); - void parse_macro(Config &dst); - - void - tag(Config &var) - { - var.tag(_doc, _line_nr, column()); - } - - State - get_state() const - { - return {_ptr, _line_nr, _line_start}; - } - - void - set_state(State s) - { - _ptr = s.ptr; - _line_nr = s.line_nr; - _line_start = s.line_start; - } - - Index - column() const - { - return static_cast(_ptr - _line_start + 1); - } - - const char * - start_of_line() const - { - return _line_start; - } - - const char * - end_of_line() const - { - const char *p = _ptr; - while (*p && *p != '\r' && *p != '\n') { - ++p; - } - return p; - } - - void - throw_error(const std::string &desc) CONFIGURU_NORETURN - { - const char *sol = start_of_line(); - const char *eol = end_of_line(); - std::string orientation; - for (const char *p = sol; p != eol; ++p) { - if (*p == '\t') { - orientation += " "; - } else { - orientation.push_back(*p); - } - } - - orientation += "\n"; - for (const char *p = sol; p != _ptr; ++p) { - if (*p == '\t') { - orientation += " "; - } else { - orientation.push_back(' '); - } - } - orientation += "^"; - - throw ParseError(_doc, _line_nr, column(), desc + "\n" + orientation); - } - - void - throw_indentation_error(int found_tabs, int expected_tabs) - { - if (_options.enforce_indentation) { - char buff[128]; - snprintf(buff, sizeof(buff), "Bad indentation: expected %d tabs, found %d", found_tabs, expected_tabs); - throw_error(buff); - } - } - - void - parse_assert(bool b, const char *error_msg) - { - if (!b) { - throw_error(error_msg); - } - } - - void - parse_assert(bool b, const char *error_msg, const State &error_state) - { - if (!b) { - set_state(error_state); - throw_error(error_msg); - } - } - - void - swallow(char c) - { - if (_ptr[0] == c) { - _ptr += 1; - } else { - throw_error("Expected " + quote(c)); - } - } - - bool - try_swallow(const char *str) - { - auto n = strlen(str); - if (strncmp(str, _ptr, n) == 0) { - _ptr += n; - return true; - } else { - return false; - } - } - - void - swallow(const char *str, const char *error_msg) - { - parse_assert(try_swallow(str), error_msg); - } - - bool - is_reserved_identifier(const char *ptr) - { - if (strncmp(ptr, "true", 4) == 0 || strncmp(ptr, "null", 4) == 0) { - return !IDENT_CHARS[static_cast(ptr[4])]; - } else if (strncmp(ptr, "false", 5) == 0) { - return !IDENT_CHARS[static_cast(ptr[5])]; - } else { - return false; - } - } - -private: - bool IDENT_STARTERS[256] = {0}; - bool IDENT_CHARS[256] = {0}; - bool MAYBE_WHITE[256] = {0}; - bool SPECIAL_CHARACTERS[256] = {0}; - -private: - FormatOptions _options; - DocInfo_SP _doc; - ParseInfo &_info; - - const char *_ptr; - Index _line_nr; - const char *_line_start; - int _indentation = 0; // Expected number of tabs between a \n and the next key/value -}; - -// -------------------------------------------- - -// Sets an inclusive range -void -set_range(bool lookup[256], char a, char b) -{ - for (char c = a; c <= b; ++c) { - lookup[static_cast(c)] = true; - } -} - -Parser::Parser(const char *str, const FormatOptions &options, DocInfo_SP doc, ParseInfo &info) : _doc(doc), _info(info) -{ - _options = options; - _line_nr = 1; - _ptr = str; - _line_start = str; - - IDENT_STARTERS[static_cast('_')] = true; - set_range(IDENT_STARTERS, 'a', 'z'); - set_range(IDENT_STARTERS, 'A', 'Z'); - - IDENT_CHARS[static_cast('_')] = true; - set_range(IDENT_CHARS, 'a', 'z'); - set_range(IDENT_CHARS, 'A', 'Z'); - set_range(IDENT_CHARS, '0', '9'); - - MAYBE_WHITE[static_cast('\n')] = true; - MAYBE_WHITE[static_cast('\r')] = true; - MAYBE_WHITE[static_cast('\t')] = true; - MAYBE_WHITE[static_cast(' ')] = true; - MAYBE_WHITE[static_cast('/')] = true; // Maybe a comment - - SPECIAL_CHARACTERS[static_cast('\0')] = true; - SPECIAL_CHARACTERS[static_cast('\\')] = true; - SPECIAL_CHARACTERS[static_cast('\"')] = true; - SPECIAL_CHARACTERS[static_cast('\n')] = true; - SPECIAL_CHARACTERS[static_cast('\t')] = true; - - CONFIGURU_ASSERT(_options.indentation != "" || !_options.enforce_indentation); -} - -// Returns true if we did skip white-space. -// out_indentation is the depth of indentation on the last line we did skip on. -// iff out_indentation is -1 there is a non-tab on the last line. -bool -Parser::skip_white(Comments *out_comments, int &out_indentation, bool break_on_newline) -{ - auto start_ptr = _ptr; - out_indentation = 0; - bool found_newline = false; - - const std::string &indentation = _options.indentation; - - while (MAYBE_WHITE[static_cast(_ptr[0])]) { - if (_ptr[0] == '\n') { - // Unix style newline - _ptr += 1; - _line_nr += 1; - _line_start = _ptr; - out_indentation = 0; - if (break_on_newline) { - return true; - } - found_newline = true; - } else if (_ptr[0] == '\r') { - // CR-LF - windows style newline - parse_assert(_ptr[1] == '\n', "CR with no LF. \\r only allowed before \\n."); // TODO: this is OK in JSON. - _ptr += 2; - _line_nr += 1; - _line_start = _ptr; - out_indentation = 0; - if (break_on_newline) { - return true; - } - found_newline = true; - } else if (!indentation.empty() && strncmp(_ptr, indentation.c_str(), indentation.size()) == 0) { - _ptr += indentation.size(); - if (_options.enforce_indentation && indentation == "\t") { - parse_assert(out_indentation != -1, "Tabs should only occur on the start of a line!"); - } - ++out_indentation; - } else if (_ptr[0] == '\t') { - ++_ptr; - if (_options.enforce_indentation) { - parse_assert(out_indentation != -1, "Tabs should only occur on the start of a line!"); - } - ++out_indentation; - } else if (_ptr[0] == ' ') { - if (found_newline && _options.enforce_indentation) { - if (indentation == "\t") { - throw_error("Found a space at beginning of a line. Indentation must be done using tabs!"); - } else { - throw_error("Indentation should be a multiple of " + std::to_string(indentation.size()) + " spaces."); - } - } - ++_ptr; - out_indentation = -1; - } else if (_ptr[0] == '/' && _ptr[1] == '/') { - parse_assert(_options.single_line_comments, "Single line comments forbidden."); - // Single line comment - auto start = _ptr; - _ptr += 2; - while (_ptr[0] && _ptr[0] != '\n') { - _ptr += 1; - } - if (out_comments) { - out_comments->emplace_back(start, _ptr - start); - } - out_indentation = 0; - if (break_on_newline) { - return true; - } - } else if (_ptr[0] == '/' && _ptr[1] == '*') { - parse_assert(_options.block_comments, "Block comments forbidden."); - // Multi-line comment - auto state = get_state(); // So we can point out the start if there's an error - _ptr += 2; - unsigned nesting = 1; // We allow nested /**/ comments - do { - if (_ptr[0] == 0) { - set_state(state); - throw_error("Non-ending /* comment"); - } else if (_ptr[0] == '/' && _ptr[1] == '*') { - _ptr += 2; - parse_assert(_options.nesting_block_comments, "Nesting comments (/* /* */ */) forbidden."); - nesting += 1; - } else if (_ptr[0] == '*' && _ptr[1] == '/') { - _ptr += 2; - nesting -= 1; - } else if (_ptr[0] == '\n') { - _ptr += 1; - _line_nr += 1; - _line_start = _ptr; - } else { - _ptr += 1; - } - } while (nesting > 0); - if (out_comments) { - out_comments->emplace_back(state.ptr, _ptr - state.ptr); - } - out_indentation = -1; - if (break_on_newline) { - return true; - } - } else { - break; - } - } - - if (start_ptr == _ptr) { - out_indentation = -1; - return false; - } else { - return true; - } -} - -/* -The top-level can be any value, OR the innerds of an object: -foo = 1 -"bar": 2 -*/ -Config -Parser::top_level() -{ - bool is_object = false; - - if (_options.implicit_top_object) { - auto state = get_state(); - skip_white_ignore_comments(); - - if (IDENT_STARTERS[static_cast(_ptr[0])] && !is_reserved_identifier(_ptr)) { - is_object = true; - } else if (_ptr[0] == '"' || _ptr[0] == '@') { - parse_string(); - skip_white_ignore_comments(); - is_object = (_ptr[0] == ':' || _ptr[0] == '='); - } - - set_state(state); // restore - } - - Config ret; - tag(ret); - - if (is_object) { - parse_object_contents(ret); - } else { - parse_array_contents(ret); - parse_assert(ret.array_size() <= 1 || _options.implicit_top_array, "Multiple values not allowed without enclosing []"); - } - - skip_post_white(&ret); - - parse_assert(_ptr[0] == 0, "Expected EoF"); - - if (!is_object && ret.array_size() == 0) { - if (_options.empty_file) { - auto empty_object = Config::object(); - if (ret.has_comments()) { - empty_object.comments() = std::move(ret.comments()); - } - return empty_object; - } else { - throw_error("Empty file"); - } - } - - if (!is_object && ret.array_size() == 1) { - // A single value - not an array after all: - Config first(std::move(ret[0])); - if (ret.has_comments()) { - first.comments().append(std::move(ret.comments())); - } - return first; - } - - return ret; -} - -void -Parser::parse_value(Config &dst, bool *out_did_skip_postwhites) -{ - int line_indentation; - skip_pre_white(&dst, line_indentation); - tag(dst); - - if (line_indentation >= 0 && _indentation - 1 != line_indentation) { - throw_indentation_error(_indentation - 1, line_indentation); - } - - if (_ptr[0] == '"' || _ptr[0] == '@') { - dst = parse_string(); - } else if (_ptr[0] == 'n') { - parse_assert(_ptr[1] == 'u' && _ptr[2] == 'l' && _ptr[3] == 'l', "Expected 'null'"); - parse_assert(!IDENT_CHARS[static_cast(_ptr[4])], "Expected 'null'"); - _ptr += 4; - dst = nullptr; - } else if (_ptr[0] == 't') { - parse_assert(_ptr[1] == 'r' && _ptr[2] == 'u' && _ptr[3] == 'e', "Expected 'true'"); - parse_assert(!IDENT_CHARS[static_cast(_ptr[4])], "Expected 'true'"); - _ptr += 4; - dst = true; - } else if (_ptr[0] == 'f') { - parse_assert(_ptr[1] == 'a' && _ptr[2] == 'l' && _ptr[3] == 's' && _ptr[4] == 'e', "Expected 'false'"); - parse_assert(!IDENT_CHARS[static_cast(_ptr[5])], "Expected 'false'"); - _ptr += 5; - dst = false; - } else if (_ptr[0] == '{') { - parse_object(dst); - } else if (_ptr[0] == '[') { - parse_array(dst); - } else if (_ptr[0] == '#') { - parse_macro(dst); - } else if (_ptr[0] == '+' || _ptr[0] == '-' || _ptr[0] == '.' || ('0' <= _ptr[0] && _ptr[0] <= '9')) { - // Some kind of number: - - if (_ptr[0] == '-' && _ptr[1] == 'i' && _ptr[2] == 'n' && _ptr[3] == 'f') { - parse_assert(!IDENT_CHARS[static_cast(_ptr[4])], "Expected -inf"); - parse_assert(_options.inf, "infinity forbidden."); - _ptr += 4; - dst = -std::numeric_limits::infinity(); - } else if (_ptr[0] == '+' && _ptr[1] == 'i' && _ptr[2] == 'n' && _ptr[3] == 'f') { - parse_assert(!IDENT_CHARS[static_cast(_ptr[4])], "Expected +inf"); - parse_assert(_options.inf, "infinity forbidden."); - _ptr += 4; - dst = std::numeric_limits::infinity(); - } else if (_ptr[0] == '+' && _ptr[1] == 'N' && _ptr[2] == 'a' && _ptr[3] == 'N') { - parse_assert(!IDENT_CHARS[static_cast(_ptr[4])], "Expected +NaN"); - parse_assert(_options.nan, "NaN (Not a Number) forbidden."); - _ptr += 4; - dst = std::numeric_limits::quiet_NaN(); - } else { - parse_finite_number(dst); - } - } else { - throw_error("Expected value"); - } - - *out_did_skip_postwhites = skip_post_white(&dst); -} - -void -Parser::parse_array(Config &array) -{ - auto state = get_state(); - - swallow('['); - - _indentation += 1; - parse_array_contents(array); - _indentation -= 1; - - if (_ptr[0] == ']') { - _ptr += 1; - } else { - set_state(state); - throw_error("Non-terminated array"); - } -} - -void -Parser::parse_array_contents(Config &array_cfg) -{ - array_cfg.make_array(); - auto &array_impl = array_cfg.as_array(); - - Comments next_prefix_comments; - - for (;;) { - Config value; - if (!next_prefix_comments.empty()) { - std::swap(value.comments().prefix, next_prefix_comments); - } - int line_indentation; - skip_pre_white(&value, line_indentation); - - if (_ptr[0] == ']') { - if (line_indentation >= 0 && _indentation - 1 != line_indentation) { - throw_indentation_error(_indentation - 1, line_indentation); - } - if (value.has_comments()) { - array_cfg.comments().pre_end_brace = value.comments().prefix; - } - break; - } - - if (!_ptr[0]) { - if (value.has_comments()) { - array_cfg.comments().pre_end_brace = value.comments().prefix; - } - break; - } - - if (line_indentation >= 0 && _indentation != line_indentation) { - throw_indentation_error(_indentation, line_indentation); - } - - if (IDENT_STARTERS[static_cast(_ptr[0])] && !is_reserved_identifier(_ptr)) { - throw_error("Found identifier; expected value. Did you mean to use a {object} rather than a [array]?"); - } - - bool has_separator; - parse_value(value, &has_separator); - int ignore; - skip_white(&next_prefix_comments, ignore, false); - - auto comma_state = get_state(); - bool has_comma = _ptr[0] == ','; - - if (has_comma) { - _ptr += 1; - skip_post_white(&value); - has_separator = true; - } - - array_impl.emplace_back(std::move(value)); - - bool is_last_element = !_ptr[0] || _ptr[0] == ']'; - - if (is_last_element) { - parse_assert(!has_comma || _options.array_trailing_comma, "Trailing comma forbidden.", comma_state); - } else { - if (_options.array_omit_comma) { - parse_assert(has_separator, "Expected a space, newline, comma or ]"); - } else { - parse_assert(has_comma, "Expected a comma or ]"); - } - } - } -} - -void -Parser::parse_object(Config &object) -{ - auto state = get_state(); - - swallow('{'); - - _indentation += 1; - parse_object_contents(object); - _indentation -= 1; - - if (_ptr[0] == '}') { - _ptr += 1; - } else { - set_state(state); - throw_error("Non-terminated object"); - } -} - -void -Parser::parse_object_contents(Config &object) -{ - object.make_object(); - - Comments next_prefix_comments; - - for (;;) { - Config value; - if (!next_prefix_comments.empty()) { - std::swap(value.comments().prefix, next_prefix_comments); - } - int line_indentation; - skip_pre_white(&value, line_indentation); - - if (_ptr[0] == '}') { - if (line_indentation >= 0 && _indentation - 1 != line_indentation) { - throw_indentation_error(_indentation - 1, line_indentation); - } - if (value.has_comments()) { - object.comments().pre_end_brace = value.comments().prefix; - } - break; - } - - if (!_ptr[0]) { - if (value.has_comments()) { - object.comments().pre_end_brace = value.comments().prefix; - } - break; - } - - if (line_indentation >= 0 && _indentation != line_indentation) { - throw_indentation_error(_indentation, line_indentation); - } - - auto pre_key_state = get_state(); - std::string key; - - if (IDENT_STARTERS[static_cast(_ptr[0])] && !is_reserved_identifier(_ptr)) { - parse_assert(_options.identifiers_keys, "You need to surround keys with quotes"); - while (IDENT_CHARS[static_cast(_ptr[0])]) { - key += _ptr[0]; - _ptr += 1; - } - } else if (_ptr[0] == '"' || _ptr[0] == '@') { - key = parse_string(); - } else { - throw_error("Object key expected (either an identifier or a quoted string), got " + quote(_ptr[0])); - } - - if (!_options.object_duplicate_keys && object.has_key(key)) { - set_state(pre_key_state); - throw_error("Duplicate key: \"" + key + "\". Already set at " + object[key].where()); - } - - bool space_after_key = skip_white_ignore_comments(); - - if (_ptr[0] == ':' || (_options.object_separator_equal && _ptr[0] == '=')) { - parse_assert(_options.allow_space_before_colon || _ptr[0] != ':' || !space_after_key, "No space allowed before colon"); - _ptr += 1; - skip_white_ignore_comments(); - } else if (_options.omit_colon_before_object && (_ptr[0] == '{' || _ptr[0] == '#')) { - // Ok to omit : in this case - } else { - if (_options.object_separator_equal && _options.omit_colon_before_object) { - throw_error("Expected one of '=', ':', '{' or '#' after object key"); - } else { - throw_error("Expected : after object key"); - } - } - - bool has_separator; - parse_value(value, &has_separator); - int ignore; - skip_white(&next_prefix_comments, ignore, false); - - auto comma_state = get_state(); - bool has_comma = _ptr[0] == ','; - - if (has_comma) { - _ptr += 1; - skip_post_white(&value); - has_separator = true; - } - - object.emplace(std::move(key), std::move(value)); - - bool is_last_element = !_ptr[0] || _ptr[0] == '}'; - - if (is_last_element) { - parse_assert(!has_comma || _options.object_trailing_comma, "Trailing comma forbidden.", comma_state); - } else { - if (_options.object_omit_comma) { - parse_assert(has_separator, "Expected a space, newline, comma or }"); - } else { - parse_assert(has_comma, "Expected a comma or }"); - } - } - } -} - -void -Parser::parse_int(Config &out) -{ - const auto start = _ptr; - const auto result = strtoll(start, const_cast(&_ptr), 10); - parse_assert(start < _ptr, "Invalid integer"); - parse_assert(start[0] != '0' || result == 0, "Integer may not start with a zero"); - out = result; -} - -void -Parser::parse_float(Config &out) -{ - const auto start = _ptr; - const double result = strtod(start, const_cast(&_ptr)); - parse_assert(start < _ptr, "Invalid number"); - out = result; -} - -void -Parser::parse_finite_number(Config &out) -{ - const auto pre_sign = _ptr; - int sign = +1; - - if (_ptr[0] == '+') { - parse_assert(_options.unary_plus, "Prefixing numbers with + is forbidden."); - _ptr += 1; - } - if (_ptr[0] == '-') { - _ptr += 1; - sign = -1; - } - - parse_assert(_ptr[0] != '+' && _ptr[0] != '-', "Duplicate sign"); - - // Check if it's an integer: - if (_ptr[0] == '0' && _ptr[1] == 'x') { - parse_assert(_options.hexadecimal_integers, "Hexadecimal numbers forbidden."); - _ptr += 2; - auto start = _ptr; - out = sign * static_cast(strtoull(start, const_cast(&_ptr), 16)); - parse_assert(start < _ptr, "Missing hexaxdecimal digits after 0x"); - return; - } - - if (_ptr[0] == '0' && _ptr[1] == 'b') { - parse_assert(_options.binary_integers, "Binary numbers forbidden."); - _ptr += 2; - auto start = _ptr; - out = sign * static_cast(strtoull(start, const_cast(&_ptr), 2)); - parse_assert(start < _ptr, "Missing binary digits after 0b"); - return; - } - - const char *p = _ptr; - - while ('0' <= *p && *p <= '9') { - p += 1; - } - - if (*p == '.' || *p == 'e' || *p == 'E') { - _ptr = pre_sign; - return parse_float(out); - } - - // It looks like an integer - but it may be too long to represent as one! - const auto MAX_INT_STR = (sign == +1 ? "9223372036854775807" : "9223372036854775808"); - - const auto length = p - _ptr; - - if (length < 19) { - _ptr = pre_sign; - return parse_int(out); - } - - if (length > 19) { - _ptr = pre_sign; - return parse_float(out); // Uncommon case optimization - } - - // Compare fast: - for (int i = 0; i < 19; ++i) { - if (_ptr[i] > MAX_INT_STR[i]) { - _ptr = pre_sign; - return parse_float(out); - } - if (_ptr[i] < MAX_INT_STR[i]) { - _ptr = pre_sign; - return parse_int(out); - } - } - _ptr = pre_sign; - return parse_int(out); // Exactly max int -} - -std::string -Parser::parse_c_sharp_string() -{ - // C# style verbatim string - everything until the next " except "" which is ": - auto state = get_state(); - parse_assert(_options.str_csharp_verbatim, "C# @-style verbatim strings forbidden."); - swallow('@'); - swallow('"'); - - std::string str; - - for (;;) { - if (_ptr[0] == 0) { - set_state(state); - throw_error("Unterminated verbatim string"); - } else if (_ptr[0] == '\n') { - throw_error("Newline in verbatim string"); - } else if (_ptr[0] == '"' && _ptr[1] == '"') { - // Escaped quote - _ptr += 2; - str.push_back('"'); - } else if (_ptr[0] == '"') { - _ptr += 1; - return str; - } else { - str += _ptr[0]; - _ptr += 1; - } - } -} - -std::string -Parser::parse_string() -{ - if (_ptr[0] == '@') { - return parse_c_sharp_string(); - } - - auto state = get_state(); - parse_assert(_ptr[0] == '"', "Quote (\") expected"); - - if (_ptr[1] == '"' && _ptr[2] == '"') { - // Python style multiline string - everything until the next """: - parse_assert(_options.str_python_multiline, "Python \"\"\"-style multiline strings forbidden."); - _ptr += 3; - const char *start = _ptr; - for (;;) { - if (_ptr[0] == 0 || _ptr[1] == 0 || _ptr[2] == 0) { - set_state(state); - throw_error("Unterminated multiline string"); - } - - if (_ptr[0] == '"' && _ptr[1] == '"' && _ptr[2] == '"' && _ptr[3] != '"') { - std::string str(start, _ptr); - _ptr += 3; - return str; - } - - if (_ptr[0] == '\n') { - _ptr += 1; - _line_nr += 1; - _line_start = _ptr; - } else { - _ptr += 1; - } - } - } else { - // Normal string - _ptr += 1; // Swallow quote - - std::string str; - - for (;;) { - // Handle larges swats of safe characters at once: - auto safe_end = _ptr; - while (!SPECIAL_CHARACTERS[static_cast(*safe_end)]) { - ++safe_end; - } - - if (_ptr != safe_end) { - str.append(_ptr, safe_end - _ptr); - _ptr = safe_end; - } - - if (_ptr[0] == 0) { - set_state(state); - throw_error("Unterminated string"); - } - if (_ptr[0] == '"') { - _ptr += 1; - return str; - } - if (_ptr[0] == '\n') { - throw_error("Newline in string"); - } - if (_ptr[0] == '\t') { - parse_assert(_options.str_allow_tab, "Un-escaped tab not allowed in string"); - } - - if (_ptr[0] == '\\') { - // Escape sequence - _ptr += 1; - - if (_ptr[0] == '"') { - str.push_back('"'); - _ptr += 1; - } else if (_ptr[0] == '\\') { - str.push_back('\\'); - _ptr += 1; - } else if (_ptr[0] == '/') { - str.push_back('/'); - _ptr += 1; - } else if (_ptr[0] == 'b') { - str.push_back('\b'); - _ptr += 1; - } else if (_ptr[0] == 'f') { - str.push_back('\f'); - _ptr += 1; - } else if (_ptr[0] == 'n') { - str.push_back('\n'); - _ptr += 1; - } else if (_ptr[0] == 'r') { - str.push_back('\r'); - _ptr += 1; - } else if (_ptr[0] == 't') { - str.push_back('\t'); - _ptr += 1; - } else if (_ptr[0] == 'u') { - // Four hexadecimal characters - _ptr += 1; - uint64_t codepoint = parse_hex(4); - - if (0xD800 <= codepoint && codepoint <= 0xDBFF) { - // surrogate pair - parse_assert(_ptr[0] == '\\' && _ptr[1] == 'u', "Missing second unicode surrogate."); - _ptr += 2; - uint64_t codepoint2 = parse_hex(4); - parse_assert(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF, "Invalid second unicode surrogate"); - codepoint = (codepoint << 10) + codepoint2 - 0x35FDC00; - } - - auto num_bytes_written = encode_utf8(str, codepoint); - parse_assert(num_bytes_written > 0, "Bad unicode codepoint"); - } else if (_ptr[0] == 'U') { - // Eight hexadecimal characters - parse_assert(_options.str_32bit_unicode, "\\U 32 bit unicodes forbidden."); - _ptr += 1; - uint64_t unicode = parse_hex(8); - auto num_bytes_written = encode_utf8(str, unicode); - parse_assert(num_bytes_written > 0, "Bad unicode codepoint"); - } else { - throw_error("Unknown escape character " + quote(_ptr[0])); - } - } else { - str.push_back(_ptr[0]); - _ptr += 1; - } - } - } -} - -uint64_t -Parser::parse_hex(int count) -{ - uint64_t ret = 0; - for (int i = 0; i < count; ++i) { - ret *= 16; - char c = _ptr[i]; - if ('0' <= c && c <= '9') { - ret += static_cast(c - '0'); - } else if ('a' <= c && c <= 'f') { - ret += static_cast(10 + c - 'a'); - } else if ('A' <= c && c <= 'F') { - ret += static_cast(10 + c - 'A'); - } else { - throw_error("Expected hexadecimal digit, got " + quote(_ptr[0])); - } - } - _ptr += count; - return ret; -} - -void -Parser::parse_macro(Config &dst) -{ - parse_assert(_options.allow_macro, "#macros forbidden."); - - swallow("#include", "Expected '#include'"); - skip_white_ignore_comments(); - - bool absolute; - char terminator; - - if (_ptr[0] == '"') { - absolute = false; - terminator = '"'; - } else if (_ptr[0] == '<') { - absolute = true; - terminator = '>'; - } else { - throw_error("Expected \" or <"); - } - - auto state = get_state(); - _ptr += 1; - auto start = _ptr; - std::string path; - for (;;) { - if (_ptr[0] == 0) { - set_state(state); - throw_error("Unterminated include path"); - } else if (_ptr[0] == terminator) { - path = std::string(start, static_cast(_ptr - start)); - _ptr += 1; - break; - } else if (_ptr[0] == '\n') { - throw_error("Newline in string"); - } else { - _ptr += 1; - } - } - - if (!absolute) { - auto my_path = _doc->filename; - auto pos = my_path.find_last_of('/'); - if (pos != std::string::npos) { - auto my_dir = my_path.substr(0, pos + 1); - path = my_dir + path; - } - } - - auto it = _info.parsed_files.find(path); - if (it == _info.parsed_files.end()) { - auto child_doc = std::make_shared(path); - child_doc->includers.emplace_back(_doc, _line_nr); - dst = parse_file(path.c_str(), _options, child_doc, _info); - _info.parsed_files[path] = dst; - } else { - auto child_doc = it->second.doc(); - child_doc->includers.emplace_back(_doc, _line_nr); - dst = it->second; - } -} - -// ---------------------------------------------------------------------------------------- - -Config -parse_string(const char *str, const FormatOptions &options, DocInfo_SP doc, ParseInfo &info) -{ - Parser p(str, options, doc, info); - return p.top_level(); -} - -Config -parse_string(const char *str, const FormatOptions &options, const char *name) -{ - ParseInfo info; - return parse_string(str, options, std::make_shared(name), info); -} - -std::string -read_text_file(const char *path) -{ - FILE *fp = fopen(path, "rb"); - if (fp == nullptr) { - CONFIGURU_ONERROR(std::string("Failed to open '") + path + "' for reading: " + strerror(errno)); - } - std::string contents; - fseek(fp, 0, SEEK_END); - const auto size = ftell(fp); - if (size < 0) { - fclose(fp); - CONFIGURU_ONERROR(std::string("Failed to find out size of '") + path + "': " + strerror(errno)); - } - contents.resize(static_cast(size)); - rewind(fp); - const auto num_read = fread(&contents[0], 1, contents.size(), fp); - fclose(fp); - if (num_read != contents.size()) { - CONFIGURU_ONERROR(std::string("Failed to read from '") + path + "': " + strerror(errno)); - } - return contents; -} - -Config -parse_file(const std::string &path, const FormatOptions &options, DocInfo_SP doc, ParseInfo &info) -{ - // auto file = util::FILEWrapper::read_text_file(path); - auto file = read_text_file(path.c_str()); - return parse_string(file.c_str(), options, doc, info); -} - -Config -parse_file(const std::string &path, const FormatOptions &options) -{ - ParseInfo info; - return parse_file(path, options, std::make_shared(path), info); -} -} - -// ---------------------------------------------------------------------------- -// Yb dP 88""Yb 88 888888 888888 88""Yb -// Yb db dP 88__dP 88 88 88__ 88__dP -// YbdPYbdP 88"Yb 88 88 88"" 88"Yb -// YP YP 88 Yb 88 88 888888 88 Yb - -#include // strtod - -namespace configuru -{ -bool -is_identifier(const char *p) -{ - if (*p == '_' || ('a' <= *p && *p <= 'z') || ('A' <= *p && *p <= 'Z')) { - ++p; - while (*p) { - if (*p == '_' || ('a' <= *p && *p <= 'z') || ('A' <= *p && *p <= 'Z') || ('0' <= *p && *p <= '9')) { - ++p; - } else { - return false; - } - } - return true; - } else { - return false; - } -} - -bool -has_pre_end_brace_comments(const Config &cfg) -{ - return cfg.has_comments() && !cfg.comments().pre_end_brace.empty(); -} - -struct Writer { - std::string _out; - bool _compact; - FormatOptions _options; - bool SAFE_CHARACTERS[256]; - DocInfo_SP _doc; - - Writer(const FormatOptions &options, DocInfo_SP doc) : _options(options), _doc(std::move(doc)) - { - _compact = _options.compact(); - - for (int i = 0; i < 256; ++i) { - SAFE_CHARACTERS[i] = i >= 0x20; - } - - SAFE_CHARACTERS[static_cast('\\')] = false; - SAFE_CHARACTERS[static_cast('\"')] = false; - SAFE_CHARACTERS[static_cast('\0')] = false; - SAFE_CHARACTERS[static_cast('\b')] = false; - SAFE_CHARACTERS[static_cast('\f')] = false; - SAFE_CHARACTERS[static_cast('\n')] = false; - SAFE_CHARACTERS[static_cast('\r')] = false; - SAFE_CHARACTERS[static_cast('\t')] = false; - } - - inline void - write_indent(unsigned indent) - { - if (_compact) { - return; - } - for (unsigned i = 0; i < indent; ++i) { - _out += _options.indentation; - } - } - - void - write_prefix_comments(unsigned indent, const Comments &comments) - { - if (!_options.write_comments) { - return; - } - if (!comments.empty()) { - _out.push_back('\n'); - for (auto &&c : comments) { - write_indent(indent); - _out += c; - _out.push_back('\n'); - } - } - } - - void - write_prefix_comments(unsigned indent, const Config &cfg) - { - if (!_options.write_comments) { - return; - } - if (cfg.has_comments()) { - write_prefix_comments(indent, cfg.comments().prefix); - } - } - - void - write_postfix_comments(unsigned indent, const Comments &comments) - { - if (!_options.write_comments) { - return; - } - (void)indent; // TODO: reindent comments - for (auto &&c : comments) { - _out.push_back(' '); - ; - _out += c; - } - } - - void - write_pre_brace_comments(unsigned indent, const Comments &comments) - { - write_prefix_comments(indent, comments); - } - - void - write_value(unsigned indent, const Config &config, bool write_prefix, bool write_postfix) - { - if (_options.allow_macro && config.doc() && config.doc() != _doc) { - dump_file(config.doc()->filename, config, _options); - _out += "#include <"; - _out += config.doc()->filename; - _out.push_back('>'); - return; - } - - if (write_prefix) { - write_prefix_comments(indent, config); - } - - if (config.is_null()) { - _out += "null"; - } else if (config.is_bool()) { - _out += (config.as_bool() ? "true" : "false"); - } else if (config.is_int()) { - char temp_buff[64]; - snprintf(temp_buff, sizeof(temp_buff), "%lld", static_cast(config)); - _out += temp_buff; - } else if (config.is_float()) { - write_number(config.as_double()); - } else if (config.is_string()) { - write_string(config.as_string()); - } else if (config.is_array()) { - if (config.array_size() == 0 && !has_pre_end_brace_comments(config)) { - if (_compact) { - _out += "[]"; - } else { - _out += "[ ]"; - } - } else if (_compact || is_simple_array(config)) { - _out.push_back('['); - if (!_compact) { - _out.push_back(' '); - } - auto &&array = config.as_array(); - for (size_t i = 0; i < array.size(); ++i) { - write_value(indent + 1, array[i], false, true); - if (_compact) { - if (i + 1 < array.size()) { - _out.push_back(','); - } - } else if (_options.array_omit_comma || i + 1 == array.size()) { - _out.push_back(' '); - } else { - _out += ", "; - } - } - write_pre_brace_comments(indent + 1, config.comments().pre_end_brace); - _out += "]"; - } else { - _out += "[\n"; - auto &&array = config.as_array(); - for (size_t i = 0; i < array.size(); ++i) { - write_prefix_comments(indent + 1, array[i]); - write_indent(indent + 1); - write_value(indent + 1, array[i], false, true); - if (_options.array_omit_comma || i + 1 == array.size()) { - _out.push_back('\n'); - } else { - _out += ",\n"; - } - } - write_pre_brace_comments(indent + 1, config.comments().pre_end_brace); - write_indent(indent); - _out += "]"; - } - } else if (config.is_object()) { - if (config.object_size() == 0 && !has_pre_end_brace_comments(config)) { - if (_compact) { - _out += "{}"; - } else { - _out += "{ }"; - } - } else { - if (_compact) { - _out.push_back('{'); - } else { - _out += "{\n"; - } - write_object_contents(indent + 1, config); - write_indent(indent); - _out.push_back('}'); - } - } else { - if (_options.write_uninitialized) { - _out += "UNINITIALIZED"; - } else { - CONFIGURU_ONERROR("Failed to serialize uninitialized Config"); - } - } - - if (write_postfix) { - write_postfix_comments(indent, config.comments().postfix); - } - } - - void - write_object_contents(unsigned indent, const Config &config) - { - // Write in same order as input: - auto &&object = config.as_object()._impl; - - using ObjIterator = Config::ConfigObjectImpl::const_iterator; - std::vector pairs; - pairs.reserve(object.size()); - - size_t longest_key = 0; - bool align_values = !_compact && _options.object_align_values; - - for (auto it = object.begin(); it != object.end(); ++it) { - pairs.push_back(it); - if (align_values) { - longest_key = (std::max)(longest_key, it->first.size()); - } - } - - if (_options.sort_keys) { - std::sort(begin(pairs), end(pairs), [](const ObjIterator &a, const ObjIterator &b) { return a->first < b->first; }); - } else { - std::sort(begin(pairs), end(pairs), [](const ObjIterator &a, const ObjIterator &b) { return a->second._nr < b->second._nr; }); - } - - size_t i = 0; - for (auto &&it : pairs) { - auto &&value = it->second._value; - write_prefix_comments(indent, value); - write_indent(indent); - write_key(it->first); - if (_compact) { - _out.push_back(':'); - } else if (_options.omit_colon_before_object && value.is_object() && value.object_size() != 0) { - _out.push_back(' '); - } else { - _out += ": "; - if (align_values) { - for (size_t j = it->first.size(); j < longest_key; ++j) { - _out.push_back(' '); - } - } - } - write_value(indent, value, false, true); - if (_compact) { - if (i + 1 < pairs.size()) { - _out.push_back(','); - } - } else if (_options.array_omit_comma || i + 1 == pairs.size()) { - _out.push_back('\n'); - } else { - _out += ",\n"; - } - i += 1; - } - - write_pre_brace_comments(indent, config.comments().pre_end_brace); - } - - void - write_key(const std::string &str) - { - if (_options.identifiers_keys && is_identifier(str.c_str())) { - _out += str; - } else { - write_string(str); - } - } - - void - write_number(double val) - { - if (_options.distinct_floats && val == 0 && std::signbit(val)) { - _out += "-0.0"; - return; - } - - const auto as_int = static_cast(val); - if (static_cast(as_int) == val) { - char temp_buff[64]; - snprintf(temp_buff, sizeof(temp_buff), "%lld", as_int); - _out += temp_buff; - if (_options.distinct_floats) { - _out += ".0"; - } - return; - } - - if (std::isfinite(val)) { - char temp_buff[64]; - - const auto as_float = static_cast(val); - if (static_cast(as_float) == val) { - // It's actually a float! - snprintf(temp_buff, sizeof(temp_buff), "%g", as_float); - if (std::strtof(temp_buff, nullptr) == as_float) { - _out += temp_buff; - } else { - snprintf(temp_buff, sizeof(temp_buff), "%.8g", as_float); - _out += temp_buff; - } - return; - } - - // Try single digit of precision (for denormals): - snprintf(temp_buff, sizeof(temp_buff), "%.1g", val); - if (std::strtod(temp_buff, nullptr) == val) { - _out += temp_buff; - return; - } - - // Try default digits of precision: - snprintf(temp_buff, sizeof(temp_buff), "%g", val); - if (std::strtod(temp_buff, nullptr) == val) { - _out += temp_buff; - return; - } - - // Try 16 digits of precision: - snprintf(temp_buff, sizeof(temp_buff), "%.16g", val); - if (std::strtod(temp_buff, nullptr) == val) { - _out += temp_buff; - return; - } - - // Nope, full 17 digits needed: - snprintf(temp_buff, sizeof(temp_buff), "%.17g", val); - _out += temp_buff; - } else if (val == +std::numeric_limits::infinity()) { - if (!_options.inf) { - CONFIGURU_ONERROR("Can't encode infinity"); - } - _out += "+inf"; - } else if (val == -std::numeric_limits::infinity()) { - if (!_options.inf) { - CONFIGURU_ONERROR("Can't encode negative infinity"); - } - _out += "-inf"; - } else { - if (!_options.nan) { - CONFIGURU_ONERROR("Can't encode NaN"); - } - _out += "+NaN"; - } - } - - void - write_string(const std::string &str) - { - const size_t LONG_LINE = 240; - - if (!_options.str_python_multiline || str.find('\n') == std::string::npos || str.length() < LONG_LINE || - str.find("\"\"\"") != std::string::npos) { - write_quoted_string(str); - } else { - write_verbatim_string(str); - } - } - - void - write_hex_digit(unsigned num) - { - CONFIGURU_ASSERT(num < 16u); - if (num < 10u) { - _out.push_back(char('0' + num)); - } else { - _out.push_back(char('a' + num - 10)); - } - } - - void - write_hex_16(uint16_t n) - { - write_hex_digit((n >> 12) & 0x0f); - write_hex_digit((n >> 8) & 0x0f); - write_hex_digit((n >> 4) & 0x0f); - write_hex_digit((n >> 0) & 0x0f); - } - - void - write_unicode_16(uint16_t c) - { - _out += "\\u"; - write_hex_16(c); - } - - void - write_quoted_string(const std::string &str) - { - _out.push_back('"'); - - const char *ptr = str.c_str(); - const char *end = ptr + str.size(); - while (ptr < end) { - // Output large swats of safe characters at once: - auto start = ptr; - while (SAFE_CHARACTERS[static_cast(*ptr)]) { - ++ptr; - } - if (start < ptr) { - _out.append(start, ptr - start); - } - if (ptr == end) { - break; - } - - char c = *ptr; - ++ptr; - if (c == '\\') { - _out += "\\\\"; - } else if (c == '\"') { - _out += "\\\""; - } - // else if (c == '\'') { _out += "\\\'"; } - else if (c == '\0') { - _out += "\\0"; - } else if (c == '\b') { - _out += "\\b"; - } else if (c == '\f') { - _out += "\\f"; - } else if (c == '\n') { - _out += "\\n"; - } else if (c == '\r') { - _out += "\\r"; - } else if (c == '\t') { - _out += "\\t"; - } else /*if (0 <= c && c < 0x20)*/ { - write_unicode_16(static_cast(c)); - } - } - - _out.push_back('"'); - } - - void - write_verbatim_string(const std::string &str) - { - _out += "\"\"\""; - _out += str; - _out += "\"\"\""; - } - - bool - is_simple(const Config &var) - { - if (var.is_array() && var.array_size() > 0) { - return false; - } - if (var.is_object() && var.object_size() > 0) { - return false; - } - if (_options.write_comments && var.has_comments()) { - return false; - } - return true; - } - - bool - is_all_numbers(const Config &array) - { - for (auto &v : array.as_array()) { - if (!v.is_number()) { - return false; - } - } - return true; - } - - bool - is_simple_array(const Config &array) - { - if (array.array_size() <= 16 && is_all_numbers(array)) { - return true; // E.g., a 4x4 matrix - } - - if (array.array_size() > 4) { - return false; - } - size_t estimated_width = 0; - for (auto &v : array.as_array()) { - if (!is_simple(v)) { - return false; - } - if (v.is_string()) { - estimated_width += 2 + v.as_string().size(); - } else { - estimated_width += 5; - } - estimated_width += 2; - } - return estimated_width < 60; - } -}; // struct Writer - -std::string -dump_string(const Config &config, const FormatOptions &options) -{ - Writer w(options, config.doc()); - - if (options.implicit_top_object && config.is_object()) { - w.write_object_contents(0, config); - } else { - w.write_value(0, config, true, true); - - if (options.end_with_newline && !options.compact()) { - w._out.push_back('\n'); // Good form - } - } - - if (options.mark_accessed) { - config.mark_accessed(true); - } - return std::move(w._out); -} - -static void -write_text_file(const char *path, const std::string &data) -{ - auto fp = fopen(path, "wb"); - if (fp == nullptr) { - CONFIGURU_ONERROR(std::string("Failed to open '") + path + "' for writing: " + strerror(errno)); - } - auto num_bytes_written = fwrite(data.data(), 1, data.size(), fp); - fclose(fp); - if (num_bytes_written != data.size()) { - CONFIGURU_ONERROR(std::string("Failed to write to '") + path + "': " + strerror(errno)); - } -} - -void -dump_file(const std::string &path, const configuru::Config &config, const FormatOptions &options) -{ - auto str = dump_string(config, options); - write_text_file(path.c_str(), str); -} -} // namespace configuru - -// ---------------------------------------------------------------------------- diff --git a/plugins/experimental/fastcgi/src/connection_pool.cc b/plugins/experimental/fastcgi/src/connection_pool.cc deleted file mode 100644 index d8bdeb8a3b3..00000000000 --- a/plugins/experimental/fastcgi/src/connection_pool.cc +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ts/ts.h" -#include "ats_fastcgi.h" -#include "connection_pool.h" -#include "server_connection.h" -#include "server.h" - -using namespace ats_plugin; -ConnectionPool::ConnectionPool(Server *server, TSEventFunc funcp) - : _server(server), _funcp(funcp), _availableConn_mutex(TSMutexCreate()), _conn_mutex(TSMutexCreate()) -{ - // TODO: For now we are setting maxConn as hard coded values - ats_plugin::FcgiPluginConfig *gConfig = InterceptGlobal::plugin_data->getGlobalConfigObj(); - _maxConn = gConfig->getMaxConnLength() / 6; -} - -ConnectionPool::~ConnectionPool() -{ - Dbg(dbg_ctl, "Destroying connectionPool Obj..."); - TSMutexDestroy(_availableConn_mutex); - TSMutexDestroy(_conn_mutex); -} - -int -ConnectionPool::checkAvailability() -{ - return _available_connections.size(); -} - -ServerConnection * -ConnectionPool::getAvailableConnection() -{ - ServerConnection *conn = nullptr; - if (!_available_connections.empty() && _connections.size() >= _maxConn) { - Dbg(dbg_ctl, "%s: available connections %ld", __FUNCTION__, _available_connections.size()); - conn = _available_connections.front(); - _available_connections.pop_front(); - conn->setState(ServerConnection::READY); - Dbg(dbg_ctl, "%s: available connections %ld. Connection from available pool, %p", __FUNCTION__, _available_connections.size(), - conn); - } - - if (_connections.size() < _maxConn) { - Dbg(dbg_ctl, "%s: Setting up new connection, maxConn: %d", __FUNCTION__, _maxConn); - conn = new ServerConnection(_server, _funcp); - addConnection(conn); - } - return conn; -} - -void -ConnectionPool::addConnection(ServerConnection *connection) -{ - _connections.push_back(connection); -} - -void -ConnectionPool::reuseConnection(ServerConnection *connection) -{ - connection->readio.readEnable = false; - connection->writeio.readEnable = false; - - connection->setState(ServerConnection::READY); - _available_connections.push_back(connection); - Dbg(dbg_ctl, "%s: Connection added, available connections %ld", __FUNCTION__, _available_connections.size()); -} - -void -ConnectionPool::connectionClosed(ServerConnection *connection) -{ - _available_connections.remove(connection); - _connections.remove(connection); - delete connection; -} diff --git a/plugins/experimental/fastcgi/src/connection_pool.h b/plugins/experimental/fastcgi/src/connection_pool.h deleted file mode 100644 index d7325d7083d..00000000000 --- a/plugins/experimental/fastcgi/src/connection_pool.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "ts/ts.h" - -namespace ats_plugin -{ -class Server; -class ServerConnection; - -// Connection Pool class which creates a pool of connections when certain -// threshold is reached. Possibly used connections also can be re-added to pool -// if connection does not close. - -class ConnectionPool -{ -public: - ConnectionPool(Server *server, TSEventFunc funcp); - ~ConnectionPool(); - - ServerConnection *getAvailableConnection(); - int checkAvailability(); - - void addConnection(ServerConnection *); - void reuseConnection(ServerConnection *connection); - void connectionClosed(ServerConnection *connection); - -private: - void createConnections(); - uint _maxConn; - Server *_server; - TSEventFunc _funcp; - TSMutex _availableConn_mutex, _conn_mutex; - std::list _available_connections; - std::list _connections; -}; -} // namespace ats_plugin diff --git a/plugins/experimental/fastcgi/src/fcgi_config.cc b/plugins/experimental/fastcgi/src/fcgi_config.cc deleted file mode 100644 index f425bbffb26..00000000000 --- a/plugins/experimental/fastcgi/src/fcgi_config.cc +++ /dev/null @@ -1,804 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "fcgi_config.h" -#include -#include -#include -#include -#include -#include -#include - -static char DEFAULT_HOSTNAME[] = "localhost"; -static char DEFAULT_SERVER_IP[] = "127.0.0.1"; -static char DEFAULT_SERVER_PORT[] = "60000"; -static char DEFAULT_INCLUDE_FILE[] = "fastcgi.config"; -static char DEFAULT_DOCUMENT_ROOT[] = "/var/www/html/"; -static char DEFAULT_HTML[] = "index.php"; -static int DEFAULT_MIN_CONNECTION = 4; -static int DEFAULT_MAX_CONNECTION = 10; -static int DEFAULT_MAX_REQUEST = 1000; -static int DEFAULT_REQUEST_QUEUE_SIZE = 250; -using namespace ats_plugin; -inline TSRecordDataType -str_to_datatype(char *str) -{ - TSRecordDataType type = TS_RECORDDATATYPE_NULL; - - if (!str || !*str) { - return TS_RECORDDATATYPE_NULL; - } - - if (!strcmp(str, "INT")) { - type = TS_RECORDDATATYPE_INT; - } else if (!strcmp(str, "STRING")) { - type = TS_RECORDDATATYPE_STRING; - } - - return type; -} -bool -FcgiPluginConfig::getFcgiEnabledStatus() -{ - return enabled; -} -void -FcgiPluginConfig::setFcgiEnabledStatus(bool val) -{ - enabled = val; -} - -TSMgmtString -FcgiPluginConfig::getHostname() -{ - return hostname; -} -void -FcgiPluginConfig::setHostname(char *str) -{ - hostname = str; -} -TSMgmtString -FcgiPluginConfig::getServerIp() -{ - return server_ip; -} -void -FcgiPluginConfig::setServerIp(char *str) -{ - server_ip = str; -} -TSMgmtString -FcgiPluginConfig::getServerPort() -{ - return server_port; -} -void -FcgiPluginConfig::setServerPort(char *str) -{ - server_port = str; -} - -TSMgmtString -FcgiPluginConfig::getIncludeFilePath() -{ - return include; -} -void -FcgiPluginConfig::setIncludeFilePath(char *str) -{ - include = str; -} -FCGIParams * -FcgiPluginConfig::getFcgiParams() -{ - return params; -} -void -FcgiPluginConfig::setFcgiParams(FCGIParams *params) -{ - this->params = params; -} -TSMgmtString -FcgiPluginConfig::getDocumentRootDir() -{ - return document_root; -} -void -FcgiPluginConfig::setDocumentRootDir(char *str) -{ - document_root = str; -} - -TSMgmtString -FcgiPluginConfig::getHtml() -{ - return html; -} - -void -FcgiPluginConfig::setHtml(char *str) -{ - html = str; -} - -TSMgmtInt -FcgiPluginConfig::getMinConnLength() -{ - return min_connections; -} -void -FcgiPluginConfig::setMinConnLength(int64_t minLen) -{ - min_connections = minLen; -} -TSMgmtInt -FcgiPluginConfig::getMaxConnLength() -{ - return max_connections; -} -void -FcgiPluginConfig::setMaxConnLength(int64_t maxLen) -{ - max_connections = maxLen; -} - -TSMgmtInt -FcgiPluginConfig::getMaxReqLength() -{ - return max_requests; -} - -void -FcgiPluginConfig::setMaxReqLength(int64_t maxLen) -{ - max_requests = maxLen; -} - -TSMgmtInt -FcgiPluginConfig::getRequestQueueSize() -{ - return request_queue_size; -} -void -FcgiPluginConfig::setRequestQueueSize(int64_t queueSize) -{ - request_queue_size = queueSize; -} - -static TSReturnCode -fcgiHttpTxnConfigFind(const char *name, int length, FcgiConfigKey *conf, TSRecordDataType *type) -{ - *type = TS_RECORDDATATYPE_NULL; - if (length == -1) { - length = strlen(name); - } - - if (!strncmp(name, "proxy.config.http.fcgi.enabled", length)) { - *conf = fcgiEnabled; - *type = TS_RECORDDATATYPE_INT; - return TS_SUCCESS; - } - - if (!strncmp(name, "proxy.config.http.fcgi.host.hostname", length)) { - *conf = fcgiHostname; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - - if (!strncmp(name, "proxy.config.http.fcgi.host.server_ip", length)) { - *conf = fcgiServerIp; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - - if (!strncmp(name, "proxy.config.http.fcgi.host.server_port", length)) { - *conf = fcgiServerPort; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - - if (!strncmp(name, "proxy.config.http.fcgi.host.include", length)) { - *conf = fcgiInclude; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "proxy.config.http.fcgi.host.document_root", length)) { - *conf = fcgiDocumentRoot; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "proxy.config.http.fcgi.host.html", length)) { - *conf = fcgiHtml; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "proxy.config.http.fcgi.host.min_connections", length)) { - *conf = fcgiMinConnections; - *type = TS_RECORDDATATYPE_INT; - return TS_SUCCESS; - } - if (!strncmp(name, "proxy.config.http.fcgi.host.max_connections", length)) { - *conf = fcgiMaxConnections; - *type = TS_RECORDDATATYPE_INT; - return TS_SUCCESS; - } - if (!strncmp(name, "proxy.config.http.fcgi.host.max_requests", length)) { - *conf = fcgiMaxRequests; - *type = TS_RECORDDATATYPE_INT; - return TS_SUCCESS; - } - if (!strncmp(name, "proxy.config.http.fcgi.host.request_queue_size", length)) { - *conf = fcgiRequestQueueSize; - *type = TS_RECORDDATATYPE_INT; - return TS_SUCCESS; - } - - return TS_ERROR; -} -static TSReturnCode -fcgiParamConfigFind(const char *name, int length, FcgiParamKey *conf, TSRecordDataType *type) -{ - *type = TS_RECORDDATATYPE_NULL; - if (length == -1) { - length = strlen(name); - } - if (!strncmp(name, "GATEWAY_INTERFACE", length)) { - *conf = gatewayInterface; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "SERVER_SOFTWARE", length)) { - *conf = serverSoftware; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "QUERY_STRING", length)) { - *conf = queryString; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "REQUEST_METHOD", length)) { - *conf = requestMethod; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "CONTENT_TYPE", length)) { - *conf = contentType; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "CONTENT_LENGTH", length)) { - *conf = contentLength; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "SCRIPT_FILENAME", length)) { - *conf = scriptFilename; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "SCRIPT_NAME", length)) { - *conf = scriptName; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "REQUEST_URI", length)) { - *conf = requestUri; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "DOCUMENT_URI", length)) { - *conf = documentUri; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "DOCUMENT_ROOT", length)) { - *conf = documentRoot; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "SERVER_PROTOCOL", length)) { - *conf = serverProtocol; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "REMOTE_ADDR", length)) { - *conf = remoteAddr; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "REMOTE_PORT", length)) { - *conf = remotePort; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "SERVER_ADDR", length)) { - *conf = serverAddr; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "SERVER_PORT", length)) { - *conf = serverPort; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - if (!strncmp(name, "SERVER_NAME", length)) { - *conf = serverName; - *type = TS_RECORDDATATYPE_STRING; - return TS_SUCCESS; - } - - return TS_ERROR; -} - -static void -initFcgiParam(const char *fn, FCGIParams *fcgiParams) -{ - int line_num = 0; - TSFile file; - char buf[8192]; - FcgiParamKey name; - TSRecordDataType type, expected_type; - if (nullptr == (file = TSfopen(fn, "r"))) { - TSError("[ats_fastcgi] Could not open fcgiParam.config file %s", fn); - } else { - while (nullptr != TSfgets(file, buf, sizeof(buf))) { - char *ln, *tok; - char *s = buf; - - ++line_num; // First line is #1 ... - while (isspace(*s)) { - ++s; - } - tok = strtok_r(s, " \t", &ln); - - // check for blank lines and comments - if ((!tok) || (tok && ('#' == *tok))) { - continue; - } - - if (strncmp(tok, "fastcgi_param", 13)) { - TSError("[ats_fastcgi] File %s, line %d: non-CONFIG line encountered", fn, line_num); - continue; - } - - // Find the configuration name - tok = strtok_r(nullptr, " \t", &ln); - if (fcgiParamConfigFind(tok, -1, &name, &expected_type) != TS_SUCCESS) { - TSError("[ats_fastcgi] File %s, line %d: no ats_fastcgi.config name given", fn, line_num); - continue; - } - - // Find the type (INT or STRING only) - tok = strtok_r(nullptr, " \t", &ln); - if (TS_RECORDDATATYPE_NULL == (type = str_to_datatype(tok))) { - TSError("[ats_fastcgi] File %s, line %d: only INT and STRING types supported", fn, line_num); - continue; - } - - if (type != expected_type) { - TSError("[ats_fastcgi] File %s, line %d: mismatch between provide data type, and expected type", fn, line_num); - continue; - } - - // Find the value (which depends on the type above) - if (ln) { - while (isspace(*ln)) { - ++ln; - } - - if ('\0' == *ln) { - tok = nullptr; - } else { - tok = ln; - - while (*ln != '\0') { - ++ln; - } - - --ln; - - while (isspace(*ln) && (ln > tok)) { - --ln; - } - - ++ln; - *ln = '\0'; - } - } else { - tok = nullptr; - } - - if (!tok) { - TSError("[ats_fastcgi] File %s, line %d: the configuration must provide a value", fn, line_num); - continue; - } - // Now store the new config - switch (name) { - case gatewayInterface: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("GATEWAY_INTERFACE", "")); - } else { - fcgiParams->insert(std::pair("GATEWAY_INTERFACE", tok)); - } - break; - case serverSoftware: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("SERVER_SOFTWARE", "")); - } else { - fcgiParams->insert(std::pair("SERVER_SOFTWARE", tok)); - } - break; - case queryString: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("QUERY_STRING", "")); - } else { - fcgiParams->insert(std::pair("QUERY_STRING", tok)); - } - break; - case requestMethod: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("REQUEST_METHOD", "")); - } else { - fcgiParams->insert(std::pair("REQUEST_METHOD", tok)); - } - break; - case contentType: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("CONTENT_TYPE", "")); - } else { - fcgiParams->insert(std::pair("CONTENT_TYPE", tok)); - } - break; - case contentLength: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("CONTENT_LENGTH", "")); - } else { - fcgiParams->insert(std::pair("CONTENT_LENGTH", tok)); - } - break; - case scriptFilename: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("SCRIPT_FILENAME", "")); - } else { - fcgiParams->insert(std::pair("SCRIPT_FILENAME", tok)); - } - break; - case scriptName: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("SCRIPT_NAME", "")); - } else { - fcgiParams->insert(std::pair("SCRIPT_NAME", tok)); - } - break; - case requestUri: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("REQUEST_URI", "")); - } else { - fcgiParams->insert(std::pair("REQUEST_URI", tok)); - } - break; - case documentUri: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("DOCUMENT_URI", "")); - } else { - fcgiParams->insert(std::pair("DOCUMENT_URI", tok)); - } - break; - case documentRoot: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("DOCUMENT_ROOT", "")); - } else { - fcgiParams->insert(std::pair("DOCUMENT_ROOT", tok)); - } - break; - case serverProtocol: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("SERVER_PROTOCOL", "")); - } else { - fcgiParams->insert(std::pair("SERVER_PROTOCOL", tok)); - } - break; - case remoteAddr: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("REMOTE_ADDR", "")); - } else { - fcgiParams->insert(std::pair("REMOTE_ADDR", tok)); - } - break; - case remotePort: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("REMOTE_PORT", "")); - } else { - fcgiParams->insert(std::pair("REMOTE_PORT", tok)); - } - break; - case serverAddr: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("SERVER_ADDR", "")); - } else { - fcgiParams->insert(std::pair("SERVER_ADDR", tok)); - } - break; - case serverPort: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("SERVER_PORT", "")); - } else { - fcgiParams->insert(std::pair("SERVER_PORT", tok)); - } - break; - case serverName: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - fcgiParams->insert(std::pair("SERVER_NAME", "")); - } else { - fcgiParams->insert(std::pair("SERVER_NAME", tok)); - } - break; - default: - break; - } - } - - TSfclose(file); - } -} - -FcgiPluginConfig * -FcgiPluginConfig::initConfig(const char *fn) -{ - InterceptPluginData *plugin_data = new InterceptPluginData(); - FcgiPluginConfig *config = new FcgiPluginConfig(); // static_cast(TSmalloc(sizeof(FcgiPluginConfig))); - // Default config - Dbg(dbg_ctl, "Setting config..."); - if (plugin_data || nullptr == plugin_data->getGlobalConfigObj()) { - config->enabled = true; - config->hostname = DEFAULT_HOSTNAME; - config->server_ip = DEFAULT_SERVER_IP; - config->server_port = DEFAULT_SERVER_PORT; - config->include = DEFAULT_INCLUDE_FILE; - config->document_root = DEFAULT_DOCUMENT_ROOT; - config->html = DEFAULT_HTML; - config->params = new FCGIParams(); - config->max_connections = DEFAULT_MAX_CONNECTION; - config->min_connections = DEFAULT_MIN_CONNECTION; - config->max_requests = DEFAULT_MAX_REQUEST; - config->request_queue_size = DEFAULT_REQUEST_QUEUE_SIZE; - } else { - // Inherit from global config - FcgiPluginConfig *global_config = plugin_data->getGlobalConfigObj(); - config->enabled = global_config->getFcgiEnabledStatus(); - config->hostname = TSstrdup(global_config->getHostname()); - config->server_ip = TSstrdup(global_config->getServerIp()); - config->server_port = TSstrdup(global_config->getServerPort()); - config->include = TSstrdup(global_config->getIncludeFilePath()); - config->params = new FCGIParams(); - config->document_root = TSstrdup(global_config->getDocumentRootDir()); - config->html = TSstrdup(global_config->getHtml()); - config->max_connections = global_config->getMaxConnLength(); - config->min_connections = global_config->getMinConnLength(); - config->max_requests = global_config->getMaxReqLength(); - config->request_queue_size = global_config->getRequestQueueSize(); - } - - if (fn) { - if (1 == strlen(fn)) { - if (0 == strcmp("0", fn)) { - config->enabled = false; - } else if (0 == strcmp("1", fn)) { - config->enabled = true; - } else { - TSError("[ats_fastcgi] Parameter '%s' ignored", fn); - } - } else { - int line_num = 0; - TSFile file; - char buf[8192]; - FcgiConfigKey name; - TSRecordDataType type, expected_type; - if (nullptr == (file = TSfopen(fn, "r"))) { - TSError("[ats_fastcgi] Could not open config file %s", fn); - } else { - while (nullptr != TSfgets(file, buf, sizeof(buf))) { - char *ln, *tok; - char *s = buf; - - ++line_num; // First line is #1 ... - while (isspace(*s)) { - ++s; - } - tok = strtok_r(s, " \t", &ln); - - // check for blank lines and comments - if ((!tok) || (tok && ('#' == *tok))) { - continue; - } - - if (strncmp(tok, "CONFIG", 6)) { - TSError("[ats_fastcgi] File %s, line %d: non-CONFIG line encountered", fn, line_num); - continue; - } - - // Find the configuration name - tok = strtok_r(nullptr, " \t", &ln); - if (fcgiHttpTxnConfigFind(tok, -1, &name, &expected_type) != TS_SUCCESS) { - TSError("[ats_fastcgi] File %s, line %d: no records.yaml name given", fn, line_num); - continue; - } - - // Find the type (INT or STRING only) - tok = strtok_r(nullptr, " \t", &ln); - if (TS_RECORDDATATYPE_NULL == (type = str_to_datatype(tok))) { - TSError("[ats_fastcgi] File %s, line %d: only INT and STRING " - "types supported", - fn, line_num); - continue; - } - - if (type != expected_type) { - TSError("[ats_fastcgi] File %s, line %d: mismatch between provide " - "data type, and expected type", - fn, line_num); - continue; - } - - // Find the value (which depends on the type above) - if (ln) { - while (isspace(*ln)) { - ++ln; - } - - if ('\0' == *ln) { - tok = nullptr; - } else { - tok = ln; - - while (*ln != '\0') { - ++ln; - } - - --ln; - - while (isspace(*ln) && (ln > tok)) { - --ln; - } - - ++ln; - *ln = '\0'; - } - } else { - tok = nullptr; - } - - if (!tok) { - TSError("[ats_fastcgi] File %s, line %d: the configuration must " - "provide a value", - fn, line_num); - continue; - } - // Now store the new config - switch (name) { - case fcgiEnabled: - config->enabled = tok; - break; - - case fcgiHostname: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - config->hostname = nullptr; - } else { - config->hostname = TSstrdup(tok); - } - break; - - case fcgiServerIp: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - config->server_ip = nullptr; - } else { - config->server_ip = TSstrdup(tok); - } - break; - - case fcgiServerPort: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - config->server_port = nullptr; - } else { - config->server_port = TSstrdup(tok); - } - break; - - case fcgiInclude: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - config->include = nullptr; - Dbg(dbg_ctl, "Failed to load FCGIParams config file."); - } else { - config->include = TSstrdup(tok); - // This will read fcgiparams from config file and stored in config->params map - FCGIParams *params = config->params; - initFcgiParam(config->include, params); - Dbg(dbg_ctl, "Reading fcgiParams config from %s file complete.", config->include); - // FCGIParams::iterator it = params->begin(); - // std::cout << "mymap contains:\n"; - // for (it = params->begin(); it != params->end(); ++it) - // std::cout << it->first << " => " << it->second << '\n'; - } - - break; - case fcgiDocumentRoot: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - config->document_root = nullptr; - } else { - config->document_root = TSstrdup(tok); - } - break; - case fcgiHtml: - if (4 == strlen(tok) && 0 == strcmp(tok, "NULL")) { - config->html = nullptr; - } else { - config->html = TSstrdup(tok); - } - break; - - case fcgiMinConnections: { - config->min_connections = strtoll(tok, nullptr, 10); - Dbg(dbg_ctl, "min_connections = %ld", config->min_connections); - } break; - - case fcgiMaxConnections: { - config->max_connections = strtoll(tok, nullptr, 10); - Dbg(dbg_ctl, "max_connections = %ld", config->max_connections); - } break; - case fcgiMaxRequests: { - config->max_requests = strtoll(tok, nullptr, 10); - Dbg(dbg_ctl, "max_requests = %ld", config->max_requests); - } break; - case fcgiRequestQueueSize: { - config->request_queue_size = strtoll(tok, nullptr, 10); - Dbg(dbg_ctl, "request_queue_size = %ld", config->request_queue_size); - } break; - default: - break; - } - } - - TSfclose(file); - } - } - } - - Dbg(dbg_ctl, "enabled = %d", static_cast(config->enabled)); - Dbg(dbg_ctl, "hostname = %s", config->hostname); - Dbg(dbg_ctl, "server_ip = %s", config->server_ip); - Dbg(dbg_ctl, "server_port = %s", config->server_port); - Dbg(dbg_ctl, "include = %s", config->include); - Dbg(dbg_ctl, "document_root = %s", config->document_root); - Dbg(dbg_ctl, "html = %s", config->html); - return config; -} - -// Getter setter of global config obj -FcgiPluginConfig * -InterceptPluginData::getGlobalConfigObj() -{ - return global_config; -} -void -InterceptPluginData::setGlobalConfigObj(FcgiPluginConfig *config) -{ - global_config = config; -} diff --git a/plugins/experimental/fastcgi/src/fcgi_config.h b/plugins/experimental/fastcgi/src/fcgi_config.h deleted file mode 100644 index e3c381eda4f..00000000000 --- a/plugins/experimental/fastcgi/src/fcgi_config.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#define PLUGIN_NAME "ats_fastcgi" -#define PLUGIN_VENDOR "Apache Software Foundation" -#define PLUGIN_SUPPORT "dev@trafficserver.apache.org" -#pragma once -namespace ats_plugin -{ -enum FcgiConfigKey { - fcgiEnabled, - fcgiHostname, - fcgiServerIp, - fcgiServerPort, - fcgiInclude, - fcgiDocumentRoot, - fcgiHtml, - fcgiMinConnections, - fcgiMaxConnections, - fcgiMaxRequests, - fcgiRequestQueueSize -}; -enum FcgiParamKey { - gatewayInterface, - serverSoftware, - queryString, - requestMethod, - contentType, - contentLength, - scriptFilename, - scriptName, - requestUri, - documentUri, - documentRoot, - serverProtocol, - remoteAddr, - remotePort, - serverAddr, - serverPort, - serverName -}; - -using UintMap = std::map; -using FCGIParams = std::map; - -class FcgiPluginConfig -{ - bool enabled; - TSMgmtString hostname; - TSMgmtString server_ip; - TSMgmtString server_port; - TSMgmtString include; - FCGIParams *params; - TSMgmtString document_root; - TSMgmtString html; - TSMgmtInt min_connections, max_connections, max_requests, request_queue_size; - -public: - FcgiPluginConfig() - : enabled(true), - hostname(nullptr), - server_ip(nullptr), - server_port(nullptr), - include(nullptr), - params(nullptr), - document_root(nullptr), - html(nullptr), - min_connections(0), - max_connections(0), - max_requests(0), - request_queue_size(0) - { - } - - ~FcgiPluginConfig() - { - hostname = nullptr; - server_ip = nullptr; - server_port = nullptr; - include = nullptr; - document_root = nullptr; - html = nullptr; - min_connections = 0; - max_connections = 0; - max_requests = 0; - request_queue_size = 0; - } - - FcgiPluginConfig *initConfig(const char *fn); - bool getFcgiEnabledStatus(); - void setFcgiEnabledStatus(bool val); - - TSMgmtString getHostname(); - void setHostname(char *str); - TSMgmtString getServerIp(); - void setServerIp(char *str); - TSMgmtString getServerPort(); - void setServerPort(char *str); - TSMgmtString getIncludeFilePath(); - void setIncludeFilePath(char *str); - FCGIParams *getFcgiParams(); - void setFcgiParams(FCGIParams *params); - TSMgmtString getDocumentRootDir(); - void setDocumentRootDir(char *str); - TSMgmtString getHtml(); - void setHtml(char *str); - TSMgmtInt getMinConnLength(); - void setMinConnLength(int64_t minLen); - TSMgmtInt getMaxConnLength(); - void setMaxConnLength(int64_t maxLen); - TSMgmtInt getMaxReqLength(); - void setMaxReqLength(int64_t maxLen); - TSMgmtInt getRequestQueueSize(); - void setRequestQueueSize(int64_t queueSize); -}; - -class InterceptPluginData -{ - UintMap *active_hash_map; - TSMutex mutex; - uint64_t seq_id; - int txn_slot; - FcgiPluginConfig *global_config; - TSHRTime last_gc_time; - bool read_while_writer; - int tol_global_hook_reqs; - int tol_remap_hook_reqs; - int tol_non_cacheable_reqs; - int tol_got_passed_reqs; - -public: - InterceptPluginData() - : active_hash_map(nullptr), - mutex(nullptr), - seq_id(0), - txn_slot(0), - global_config(nullptr), - last_gc_time(0), - read_while_writer(false), - tol_global_hook_reqs(0), - tol_remap_hook_reqs(0), - tol_non_cacheable_reqs(0), - tol_got_passed_reqs(0){ - // Dbg(dbg_ctl, "FCGIPluginData Initialised."); - }; - ~InterceptPluginData(); - FcgiPluginConfig *getGlobalConfigObj(); - void setGlobalConfigObj(FcgiPluginConfig *config); -}; -} // namespace ats_plugin diff --git a/plugins/experimental/fastcgi/src/fcgi_protocol.h b/plugins/experimental/fastcgi/src/fcgi_protocol.h deleted file mode 100644 index 1bbac1ba6a4..00000000000 --- a/plugins/experimental/fastcgi/src/fcgi_protocol.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - Header file declaring FCGI Specification - More info @http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html -*/ - -#pragma once - -#define FCGI_LISTENSOCK_FILENO 0 - -struct FCGI_Header { - unsigned char version; - unsigned char type; - unsigned char requestIdB1; - unsigned char requestIdB0; - unsigned char contentLengthB1; - unsigned char contentLengthB0; - unsigned char paddingLength; - unsigned char reserved; -}; - -#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 - -struct FCGI_BeginRequestBody { - unsigned char roleB1; - unsigned char roleB0; - unsigned char flags; - unsigned char reserved[5]; -}; - -struct FCGI_BeginRequest { - FCGI_Header *header; - FCGI_BeginRequestBody *body; -}; - -/* - * 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 - -struct FCGI_EndRequestBody { - unsigned char appStatusB3; - unsigned char appStatusB2; - unsigned char appStatusB1; - unsigned char appStatusB0; - unsigned char protocolStatus; - unsigned char reserved[3]; -}; - -struct FCGI_EndRequest { - FCGI_Header header; - FCGI_EndRequestBody body; -}; - -/* - * 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" - -struct FCGI_UnknownTypeBody { - unsigned char type; - unsigned char reserved[7]; -}; - -struct FCGI_UnknownTypeRequest { - FCGI_Header header; - FCGI_UnknownTypeBody body; -}; diff --git a/plugins/experimental/fastcgi/src/request_queue.cc b/plugins/experimental/fastcgi/src/request_queue.cc deleted file mode 100644 index 7d359ea563d..00000000000 --- a/plugins/experimental/fastcgi/src/request_queue.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "request_queue.h" -#include "ats_fastcgi.h" -using namespace ats_plugin; - -RequestQueue::RequestQueue() -{ - mutex = TSMutexCreate(); - FcgiPluginConfig *gConfig = InterceptGlobal::plugin_data->getGlobalConfigObj(); - max_queue_size = gConfig->getRequestQueueSize(); -} - -RequestQueue::~RequestQueue() -{ - max_queue_size = 0; - TSMutexDestroy(mutex); -} - -uint -RequestQueue::isQueueFull() -{ - if (pending_list.size() >= max_queue_size) { - return 1; - } else { - return 0; - } -} - -uint -RequestQueue::getSize() -{ - return pending_list.size(); -} - -uint -RequestQueue::isQueueEmpty() -{ - if (pending_list.empty()) { - return 1; - } else { - return 0; - } -} - -uint -RequestQueue::addToQueue(ServerIntercept *intercept) -{ - // TSMutexLock(mutex); - if (!isQueueFull()) { - pending_list.push(intercept); - } - // TODO: handle queue full use case - // TSMutexUnlock(mutex); - return 1; -} - -ServerIntercept * -RequestQueue::popFromQueue() -{ - ServerIntercept *intercept = nullptr; - // TSMutexLock(mutex); - if (!pending_list.empty()) { - intercept = pending_list.front(); - pending_list.pop(); - } - // TSMutexUnlock(mutex); - return intercept; -} diff --git a/plugins/experimental/fastcgi/src/request_queue.h b/plugins/experimental/fastcgi/src/request_queue.h deleted file mode 100644 index 63fd251630f..00000000000 --- a/plugins/experimental/fastcgi/src/request_queue.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "server_intercept.h" -#include "fcgi_config.h" -#include -namespace ats_plugin -{ -class ServerIntercept; -class RequestQueue -{ - TSMutex mutex; - uint max_queue_size; - std::queue pending_list; - -public: - RequestQueue(); - ~RequestQueue(); - - uint isQueueFull(); - uint isQueueEmpty(); - uint getSize(); - uint addToQueue(ServerIntercept *); - ServerIntercept *popFromQueue(); -}; -} // namespace ats_plugin diff --git a/plugins/experimental/fastcgi/src/server.cc b/plugins/experimental/fastcgi/src/server.cc deleted file mode 100644 index 36d09ecad4a..00000000000 --- a/plugins/experimental/fastcgi/src/server.cc +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "server.h" - -#include - -#include "server_intercept.h" -#include "server_connection.h" -#include "connection_pool.h" -#include "ats_fastcgi.h" -using namespace ats_plugin; - -uint UniqueRequesID::_id = 1; - -bool -interceptTransferData(ServerIntercept *intercept, ServerConnection *server_conn) -{ - TSIOBufferBlock block; - int64_t consumed = 0; - bool responseStatus = false; - std::ostringstream output; - - // Walk the list of buffer blocks in from the read VIO. - for (block = TSIOBufferReaderStart(server_conn->readio.reader); block; block = TSIOBufferBlockNext(block)) { - int64_t remain = 0; - const char *ptr; - ptr = TSIOBufferBlockReadStart(block, server_conn->readio.reader, &remain); - - if (remain) { - responseStatus = server_conn->fcgiRequest()->fcgiDecodeRecordChunk((uchar *)ptr, remain, output); - } - consumed += remain; - } - - if (consumed) { - Dbg(dbg_ctl, "[%s] Read %ld bytes from server and writing it to client side.", __FUNCTION__, consumed); - TSIOBufferReaderConsume(server_conn->readio.reader, consumed); - } - TSVIONDoneSet(server_conn->readio.vio, TSVIONDoneGet(server_conn->readio.vio) + consumed); - - std::string data = std::move(output.str()); - // TODO(oschaaf): check this if statement. - if (data.size()) { - // std::cout << "Output: " << data << std::endl; - intercept->writeResponseChunkToATS(data); - } - return responseStatus; -} - -static int -handlePHPConnectionEvents(TSCont contp, TSEvent event, void *edata) -{ - Dbg(dbg_ctl, "[%s]: event( %d )\tEventName: %s\tContp: %p ", __FUNCTION__, event, TSHttpEventNameLookup(event), contp); - ServerConnectionInfo *conn_info = static_cast(TSContDataGet(contp)); - Server *server = conn_info->server; - ServerConnection *server_connection = conn_info->server_connection; - - switch (event) { - case TS_EVENT_NET_CONNECT: { - TSStatIntIncrement(InterceptGlobal::phpConnCount, 1); - server_connection->vc_ = static_cast(edata); - server_connection->setState(ServerConnection::READY); - Dbg(dbg_ctl, "%s: New Connection success, %p", __FUNCTION__, server_connection); - ServerIntercept *intercept = server->getIntercept(server_connection->requestId()); - if (intercept) { - server_connection->createFCGIClient(intercept); - } - - } break; - - case TS_EVENT_NET_CONNECT_FAILED: { - // Try reconnection with new connection. - // TODO: Have to stop trying to reconnect after some tries - TSStatIntIncrement(InterceptGlobal::phpConnCount, 1); - server->reConnect(server_connection->requestId()); - server_connection->setState(ServerConnection::CLOSED); - server->connectionClosed(server_connection); - return TS_EVENT_NONE; - } break; - - case TS_EVENT_VCONN_READ_READY: { - ServerIntercept *intercept = server->getIntercept(server_connection->requestId()); - if (intercept && interceptTransferData(intercept, server_connection)) { - server_connection->setState(ServerConnection::COMPLETE); - intercept->setResponseOutputComplete(); - TSStatIntIncrement(InterceptGlobal::respBegId, 1); - // TSContCall(TSVIOContGet(server_connection->readio.vio), TS_EVENT_VCONN_READ_COMPLETE, server_connection->readio.vio); - } - } break; - - case TS_EVENT_VCONN_READ_COMPLETE: { - Dbg(dbg_ctl, "[%s]: ResponseComplete...Sending Response to client stream. _request_id: %d", __FUNCTION__, - server_connection->requestId()); - ServerIntercept *intercept = server->getIntercept(server_connection->requestId()); - server_connection->setState(ServerConnection::COMPLETE); - intercept->setResponseOutputComplete(); - TSStatIntIncrement(InterceptGlobal::respBegId, 1); - } break; - - case TS_EVENT_VCONN_WRITE_READY: { - if (server_connection->writeio.readEnable) { - TSContCall(TSVIOContGet(server_connection->writeio.vio), TS_EVENT_VCONN_WRITE_COMPLETE, server_connection->writeio.vio); - } - - } break; - - case TS_EVENT_VCONN_WRITE_COMPLETE: { - TSStatIntIncrement(InterceptGlobal::reqEndId, 1); - server_connection->readio.read(server_connection->vc_, server_connection->contp()); - } break; - - case TS_EVENT_VCONN_EOS: { - ServerIntercept *intercept = server->getIntercept(server_connection->requestId()); - if (!server_connection->writeio.readEnable) { - Dbg(dbg_ctl, "[%s]: EOS Request Failed. _request_id: %d, connection: %p,maxConn: %d, requestCount: %d", __FUNCTION__, - server_connection->requestId(), server_connection, server_connection->maxRequests(), server_connection->requestCount()); - - server->reConnect(server_connection->requestId()); - server_connection->setState(ServerConnection::CLOSED); - server->connectionClosed(server_connection); - break; - } - - if (server_connection->getState() != ServerConnection::COMPLETE) { - if (intercept && !intercept->getOutputCompleteState()) { - Dbg(dbg_ctl, "[%s]: EOS intercept->setResponseOutputComplete, _request_id: %d, connection: %p", __FUNCTION__, - server_connection->requestId(), server_connection); - Transaction &transaction = utils::internal::getTransaction(intercept->_txn); - transaction.error("Internal server error"); - } - } - server_connection->setState(ServerConnection::CLOSED); - server->connectionClosed(server_connection); - } break; - - case TS_EVENT_ERROR: { - TSVConnAbort(server_connection->vc_, 1); - ServerIntercept *intercept = server->getIntercept(server_connection->requestId()); - if (intercept) { - Dbg(dbg_ctl, "[%s]:ERROR intercept->setResponseOutputComplete", __FUNCTION__); - server_connection->setState(ServerConnection::CLOSED); - Transaction &transaction = utils::internal::getTransaction(intercept->_txn); - transaction.setStatusCode(HTTP_STATUS_BAD_GATEWAY); - transaction.error("Internal server error"); - } - - server->connectionClosed(server_connection); - } break; - - default: - break; - } - - return TS_EVENT_NONE; -} - -void -ThreadData::createConnectionPool(Server *server) -{ - _connection_pool = new ConnectionPool(server, handlePHPConnectionEvents); -} - -Server * -Server::server() -{ - return InterceptGlobal::gServer; -} - -Server::Server() : _reqId_mutex(TSMutexCreate()), _intecept_mutex(TSMutexCreate()) {} - -bool -Server::setupThreadLocalStorage() -{ - int result = 0; - if ((result = pthread_key_create(&InterceptGlobal::threadKey, nullptr)) == 0) { - ThreadData *threadData; - if ((threadData = static_cast(pthread_getspecific(InterceptGlobal::threadKey))) == nullptr) { - threadData = new ThreadData(this); - if (pthread_setspecific(InterceptGlobal::threadKey, threadData)) { - Dbg(dbg_ctl, "[Server:%s] Unable to set threadData to the key", __FUNCTION__); - pthread_key_delete(InterceptGlobal::threadKey); - InterceptGlobal::threadKey = 0; - return false; - } - - TSStatIntIncrement(InterceptGlobal::threadCount, 1); - Dbg(dbg_ctl, "[Server:%s] Data is set for this thread [threadData]%p [threadID]%lu", __FUNCTION__, threadData, - pthread_self()); - } - - return true; - } - - Dbg(dbg_ctl, "[Server:%s] Could not create key", __FUNCTION__); - return false; -} - -ServerIntercept * -Server::getIntercept(uint request_id) -{ - TSMutexLock(_intecept_mutex); - auto itr = _intercept_list.find(request_id); - if (itr != _intercept_list.end()) { - TSMutexUnlock(_intecept_mutex); - return std::get<0>(itr->second); - } - TSMutexUnlock(_intecept_mutex); - return nullptr; -} - -ServerConnection * -Server::getServerConnection(uint request_id) -{ - TSMutexLock(_intecept_mutex); - auto itr = _intercept_list.find(request_id); - if (itr != _intercept_list.end()) { - TSMutexUnlock(_intecept_mutex); - return std::get<1>(itr->second); - } - TSMutexUnlock(_intecept_mutex); - return nullptr; -} - -void -Server::removeIntercept(uint request_id) -{ - ThreadData *tdata = static_cast(pthread_getspecific(InterceptGlobal::threadKey)); - TSMutexLock(_intecept_mutex); - auto itr = _intercept_list.find(request_id); - if (itr != _intercept_list.end()) { - ServerConnection *serv_conn = std::get<1>(itr->second); - - _intercept_list.erase(itr); - TSMutexUnlock(_intecept_mutex); - Dbg(dbg_ctl, "[Server:%s] ReqQueueLength:%d ,request_id: %d,ServerConn: %p ,max_requests: %d, req_count: %d ", __FUNCTION__, - tdata->getRequestQueue()->getSize(), serv_conn->requestId(), serv_conn, serv_conn->maxRequests(), - serv_conn->requestCount()); - - serv_conn->releaseFCGIClient(); - serv_conn->setRequestId(0); - // TODO(oschaaf): fix hang and re-enable. - if (false && serv_conn->maxRequests() > serv_conn->requestCount()) { - tdata->getConnectionPool()->reuseConnection(serv_conn); - } else { - serv_conn->setState(ServerConnection::CLOSED); - connectionClosed(serv_conn); - } - - ServerIntercept *intercept = tdata->getRequestQueue()->popFromQueue(); - if (intercept) { - connect(intercept); - } - - return; - } - TSMutexUnlock(_intecept_mutex); - return; -} - -bool -Server::writeRequestHeader(uint request_id) -{ - ServerConnection *server_conn = getServerConnection(request_id); - if (!server_conn) { - return false; - } - - Dbg(dbg_ctl, "[Server::%s] : Write Request Header: _request_id: %d,ServerConn: %p", __FUNCTION__, request_id, server_conn); - - FCGIClientRequest *fcgiRequest = server_conn->fcgiRequest(); - unsigned char *clientReq; - int reqLen = 0; - // TODO: possibly move all this as one function in server_connection - fcgiRequest->createBeginRequest(); - clientReq = fcgiRequest->addClientRequest(reqLen); - bool endflag = false; - server_conn->writeio.phpWrite(server_conn->vc_, server_conn->contp(), clientReq, reqLen, endflag); - return true; -} - -bool -Server::writeRequestBody(uint request_id, const string &data) -{ - ServerConnection *server_conn = getServerConnection(request_id); - if (!server_conn) { - return false; - } - - Dbg(dbg_ctl, "[Server::%s] : Write Request Body: request_id: %d,Server_conn: %p", __FUNCTION__, request_id, server_conn); - FCGIClientRequest *fcgiRequest = server_conn->fcgiRequest(); - // TODO: possibly move all this as one function in server_connection - unsigned char *clientReq; - int reqLen = 0; - fcgiRequest->postData = data; - fcgiRequest->postBodyChunk(); - clientReq = fcgiRequest->addClientRequest(reqLen); - bool endflag = false; - server_conn->writeio.phpWrite(server_conn->vc_, server_conn->contp(), clientReq, reqLen, endflag); - return true; -} - -bool -Server::writeRequestBodyComplete(uint request_id) -{ - ServerConnection *server_conn = getServerConnection(request_id); - if (!server_conn) { - return false; - } - - Dbg(dbg_ctl, "[Server::%s] : Write Request Complete: request_id: %d,Server_conn: %p", __FUNCTION__, request_id, server_conn); - FCGIClientRequest *fcgiRequest = server_conn->fcgiRequest(); - // TODO: possibly move all this as one function in server_connection - unsigned char *clientReq; - int reqLen = 0; - fcgiRequest->emptyParam(); - clientReq = fcgiRequest->addClientRequest(reqLen); - bool endflag = true; - server_conn->writeio.phpWrite(server_conn->vc_, server_conn->contp(), clientReq, reqLen, endflag); - return true; -} - -const uint -Server::connect(ServerIntercept *intercept) -{ - // Get connections from thread Local storage and use it or store it in thread Queue - ThreadData *tdata = static_cast(pthread_getspecific(InterceptGlobal::threadKey)); - if (tdata) { - ServerConnection *conn = nullptr; - conn = tdata->getConnectionPool()->getAvailableConnection(); - if (conn) { - initiateBackendConnection(intercept, conn); - return 0; - } - Dbg(dbg_ctl, "[Server:%s] : Added to RequestQueue. QueueSize: %d", __FUNCTION__, tdata->getRequestQueue()->getSize()); - tdata->getRequestQueue()->addToQueue(intercept); - return 0; - } - return 1; -} - -void -Server::reConnect(uint request_id) -{ - ServerIntercept *intercept = getIntercept(request_id); - if (intercept) { - TSMutexLock(_intecept_mutex); - _intercept_list.erase(request_id); - TSMutexUnlock(_intecept_mutex); - connect(intercept); - Dbg(dbg_ctl, "[Server:%s]: Initiating reconnection...", __FUNCTION__); - } -} - -void -Server::initiateBackendConnection(ServerIntercept *intercept, ServerConnection *conn) -{ - TSMutexLock(_reqId_mutex); - const uint request_id = UniqueRequesID::getNext(); - TSMutexUnlock(_reqId_mutex); - - intercept->setRequestId(request_id); - conn->setRequestId(request_id); - - TSMutexLock(_intecept_mutex); - _intercept_list[request_id] = std::make_tuple(intercept, conn); - TSMutexUnlock(_intecept_mutex); - - Dbg(dbg_ctl, "[Server: %s] ServerConn: %p,_request_id: %d", __FUNCTION__, conn, request_id); - if (conn->getState() != ServerConnection::READY) { - Dbg(dbg_ctl, "[Server: %s] Setting up a new php Connection..", __FUNCTION__); - conn->createConnection(); - return; - } - - conn->createFCGIClient(intercept); - return; -} - -void -Server::connectionClosed(ServerConnection *server_conn) -{ - TSMutexLock(_intecept_mutex); - auto itr = _intercept_list.find(server_conn->requestId()); - if (itr != _intercept_list.end()) { - _intercept_list.erase(itr); - } - TSMutexUnlock(_intecept_mutex); - ThreadData *tdata = static_cast(pthread_getspecific(InterceptGlobal::threadKey)); - tdata->getConnectionPool()->connectionClosed(server_conn); - TSStatIntDecrement(InterceptGlobal::phpConnCount, 1); -} diff --git a/plugins/experimental/fastcgi/src/server.h b/plugins/experimental/fastcgi/src/server.h deleted file mode 100644 index 184cf58388d..00000000000 --- a/plugins/experimental/fastcgi/src/server.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "server_intercept.h" -#include "request_queue.h" -#include -#include -namespace ats_plugin -{ -class ServerIntercept; -class ServerConnection; -class ConnectionPool; -class Server; - -class UniqueRequesID -{ -public: - static const uint - getNext() - { - // TODO add mutext here - return _id++; - } - -private: - static uint _id; -}; - -struct ServerConnectionInfo { -public: - ServerConnectionInfo(Server *fServer, ServerConnection *server_conn) : server(fServer), server_connection(server_conn) {} - ~ServerConnectionInfo() {} - Server *server; - ServerConnection *server_connection; -}; - -class ThreadData -{ -public: - ThreadData(ThreadData const &) = delete; - void operator=(ThreadData const &) = delete; - - ThreadData(Server *server) : _server(server) - { - tid = pthread_self(); - createConnectionPool(_server); - _pendingReqQueue = new RequestQueue(); - } - ~ThreadData() - { - delete _pendingReqQueue; - // delete _connection_pool; - } - void createConnectionPool(Server *server); - - ConnectionPool * - getConnectionPool() - { - return _connection_pool; - } - - RequestQueue * - getRequestQueue() - { - return _pendingReqQueue; - } - -private: - pthread_t tid; - Server *_server; - RequestQueue *_pendingReqQueue; - ConnectionPool *_connection_pool; -}; - -class Server -{ -public: - static Server *server(); - Server(Server const &) = delete; - void operator=(Server const &) = delete; - - Server(); - ~Server(); - - bool setupThreadLocalStorage(); - const uint connect(ServerIntercept *intercept); - void reConnect(uint request_id); - ServerConnection *getServerConnection(uint request_id); - - ServerIntercept *getIntercept(uint request_id); - void removeIntercept(uint request_id); - - bool writeRequestHeader(uint request_id); - bool writeRequestBody(uint request_id, const std::string &data); - bool writeRequestBodyComplete(uint request_id); - - void connectionClosed(ServerConnection *server_conn); - -private: - void initiateBackendConnection(ServerIntercept *intercept, ServerConnection *conn); - std::map> _intercept_list; - TSMutex _reqId_mutex; - TSMutex _intecept_mutex; -}; -} // namespace ats_plugin diff --git a/plugins/experimental/fastcgi/src/server_connection.cc b/plugins/experimental/fastcgi/src/server_connection.cc deleted file mode 100644 index e547157adb4..00000000000 --- a/plugins/experimental/fastcgi/src/server_connection.cc +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "server_connection.h" - -#include "ats_fcgi_client.h" -#include "ats_fastcgi.h" -using namespace ats_plugin; -InterceptIOChannel::InterceptIOChannel() : vio(nullptr), iobuf(nullptr), reader(nullptr), total_bytes_written(0), readEnable(false) -{ -} -InterceptIOChannel::~InterceptIOChannel() -{ - if (this->reader) { - TSIOBufferReaderFree(this->reader); - } - - if (this->iobuf) { - TSIOBufferDestroy(this->iobuf); - } - vio = nullptr; - total_bytes_written = 0; -} - -void -InterceptIOChannel::read(TSVConn vc, TSCont contp) -{ - if (TSVConnClosedGet(vc)) { - TSError("[InterceptIOChannel:%s] Connection Closed...", __FUNCTION__); - return; - } - if (!this->iobuf) { - this->iobuf = TSIOBufferCreate(); - this->reader = TSIOBufferReaderAlloc(this->iobuf); - this->vio = TSVConnRead(vc, contp, this->iobuf, INT64_MAX); - if (this->vio == nullptr) { - TSError("[InterceptIOChannel:%s] ERROR While reading from server", __FUNCTION__); - return; - } - Dbg(dbg_ctl, "[InterceptIOChannel:%s] ReadIO.vio :%p ", __FUNCTION__, this->vio); - } -} - -void -InterceptIOChannel::write(TSVConn vc, TSCont contp) -{ - TSReleaseAssert(this->vio == nullptr); - TSReleaseAssert((this->iobuf = TSIOBufferCreate())); - TSReleaseAssert((this->reader = TSIOBufferReaderAlloc(this->iobuf))); - if (TSVConnClosedGet(contp)) { - TSError("[%s] Connection Closed...", __FUNCTION__); - return; - } - this->vio = TSVConnWrite(vc, contp, this->reader, INT64_MAX); -} - -void -InterceptIOChannel::phpWrite(TSVConn vc, TSCont contp, unsigned char *buf, int data_size, bool endflag) -{ - if (TSVConnClosedGet(vc)) { - TSError("[InterceptIOChannel:%s] Connection Closed...", __FUNCTION__); - return; - } - - if (!this->iobuf) { - this->iobuf = TSIOBufferCreate(); - this->reader = TSIOBufferReaderAlloc(this->iobuf); - this->vio = TSVConnWrite(vc, contp, this->reader, INT64_MAX); - if (this->vio == nullptr) { - TSError("[InterceptIOChannel:%s] Error TSVIO returns null. ", __FUNCTION__); - return; - } - } - - int num_bytes_written = TSIOBufferWrite(this->iobuf, (const void *)buf, data_size); - if (num_bytes_written != data_size) { - TSError("[InterceptIOChannel:%s] Error while writing to buffer! Attempted %d bytes but only " - "wrote %d bytes", - PLUGIN_NAME, data_size, num_bytes_written); - return; - } - - total_bytes_written += data_size; - if (!endflag) { - TSMutexLock(TSVIOMutexGet(vio)); - TSVIOReenable(this->vio); - TSMutexUnlock(TSVIOMutexGet(vio)); - return; - } - - this->readEnable = true; - Dbg(dbg_ctl, "[%s] Done: %ld \tnBytes: %ld", __FUNCTION__, TSVIONDoneGet(this->vio), TSVIONBytesGet(this->vio)); -} - -ServerConnection::ServerConnection(Server *server, TSEventFunc funcp) - : vc_(nullptr), - _fcgiRequest(nullptr), - _state(INITIATED), - _server(server), - _funcp(funcp), - _contp(nullptr), - _sConnInfo(nullptr), - _requestId(0), - _max_requests(0), - _req_count(0) -{ - ats_plugin::FcgiPluginConfig *gConfig = InterceptGlobal::plugin_data->getGlobalConfigObj(); - _max_requests = gConfig->getMaxReqLength(); -} - -ServerConnection::~ServerConnection() -{ - Dbg(dbg_ctl, "Destroying server Connection Obj.ServerConn: %p ,request_id: %d,max_requests: %d, req_count: %d ", this, _requestId, - _max_requests, _req_count); - - if (vc_) { - TSVConnClose(vc_); - vc_ = nullptr; - } - // XXX(oschaaf): check commented line below. - // readio.vio = writeio.vio = nullptr; - _requestId = 0; - _max_requests = 0; - _req_count = 0; - TSContDestroy(_contp); - _contp = nullptr; - if (_fcgiRequest != nullptr) { - delete _fcgiRequest; - } - delete _sConnInfo; -} - -void -ServerConnection::createFCGIClient(ServerIntercept *intercept) -{ - if (_state == READY || _state == COMPLETE) { - Transaction &transaction = utils::internal::getTransaction(intercept->_txn); - transaction.addPlugin(intercept); - transaction.resume(); - _fcgiRequest = new FCGIClientRequest(_requestId, intercept->_txn); - _state = INUSE; - _req_count++; - } -} - -void -ServerConnection::releaseFCGIClient() -{ - if (_state == COMPLETE) { - Dbg(dbg_ctl, "[ServerConnection:%s] Release FCGI resource of ServerConn: %p ,request_id: %d,max_requests: %d, req_count: %d ", - __FUNCTION__, this, _requestId, _max_requests, _req_count); - delete _fcgiRequest; - _fcgiRequest = nullptr; - _state = READY; - } -} - -void -ServerConnection::createConnection() -{ - struct sockaddr_in ip_addr; - unsigned short int a, b, c, d, p; - char *arr = InterceptGlobal::plugin_data->getGlobalConfigObj()->getServerIp(); - char *port = InterceptGlobal::plugin_data->getGlobalConfigObj()->getServerPort(); - sscanf(arr, "%hu.%hu.%hu.%hu", &a, &b, &c, &d); - sscanf(port, "%hu", &p); - int new_ip = (a << 24) | (b << 16) | (c << 8) | (d); - memset(&ip_addr, 0, sizeof(ip_addr)); - ip_addr.sin_family = AF_INET; - ip_addr.sin_addr.s_addr = htonl(new_ip); /* Should be in network byte order */ - ip_addr.sin_port = htons(p); // server_port; - - // contp is a global netconnect handler which will be used to connect with - // php server - _contp = TSContCreate(_funcp, TSMutexCreate()); - _sConnInfo = new ServerConnectionInfo(_server, this); - TSContDataSet(_contp, _sConnInfo); - // TODO: Need to handle return value of NetConnect - TSNetConnect(_contp, reinterpret_cast(&ip_addr)); -} diff --git a/plugins/experimental/fastcgi/src/server_connection.h b/plugins/experimental/fastcgi/src/server_connection.h deleted file mode 100644 index 866b667abb2..00000000000 --- a/plugins/experimental/fastcgi/src/server_connection.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include "ts/ts.h" - -namespace ats_plugin -{ -class Server; -class FCGIClientRequest; -class ServerIntercept; -struct ServerConnectionInfo; -struct InterceptIOChannel { - TSVIO vio; - TSIOBuffer iobuf; - TSIOBufferReader reader; - int total_bytes_written; - bool readEnable; - InterceptIOChannel(); - ~InterceptIOChannel(); - - void read(TSVConn vc, TSCont contp); - void write(TSVConn vc, TSCont contp); - void phpWrite(TSVConn vc, TSCont contp, unsigned char *buf, int data_size, bool endflag); -}; - -class ServerConnection -{ -public: - ServerConnection(Server *server, TSEventFunc funcp); - ~ServerConnection(); - - enum State { INITIATED, READY, INUSE, COMPLETE, CLOSED }; - - void - setState(State state) - { - _state = state; - } - - State - getState() - { - return _state; - } - - void - setRequestId(uint requestId) - { - _requestId = requestId; - } - - uint - requestId() - { - return _requestId; - } - - uint - maxRequests() - { - return _max_requests; - } - - uint - requestCount() - { - return _req_count; - } - - void createFCGIClient(ServerIntercept *intercept); - void releaseFCGIClient(); - FCGIClientRequest * - fcgiRequest() - { - return _fcgiRequest; - } - - TSCont & - contp() - { - return _contp; - } - -public: - TSVConn vc_; - std::string clientData, clientRequestBody, serverResponse; - InterceptIOChannel readio; - InterceptIOChannel writeio; - FCGIClientRequest *_fcgiRequest; - void createConnection(); - -private: - State _state; - Server *_server; - TSEventFunc _funcp; - TSCont _contp; - ServerConnectionInfo *_sConnInfo; - uint _requestId, _max_requests, _req_count; -}; -} // namespace ats_plugin diff --git a/plugins/experimental/fastcgi/src/server_intercept.cc b/plugins/experimental/fastcgi/src/server_intercept.cc deleted file mode 100644 index 8ef6d580137..00000000000 --- a/plugins/experimental/fastcgi/src/server_intercept.cc +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "server_intercept.h" - -#include -#include -#include -#include -#include - -#include -#include "atscppapi/HttpMethod.h" -#include "atscppapi/Transaction.h" -#include "atscppapi/TransactionPlugin.h" -#include "../../../../src/tscpp/api/utils_internal.h" -#include -#include - -#include "ats_fcgi_client.h" -#include "fcgi_config.h" -#include "server.h" -#include "server_connection.h" -#include "ats_fastcgi.h" -using namespace atscppapi; -using namespace ats_plugin; - -ServerIntercept::~ServerIntercept() -{ - Dbg(dbg_ctl, "~ServerIntercept : Shutting down server intercept._request_id: %d", _request_id); - _txn = nullptr; - if (!outputCompleteState) { - clientAborted = true; - Server::server()->removeIntercept(_request_id); - } - TSStatIntIncrement(InterceptGlobal::respEndId, 1); -} - -void -ServerIntercept::consume(const string &data, InterceptPlugin::RequestDataType type) -{ - if (type == InterceptPlugin::REQUEST_HEADER) { - streamReqHeader(data); - } else { - streamReqBody(data); - } -} - -void -ServerIntercept::streamReqHeader(const string &data) -{ - if (!Server::server()->writeRequestHeader(_request_id)) { - dataBuffered = true; - clientHeader += data; - } -} - -void -ServerIntercept::streamReqBody(const string &data) -{ - Dbg(dbg_ctl, "[ServerIntercept:%s] bodyCount: %d", __FUNCTION__, bodyCount++); - if (!Server::server()->writeRequestBody(_request_id, data)) { - dataBuffered = true; - clientBody += data; - } -} - -void -ServerIntercept::handleInputComplete() -{ - Dbg(dbg_ctl, "[ServerIntercept:%s] Count : %d \t_request_id: %d", __FUNCTION__, emptyCount++, _request_id); - if (!Server::server()->writeRequestBodyComplete(_request_id)) { - return; - } - inputCompleteState = true; -} - -bool -ServerIntercept::writeResponseChunkToATS(std::string &data) -{ - return InterceptPlugin::produce(data); -} - -bool -ServerIntercept::setResponseOutputComplete() -{ - bool status = false; - status = InterceptPlugin::setOutputComplete(); - outputCompleteState = true; - Server::server()->removeIntercept(_request_id); - return status; -} diff --git a/plugins/experimental/fastcgi/src/server_intercept.h b/plugins/experimental/fastcgi/src/server_intercept.h deleted file mode 100644 index c7767f79118..00000000000 --- a/plugins/experimental/fastcgi/src/server_intercept.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "ats_fcgi_client.h" -#include "fcgi_config.h" -#include "atscppapi/Transaction.h" -#include "atscppapi/TransactionPlugin.h" -#include "ts/ink_defs.h" -#include "ts/ts.h" -#include "../../../../src/tscpp/api/utils_internal.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#define PORT 60000 - -using std::cout; -using std::endl; -using std::string; - -using namespace atscppapi; - -namespace ats_plugin -{ -class ServerConnection; -class ServerIntercept : public InterceptPlugin -{ -public: - int headCount = 0, bodyCount = 0, emptyCount = 0; - bool dataBuffered, clientAborted = false; - bool serverDataBuffered; - string serverResponse; - TSHttpTxn _txn; - ServerIntercept(Transaction &transaction) : InterceptPlugin(transaction, InterceptPlugin::SERVER_INTERCEPT) - { - _txn = static_cast(transaction.getAtsHandle()); - clientAborted = false; - dataBuffered = false; - serverDataBuffered = false; - inputCompleteState = false; - outputCompleteState = false; - Dbg(dbg_ctl, "ServerIntercept : Added Server intercept"); - } - - ~ServerIntercept() override; - - void consume(const string &data, InterceptPlugin::RequestDataType type) override; - void handleInputComplete() override; - void streamReqHeader(const string &data); - void streamReqBody(const string &data); - - bool writeResponseChunkToATS(std::string &data); - bool setResponseOutputComplete(); - - void - setRequestId(uint request_id) - { - _request_id = request_id; - } - - uint - requestId() - { - return _request_id; - } - - bool - getOutputCompleteState() - { - return outputCompleteState; - } - -private: - uint _request_id; - string clientHeader, clientBody; - - bool inputCompleteState, outputCompleteState; -}; -} // namespace ats_plugin