HTTP Speed Test for REST SMS services

Need to measure the performance of your HTTP-based SMS service? The HTTP Speed Test for REST SMS Services is a powerful tool designed to evaluate the speed and reliability of your JSON/REST API endpoints. This downloadable utility allows you to simulate high-volume SMS requests, track success rates, and calculate requests per second—helping you optimize server performance under load. Whether you're stress-testing a local development environment or benchmarking a production SMS gateway, this tool provides clear metrics for total requests, concurrent connections, and HTTP Keep-Alive efficiency. Download the ready-to-use C# solution or integrate the provided source code into your testing workflow for precise API performance analysis.

Download

HttpSpeedTest.zip

Example usage

Figure 1 - How to use the speed test tool

Source code

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

class Program
{
    static readonly Random random = new Random();
    static readonly string[] sampleMessages = {
        "Hello, this is a test message.",
        "Performance testing HTTP server capabilities.",
        "Measuring requests per second for JSON API.",
        "Random message content for load testing.",
        "Another test message with different content.",
        "Checking server capacity under load.",
        "How many requests can the server handle?",
        "Stress testing the SMS API endpoint.",
        "This message is automatically generated.",
        "Final test message in the sample set."
    };

    static async Task Main(string[] args)
    {
        Console.WriteLine("HTTP Server JSON API Performance Tester");
        Console.WriteLine("---------------------------------------");

        if (args.Length < 3)
        {
            Console.WriteLine("Usage: Program    [useKeepAlive]");
            Console.WriteLine("Example: Program http://198.50.122.178:8080 1000 50 true");
            Console.WriteLine("Example: Program https://api.example.com/sms 5000 100 false");
            Console.WriteLine("Example: Program http://localhost/innotest/innotest.php 50 10 false");
        
            return;
        }

        string baseUrl = args[0];
        if (!Uri.TryCreate(baseUrl, UriKind.Absolute, out Uri uriResult) ||
            (uriResult.Scheme != Uri.UriSchemeHttp && uriResult.Scheme != Uri.UriSchemeHttps))
        {
            Console.WriteLine("Invalid base URL. Must be a valid HTTP or HTTPS URL.");
            return;
        }

        if (!int.TryParse(args[1], out int totalRequests) || totalRequests <= 0)
        {
            Console.WriteLine("Invalid totalRequests value. Must be a positive integer.");
            return;
        }

        if (!int.TryParse(args[2], out int concurrentRequests) || concurrentRequests <= 0)
        {
            Console.WriteLine("Invalid concurrentRequests value. Must be a positive integer.");
            return;
        }

        bool useKeepAlive = args.Length > 3 && bool.TryParse(args[3], out bool keepAlive) && keepAlive;

        Console.WriteLine($"Test Parameters:");
        Console.WriteLine($"- Base URL: {baseUrl}");
        Console.WriteLine($"- Total Requests: {totalRequests}");
        Console.WriteLine($"- Concurrent Requests: {concurrentRequests}");
        Console.WriteLine($"- HTTP Keep-Alive: {(useKeepAlive ? "Enabled" : "Disabled")}");
        Console.WriteLine();

        var httpClientHandler = new HttpClientHandler
        {
            UseProxy = false,
            MaxConnectionsPerServer = concurrentRequests
        };

        var httpClient = new HttpClient(httpClientHandler)
        {
            Timeout = TimeSpan.FromSeconds(30),
            BaseAddress = new Uri(baseUrl)
        };

        if (useKeepAlive)
        {
            httpClient.DefaultRequestHeaders.ConnectionClose = false;
            httpClient.DefaultRequestHeaders.Connection.Add("keep-alive");
        }
        else
        {
            httpClient.DefaultRequestHeaders.ConnectionClose = true;
        }

        httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(
         "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "+
         "(KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36");
        httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/json");

        var stopwatch = Stopwatch.StartNew();
        var tasks = new List(concurrentRequests);
        var completedRequests = 0;
        var successfulRequests = 0;
        var failedRequests = 0;

        Console.WriteLine("Starting test...");

        var semaphore = new System.Threading.SemaphoreSlim(concurrentRequests, concurrentRequests);
        var jsonOptions = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            WriteIndented = false
        };

        for (int i = 0; i < totalRequests; i++)
        {
            await semaphore.WaitAsync();

            tasks.Add(Task.Run(async () =>
            {
                try
                {
                    var requestData = new
                    {
                        sender = "+0000000",
                        gsm = GenerateRandomPhoneNumber(),
                        text = GetRandomMessage(),
                        usrClient = "Pruebas",
                        pasClient = "Pruebas"
                    };

                    var json = JsonSerializer.Serialize(requestData, jsonOptions);
                    var content = new StringContent(json, Encoding.UTF8, "application/json");

                    var response = await httpClient.PostAsync(baseUrl, content);

                    System.Threading.Interlocked.Increment(ref completedRequests);

                    if (response.IsSuccessStatusCode)
                    {
                        System.Threading.Interlocked.Increment(ref successfulRequests);
                    }
                    else
                    {
                        System.Threading.Interlocked.Increment(ref failedRequests);
                        Console.WriteLine($"Request failed with status: {response.StatusCode}");
                    }
                }
                catch (Exception ex)
                {
                    System.Threading.Interlocked.Increment(ref completedRequests);
                    System.Threading.Interlocked.Increment(ref failedRequests);
                    Console.WriteLine($"Request failed: {ex.Message}");
                }
                finally
                {
                    semaphore.Release();
                }
            }));
        }

        await Task.WhenAll(tasks);
        stopwatch.Stop();

        Console.WriteLine("\nTest completed!");
        Console.WriteLine($"Total time: {stopwatch.Elapsed.TotalSeconds:F2} seconds");
        Console.WriteLine($"Completed requests: {completedRequests}");
        Console.WriteLine($"Successful requests: {successfulRequests}");
        Console.WriteLine($"Failed requests: {failedRequests}");
        Console.WriteLine($"Requests per second: {totalRequests / stopwatch.Elapsed.TotalSeconds:F2}");
    }

    static string GenerateRandomPhoneNumber()
    {
        return $"+{random.Next(100, 999)}{random.Next(1000000, 9999999)}";
    }

    static string GetRandomMessage()
    {
        return sampleMessages[random.Next(sampleMessages.Length)];
    }
}

Code explanation

1. Overview

This C# console application load tests an HTTP(S) SMS API endpoint by sending concurrent JSON requests, measuring performance metrics like:

  • Requests per second (RPS)
  • Success/failure rates
  • Total execution time

2. Key Components

A. Setup and Configuration

Random Data Generation:

  • sampleMessages: Predefined set of SMS text samples for realistic testing.
  • GenerateRandomPhoneNumber(): Creates fake phone numbers (e.g., +1234567890).
  • GetRandomMessage(): Picks a random message from sampleMessages.

Command-Line Arguments:

Program <baseUrl> <totalRequests> <concurrentRequests> [useKeepAlive]
  • Validates inputs (URL format, positive integers for request counts).
  • Configures HTTP client settings (timeout, keep-alive, user-agent).

B. HTTP Client Configuration

HttpClient Customization:

  • MaxConnectionsPerServer: Limits parallel connections to the server.
  • ConnectionClose/keep-alive: Toggles HTTP persistent connections.
  • Timeout: 30 seconds per request.
  • Headers: Sets User-Agent and Accept: application/json.

C. Load Test Execution

Concurrency Control:

  • Uses SemaphoreSlim to throttle concurrent requests (e.g., 50 at a time).
  • Async/await (Task.Run) for non-blocking I/O operations.

Request Workflow:

  1. Serializes a JSON payload with random phone numbers/messages:
    {
        "sender": "+0000000",
        "gsm": "+1234567890",
        "text": "Hello, this is a test message.",
        "usrClient": "Pruebas",
        "pasClient": "Pruebas"
    }
  2. Sends a POST request to the specified endpoint.
  3. Tracks successes/failures using Interlocked for thread-safe counters.

D. Metrics and Reporting

  • Stopwatch: Measures total test duration.
  • Output Metrics:
    • Total time elapsed.
    • Completed/successful/failed requests.
    • Requests per second (RPS): totalRequests / elapsedTime.

3. Technical Highlights

  • Thread Safety:
    • Interlocked.Increment ensures atomic counter updates across threads.
    • SemaphoreSlim prevents overloading the server or client.
  • Error Handling:
    • Catches exceptions (e.g., timeouts, network errors) and logs failures.
    • Validates HTTP status codes (IsSuccessStatusCode).
  • Performance Optimizations:
    • Reuses HttpClient (best practice for TCP port exhaustion avoidance).
    • Configurable keep-alive to mimic real-world scenarios.

4. Example Use Case

To test an SMS API at http://localhost:8080/sms with 1,000 total requests (50 concurrent, keep-alive enabled):

Program http://localhost:8080/sms 1000 50 true

Output:

Test completed!
Total time: 12.34 seconds
Completed requests: 1000
Successful requests: 980
Failed requests: 20
Requests per second: 81.04

5. Why This Matters

  • Benchmarking: Identifies API bottlenecks (e.g., server limits, network latency).
  • Regression Testing: Ensures performance doesn't degrade after updates.
  • Capacity Planning: Determines max load the service can handle.

6. Potential Enhancements

  • Add retry logic for transient failures.
  • Support for authentication (OAuth/API keys).
  • Export results to CSV/JSON for analysis.

This tool is ideal for developers and DevOps teams validating SMS gateway performance before deployment.

Conclusion

The HTTP Speed Test for REST SMS Services provides a robust, easy-to-use solution for benchmarking the performance and reliability of SMS API endpoints. By simulating real-world traffic with configurable concurrency, random message generation, and detailed performance metrics, this tool helps developers and QA teams identify bottlenecks, validate scalability, and optimize API responsiveness. Whether you're stress-testing a new deployment or auditing an existing SMS gateway, the included C# source code offers flexibility for customization, while the pre-built executable delivers quick, actionable insights. Download the tool today to ensure your SMS service meets performance expectations under load—and deliver a seamless experience to end-users.

More information