passenger 3.0.2 → 3.0.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of passenger might be problematic. Click here for more details.
- data/NEWS +29 -0
- data/build/agents.rb +1 -0
- data/build/apache2.rb +2 -1
- data/build/cxx_tests.rb +7 -0
- data/build/rpm.rb +17 -22
- data/doc/cxxapi/Constants_8h_source.html +1 -1
- data/doc/cxxapi/Exceptions_8h_source.html +18 -4
- data/doc/cxxapi/Logging_8h_source.html +332 -262
- data/doc/cxxapi/annotated.html +1 -0
- data/doc/cxxapi/classes.html +4 -4
- data/doc/cxxapi/group__Exceptions.html +2 -0
- data/doc/cxxapi/hierarchy.html +1 -0
- data/doc/cxxapi/inherit__graph__35.map +1 -3
- data/doc/cxxapi/inherit__graph__35.md5 +1 -1
- data/doc/cxxapi/inherit__graph__35.png +0 -0
- data/doc/cxxapi/inherit__graph__36.map +3 -1
- data/doc/cxxapi/inherit__graph__36.md5 +1 -1
- data/doc/cxxapi/inherit__graph__36.png +0 -0
- data/doc/cxxapi/inherit__graph__37.map +1 -1
- data/doc/cxxapi/inherit__graph__37.md5 +1 -1
- data/doc/cxxapi/inherit__graph__37.png +0 -0
- data/doc/cxxapi/inherit__graph__38.map +1 -1
- data/doc/cxxapi/inherit__graph__38.md5 +1 -1
- data/doc/cxxapi/inherit__graph__38.png +0 -0
- data/doc/cxxapi/inherit__graph__39.map +1 -1
- data/doc/cxxapi/inherit__graph__39.md5 +1 -1
- data/doc/cxxapi/inherit__graph__39.png +0 -0
- data/doc/cxxapi/inherit__graph__40.map +1 -1
- data/doc/cxxapi/inherit__graph__40.md5 +1 -1
- data/doc/cxxapi/inherit__graph__40.png +0 -0
- data/doc/cxxapi/inherits.html +9 -6
- data/doc/cxxapi/namespacePassenger.html +2 -0
- data/doc/cxxapi/tree.html +2 -0
- data/doc/rdoc/classes/ConditionVariable.html +215 -0
- data/doc/rdoc/classes/Exception.html +120 -0
- data/doc/rdoc/classes/GC.html +113 -0
- data/doc/rdoc/classes/IO.html +221 -0
- data/doc/rdoc/classes/PhusionPassenger.html +397 -0
- data/doc/rdoc/classes/PhusionPassenger/AbstractInstaller.html +180 -0
- data/doc/rdoc/classes/PhusionPassenger/AbstractRequestHandler.html +647 -0
- data/doc/rdoc/classes/PhusionPassenger/AbstractServer.html +654 -0
- data/doc/rdoc/classes/PhusionPassenger/AbstractServer/InvalidPassword.html +92 -0
- data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerAlreadyStarted.html +97 -0
- data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerError.html +96 -0
- data/doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerNotStarted.html +97 -0
- data/doc/rdoc/classes/PhusionPassenger/AbstractServer/UnknownMessage.html +96 -0
- data/doc/rdoc/classes/PhusionPassenger/AbstractServerCollection.html +619 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools.html +142 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/MemoryStats.html +368 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/MemoryStats/Process.html +231 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/ServerInstance.html +588 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/ServerInstance/CorruptedDirectoryError.html +92 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/ServerInstance/GenerationsAbsentError.html +92 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/ServerInstance/Group.html +147 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/ServerInstance/Process.html +279 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/ServerInstance/RoleDeniedError.html +92 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/ServerInstance/StaleDirectoryError.html +92 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/ServerInstance/Stats.html +123 -0
- data/doc/rdoc/classes/PhusionPassenger/AdminTools/ServerInstance/UnsupportedGenerationStructureVersionError.html +92 -0
- data/doc/rdoc/classes/PhusionPassenger/AnalyticsLogger.html +368 -0
- data/doc/rdoc/classes/PhusionPassenger/AnalyticsLogger/Log.html +299 -0
- data/doc/rdoc/classes/PhusionPassenger/AnalyticsLogger/SharedData.html +206 -0
- data/doc/rdoc/classes/PhusionPassenger/AppInitError.html +155 -0
- data/doc/rdoc/classes/PhusionPassenger/AppProcess.html +367 -0
- data/doc/rdoc/classes/PhusionPassenger/ClassicRails.html +95 -0
- data/doc/rdoc/classes/PhusionPassenger/ClassicRails/ApplicationSpawner.html +351 -0
- data/doc/rdoc/classes/PhusionPassenger/ClassicRails/ApplicationSpawner/Error.html +98 -0
- data/doc/rdoc/classes/PhusionPassenger/ClassicRails/CGIFixed.html +200 -0
- data/doc/rdoc/classes/PhusionPassenger/ClassicRails/FrameworkSpawner.html +410 -0
- data/doc/rdoc/classes/PhusionPassenger/ClassicRails/FrameworkSpawner/Error.html +98 -0
- data/doc/rdoc/classes/PhusionPassenger/ClassicRails/RequestHandler.html +156 -0
- data/doc/rdoc/classes/PhusionPassenger/ClassicRailsExtensions.html +115 -0
- data/doc/rdoc/classes/PhusionPassenger/ClassicRailsExtensions/AnalyticsLogging.html +202 -0
- data/doc/rdoc/classes/PhusionPassenger/ConsoleTextTemplate.html +172 -0
- data/doc/rdoc/classes/PhusionPassenger/DebugLogging.html +273 -0
- data/doc/rdoc/classes/PhusionPassenger/FrameworkInitError.html +145 -0
- data/doc/rdoc/classes/PhusionPassenger/HTMLTemplate.html +162 -0
- data/doc/rdoc/classes/PhusionPassenger/InitializationError.html +141 -0
- data/doc/rdoc/classes/PhusionPassenger/InvalidPath.html +92 -0
- data/doc/rdoc/classes/PhusionPassenger/MessageChannel.html +673 -0
- data/doc/rdoc/classes/PhusionPassenger/MessageChannel/InvalidHashError.html +92 -0
- data/doc/rdoc/classes/PhusionPassenger/MessageClient.html +415 -0
- data/doc/rdoc/classes/PhusionPassenger/NativeSupportLoader.html +134 -0
- data/doc/rdoc/classes/PhusionPassenger/Packaging.html +129 -0
- data/doc/rdoc/classes/PhusionPassenger/PlatformInfo.html +1972 -0
- data/doc/rdoc/classes/PhusionPassenger/Plugin.html +237 -0
- data/doc/rdoc/classes/PhusionPassenger/Rack.html +91 -0
- data/doc/rdoc/classes/PhusionPassenger/Rack/ApplicationSpawner.html +312 -0
- data/doc/rdoc/classes/PhusionPassenger/Rack/ApplicationSpawner/Error.html +98 -0
- data/doc/rdoc/classes/PhusionPassenger/Rack/RequestHandler.html +218 -0
- data/doc/rdoc/classes/PhusionPassenger/Rails3Extensions.html +114 -0
- data/doc/rdoc/classes/PhusionPassenger/Rails3Extensions/AnalyticsLogging.html +259 -0
- data/doc/rdoc/classes/PhusionPassenger/Rails3Extensions/AnalyticsLogging/ACExtension.html +139 -0
- data/doc/rdoc/classes/PhusionPassenger/Rails3Extensions/AnalyticsLogging/ASBenchmarkableExtension.html +118 -0
- data/doc/rdoc/classes/PhusionPassenger/Rails3Extensions/AnalyticsLogging/ExceptionLogger.html +135 -0
- data/doc/rdoc/classes/PhusionPassenger/SpawnManager.html +378 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone.html +111 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/AppFinder.html +252 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/Command.html +161 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/ConfigFile.html +368 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/ConfigFile/DisallowedContextError.html +132 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/HelpCommand.html +151 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/Main.html +189 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/PackageRuntimeCommand.html +177 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/RuntimeInstaller.html +341 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/StartCommand.html +203 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/StatusCommand.html +156 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/StopCommand.html +168 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/Utils.html +86 -0
- data/doc/rdoc/classes/PhusionPassenger/Standalone/VersionCommand.html +135 -0
- data/doc/rdoc/classes/PhusionPassenger/UnknownError.html +125 -0
- data/doc/rdoc/classes/PhusionPassenger/Utils.html +1543 -0
- data/doc/rdoc/classes/PhusionPassenger/Utils/FileSystemWatcher.html +204 -0
- data/doc/rdoc/classes/PhusionPassenger/Utils/FileSystemWatcher/DirInfo.html +171 -0
- data/doc/rdoc/classes/PhusionPassenger/Utils/FileSystemWatcher/FileInfo.html +140 -0
- data/doc/rdoc/classes/PhusionPassenger/Utils/HostsFileParser.html +260 -0
- data/doc/rdoc/classes/PhusionPassenger/Utils/PseudoIO.html +194 -0
- data/doc/rdoc/classes/PhusionPassenger/Utils/RewindableInput.html +265 -0
- data/doc/rdoc/classes/PhusionPassenger/Utils/RewindableInput/Tempfile.html +120 -0
- data/doc/rdoc/classes/PhusionPassenger/Utils/UnseekableSocket.html +561 -0
- data/doc/rdoc/classes/PhusionPassenger/VersionNotFound.html +140 -0
- data/doc/rdoc/classes/PhusionPassenger/WSGI.html +89 -0
- data/doc/rdoc/classes/PhusionPassenger/WSGI/ApplicationSpawner.html +182 -0
- data/doc/rdoc/classes/Process.html +115 -0
- data/doc/rdoc/classes/Signal.html +139 -0
- data/doc/rdoc/created.rid +1 -0
- data/doc/rdoc/files/DEVELOPERS_TXT.html +280 -0
- data/doc/rdoc/files/README.html +157 -0
- data/doc/rdoc/files/lib/phusion_passenger/abstract_installer_rb.html +130 -0
- data/doc/rdoc/files/lib/phusion_passenger/abstract_request_handler_rb.html +135 -0
- data/doc/rdoc/files/lib/phusion_passenger/abstract_server_collection_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/abstract_server_rb.html +128 -0
- data/doc/rdoc/files/lib/phusion_passenger/admin_tools/memory_stats_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/admin_tools/server_instance_rb.html +132 -0
- data/doc/rdoc/files/lib/phusion_passenger/admin_tools_rb.html +122 -0
- data/doc/rdoc/files/lib/phusion_passenger/analytics_logger_rb.html +129 -0
- data/doc/rdoc/files/lib/phusion_passenger/app_process_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/classic_rails/application_spawner_rb.html +141 -0
- data/doc/rdoc/files/lib/phusion_passenger/classic_rails/cgi_fixed_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/classic_rails/framework_spawner_rb.html +146 -0
- data/doc/rdoc/files/lib/phusion_passenger/classic_rails/request_handler_rb.html +125 -0
- data/doc/rdoc/files/lib/phusion_passenger/classic_rails_extensions/init_rb.html +132 -0
- data/doc/rdoc/files/lib/phusion_passenger/console_text_template_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/constants_rb.html +122 -0
- data/doc/rdoc/files/lib/phusion_passenger/debug_logging_rb.html +122 -0
- data/doc/rdoc/files/lib/phusion_passenger/dependencies_rb.html +147 -0
- data/doc/rdoc/files/lib/phusion_passenger/exceptions_rb.html +122 -0
- data/doc/rdoc/files/lib/phusion_passenger/html_template_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/message_channel_rb.html +120 -0
- data/doc/rdoc/files/lib/phusion_passenger/message_client_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/native_support_rb.html +132 -0
- data/doc/rdoc/files/lib/phusion_passenger/packaging_rb.html +122 -0
- data/doc/rdoc/files/lib/phusion_passenger/platform_info/apache_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/platform_info/binary_compatibility_rb.html +129 -0
- data/doc/rdoc/files/lib/phusion_passenger/platform_info/compiler_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/platform_info/curl_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/platform_info/documentation_tools_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/platform_info/linux_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/platform_info/operating_system_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/platform_info/ruby_rb.html +128 -0
- data/doc/rdoc/files/lib/phusion_passenger/platform_info/zlib_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/platform_info_rb.html +122 -0
- data/doc/rdoc/files/lib/phusion_passenger/plugin_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/public_api_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/rack/application_spawner_rb.html +137 -0
- data/doc/rdoc/files/lib/phusion_passenger/rack/request_handler_rb.html +125 -0
- data/doc/rdoc/files/lib/phusion_passenger/rails3_extensions/init_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/simple_benchmarking_rb.html +122 -0
- data/doc/rdoc/files/lib/phusion_passenger/spawn_manager_rb.html +160 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/app_finder_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/command_rb.html +136 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/config_file_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/help_command_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/main_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/package_runtime_command_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/runtime_installer_rb.html +133 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/start_command_rb.html +136 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/status_command_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/stop_command_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/utils_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/standalone/version_command_rb.html +127 -0
- data/doc/rdoc/files/lib/phusion_passenger/utils/file_system_watcher_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/utils/hosts_file_parser_rb.html +120 -0
- data/doc/rdoc/files/lib/phusion_passenger/utils/rewindable_input_rb.html +100 -0
- data/doc/rdoc/files/lib/phusion_passenger/utils/tmpdir_rb.html +122 -0
- data/doc/rdoc/files/lib/phusion_passenger/utils/unseekable_socket_rb.html +126 -0
- data/doc/rdoc/files/lib/phusion_passenger/utils_rb.html +179 -0
- data/doc/rdoc/files/lib/phusion_passenger/wsgi/application_spawner_rb.html +132 -0
- data/doc/rdoc/fr_class_index.html +139 -0
- data/doc/rdoc/fr_file_index.html +108 -0
- data/doc/rdoc/fr_method_index.html +439 -0
- data/doc/rdoc/index.html +26 -0
- data/doc/rdoc/rdoc-style.css +187 -0
- data/ext/apache2/Configuration.cpp +41 -0
- data/ext/apache2/Configuration.hpp +19 -0
- data/ext/apache2/Hooks.cpp +67 -5
- data/ext/common/Constants.h +1 -1
- data/ext/common/Exceptions.h +14 -0
- data/ext/common/Logging.h +76 -6
- data/ext/common/LoggingAgent/FilterSupport.h +1317 -0
- data/ext/common/LoggingAgent/LoggingServer.h +93 -4
- data/ext/common/LoggingAgent/RemoteSender.h +5 -5
- data/ext/common/Utils/StrIntUtils.cpp +12 -1
- data/ext/common/Utils/StrIntUtils.h +2 -1
- data/ext/common/Utils/StringMap.h +100 -0
- data/ext/nginx/Configuration.c +0 -1
- data/ext/nginx/ContentHandler.c +33 -7
- data/lib/phusion_passenger.rb +2 -2
- data/lib/phusion_passenger/abstract_request_handler.rb +5 -0
- data/lib/phusion_passenger/analytics_logger.rb +77 -11
- data/lib/phusion_passenger/dependencies.rb +11 -1
- data/lib/phusion_passenger/platform_info.rb +1 -1
- data/lib/phusion_passenger/utils.rb +9 -1
- data/test/cxx/FilterSupportTest.cpp +276 -0
- data/test/cxx/LoggingTest.cpp +28 -0
- data/test/cxx/StringMapTest.cpp +70 -0
- data/test/integration_tests/cgi_environment_spec.rb +10 -0
- metadata +170 -8
- data/ext/common/libboost_oxt/aggregate.cpp +0 -10
- data/ext/common/libpassenger_common/aggregate.cpp +0 -15
data/ext/common/Logging.h
CHANGED
@@ -430,6 +430,47 @@ public:
|
|
430
430
|
|
431
431
|
class AnalyticsLogger {
|
432
432
|
private:
|
433
|
+
/** A special lock type for AnalyticsLoggerSharedData that also
|
434
|
+
* keeps a smart pointer to the data structure so that the mutex
|
435
|
+
* is not destroyed prematurely.
|
436
|
+
*/
|
437
|
+
struct SharedDataLock {
|
438
|
+
AnalyticsLoggerSharedDataPtr sharedData;
|
439
|
+
bool locked;
|
440
|
+
|
441
|
+
SharedDataLock(const AnalyticsLoggerSharedDataPtr &d)
|
442
|
+
: sharedData(d)
|
443
|
+
{
|
444
|
+
d->lock.lock();
|
445
|
+
locked = true;
|
446
|
+
}
|
447
|
+
|
448
|
+
~SharedDataLock() {
|
449
|
+
if (locked) {
|
450
|
+
sharedData->lock.unlock();
|
451
|
+
}
|
452
|
+
}
|
453
|
+
|
454
|
+
void reset(const AnalyticsLoggerSharedDataPtr &d, bool lockNow = true) {
|
455
|
+
if (locked) {
|
456
|
+
sharedData->lock.unlock();
|
457
|
+
}
|
458
|
+
sharedData = d;
|
459
|
+
if (lockNow) {
|
460
|
+
sharedData->lock.lock();
|
461
|
+
locked = true;
|
462
|
+
} else {
|
463
|
+
locked = false;
|
464
|
+
}
|
465
|
+
}
|
466
|
+
|
467
|
+
void lock() {
|
468
|
+
assert(!locked);
|
469
|
+
sharedData->lock.lock();
|
470
|
+
locked = true;
|
471
|
+
}
|
472
|
+
};
|
473
|
+
|
433
474
|
static const int RETRY_SLEEP = 200000; // microseconds
|
434
475
|
|
435
476
|
string serverAddress;
|
@@ -437,10 +478,13 @@ private:
|
|
437
478
|
string password;
|
438
479
|
string nodeName;
|
439
480
|
RandomGenerator randomGenerator;
|
481
|
+
|
482
|
+
/** Lock protecting the fields that follow, but not the contents of the shared data. */
|
483
|
+
mutable boost::mutex lock;
|
484
|
+
|
440
485
|
unsigned int maxConnectTries;
|
441
486
|
unsigned long long reconnectTimeout;
|
442
487
|
unsigned long long nextReconnectTime;
|
443
|
-
|
444
488
|
/** @invariant sharedData != NULL */
|
445
489
|
AnalyticsLoggerSharedDataPtr sharedData;
|
446
490
|
|
@@ -511,7 +555,8 @@ public:
|
|
511
555
|
}
|
512
556
|
|
513
557
|
AnalyticsLogPtr newTransaction(const string &groupName, const string &category = "requests",
|
514
|
-
const string &unionStationKey = string()
|
558
|
+
const string &unionStationKey = string(),
|
559
|
+
const string &filters = string())
|
515
560
|
{
|
516
561
|
if (serverAddress.empty()) {
|
517
562
|
return ptr(new AnalyticsLog());
|
@@ -546,7 +591,8 @@ public:
|
|
546
591
|
|
547
592
|
integerToHexatri<unsigned long long>(timestamp, timestampStr);
|
548
593
|
|
549
|
-
|
594
|
+
unique_lock<boost::mutex> l(lock);
|
595
|
+
SharedDataLock sl(sharedData);
|
550
596
|
|
551
597
|
if (SystemTime::getUsec() >= nextReconnectTime) {
|
552
598
|
unsigned int tryCount = 0;
|
@@ -565,7 +611,20 @@ public:
|
|
565
611
|
timestampStr,
|
566
612
|
unionStationKey.c_str(),
|
567
613
|
"true",
|
614
|
+
"true",
|
615
|
+
filters.c_str(),
|
568
616
|
NULL);
|
617
|
+
|
618
|
+
vector<string> args;
|
619
|
+
sharedData->client.read(args);
|
620
|
+
if (args.size() == 2 && args[0] == "error") {
|
621
|
+
disconnect();
|
622
|
+
throw IOException("The logging server responded with an error: " + args[1]);
|
623
|
+
} else if (args.empty() || args[0] != "ok") {
|
624
|
+
disconnect();
|
625
|
+
throw IOException("The logging server sent an unexpected reply.");
|
626
|
+
}
|
627
|
+
|
569
628
|
return ptr(new AnalyticsLog(sharedData,
|
570
629
|
string(txnId, end - txnId),
|
571
630
|
groupName, category,
|
@@ -575,9 +634,13 @@ public:
|
|
575
634
|
if (e.code() == ENOENT || isNetworkError(e.code())) {
|
576
635
|
tryCount++;
|
577
636
|
disconnect(true);
|
637
|
+
sl.reset(sharedData, false);
|
638
|
+
l.unlock();
|
578
639
|
if (tryCount < maxConnectTries) {
|
579
640
|
syscalls::usleep(RETRY_SLEEP);
|
580
641
|
}
|
642
|
+
l.lock();
|
643
|
+
sl.lock();
|
581
644
|
} else {
|
582
645
|
disconnect();
|
583
646
|
throw;
|
@@ -603,7 +666,8 @@ public:
|
|
603
666
|
char timestampStr[2 * sizeof(unsigned long long) + 1];
|
604
667
|
integerToHexatri<unsigned long long>(SystemTime::getUsec(), timestampStr);
|
605
668
|
|
606
|
-
|
669
|
+
unique_lock<boost::mutex> l(lock);
|
670
|
+
SharedDataLock sl(sharedData);
|
607
671
|
|
608
672
|
if (SystemTime::getUsec() >= nextReconnectTime) {
|
609
673
|
unsigned int tryCount = 0;
|
@@ -631,9 +695,13 @@ public:
|
|
631
695
|
if (e.code() == EPIPE || isNetworkError(e.code())) {
|
632
696
|
tryCount++;
|
633
697
|
disconnect(true);
|
698
|
+
sl.reset(sharedData, false);
|
699
|
+
l.unlock();
|
634
700
|
if (tryCount < maxConnectTries) {
|
635
701
|
syscalls::usleep(RETRY_SLEEP);
|
636
702
|
}
|
703
|
+
l.lock();
|
704
|
+
sl.lock();
|
637
705
|
} else {
|
638
706
|
disconnect();
|
639
707
|
throw;
|
@@ -650,12 +718,12 @@ public:
|
|
650
718
|
}
|
651
719
|
|
652
720
|
void setMaxConnectTries(unsigned int value) {
|
653
|
-
lock_guard<boost::mutex> l(
|
721
|
+
lock_guard<boost::mutex> l(lock);
|
654
722
|
maxConnectTries = value;
|
655
723
|
}
|
656
724
|
|
657
725
|
void setReconnectTimeout(unsigned long long usec) {
|
658
|
-
lock_guard<boost::mutex> l(
|
726
|
+
lock_guard<boost::mutex> l(lock);
|
659
727
|
reconnectTimeout = usec;
|
660
728
|
}
|
661
729
|
|
@@ -676,6 +744,8 @@ public:
|
|
676
744
|
}
|
677
745
|
|
678
746
|
FileDescriptor getConnection() const {
|
747
|
+
lock_guard<boost::mutex> l(lock);
|
748
|
+
lock_guard<boost::mutex> l2(sharedData->lock);
|
679
749
|
return sharedData->client.getConnection();
|
680
750
|
}
|
681
751
|
|
@@ -0,0 +1,1317 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - http://www.modrails.com/
|
3
|
+
* Copyright (c) 2011 Phusion
|
4
|
+
*
|
5
|
+
* "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
|
6
|
+
*
|
7
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
* of this software and associated documentation files (the "Software"), to deal
|
9
|
+
* in the Software without restriction, including without limitation the rights
|
10
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
* copies of the Software, and to permit persons to whom the Software is
|
12
|
+
* furnished to do so, subject to the following conditions:
|
13
|
+
*
|
14
|
+
* The above copyright notice and this permission notice shall be included in
|
15
|
+
* all copies or substantial portions of the Software.
|
16
|
+
*
|
17
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
* THE SOFTWARE.
|
24
|
+
*/
|
25
|
+
#ifndef _PASSENGER_FILTER_SUPPORT_H_
|
26
|
+
#define _PASSENGER_FILTER_SUPPORT_H_
|
27
|
+
|
28
|
+
#include <boost/shared_ptr.hpp>
|
29
|
+
#include <boost/make_shared.hpp>
|
30
|
+
#include <oxt/tracable_exception.hpp>
|
31
|
+
|
32
|
+
#include <string>
|
33
|
+
#include <set>
|
34
|
+
#include <regex.h>
|
35
|
+
#include <cstring>
|
36
|
+
|
37
|
+
#include <StaticString.h>
|
38
|
+
#include <Utils/StrIntUtils.h>
|
39
|
+
|
40
|
+
namespace Passenger {
|
41
|
+
namespace FilterSupport {
|
42
|
+
|
43
|
+
using namespace std;
|
44
|
+
using namespace boost;
|
45
|
+
using namespace oxt;
|
46
|
+
|
47
|
+
|
48
|
+
class Tokenizer {
|
49
|
+
public:
|
50
|
+
enum TokenType {
|
51
|
+
NONE,
|
52
|
+
NOT,
|
53
|
+
AND,
|
54
|
+
OR,
|
55
|
+
MATCHES,
|
56
|
+
NOT_MATCHES,
|
57
|
+
EQUALS,
|
58
|
+
NOT_EQUALS,
|
59
|
+
GREATER_THAN,
|
60
|
+
GREATER_THAN_OR_EQUALS,
|
61
|
+
LESS_THAN,
|
62
|
+
LESS_THAN_OR_EQUALS,
|
63
|
+
LPARENTHESIS,
|
64
|
+
RPARENTHESIS,
|
65
|
+
COMMA,
|
66
|
+
REGEXP,
|
67
|
+
STRING,
|
68
|
+
INTEGER,
|
69
|
+
IDENTIFIER,
|
70
|
+
END_OF_DATA
|
71
|
+
};
|
72
|
+
|
73
|
+
enum TokenOptions {
|
74
|
+
NO_OPTIONS = 0,
|
75
|
+
REGEXP_OPTION_CASE_INSENSITIVE = 1
|
76
|
+
};
|
77
|
+
|
78
|
+
struct Token {
|
79
|
+
TokenType type;
|
80
|
+
int options;
|
81
|
+
unsigned int pos;
|
82
|
+
unsigned int size;
|
83
|
+
StaticString rawValue;
|
84
|
+
|
85
|
+
Token() {
|
86
|
+
type = NONE;
|
87
|
+
}
|
88
|
+
|
89
|
+
Token(TokenType _type, unsigned int _pos, unsigned int _size, const StaticString &_rawValue)
|
90
|
+
: type(_type),
|
91
|
+
options(NO_OPTIONS),
|
92
|
+
pos(_pos),
|
93
|
+
size(_size),
|
94
|
+
rawValue(_rawValue)
|
95
|
+
{ }
|
96
|
+
|
97
|
+
string toString() const {
|
98
|
+
return Tokenizer::typeToString(type);
|
99
|
+
}
|
100
|
+
};
|
101
|
+
|
102
|
+
private:
|
103
|
+
StaticString data;
|
104
|
+
bool debug;
|
105
|
+
unsigned int pos;
|
106
|
+
|
107
|
+
static bool isWhitespace(char ch) {
|
108
|
+
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
|
109
|
+
}
|
110
|
+
|
111
|
+
void skipWhitespaces() {
|
112
|
+
while (pos < data.size() && isWhitespace(data[pos])) {
|
113
|
+
pos++;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
unsigned int available() const {
|
118
|
+
return data.size() - pos;
|
119
|
+
}
|
120
|
+
|
121
|
+
char current() const {
|
122
|
+
return data[pos];
|
123
|
+
}
|
124
|
+
|
125
|
+
char next() const {
|
126
|
+
return data[pos + 1];
|
127
|
+
}
|
128
|
+
|
129
|
+
static bool isLiteralChar(char ch) {
|
130
|
+
return (ch >= 'a' && ch <= 'z')
|
131
|
+
|| (ch >= 'A' && ch <= 'Z')
|
132
|
+
|| (ch >= '0' && ch <= '9')
|
133
|
+
|| ch == '_';
|
134
|
+
}
|
135
|
+
|
136
|
+
static bool isDigit(char ch) {
|
137
|
+
return ch >= '0' && ch <= '9';
|
138
|
+
}
|
139
|
+
|
140
|
+
Token logToken(const Token &token) const {
|
141
|
+
if (debug) {
|
142
|
+
printf("Token: %s\n", token.toString().c_str());
|
143
|
+
}
|
144
|
+
return token;
|
145
|
+
}
|
146
|
+
|
147
|
+
void raiseSyntaxError(const string &message = "") {
|
148
|
+
string msg = "Syntax error at character " + toString(pos + 1);
|
149
|
+
if (!message.empty()) {
|
150
|
+
msg.append(": ");
|
151
|
+
msg.append(message);
|
152
|
+
}
|
153
|
+
throw SyntaxError(msg);
|
154
|
+
}
|
155
|
+
|
156
|
+
void expectingAtLeast(unsigned int size) {
|
157
|
+
if (available() < size) {
|
158
|
+
raiseSyntaxError("at least " + toString(size) +
|
159
|
+
" more characters expected");
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
void expectingNextChar(char ch) {
|
164
|
+
expectingAtLeast(2);
|
165
|
+
if (next() != ch) {
|
166
|
+
raiseSyntaxError("expected '" + toString(ch) +
|
167
|
+
"', but found '" + toString(next()) +
|
168
|
+
"'");
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
Token matchToken(TokenType type, unsigned int size = 0) {
|
173
|
+
unsigned int oldPos = pos;
|
174
|
+
pos += size;
|
175
|
+
return Token(type, oldPos, size, data.substr(oldPos, size));
|
176
|
+
}
|
177
|
+
|
178
|
+
Token matchTokensStartingWithNegation() {
|
179
|
+
expectingAtLeast(2);
|
180
|
+
switch (next()) {
|
181
|
+
case '~':
|
182
|
+
return matchToken(NOT_MATCHES, 2);
|
183
|
+
case '=':
|
184
|
+
return matchToken(NOT_EQUALS, 2);
|
185
|
+
default:
|
186
|
+
raiseSyntaxError("unrecognized operator '" + data.substr(pos, 2) + "'");
|
187
|
+
return Token(); // Shut up compiler warning.
|
188
|
+
};
|
189
|
+
}
|
190
|
+
|
191
|
+
Token matchAnd() {
|
192
|
+
expectingNextChar('&');
|
193
|
+
return matchToken(AND, 2);
|
194
|
+
}
|
195
|
+
|
196
|
+
Token matchOr() {
|
197
|
+
expectingNextChar('|');
|
198
|
+
return matchToken(OR, 2);
|
199
|
+
}
|
200
|
+
|
201
|
+
Token matchTokensStartingWithEquals() {
|
202
|
+
expectingAtLeast(2);
|
203
|
+
switch (next()) {
|
204
|
+
case '~':
|
205
|
+
return matchToken(MATCHES, 2);
|
206
|
+
case '=':
|
207
|
+
return matchToken(EQUALS, 2);
|
208
|
+
default:
|
209
|
+
raiseSyntaxError("unrecognized operator '" + data.substr(pos, 2) + "'");
|
210
|
+
return Token(); // Shut up compiler warning.
|
211
|
+
}
|
212
|
+
}
|
213
|
+
|
214
|
+
Token matchTokensStartingWithGreaterThan() {
|
215
|
+
if (available() == 0 || next() != '=') {
|
216
|
+
return matchToken(GREATER_THAN, 1);
|
217
|
+
} else {
|
218
|
+
return matchToken(GREATER_THAN_OR_EQUALS, 2);
|
219
|
+
}
|
220
|
+
}
|
221
|
+
|
222
|
+
Token matchTokensStartingWithLessThan() {
|
223
|
+
if (available() == 0 || next() != '=') {
|
224
|
+
return matchToken(LESS_THAN, 1);
|
225
|
+
} else {
|
226
|
+
return matchToken(LESS_THAN_OR_EQUALS, 2);
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
Token matchRegexp() {
|
231
|
+
unsigned int start = pos;
|
232
|
+
bool endFound = false;
|
233
|
+
|
234
|
+
// Match initial quote slash.
|
235
|
+
pos++;
|
236
|
+
|
237
|
+
// Match rest of regexp including terminating slash.
|
238
|
+
while (pos < data.size() && !endFound) {
|
239
|
+
char ch = current();
|
240
|
+
if (ch == '\\') {
|
241
|
+
pos++;
|
242
|
+
if (pos >= data.size()) {
|
243
|
+
raiseSyntaxError("unterminated regular expression");
|
244
|
+
} else {
|
245
|
+
pos++;
|
246
|
+
}
|
247
|
+
} else if (ch == '/') {
|
248
|
+
pos++;
|
249
|
+
endFound = true;
|
250
|
+
} else {
|
251
|
+
pos++;
|
252
|
+
}
|
253
|
+
}
|
254
|
+
|
255
|
+
if (endFound) {
|
256
|
+
Token t(REGEXP, start, pos - start, data.substr(start, pos - start));
|
257
|
+
|
258
|
+
// Match regexp options.
|
259
|
+
endFound = false;
|
260
|
+
while (pos < data.size() && !endFound) {
|
261
|
+
char ch = current();
|
262
|
+
if (ch == 'i') {
|
263
|
+
t.options |= Tokenizer::REGEXP_OPTION_CASE_INSENSITIVE;
|
264
|
+
} else if (isWhitespace(ch)) {
|
265
|
+
endFound = true;
|
266
|
+
}
|
267
|
+
pos++;
|
268
|
+
}
|
269
|
+
|
270
|
+
return t;
|
271
|
+
} else {
|
272
|
+
raiseSyntaxError("unterminated regular expression");
|
273
|
+
return Token(); // Shut up compiler warning.
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
Token matchString() {
|
278
|
+
unsigned int start = pos;
|
279
|
+
bool endFound = false;
|
280
|
+
|
281
|
+
// Match initial quote character.
|
282
|
+
pos++;
|
283
|
+
|
284
|
+
// Match rest of string including terminating quote.
|
285
|
+
while (pos < data.size() && !endFound) {
|
286
|
+
char ch = current();
|
287
|
+
if (ch == '\\') {
|
288
|
+
pos++;
|
289
|
+
if (pos >= data.size()) {
|
290
|
+
raiseSyntaxError("unterminated string");
|
291
|
+
} else {
|
292
|
+
pos++;
|
293
|
+
}
|
294
|
+
} else if (ch == '"') {
|
295
|
+
pos++;
|
296
|
+
endFound = true;
|
297
|
+
} else {
|
298
|
+
pos++;
|
299
|
+
}
|
300
|
+
}
|
301
|
+
|
302
|
+
if (endFound) {
|
303
|
+
return Token(STRING, start, pos - start, data.substr(start, pos - start));
|
304
|
+
} else {
|
305
|
+
raiseSyntaxError("unterminated string");
|
306
|
+
return Token(); // Shut up compiler warning.
|
307
|
+
}
|
308
|
+
}
|
309
|
+
|
310
|
+
Token matchInteger() {
|
311
|
+
unsigned int start = pos;
|
312
|
+
|
313
|
+
// Accept initial minus or digit.
|
314
|
+
pos++;
|
315
|
+
|
316
|
+
while (pos < data.size() && isDigit(data[pos])) {
|
317
|
+
pos++;
|
318
|
+
}
|
319
|
+
|
320
|
+
return Token(INTEGER, start, pos - start, data.substr(start, pos - start));
|
321
|
+
}
|
322
|
+
|
323
|
+
Token matchIdentifier() {
|
324
|
+
char ch = current();
|
325
|
+
if ((ch >= 'a' && ch <= 'z') ||
|
326
|
+
(ch >= 'A' && ch <= 'Z') ||
|
327
|
+
ch == '_') {
|
328
|
+
unsigned int start = pos;
|
329
|
+
pos++;
|
330
|
+
while (pos < data.size() && isLiteralChar(current())) {
|
331
|
+
pos++;
|
332
|
+
}
|
333
|
+
return Token(IDENTIFIER, start, pos - start, data.substr(start, pos - start));
|
334
|
+
} else {
|
335
|
+
raiseSyntaxError();
|
336
|
+
return Token(); // Shut up compiler warning.
|
337
|
+
}
|
338
|
+
}
|
339
|
+
|
340
|
+
public:
|
341
|
+
Tokenizer(const StaticString &data, bool debug = false) {
|
342
|
+
this->data = data;
|
343
|
+
this->debug = debug;
|
344
|
+
pos = 0;
|
345
|
+
}
|
346
|
+
|
347
|
+
Token getNext() {
|
348
|
+
skipWhitespaces();
|
349
|
+
if (pos >= data.size()) {
|
350
|
+
return logToken(Token(END_OF_DATA, data.size(), 0, ""));
|
351
|
+
}
|
352
|
+
|
353
|
+
switch (current()) {
|
354
|
+
case '!':
|
355
|
+
return logToken(matchTokensStartingWithNegation());
|
356
|
+
case '&':
|
357
|
+
return logToken(matchAnd());
|
358
|
+
case '|':
|
359
|
+
return logToken(matchOr());
|
360
|
+
case '=':
|
361
|
+
return logToken(matchTokensStartingWithEquals());
|
362
|
+
case '>':
|
363
|
+
return logToken(matchTokensStartingWithGreaterThan());
|
364
|
+
case '<':
|
365
|
+
return logToken(matchTokensStartingWithLessThan());
|
366
|
+
case '(':
|
367
|
+
return logToken(matchToken(LPARENTHESIS, 1));
|
368
|
+
case ')':
|
369
|
+
return logToken(matchToken(RPARENTHESIS, 1));
|
370
|
+
case ',':
|
371
|
+
return logToken(matchToken(COMMA, 1));
|
372
|
+
case '/':
|
373
|
+
return logToken(matchRegexp());
|
374
|
+
case '"':
|
375
|
+
return logToken(matchString());
|
376
|
+
case '-':
|
377
|
+
return logToken(matchInteger());
|
378
|
+
default:
|
379
|
+
if (isDigit(current())) {
|
380
|
+
return logToken(matchInteger());
|
381
|
+
} else {
|
382
|
+
return logToken(matchIdentifier());
|
383
|
+
}
|
384
|
+
}
|
385
|
+
}
|
386
|
+
|
387
|
+
static string typeToString(TokenType type) {
|
388
|
+
switch (type) {
|
389
|
+
case NONE:
|
390
|
+
return "NONE";
|
391
|
+
case NOT:
|
392
|
+
return "NOT";
|
393
|
+
case AND:
|
394
|
+
return "AND";
|
395
|
+
case OR:
|
396
|
+
return "OR";
|
397
|
+
case MATCHES:
|
398
|
+
return "MATCHES";
|
399
|
+
case NOT_MATCHES:
|
400
|
+
return "NOT_MATCHES";
|
401
|
+
case EQUALS:
|
402
|
+
return "EQUALS";
|
403
|
+
case NOT_EQUALS:
|
404
|
+
return "NOT_EQUALS";
|
405
|
+
case GREATER_THAN:
|
406
|
+
return "GREATER_THAN";
|
407
|
+
case GREATER_THAN_OR_EQUALS:
|
408
|
+
return "GREATER_THAN_OR_EQUALS";
|
409
|
+
case LESS_THAN:
|
410
|
+
return "LESS_THAN";
|
411
|
+
case LESS_THAN_OR_EQUALS:
|
412
|
+
return "LESS_THAN_OR_EQUALS";
|
413
|
+
case LPARENTHESIS:
|
414
|
+
return "LPARENTHESIS";
|
415
|
+
case RPARENTHESIS:
|
416
|
+
return "RPARENTHESIS";
|
417
|
+
case COMMA:
|
418
|
+
return "COMMA";
|
419
|
+
case REGEXP:
|
420
|
+
return "REGEXP";
|
421
|
+
case STRING:
|
422
|
+
return "STRING";
|
423
|
+
case INTEGER:
|
424
|
+
return "INTEGER";
|
425
|
+
case IDENTIFIER:
|
426
|
+
return "IDENTIFIER";
|
427
|
+
case END_OF_DATA:
|
428
|
+
return "END_OF_DATA";
|
429
|
+
default:
|
430
|
+
return "(unknown)";
|
431
|
+
}
|
432
|
+
}
|
433
|
+
};
|
434
|
+
|
435
|
+
|
436
|
+
enum ValueType {
|
437
|
+
REGEXP_TYPE,
|
438
|
+
STRING_TYPE,
|
439
|
+
INTEGER_TYPE,
|
440
|
+
UNKNOWN_TYPE
|
441
|
+
};
|
442
|
+
|
443
|
+
|
444
|
+
class Context {
|
445
|
+
public:
|
446
|
+
enum FieldIdentifier {
|
447
|
+
URI,
|
448
|
+
CONTROLLER,
|
449
|
+
RESPONSE_TIME
|
450
|
+
};
|
451
|
+
|
452
|
+
virtual string getURI() const = 0;
|
453
|
+
virtual string getController() const = 0;
|
454
|
+
virtual int getResponseTime() const = 0;
|
455
|
+
virtual bool hasHint(const string &name) const = 0;
|
456
|
+
|
457
|
+
string queryStringField(FieldIdentifier id) const {
|
458
|
+
switch (id) {
|
459
|
+
case URI:
|
460
|
+
return getURI();
|
461
|
+
case CONTROLLER:
|
462
|
+
return getController();
|
463
|
+
case RESPONSE_TIME:
|
464
|
+
return toString(getResponseTime());
|
465
|
+
default:
|
466
|
+
return "";
|
467
|
+
}
|
468
|
+
}
|
469
|
+
|
470
|
+
int queryIntField(FieldIdentifier id) const {
|
471
|
+
switch (id) {
|
472
|
+
case RESPONSE_TIME:
|
473
|
+
return getResponseTime();
|
474
|
+
default:
|
475
|
+
return 0;
|
476
|
+
}
|
477
|
+
}
|
478
|
+
|
479
|
+
static ValueType getFieldType(FieldIdentifier id) {
|
480
|
+
switch (id) {
|
481
|
+
case URI:
|
482
|
+
case CONTROLLER:
|
483
|
+
return STRING_TYPE;
|
484
|
+
case RESPONSE_TIME:
|
485
|
+
return INTEGER_TYPE;
|
486
|
+
default:
|
487
|
+
return UNKNOWN_TYPE;
|
488
|
+
}
|
489
|
+
}
|
490
|
+
};
|
491
|
+
|
492
|
+
class SimpleContext: public Context {
|
493
|
+
public:
|
494
|
+
string uri;
|
495
|
+
string controller;
|
496
|
+
int responseTime;
|
497
|
+
set<string> hints;
|
498
|
+
|
499
|
+
SimpleContext() {
|
500
|
+
responseTime = 0;
|
501
|
+
}
|
502
|
+
|
503
|
+
virtual string getURI() const {
|
504
|
+
return uri;
|
505
|
+
}
|
506
|
+
|
507
|
+
virtual string getController() const {
|
508
|
+
return controller;
|
509
|
+
}
|
510
|
+
|
511
|
+
virtual int getResponseTime() const {
|
512
|
+
return responseTime;
|
513
|
+
}
|
514
|
+
|
515
|
+
virtual bool hasHint(const string &name) const {
|
516
|
+
return hints.find(name) != hints.end();
|
517
|
+
}
|
518
|
+
};
|
519
|
+
|
520
|
+
class ContextFromLog: public Context {
|
521
|
+
private:
|
522
|
+
StaticString logData;
|
523
|
+
mutable SimpleContext *parsedData;
|
524
|
+
|
525
|
+
struct ParseState {
|
526
|
+
unsigned long long requestProcessingStart;
|
527
|
+
unsigned long long requestProcessingEnd;
|
528
|
+
};
|
529
|
+
|
530
|
+
static void parseLine(const StaticString &txnId, unsigned long long timestamp,
|
531
|
+
const StaticString &data, SimpleContext &ctx, ParseState &state)
|
532
|
+
{
|
533
|
+
if (startsWith(data, "BEGIN: request processing")) {
|
534
|
+
state.requestProcessingStart = extractEventTimestamp(data);
|
535
|
+
} else if (startsWith(data, "END: request processing")
|
536
|
+
|| startsWith(data, "FAIL: request processing")) {
|
537
|
+
state.requestProcessingEnd = extractEventTimestamp(data);
|
538
|
+
} else if (startsWith(data, "URI: ")) {
|
539
|
+
ctx.uri = data.substr(data.find(':') + 2);
|
540
|
+
} else if (startsWith(data, "Controller action: ")) {
|
541
|
+
StaticString value = data.substr(data.find(':') + 2);
|
542
|
+
size_t pos = value.find('#');
|
543
|
+
if (pos != string::npos) {
|
544
|
+
ctx.controller = value.substr(0, pos);
|
545
|
+
}
|
546
|
+
}
|
547
|
+
}
|
548
|
+
|
549
|
+
static void reallyParse(const StaticString &data, SimpleContext &ctx) {
|
550
|
+
const char *current = data.data();
|
551
|
+
const char *end = data.data() + data.size();
|
552
|
+
ParseState state;
|
553
|
+
|
554
|
+
memset(&state, 0, sizeof(state));
|
555
|
+
while (current < end) {
|
556
|
+
current = skipNewlines(current, end);
|
557
|
+
if (current < end) {
|
558
|
+
const char *endOfLine = findEndOfLine(current, end);
|
559
|
+
StaticString line(current, endOfLine - current);
|
560
|
+
if (!line.empty()) {
|
561
|
+
StaticString txnId;
|
562
|
+
unsigned long long timestamp;
|
563
|
+
unsigned int writeCount;
|
564
|
+
StaticString lineData;
|
565
|
+
|
566
|
+
// If we want to do more complicated analysis we should sort
|
567
|
+
// the lines but for the purposes of ContextFromLog
|
568
|
+
// analyzing the data without sorting is good enough.
|
569
|
+
if (splitLine(line, txnId, timestamp, writeCount, lineData)) {
|
570
|
+
parseLine(txnId, timestamp, lineData, ctx,
|
571
|
+
state);
|
572
|
+
}
|
573
|
+
}
|
574
|
+
current = endOfLine;
|
575
|
+
}
|
576
|
+
}
|
577
|
+
|
578
|
+
if (state.requestProcessingEnd != 0) {
|
579
|
+
ctx.responseTime = int(state.requestProcessingEnd -
|
580
|
+
state.requestProcessingStart);
|
581
|
+
}
|
582
|
+
}
|
583
|
+
|
584
|
+
static bool splitLine(const StaticString &line, StaticString &txnId,
|
585
|
+
unsigned long long ×tamp, unsigned int &writeCount,
|
586
|
+
StaticString &data)
|
587
|
+
{
|
588
|
+
size_t firstDelim = line.find(' ');
|
589
|
+
if (firstDelim == string::npos) {
|
590
|
+
return false;
|
591
|
+
}
|
592
|
+
|
593
|
+
size_t secondDelim = line.find(' ', firstDelim + 1);
|
594
|
+
if (secondDelim == string::npos) {
|
595
|
+
return false;
|
596
|
+
}
|
597
|
+
|
598
|
+
size_t thirdDelim = line.find(' ', secondDelim + 1);
|
599
|
+
if (thirdDelim == string::npos) {
|
600
|
+
return false;
|
601
|
+
}
|
602
|
+
|
603
|
+
txnId = line.substr(0, firstDelim);
|
604
|
+
timestamp = hexatriToULL(line.substr(firstDelim + 1, secondDelim - firstDelim - 1));
|
605
|
+
writeCount = (unsigned int) hexatriToULL(line.substr(secondDelim + 1,
|
606
|
+
thirdDelim - secondDelim - 1));
|
607
|
+
data = line.substr(thirdDelim + 1);
|
608
|
+
return true;
|
609
|
+
}
|
610
|
+
|
611
|
+
static unsigned long long extractEventTimestamp(const StaticString &data) {
|
612
|
+
size_t pos = data.find('(');
|
613
|
+
if (pos == string::npos) {
|
614
|
+
return 0;
|
615
|
+
} else {
|
616
|
+
pos++;
|
617
|
+
size_t start = pos;
|
618
|
+
while (pos < data.size() && isDigit(data[pos])) {
|
619
|
+
pos++;
|
620
|
+
}
|
621
|
+
if (pos >= data.size()) {
|
622
|
+
return 0;
|
623
|
+
} else {
|
624
|
+
return hexatriToULL(data.substr(start, pos - start));
|
625
|
+
}
|
626
|
+
}
|
627
|
+
}
|
628
|
+
|
629
|
+
static bool isNewline(char ch) {
|
630
|
+
return ch == '\n' || ch == '\r';
|
631
|
+
}
|
632
|
+
|
633
|
+
static bool isDigit(char ch) {
|
634
|
+
return ch >= '0' && ch <= '9';
|
635
|
+
}
|
636
|
+
|
637
|
+
static const char *skipNewlines(const char *current, const char *end) {
|
638
|
+
while (current < end && isNewline(*current)) {
|
639
|
+
current++;
|
640
|
+
}
|
641
|
+
return current;
|
642
|
+
}
|
643
|
+
|
644
|
+
static const char *findEndOfLine(const char *current, const char *end) {
|
645
|
+
while (current < end && !isNewline(*current)) {
|
646
|
+
current++;
|
647
|
+
}
|
648
|
+
return current;
|
649
|
+
}
|
650
|
+
|
651
|
+
SimpleContext *parse() const {
|
652
|
+
if (parsedData == NULL) {
|
653
|
+
auto_ptr<SimpleContext> ctx(new SimpleContext());
|
654
|
+
reallyParse(logData, *ctx.get());
|
655
|
+
parsedData = ctx.release();
|
656
|
+
}
|
657
|
+
return parsedData;
|
658
|
+
}
|
659
|
+
|
660
|
+
public:
|
661
|
+
ContextFromLog(const StaticString &logData) {
|
662
|
+
this->logData = logData;
|
663
|
+
parsedData = NULL;
|
664
|
+
}
|
665
|
+
|
666
|
+
~ContextFromLog() {
|
667
|
+
delete parsedData;
|
668
|
+
}
|
669
|
+
|
670
|
+
virtual string getURI() const {
|
671
|
+
return parse()->uri;
|
672
|
+
}
|
673
|
+
|
674
|
+
virtual string getController() const {
|
675
|
+
return parse()->getController();
|
676
|
+
}
|
677
|
+
|
678
|
+
virtual int getResponseTime() const {
|
679
|
+
return parse()->getResponseTime();
|
680
|
+
}
|
681
|
+
|
682
|
+
virtual bool hasHint(const string &name) const {
|
683
|
+
return parse()->hasHint(name);
|
684
|
+
}
|
685
|
+
};
|
686
|
+
|
687
|
+
|
688
|
+
class Filter {
|
689
|
+
private:
|
690
|
+
typedef Tokenizer::Token Token;
|
691
|
+
typedef Tokenizer::TokenType TokenType;
|
692
|
+
|
693
|
+
struct BooleanComponent;
|
694
|
+
struct MultiExpression;
|
695
|
+
struct Comparison;
|
696
|
+
struct FunctionCall;
|
697
|
+
typedef shared_ptr<BooleanComponent> BooleanComponentPtr;
|
698
|
+
typedef shared_ptr<MultiExpression> MultiExpressionPtr;
|
699
|
+
typedef shared_ptr<Comparison> ComparisonPtr;
|
700
|
+
typedef shared_ptr<FunctionCall> FunctionCallPtr;
|
701
|
+
|
702
|
+
struct BooleanComponent {
|
703
|
+
virtual bool evaluate(const Context &ctx) = 0;
|
704
|
+
};
|
705
|
+
|
706
|
+
enum LogicalOperator {
|
707
|
+
AND,
|
708
|
+
OR
|
709
|
+
};
|
710
|
+
|
711
|
+
enum Comparator {
|
712
|
+
MATCHES,
|
713
|
+
NOT_MATCHES,
|
714
|
+
EQUALS,
|
715
|
+
NOT_EQUALS,
|
716
|
+
GREATER_THAN,
|
717
|
+
GREATER_THAN_OR_EQUALS,
|
718
|
+
LESS_THAN,
|
719
|
+
LESS_THAN_OR_EQUALS
|
720
|
+
};
|
721
|
+
|
722
|
+
struct MultiExpression: public BooleanComponent {
|
723
|
+
struct Part {
|
724
|
+
LogicalOperator theOperator;
|
725
|
+
BooleanComponentPtr expression;
|
726
|
+
};
|
727
|
+
|
728
|
+
BooleanComponentPtr firstExpression;
|
729
|
+
vector<Part> rest;
|
730
|
+
|
731
|
+
virtual bool evaluate(const Context &ctx) {
|
732
|
+
bool result = firstExpression->evaluate(ctx);
|
733
|
+
vector<Part>::iterator it = rest.begin(), end = rest.end();
|
734
|
+
while (it != end && result) {
|
735
|
+
Part &part = *it;
|
736
|
+
if (part.theOperator == AND) {
|
737
|
+
result = result && part.expression->evaluate(ctx);
|
738
|
+
} else {
|
739
|
+
result = result || part.expression->evaluate(ctx);
|
740
|
+
}
|
741
|
+
it++;
|
742
|
+
}
|
743
|
+
return result;
|
744
|
+
}
|
745
|
+
};
|
746
|
+
|
747
|
+
struct Negation: public BooleanComponent {
|
748
|
+
BooleanComponentPtr expr;
|
749
|
+
|
750
|
+
Negation(const BooleanComponentPtr &e)
|
751
|
+
: expr(e)
|
752
|
+
{ }
|
753
|
+
|
754
|
+
virtual bool evaluate(const Context &ctx) {
|
755
|
+
return !expr->evaluate(ctx);
|
756
|
+
}
|
757
|
+
};
|
758
|
+
|
759
|
+
struct Value {
|
760
|
+
enum Source {
|
761
|
+
REGEXP_LITERAL,
|
762
|
+
STRING_LITERAL,
|
763
|
+
INTEGER_LITERAL,
|
764
|
+
CONTEXT_FIELD_IDENTIFIER
|
765
|
+
};
|
766
|
+
|
767
|
+
Source source;
|
768
|
+
union {
|
769
|
+
struct {
|
770
|
+
char stringStorage[sizeof(string)];
|
771
|
+
struct {
|
772
|
+
regex_t regexp;
|
773
|
+
int options;
|
774
|
+
} regexp;
|
775
|
+
} stringOrRegexpValue;
|
776
|
+
int intValue;
|
777
|
+
Context::FieldIdentifier contextFieldIdentifier;
|
778
|
+
} u;
|
779
|
+
|
780
|
+
Value() {
|
781
|
+
source = INTEGER_LITERAL;
|
782
|
+
u.intValue = 0;
|
783
|
+
}
|
784
|
+
|
785
|
+
Value(const Value &other) {
|
786
|
+
initializeFrom(other);
|
787
|
+
}
|
788
|
+
|
789
|
+
Value(bool regexp, const StaticString &value, bool caseInsensitive = false) {
|
790
|
+
if (regexp) {
|
791
|
+
source = REGEXP_LITERAL;
|
792
|
+
} else {
|
793
|
+
source = STRING_LITERAL;
|
794
|
+
}
|
795
|
+
new (u.stringOrRegexpValue.stringStorage) string(value.data(), value.size());
|
796
|
+
if (regexp) {
|
797
|
+
int options = REG_EXTENDED;
|
798
|
+
if (caseInsensitive) {
|
799
|
+
options |= REG_ICASE;
|
800
|
+
u.stringOrRegexpValue.regexp.options |=
|
801
|
+
Tokenizer::REGEXP_OPTION_CASE_INSENSITIVE;
|
802
|
+
}
|
803
|
+
regcomp(&u.stringOrRegexpValue.regexp.regexp,
|
804
|
+
value.toString().c_str(),
|
805
|
+
options);
|
806
|
+
}
|
807
|
+
}
|
808
|
+
|
809
|
+
Value(int val) {
|
810
|
+
source = INTEGER_LITERAL;
|
811
|
+
u.intValue = val;
|
812
|
+
}
|
813
|
+
|
814
|
+
Value(Context::FieldIdentifier identifier) {
|
815
|
+
source = CONTEXT_FIELD_IDENTIFIER;
|
816
|
+
u.contextFieldIdentifier = identifier;
|
817
|
+
}
|
818
|
+
|
819
|
+
~Value() {
|
820
|
+
freeStorage();
|
821
|
+
}
|
822
|
+
|
823
|
+
Value &operator=(const Value &other) {
|
824
|
+
freeStorage();
|
825
|
+
initializeFrom(other);
|
826
|
+
return *this;
|
827
|
+
}
|
828
|
+
|
829
|
+
regex_t *getRegexpValue(const Context &ctx) const {
|
830
|
+
if (source == REGEXP_LITERAL) {
|
831
|
+
return &storedRegexp();
|
832
|
+
} else {
|
833
|
+
return NULL;
|
834
|
+
}
|
835
|
+
}
|
836
|
+
|
837
|
+
string getStringValue(const Context &ctx) const {
|
838
|
+
switch (source) {
|
839
|
+
case REGEXP_LITERAL:
|
840
|
+
case STRING_LITERAL:
|
841
|
+
return storedString();
|
842
|
+
case INTEGER_LITERAL:
|
843
|
+
return toString(u.intValue);
|
844
|
+
case CONTEXT_FIELD_IDENTIFIER:
|
845
|
+
return ctx.queryStringField(u.contextFieldIdentifier);
|
846
|
+
default:
|
847
|
+
return "";
|
848
|
+
}
|
849
|
+
}
|
850
|
+
|
851
|
+
int getIntegerValue(const Context &ctx) const {
|
852
|
+
switch (source) {
|
853
|
+
case REGEXP_LITERAL:
|
854
|
+
return 0;
|
855
|
+
case STRING_LITERAL:
|
856
|
+
return atoi(storedString());
|
857
|
+
case INTEGER_LITERAL:
|
858
|
+
return u.intValue;
|
859
|
+
case CONTEXT_FIELD_IDENTIFIER:
|
860
|
+
return ctx.queryIntField(u.contextFieldIdentifier);
|
861
|
+
default:
|
862
|
+
return 0;
|
863
|
+
}
|
864
|
+
}
|
865
|
+
|
866
|
+
ValueType getType() const {
|
867
|
+
switch (source) {
|
868
|
+
case REGEXP_LITERAL:
|
869
|
+
return REGEXP_TYPE;
|
870
|
+
case STRING_LITERAL:
|
871
|
+
return STRING_TYPE;
|
872
|
+
case INTEGER_LITERAL:
|
873
|
+
return INTEGER_TYPE;
|
874
|
+
case CONTEXT_FIELD_IDENTIFIER:
|
875
|
+
return Context::getFieldType(u.contextFieldIdentifier);
|
876
|
+
default:
|
877
|
+
return UNKNOWN_TYPE;
|
878
|
+
}
|
879
|
+
}
|
880
|
+
|
881
|
+
private:
|
882
|
+
const string &storedString() const {
|
883
|
+
return *((string *) u.stringOrRegexpValue.stringStorage);
|
884
|
+
}
|
885
|
+
|
886
|
+
regex_t &storedRegexp() const {
|
887
|
+
return (regex_t &) u.stringOrRegexpValue.regexp.regexp;
|
888
|
+
}
|
889
|
+
|
890
|
+
void freeStorage() {
|
891
|
+
if (source == REGEXP_LITERAL || source == STRING_LITERAL) {
|
892
|
+
storedString().~string();
|
893
|
+
if (source == REGEXP_LITERAL) {
|
894
|
+
regfree(&storedRegexp());
|
895
|
+
}
|
896
|
+
}
|
897
|
+
}
|
898
|
+
|
899
|
+
void initializeFrom(const Value &other) {
|
900
|
+
int options;
|
901
|
+
source = other.source;
|
902
|
+
switch (source) {
|
903
|
+
case REGEXP_LITERAL:
|
904
|
+
new (u.stringOrRegexpValue.stringStorage) string(other.storedString());
|
905
|
+
options = REG_EXTENDED;
|
906
|
+
if (other.u.stringOrRegexpValue.regexp.options & Tokenizer::REGEXP_OPTION_CASE_INSENSITIVE) {
|
907
|
+
options |= REG_ICASE;
|
908
|
+
}
|
909
|
+
regcomp(&u.stringOrRegexpValue.regexp.regexp,
|
910
|
+
storedString().c_str(),
|
911
|
+
options);
|
912
|
+
u.stringOrRegexpValue.regexp.options = other.u.stringOrRegexpValue.regexp.options;
|
913
|
+
break;
|
914
|
+
case STRING_LITERAL:
|
915
|
+
new (u.stringOrRegexpValue.stringStorage) string(other.storedString());
|
916
|
+
break;
|
917
|
+
case INTEGER_LITERAL:
|
918
|
+
u.intValue = other.u.intValue;
|
919
|
+
break;
|
920
|
+
case CONTEXT_FIELD_IDENTIFIER:
|
921
|
+
u.contextFieldIdentifier = other.u.contextFieldIdentifier;
|
922
|
+
break;
|
923
|
+
}
|
924
|
+
}
|
925
|
+
};
|
926
|
+
|
927
|
+
struct Comparison: public BooleanComponent {
|
928
|
+
Value subject;
|
929
|
+
Comparator comparator;
|
930
|
+
Value object;
|
931
|
+
|
932
|
+
virtual bool evaluate(const Context &ctx) {
|
933
|
+
switch (subject.getType()) {
|
934
|
+
case STRING_TYPE:
|
935
|
+
return compareStringOrRegexp(subject.getStringValue(ctx), ctx);
|
936
|
+
case INTEGER_TYPE:
|
937
|
+
return compareInteger(subject.getIntegerValue(ctx), ctx);
|
938
|
+
default:
|
939
|
+
// error
|
940
|
+
return false;
|
941
|
+
}
|
942
|
+
}
|
943
|
+
|
944
|
+
private:
|
945
|
+
bool compareStringOrRegexp(const string &str, const Context &ctx) {
|
946
|
+
switch (comparator) {
|
947
|
+
case MATCHES:
|
948
|
+
return regexec(object.getRegexpValue(ctx), str.c_str(), 0, NULL, 0) == 0;
|
949
|
+
case NOT_MATCHES:
|
950
|
+
return regexec(object.getRegexpValue(ctx), str.c_str(), 0, NULL, 0) != 0;
|
951
|
+
case EQUALS:
|
952
|
+
return str == object.getStringValue(ctx);
|
953
|
+
case NOT_EQUALS:
|
954
|
+
return str != object.getStringValue(ctx);
|
955
|
+
default:
|
956
|
+
// error
|
957
|
+
return false;
|
958
|
+
}
|
959
|
+
}
|
960
|
+
|
961
|
+
bool compareInteger(int value, const Context &ctx) {
|
962
|
+
int value2 = object.getIntegerValue(ctx);
|
963
|
+
switch (comparator) {
|
964
|
+
case EQUALS:
|
965
|
+
return value == value2;
|
966
|
+
case NOT_EQUALS:
|
967
|
+
return value != value2;
|
968
|
+
case GREATER_THAN:
|
969
|
+
return value > value2;
|
970
|
+
case GREATER_THAN_OR_EQUALS:
|
971
|
+
return value >= value2;
|
972
|
+
case LESS_THAN:
|
973
|
+
return value < value2;
|
974
|
+
case LESS_THAN_OR_EQUALS:
|
975
|
+
return value <= value2;
|
976
|
+
default:
|
977
|
+
// error
|
978
|
+
return false;
|
979
|
+
}
|
980
|
+
}
|
981
|
+
};
|
982
|
+
|
983
|
+
struct FunctionCall: public BooleanComponent {
|
984
|
+
vector<Value> arguments;
|
985
|
+
|
986
|
+
virtual void checkArguments() const = 0;
|
987
|
+
};
|
988
|
+
|
989
|
+
struct StartsWithFunctionCall: public FunctionCall {
|
990
|
+
virtual bool evaluate(const Context &ctx) {
|
991
|
+
return startsWith(arguments[0].getStringValue(ctx),
|
992
|
+
arguments[1].getStringValue(ctx));
|
993
|
+
}
|
994
|
+
|
995
|
+
virtual void checkArguments() const {
|
996
|
+
if (arguments.size() != 2) {
|
997
|
+
throw SyntaxError("you passed " + toString(arguments.size()) +
|
998
|
+
" argument(s) to starts_with(), but it accepts exactly 2 arguments");
|
999
|
+
}
|
1000
|
+
}
|
1001
|
+
};
|
1002
|
+
|
1003
|
+
struct HasHintFunctionCall: public FunctionCall {
|
1004
|
+
virtual bool evaluate(const Context &ctx) {
|
1005
|
+
return ctx.hasHint(arguments[0].getStringValue(ctx));
|
1006
|
+
}
|
1007
|
+
|
1008
|
+
virtual void checkArguments() const {
|
1009
|
+
if (arguments.size() != 1) {
|
1010
|
+
throw SyntaxError("you passed " + toString(arguments.size()) +
|
1011
|
+
" argument(s) to has_hint(), but it accepts exactly 1 argument");
|
1012
|
+
}
|
1013
|
+
}
|
1014
|
+
};
|
1015
|
+
|
1016
|
+
Tokenizer tokenizer;
|
1017
|
+
BooleanComponentPtr root;
|
1018
|
+
Token lookahead;
|
1019
|
+
|
1020
|
+
static bool isLiteralToken(const Token &token) {
|
1021
|
+
return token.type == Tokenizer::REGEXP
|
1022
|
+
|| token.type == Tokenizer::STRING
|
1023
|
+
|| token.type == Tokenizer::INTEGER;
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
static bool isValueToken(const Token &token) {
|
1027
|
+
return isLiteralToken(token) || token.type == Tokenizer::IDENTIFIER;
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
static bool isLogicalOperatorToken(const Token &token) {
|
1031
|
+
return token.type == Tokenizer::AND
|
1032
|
+
|| token.type == Tokenizer::OR;
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
static bool comparatorAcceptsValueTypes(Comparator cmp, ValueType subjectType, ValueType objectType) {
|
1036
|
+
switch (cmp) {
|
1037
|
+
case MATCHES:
|
1038
|
+
case NOT_MATCHES:
|
1039
|
+
return subjectType == STRING_TYPE && objectType == REGEXP_TYPE;
|
1040
|
+
case EQUALS:
|
1041
|
+
case NOT_EQUALS:
|
1042
|
+
return (subjectType == STRING_TYPE || subjectType == INTEGER_TYPE)
|
1043
|
+
&& subjectType == objectType;
|
1044
|
+
case GREATER_THAN:
|
1045
|
+
case GREATER_THAN_OR_EQUALS:
|
1046
|
+
case LESS_THAN:
|
1047
|
+
case LESS_THAN_OR_EQUALS:
|
1048
|
+
return subjectType == INTEGER_TYPE && objectType == INTEGER_TYPE;
|
1049
|
+
default:
|
1050
|
+
abort();
|
1051
|
+
return false; // Shut up compiler warning.
|
1052
|
+
}
|
1053
|
+
}
|
1054
|
+
|
1055
|
+
static string unescapeCString(const StaticString &data) {
|
1056
|
+
string result;
|
1057
|
+
result.reserve(data.size());
|
1058
|
+
|
1059
|
+
const char *current = data.data();
|
1060
|
+
const char *end = data.data() + data.size();
|
1061
|
+
while (current < end) {
|
1062
|
+
char ch = *current;
|
1063
|
+
if (ch == '\\') {
|
1064
|
+
current++;
|
1065
|
+
if (current < end) {
|
1066
|
+
ch = *current;
|
1067
|
+
switch (ch) {
|
1068
|
+
case 'r':
|
1069
|
+
result.append(1, '\r');
|
1070
|
+
break;
|
1071
|
+
case 'n':
|
1072
|
+
result.append(1, '\n');
|
1073
|
+
break;
|
1074
|
+
case 't':
|
1075
|
+
result.append(1, '\t');
|
1076
|
+
break;
|
1077
|
+
default:
|
1078
|
+
result.append(1, ch);
|
1079
|
+
break;
|
1080
|
+
}
|
1081
|
+
current++;
|
1082
|
+
}
|
1083
|
+
} else {
|
1084
|
+
result.append(1, ch);
|
1085
|
+
current++;
|
1086
|
+
}
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
return result;
|
1090
|
+
}
|
1091
|
+
|
1092
|
+
Token peek() const {
|
1093
|
+
return lookahead;
|
1094
|
+
}
|
1095
|
+
|
1096
|
+
bool peek(Tokenizer::TokenType type) const {
|
1097
|
+
return lookahead.type == type;
|
1098
|
+
}
|
1099
|
+
|
1100
|
+
Token match(TokenType type) {
|
1101
|
+
if (lookahead.type == type) {
|
1102
|
+
return match();
|
1103
|
+
} else {
|
1104
|
+
raiseSyntaxError("Expected a " + Tokenizer::typeToString(type) +
|
1105
|
+
" token, but got " + lookahead.toString(),
|
1106
|
+
lookahead);
|
1107
|
+
return Token(); // Shut up compiler warning.
|
1108
|
+
}
|
1109
|
+
}
|
1110
|
+
|
1111
|
+
Token match() {
|
1112
|
+
Token old = lookahead;
|
1113
|
+
lookahead = tokenizer.getNext();
|
1114
|
+
return old;
|
1115
|
+
}
|
1116
|
+
|
1117
|
+
void raiseSyntaxError(const string &msg = "", const Token &token = Token()) {
|
1118
|
+
if (token.type != Tokenizer::NONE) {
|
1119
|
+
string message = "at character " + toString(token.pos + 1);
|
1120
|
+
if (!msg.empty()) {
|
1121
|
+
message.append(": ");
|
1122
|
+
message.append(msg);
|
1123
|
+
}
|
1124
|
+
throw SyntaxError(message);
|
1125
|
+
} else {
|
1126
|
+
throw SyntaxError(msg);
|
1127
|
+
}
|
1128
|
+
}
|
1129
|
+
|
1130
|
+
BooleanComponentPtr matchMultiExpression() {
|
1131
|
+
MultiExpressionPtr result = make_shared<MultiExpression>();
|
1132
|
+
|
1133
|
+
result->firstExpression = matchExpression();
|
1134
|
+
while (isLogicalOperatorToken(peek())) {
|
1135
|
+
MultiExpression::Part part;
|
1136
|
+
part.theOperator = matchOperator();
|
1137
|
+
part.expression = matchExpression();
|
1138
|
+
result->rest.push_back(part);
|
1139
|
+
}
|
1140
|
+
|
1141
|
+
return result;
|
1142
|
+
}
|
1143
|
+
|
1144
|
+
BooleanComponentPtr matchExpression() {
|
1145
|
+
bool negate = false;
|
1146
|
+
|
1147
|
+
if (peek(Tokenizer::NOT)) {
|
1148
|
+
match();
|
1149
|
+
negate = true;
|
1150
|
+
}
|
1151
|
+
|
1152
|
+
Token next = peek();
|
1153
|
+
if (next.type == Tokenizer::LPARENTHESIS) {
|
1154
|
+
BooleanComponentPtr expression = matchMultiExpression();
|
1155
|
+
match(Tokenizer::RPARENTHESIS);
|
1156
|
+
if (negate) {
|
1157
|
+
return make_shared<Negation>(expression);
|
1158
|
+
} else {
|
1159
|
+
return expression;
|
1160
|
+
}
|
1161
|
+
} else if (isValueToken(next)) {
|
1162
|
+
BooleanComponentPtr component;
|
1163
|
+
next = match();
|
1164
|
+
|
1165
|
+
if (peek(Tokenizer::LPARENTHESIS)) {
|
1166
|
+
component = matchFunctionCall(next);
|
1167
|
+
} else {
|
1168
|
+
component = matchComparison(next);
|
1169
|
+
}
|
1170
|
+
|
1171
|
+
if (negate) {
|
1172
|
+
return make_shared<Negation>(component);
|
1173
|
+
} else {
|
1174
|
+
return component;
|
1175
|
+
}
|
1176
|
+
} else {
|
1177
|
+
raiseSyntaxError("expected a left parenthesis or an identifier", next);
|
1178
|
+
return BooleanComponentPtr(); // Shut up compiler warning.
|
1179
|
+
}
|
1180
|
+
}
|
1181
|
+
|
1182
|
+
ComparisonPtr matchComparison(const Token &subjectToken) {
|
1183
|
+
ComparisonPtr comparison = make_shared<Comparison>();
|
1184
|
+
comparison->subject = matchValue(subjectToken);
|
1185
|
+
comparison->comparator = matchComparator();
|
1186
|
+
comparison->object = matchValue(match());
|
1187
|
+
if (!comparatorAcceptsValueTypes(comparison->comparator, comparison->subject.getType(), comparison->object.getType())) {
|
1188
|
+
raiseSyntaxError("the comparator cannot operate on the given combination of types", subjectToken);
|
1189
|
+
}
|
1190
|
+
return comparison;
|
1191
|
+
}
|
1192
|
+
|
1193
|
+
FunctionCallPtr matchFunctionCall(const Token &id) {
|
1194
|
+
FunctionCallPtr function;
|
1195
|
+
|
1196
|
+
if (id.rawValue == "starts_with") {
|
1197
|
+
function = make_shared<StartsWithFunctionCall>();
|
1198
|
+
} else if (id.rawValue == "has_hint") {
|
1199
|
+
function = make_shared<HasHintFunctionCall>();
|
1200
|
+
} else {
|
1201
|
+
raiseSyntaxError("unknown function '" + id.rawValue + "'", id);
|
1202
|
+
}
|
1203
|
+
|
1204
|
+
match(Tokenizer::LPARENTHESIS);
|
1205
|
+
if (isValueToken(peek())) {
|
1206
|
+
function->arguments.push_back(matchValue(match()));
|
1207
|
+
while (peek(Tokenizer::COMMA)) {
|
1208
|
+
match();
|
1209
|
+
function->arguments.push_back(matchValue(match()));
|
1210
|
+
}
|
1211
|
+
}
|
1212
|
+
match(Tokenizer::RPARENTHESIS);
|
1213
|
+
function->checkArguments();
|
1214
|
+
return function;
|
1215
|
+
}
|
1216
|
+
|
1217
|
+
Value matchValue(const Token &token) {
|
1218
|
+
if (isLiteralToken(token)) {
|
1219
|
+
return matchLiteral(token);
|
1220
|
+
} else if (token.type == Tokenizer::IDENTIFIER) {
|
1221
|
+
return matchContextFieldIdentifier(token);
|
1222
|
+
} else {
|
1223
|
+
raiseSyntaxError("", token);
|
1224
|
+
return Value(); // Shut up compiler warning.
|
1225
|
+
}
|
1226
|
+
}
|
1227
|
+
|
1228
|
+
LogicalOperator matchOperator() {
|
1229
|
+
if (peek(Tokenizer::AND)) {
|
1230
|
+
match();
|
1231
|
+
return AND;
|
1232
|
+
} else if (peek(Tokenizer::OR)) {
|
1233
|
+
match();
|
1234
|
+
return OR;
|
1235
|
+
} else {
|
1236
|
+
raiseSyntaxError("", peek());
|
1237
|
+
return AND; // Shut up compiler warning.
|
1238
|
+
}
|
1239
|
+
}
|
1240
|
+
|
1241
|
+
Comparator matchComparator() {
|
1242
|
+
if (peek(Tokenizer::MATCHES)) {
|
1243
|
+
match();
|
1244
|
+
return MATCHES;
|
1245
|
+
} else if (peek(Tokenizer::NOT_MATCHES)) {
|
1246
|
+
match();
|
1247
|
+
return NOT_MATCHES;
|
1248
|
+
} else if (peek(Tokenizer::EQUALS)) {
|
1249
|
+
match();
|
1250
|
+
return EQUALS;
|
1251
|
+
} else if (peek(Tokenizer::NOT_EQUALS)) {
|
1252
|
+
match();
|
1253
|
+
return NOT_EQUALS;
|
1254
|
+
} else if (peek(Tokenizer::GREATER_THAN)) {
|
1255
|
+
match();
|
1256
|
+
return GREATER_THAN;
|
1257
|
+
} else if (peek(Tokenizer::GREATER_THAN_OR_EQUALS)) {
|
1258
|
+
match();
|
1259
|
+
return GREATER_THAN_OR_EQUALS;
|
1260
|
+
} else if (peek(Tokenizer::LESS_THAN)) {
|
1261
|
+
match();
|
1262
|
+
return LESS_THAN;
|
1263
|
+
} else if (peek(Tokenizer::LESS_THAN_OR_EQUALS)) {
|
1264
|
+
match();
|
1265
|
+
return LESS_THAN_OR_EQUALS;
|
1266
|
+
} else {
|
1267
|
+
raiseSyntaxError("", peek());
|
1268
|
+
return MATCHES; // Shut up compiler warning.
|
1269
|
+
}
|
1270
|
+
}
|
1271
|
+
|
1272
|
+
Value matchLiteral(const Token &token) {
|
1273
|
+
if (token.type == Tokenizer::REGEXP) {
|
1274
|
+
return Value(true, unescapeCString(token.rawValue.substr(1, token.rawValue.size() - 2)),
|
1275
|
+
token.options & Tokenizer::REGEXP_OPTION_CASE_INSENSITIVE);
|
1276
|
+
} else if (token.type == Tokenizer::STRING) {
|
1277
|
+
return Value(false, unescapeCString(token.rawValue.substr(1, token.rawValue.size() - 2)));
|
1278
|
+
} else if (token.type == Tokenizer::INTEGER) {
|
1279
|
+
return Value(atoi(token.rawValue.toString()));
|
1280
|
+
} else {
|
1281
|
+
raiseSyntaxError("regular expression, string or integer expected", token);
|
1282
|
+
return Value(); // Shut up compiler warning.
|
1283
|
+
}
|
1284
|
+
}
|
1285
|
+
|
1286
|
+
Value matchContextFieldIdentifier(const Token &token) {
|
1287
|
+
if (token.rawValue == "uri") {
|
1288
|
+
return Value(Context::URI);
|
1289
|
+
} else if (token.rawValue == "controller") {
|
1290
|
+
return Value(Context::CONTROLLER);
|
1291
|
+
} else if (token.rawValue == "response_time") {
|
1292
|
+
return Value(Context::RESPONSE_TIME);
|
1293
|
+
} else {
|
1294
|
+
raiseSyntaxError("unknown field '" + token.rawValue + "'", token);
|
1295
|
+
return Value(); // Shut up compiler warning.
|
1296
|
+
}
|
1297
|
+
}
|
1298
|
+
|
1299
|
+
public:
|
1300
|
+
Filter(const StaticString &source, bool debug = false)
|
1301
|
+
: tokenizer(source, debug)
|
1302
|
+
{
|
1303
|
+
lookahead = tokenizer.getNext();
|
1304
|
+
root = matchMultiExpression();
|
1305
|
+
match(Tokenizer::END_OF_DATA);
|
1306
|
+
}
|
1307
|
+
|
1308
|
+
bool run(const Context &ctx) {
|
1309
|
+
return root->evaluate(ctx);
|
1310
|
+
}
|
1311
|
+
};
|
1312
|
+
|
1313
|
+
|
1314
|
+
} // namespace FilterSupport
|
1315
|
+
} // namespace Passenger
|
1316
|
+
|
1317
|
+
#endif /* _PASSENGER_FILTER_SUPPORT_H_ */
|