From 89c16e8464cd7aa4a82dc3c94782d5d6d567725b Mon Sep 17 00:00:00 2001 From: Matthew Carney Date: Sun, 29 Oct 2023 19:55:29 +0000 Subject: [PATCH] Full requested url will now be shown by default Reduced default interval to 1 second Fixed a bug where an extra request would be sent when using --n argument Removed interval when on last request Improved incorrect argument check Added option to only show hostnames Fixed colour bleeding on error messages Added proper help message Added specific build platforms for x86 and x64 Improved tls and ssl setup Added more tests Bump version to 1.1 --- HttpPing.Tests/HttpPing.Tests.csproj | 26 +++++ HttpPing.Tests/ProgramTests.cs | 155 ++++++++++++++++++++++++--- HttpPing.sln | 20 ++++ HttpPing/HttpPing.csproj | 28 +++++ HttpPing/Program.cs | 145 ++++++++++++++++++------- HttpPing/Properties/AssemblyInfo.cs | 4 +- README.md | 7 +- 7 files changed, 327 insertions(+), 58 deletions(-) diff --git a/HttpPing.Tests/HttpPing.Tests.csproj b/HttpPing.Tests/HttpPing.Tests.csproj index 192d4e2..8421e75 100644 --- a/HttpPing.Tests/HttpPing.Tests.csproj +++ b/HttpPing.Tests/HttpPing.Tests.csproj @@ -38,6 +38,32 @@ prompt 4 + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + 7.3 + prompt + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + + + x86 + bin\x86\Debug\ + + + x86 + bin\x86\Release\ + ..\packages\MSTest.TestFramework.2.2.10\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll diff --git a/HttpPing.Tests/ProgramTests.cs b/HttpPing.Tests/ProgramTests.cs index 5f2b55e..56eed9c 100644 --- a/HttpPing.Tests/ProgramTests.cs +++ b/HttpPing.Tests/ProgramTests.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Text; +using web_ping; namespace HttpPing.Tests { @@ -22,48 +23,172 @@ public void Init() } [TestMethod] - public void When_params_are_empty_should_return_usage_test() + public void When_params_are_empty_should_return_help_test() { - var expectedValue = "USAGE: HttpPing web_address [-d] [-t] [-ts] [-https] [-n count] [-i interval] [-l] [-nc] [-r redirectCount] \r\n"; + string expectedValue = Program.HelpMessage.Replace("\r\n", ""); string[] parameters = new string[]{}; - web_ping.Program.Main(parameters, _environmentServiceForTestPurpose); + web_ping.Program.Run(parameters, _environmentServiceForTestPurpose); + string actualOutput = _consoleOutput.ToString().Replace("\r\n", ""); + Assert.IsTrue(actualOutput.ToString().Equals(expectedValue)); + } + + [TestMethod] + public void When_show_help_argument_show_help_message_test() + { + string expectedValue = Program.HelpMessage.Replace("\r\n", ""); - Assert.IsTrue(_consoleOutput.ToString().Equals(expectedValue)); + string[] parameters = new string[] { "-help" }; + + web_ping.Program.Run(parameters, _environmentServiceForTestPurpose); + string actualOutput = _consoleOutput.ToString().Replace("\r\n", ""); + + Assert.IsTrue(actualOutput.ToString().Equals(expectedValue)); } + [TestMethod] + public void When_full_uri_path_should_be_displayed_test() + { + string expectedOutput = +@"Sending HTTPS requests to [https://github.com/Killeroo]: +Response from https://github.com/Killeroo: Code=200:OK Size=-1 +"; + + string[] parameters = new string[] { "https://github.com/Killeroo", + "-n", "1"}; + + web_ping.Program.Run(parameters, _environmentServiceForTestPurpose); + + Assert.IsTrue(_consoleOutput.ToString().Equals(expectedOutput)); + } [TestMethod] - public void When_params_webaddress_simple_count_1_interval_500ms_should_response_test() + public void When_hosts_only_argument_uri_path_should_not_be_displayed_test() { - var expectedValue = "Sending HTTP requests to [https://www.starwars.com]:\r\nResponse from www.starwars.com: Code=200:OK Size=-1\r\nResponse from www.starwars.com: Code=200:OK Size=-1\r\n"; + string expectedOutput = +@"Sending HTTPS requests to [https://github.com/Killeroo]: +Response from github.com: Code=200:OK Size=-1 +"; - string[] parameters = new string[] { "https://www.starwars.com", + string[] parameters = new string[] { "https://github.com/Killeroo", "-n", "1", - "-i", "500" }; + "-h"}; - web_ping.Program.Main(parameters, _environmentServiceForTestPurpose); + web_ping.Program.Run(parameters, _environmentServiceForTestPurpose); - Assert.IsTrue(_consoleOutput.ToString().Equals(expectedValue)); + Assert.IsTrue(_consoleOutput.ToString().Equals(expectedOutput)); } + [TestMethod] + public void When_bad_argument_throw_exception_test() + { + string expectedOutput = +@"Incorrect argument found +" + Program.UsageMessage + @" +"; + + string[] parameters = new string[] { "https://github.com/Killeroo", + "-n", "1", + "-notrealargument"}; + + web_ping.Program.Run(parameters, _environmentServiceForTestPurpose); + + Assert.IsTrue(_consoleOutput.ToString().Equals(expectedOutput)); + } [TestMethod] - public void When_params_webaddress_contains_dash_should_not_return_error_test() + public void When_request_count_is_one_only_one_request_is_sent_test() { - //var expectedValue = "Incorrect argument found\r\nUSAGE: HttpPing web_address [-d] [-t] [-ts] [-https] [-n count] [-i interval] [-l] [-nc] [-r redirectCount] \r\n"; - var expectedValue = "Sending HTTP requests to [https://www.star-wars.com]:\r\nResponse from www.star-wars.com: Code=200:OK Size=-1\r\nResponse from www.star-wars.com: Code=200:OK Size=-1\r\n"; + string expectedOutput = +@"Sending HTTPS requests to [https://github.com/Killeroo]: +Response from https://github.com/Killeroo: Code=200:OK Size=-1 +".Replace("\r\n", ""); + + string[] parameters = new string[] { "https://github.com/Killeroo", + "-n", "1"}; + + web_ping.Program.Run(parameters, _environmentServiceForTestPurpose); + string actualOutput = _consoleOutput.ToString().Replace("\r\n", ""); + + Assert.IsTrue(actualOutput.Equals(expectedOutput)); + } + [TestMethod] + public void When_request_count_is_two_then_two_requests_are_sent_test() + { + string expectedOutput = +@"Sending HTTPS requests to [https://github.com/]: +Response from https://github.com/: Code=200:OK Size=-1 +Response from https://github.com/: Code=200:OK Size=-1 +".Replace("\r\n", ""); + + string[] parameters = new string[] { "https://github.com/", + "-n", "2"}; + + web_ping.Program.Run(parameters, _environmentServiceForTestPurpose); + string actualOutput = _consoleOutput.ToString().Replace("\r\n", ""); + + Assert.IsTrue(actualOutput.Equals(expectedOutput)); + } + + [TestMethod] + public void When_no_params_url_is_google_http_should_respond_test() + { + string expectedOutput = +@"Sending HTTP requests to [http://www.google.com]: +Response from http://www.google.com/: Code=200:OK Size=-1 +Response from http://www.google.com/: Code=200:OK Size=-1 +Response from http://www.google.com/: Code=200:OK Size=-1 +Response from http://www.google.com/: Code=200:OK Size=-1 +Response from http://www.google.com/: Code=200:OK Size=-1 +".Replace("\r\n", ""); + + string[] parameters = new string[] { "http://www.google.com" }; + + web_ping.Program.Run(parameters, _environmentServiceForTestPurpose); + string actualOutput = _consoleOutput.ToString().Replace("\r\n", ""); + + Assert.IsTrue(actualOutput.Equals(expectedOutput)); + } + + [TestMethod] + public void When_no_params_url_is_google_https_should_respond_test() + { + string expectedOutput = +@"Sending HTTPS requests to [https://www.google.com]: +Response from https://www.google.com/: Code=200:OK Size=-1 +Response from https://www.google.com/: Code=200:OK Size=-1 +Response from https://www.google.com/: Code=200:OK Size=-1 +Response from https://www.google.com/: Code=200:OK Size=-1 +Response from https://www.google.com/: Code=200:OK Size=-1 +".Replace("\r\n", ""); + + string[] parameters = new string[] { "https://www.google.com" }; + + web_ping.Program.Run(parameters, _environmentServiceForTestPurpose); + string actualOutput = _consoleOutput.ToString().Replace("\r\n", ""); + + Assert.IsTrue(actualOutput.Equals(expectedOutput)); + } + + [TestMethod] + public void When_params_webaddress_contains_dash_should_not_return_error_test() + { + string expectedOutput = +@"Sending HTTPS requests to [https://www.star-wars.com]: +Response from https://www.starwars.com/: Code=200:OK Size=-1 +".Replace("\r\n", ""); string[] parameters = new string[] { "https://www.star-wars.com", "-n", "1", "-i", "500" }; - web_ping.Program.Main(parameters, _environmentServiceForTestPurpose); + web_ping.Program.Run(parameters, _environmentServiceForTestPurpose); + string actualOutput = _consoleOutput.ToString().Replace("\r\n", ""); - Assert.IsTrue(_consoleOutput.ToString().Equals(expectedValue)); + Assert.IsTrue(actualOutput.Equals(expectedOutput)); } } } diff --git a/HttpPing.sln b/HttpPing.sln index 4c9b8e6..8fa54b8 100644 --- a/HttpPing.sln +++ b/HttpPing.sln @@ -10,17 +10,37 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Debug|x64.ActiveCfg = Debug|x64 + {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Debug|x64.Build.0 = Debug|x64 + {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Debug|x86.ActiveCfg = Debug|x86 + {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Debug|x86.Build.0 = Debug|x86 {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Release|Any CPU.ActiveCfg = Release|Any CPU {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Release|Any CPU.Build.0 = Release|Any CPU + {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Release|x64.ActiveCfg = Release|x64 + {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Release|x64.Build.0 = Release|x64 + {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Release|x86.ActiveCfg = Release|x86 + {D4D36105-4AC4-4AD6-92DD-5EF9A05AF312}.Release|x86.Build.0 = Release|x86 {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Debug|x64.ActiveCfg = Debug|x64 + {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Debug|x64.Build.0 = Debug|x64 + {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Debug|x86.ActiveCfg = Debug|x86 + {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Debug|x86.Build.0 = Debug|x86 {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Release|Any CPU.ActiveCfg = Release|Any CPU {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Release|Any CPU.Build.0 = Release|Any CPU + {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Release|x64.ActiveCfg = Release|x64 + {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Release|x64.Build.0 = Release|x64 + {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Release|x86.ActiveCfg = Release|x86 + {6006C2E4-372B-4375-904E-A4E0D67BC2F2}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/HttpPing/HttpPing.csproj b/HttpPing/HttpPing.csproj index 77b987a..d0d8d18 100644 --- a/HttpPing/HttpPing.csproj +++ b/HttpPing/HttpPing.csproj @@ -33,6 +33,34 @@ prompt 4 + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + 7.3 + prompt + true + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + true + + + x86 + bin\x86\Debug\ + + + x86 + bin\x86\Release\ + diff --git a/HttpPing/Program.cs b/HttpPing/Program.cs index 55b5799..755b5ce 100644 --- a/HttpPing/Program.cs +++ b/HttpPing/Program.cs @@ -6,7 +6,8 @@ using System.Runtime.CompilerServices; using HttpPing.Interfaces; using HttpPing; - +using System.Security.Cryptography.X509Certificates; +using System.Net.Security; namespace web_ping { @@ -19,43 +20,72 @@ class Program private static bool NoColor { get; set; } = false; private static bool UseCommonLogFormat { get; set; } = false; private static bool ForceHttps { get; set; } = false; - private static int Interval { get; set; } = 30000; + private static bool ShowOnlyHostName { get; set; } = false; + private static int Interval { get; set; } = 1000; private static int Requests { get; set; } = 5; private static int Redirects { get; set; } = 4; - // Console Properties - private static ConsoleColor DefaultBackgroundColor { get; set; } - private static ConsoleColor DefaultForegroundColor { get; set; } - // Constants - private const string Usage = "USAGE: HttpPing web_address [-d] [-t] [-ts] [-https] [-n count] [-i interval] [-l] [-nc] [-r redirectCount] "; + public const string UsageMessage = "HttpPing.exe address [-d] [-t] [-ts] [-h] [-n count] [-i interval] [-r redirectCount] [-https]"; + public const string HelpMessage = +@"HttpPing 1.1 + +Description: + Send http requests as if they were pings! + +Usage: + HttpPing web_address [-d] [-t] [-ts] [-h] [-https] [-n count] [-i interval] [-l] [-nc] [-r redirectCount] + +Arguments: + [-d] Detailed mode: shows server and cache info + [-t] Infinite mode: Keep sending requests until stopped (Ctrl-C) + [-n count] Send a specific number of requests + [-ts] Include timestamp of when each request was sent + [-h] Only show the hostname in responses + [-i interval] Interval between each request in milliseconds (default 30000) + [-l] Use Common Log Format (https://en.wikipedia.org/wiki/Common_Log_Format) + [-nc] No color + [-r redirectCount] Follow redirect requests a maximum number of times (default 4) + [-https] Force requests to use https + +You can find the project and it's code on github: https://github.com/Killeroo/HttpPing +Maintained by Matthew Carney [matthewcarney64@gmail.com] +"; private static string resolvedAddress = ""; private static IEnvironmentService EnvironmentService; + /// + /// This method is the entry point of the program + /// + /// Array of strings that contains the command-line arguments passed to the program + internal static void Main(string[] args) + { + Run(args, new EnvironmentService()); + } + /// /// This method is the entry point of the program, with the test parameter environment it wrapper exit for test purpose /// /// Array of strings that contains the command-line arguments passed to the program /// Environment (wrapper for test) - internal static void Main(string[] args, IEnvironmentService environmentService) + internal static void Run(string[] args, IEnvironmentService environmentService) { EnvironmentService = environmentService; + // Some hack to get tests to work lol + ResetArgsForTest(); + // Arguments check if (args.Length == 0) { - Console.WriteLine(Usage); + Console.WriteLine(HelpMessage); EnvironmentService.Exit(0); return; } - // Save console colors - DefaultBackgroundColor = Console.BackgroundColor; - DefaultForegroundColor = Console.ForegroundColor; - // Parse arguments try { @@ -68,7 +98,11 @@ internal static void Main(string[] args, IEnvironmentService environmentService) case "-?": case "--?": case "/?": - Exit(); + case "-help": + case "--help": + case "/help": + Console.WriteLine(HelpMessage); + environmentService.Exit(0); break; case "-i": case "--i": @@ -105,6 +139,11 @@ internal static void Main(string[] args, IEnvironmentService environmentService) case "/ts": Timestamp = true; break; + case "-h": + case "--h": + case "/h": + ShowOnlyHostName = true; + break; case "-r": case "--r": case "/r": @@ -122,8 +161,15 @@ internal static void Main(string[] args, IEnvironmentService environmentService) default: if (count > 0) { - if (arg.Contains("-")) - throw new ArgumentException(); + if (arg.Contains("-") || arg.Contains("/")) + { + // If argument isn't a url then throw and exception + if (!Uri.IsWellFormedUriString(arg, UriKind.Absolute)) + { + throw new ArgumentException(); + } + } + } break; @@ -194,20 +240,29 @@ internal static void Main(string[] args, IEnvironmentService environmentService) // Results? } - - /// - /// This method is the entry point of the program - /// - /// Array of strings that contains the command-line arguments passed to the program - internal static void Main(string[] args) + public static void ResetArgsForTest() { - Main(args, new EnvironmentService()); + Infinite = false; + Detailed = false; + Timestamp = false; + NoColor = false; + UseCommonLogFormat = false; + ForceHttps = false; + ShowOnlyHostName = false; + Interval = 1000; + Requests = 5; + Redirects = 4; } - // SO: https://stackoverflow.com/questions/27108264/c-sharp-how-to-properly-make-a-http-web-get-request private static void HttpRequestLoop(string query) { + // Setup ServicePointManager to handle http verfication + // Source: https://stackoverflow.com/a/2904963 + ServicePointManager.Expect100Continue = true; + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ServerCertificateValidationCallback); + // Construct request HttpWebRequest request = (HttpWebRequest)WebRequest.Create(query); request.MaximumAutomaticRedirections = Redirects; @@ -215,21 +270,35 @@ private static void HttpRequestLoop(string query) request.Credentials = CredentialCache.DefaultCredentials; // Send requests - int index = 0; - Console.WriteLine("Sending HTTP requests to [{0}]:", query); + int sentRequests = 0; + Console.WriteLine("Sending HTTP{1} requests to [{0}]:", query, query.Contains("https://") ? "S" : ""); - while (Infinite || index <= Requests) + while(true) { var response = HttpRequest(request); if (response != null) - DisplayResponse(response, query); + DisplayResponse(response, response.ResponseUri); - index++; + sentRequests++; + + if (!Infinite && sentRequests == Requests) + { + // Finish loop if we have sent all the requests and not infinitely sending + break; + } + + // Wait interval before sending next request Thread.Sleep(Interval); } } + private static bool ServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + { + // Verify all requests to verify cerificates on our end + return true; + } + private static HttpWebResponse HttpRequest(HttpWebRequest req) { try @@ -249,12 +318,14 @@ private static HttpWebResponse HttpRequest(HttpWebRequest req) return null; } - private static void DisplayResponse(HttpWebResponse response, string address) + private static void DisplayResponse(HttpWebResponse response, Uri responseAddress) { // !!HACK ALERT!! if (!UseCommonLogFormat) - Console.Write("Response from {0}: Code=", new Uri(address).Host.ToString()); + { + Console.Write("Response from {0}: Code=", ShowOnlyHostName ? responseAddress.Host.ToString() : responseAddress.ToString()); + } if (!NoColor) { @@ -283,12 +354,11 @@ private static void DisplayResponse(HttpWebResponse response, string address) if (UseCommonLogFormat) { - Uri addrUri = new Uri(address); Console.WriteLine("{6} {0} [{1:dd/MMM/yyyy HH:mm:ss zzz}] \"GET {2} {3}/1.0\" {4} {5}", - addrUri.Host, + responseAddress.Host, DateTime.Now, - addrUri.AbsolutePath, - addrUri.Scheme.ToString().ToUpper(), + responseAddress.AbsolutePath, + responseAddress.Scheme.ToString().ToUpper(), ((int)response.StatusCode).ToString(), response.ContentLength, resolvedAddress); @@ -355,8 +425,7 @@ private static void Error(string msg) private static void ResetConsoleColors() { - Console.BackgroundColor = DefaultBackgroundColor; - Console.ForegroundColor = DefaultForegroundColor; + Console.ResetColor(); } private static void Exit(string message = null) @@ -364,7 +433,7 @@ private static void Exit(string message = null) if (!string.IsNullOrEmpty(message)) Error(message); - Console.WriteLine(Usage); + Console.WriteLine(UsageMessage); EnvironmentService.Exit(1); } } diff --git a/HttpPing/Properties/AssemblyInfo.cs b/HttpPing/Properties/AssemblyInfo.cs index a8ceee2..22c2520 100644 --- a/HttpPing/Properties/AssemblyInfo.cs +++ b/HttpPing/Properties/AssemblyInfo.cs @@ -37,5 +37,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.0.0")] diff --git a/README.md b/README.md index f7b475f..643788e 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # HttpPing -[![](https://img.shields.io/badge/version-1.0-brightgreen.svg)]() [![Build status](https://ci.appveyor.com/api/projects/status/q7lchn78v07pmemj?svg=true)](https://ci.appveyor.com/project/Killeroo/httpping) +[![](https://img.shields.io/badge/version-1.1-brightgreen.svg)](https://github.com/Killeroo/HttpPing/releases) A small tool that sends HTTP requests, presented in a ping-like style with status codes and colored results. -Download it here: [[Stable Releases]](https://github.com/Killeroo/HttpPing/releases) [[Nightly Build]](https://ci.appveyor.com/api/projects/killeroo/httpping/artifacts/HttpPing%2Fbin%2FDebug%2FHttpPing.exe) +Download it here: [[Stable Releases]](https://github.com/Killeroo/HttpPing/releases) *** ![alt text](HttpPing/Screenshots/screenshot1.png "HttpPing in action") @@ -16,13 +16,14 @@ Download it here: [[Stable Releases]](https://github.com/Killeroo/HttpPing/relea - Common Log Format (NCSA log format) ## Usage: - HttpPing.exe address [-d] [-t] [-ts] [-n count] [-i interval] [-r redirectCount] [-https] + HttpPing.exe address [-d] [-t] [-ts] [-h] [-n count] [-i interval] [-r redirectCount] [-https] ## Arguments: [-d] Detailed mode: shows server and cache info [-t] Infinite mode: Keep sending requests until stopped (Ctrl-C) [-n count] Send a specific number of requests [-ts] Include timestamp of when each request was sent + [-h] Only show the hostname in responses [-i interval] Interval between each request in milliseconds (default 30000) [-l] Use Common Log Format (https://en.wikipedia.org/wiki/Common_Log_Format) [-nc] No color