Creating a WebSurge Addin with .NET

Creating an addin involves an easy process of creating a .NET 6 assembly, with a custom class that can hook into the WebSurge request life cycle for a test run with events that are fired at the beginning and end of the test and events fired before and after each request is run.

Addins allow you to customize request processing for Web Surge tests either by modifying individual requests, or by customizing the request workflow dynamically at runtime for tasks like capturing request data on one request and passing it to subsequent requests.

Addin Execution is disabled by Default

By default addins are not allowed to execute, as they represent a potential execution threat to your application. You have to explicitly enable them via the AllowAddinExecution in the File -> Settings Configuration.

Creating an Addin

The process to create an Addin involves the following steps:

  • Target the same .NET version as WebSurge.exe (net60-windows at this time)
  • Add a reference to WebSurge.Core.dll from the WebSurge Install folder
  • Use Properties on WebSurge.Core.dll and set Copy Local to false
  • Create a new .NET Class
  • Implement IWebSurgeAddin
  • or subclass WebSurgeAddinBase
  • Implement desired OnXXX methods to intercept request processing
  • Compile your project
  • Place the final DLL (or build into) <WebSurgeInstall>\Addins

Project Layout

The easiest way to configure a project for a WebSurge is to manually edit the project file and add the base references (adjusted for your install folder):

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0-windows</TargetFramework>
	<UseWPF>true</UseWPF>  <!-- only if you hook into UI features -->
  </PropertyGroup>

  <ItemGroup>
    <Reference Include="WebSurge.Core">
      <HintPath>c:\Program Files\West Wind WebSurge\WebSurge.Core.dll</HintPath>
      <CopyLocal>False</CopyLocal>
    </Reference>
  </ItemGroup>

  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
		<!-- 
			* Copy main output assembly and any non-WebSurge dependencies to Addins folder 
			
			* If using `Program Files` you may have to change folder permissions to allow
			  you to write the new file(s) to the target folder or manually copy with Admin
		-->
	  <Exec Command="copy &quot;$(TargetDir)SampleAddin.dll&quot; &quot;C:\Program Files\West Wind WebSurge\Addins\SampleAddin.dll&quot;" />
  </Target>

</Project>

This code builds into the default compilation folder and then explicitly copies all assemblies that are non-WebSurge related to the Addins folder. Any depedencies on libraries that WebSurge uses don't need to be copied but any additional libraries you add have to be copied.

Debugging your Addins

Because you are building a plain .NET class library assembly you can easily debug your addin with Visual Studio by running it inside of WebSurge.

To do this:

  • Open your Addin project in Visual Studio
  • Go to Properties
  • On the Build tag send the output to <WebSurgeInstallFolder>
  • On the Debug Tab start WebSurge.exe in the <WebSurgeInstallFolder>

On the Debug Tab:

Permissions Required

You may have to change the permissions in the Addins folder temporarily in order to be able to build into the folder from Visual Studio. Add the INTERACTIVE user to the folder permissions and give it Full Rights in order to be able to have Visual Studio output into the Addins folder.

With this setup you should just be able to start debugging in Visual Studio by setting breakpoints:

How it works

When WebSurge starts it looks for assemblies in the Addins folder that implement IWebSurgeAddin and loads them. You can add multiple addins into a single assembly and WebSurge will find them or look through all assemblies in the Addins folder and hook them into the processing chain.

When a test is run or you test an invididual request, the addin's OnXXXX methods are fired as requests are processed. The addin is applied to individual requests when you click the Test button, or when you run a full test using the Start button.

Request Threading

Note that requests fired during tests for OnBeforeRequestSent and OnAfterRequestSent are fired on isolated threads so if you decide to access any UI functionality in WebSurge you have to explicitly marshal the thread back to the UI thread using the WPF Dispatcher.CurrentDispatcher.

The request data that is provided to a given request is thread safe and you can safely read and write the request data. However, any other data including the passed in StressTester instance, as well as any other structures re potentially subject to thread state changes, so use care when working with WebSurge's state. Also if you write to files on disk make sure you either use locks to prevent multiple threads writing simultaneously, or else use retry logic to catch and avoid file sharing failures.

Simple Examples

Here are a couple of very simple examples that demonstrate what addin classes can look like.

Logging Request Event Hooks

The first is a simple logging addin that explicitly logs a small message for each request that is made:

public class LogAddin : IWebSurgeAddin
{
    public override bool OnBeforeRequestSent(HttpRequestData httpRequest, StressTester stressTester)
    {
        logRequests(request, 0);
        return true;
    }

    public override void OnAfterRequestSent(HttpRequestData request, StressTester stressTester)
    {
        logRequests(request, 1);
    }

    public override void OnAfterRequestSent(HttpRequestData request, StressTester stressTester)
    {
        LogString("Starting test with " + requests.Count + " in Session.");
        return true;
    }

    public void OnLoadTestCompleted(IList<HttpRequestData> results, int timeTakenForTestMs)
    {
        LogString("Completed test with " + results.Count + " requests processed.");
    }

    public override void OnLoadTestCompleted(IList<HttpRequestData> requests, TestResultView results, StressTester stressTester)
    {
        string output;

        // Check for a non-success response/errors
        bool isErorr = data.IsError;

        if (mode != 0)
            output = data.StatusCode + " " + data.HttpVerb + " " + data.Url + " " + data.TimeTakenMs;
        else
            output = data.HttpVerb + " " + data.Url;

        LogString(output);
    }

    public static object syncLock = new object();

    private void LogString(string message)
    {
        lock (syncLock)
        {
            StreamWriter streamWriter = new StreamWriter(Environment.CurrentDirectory + "\\requestlog.txt", true);
            streamWriter.WriteLine(DateTime.Now.ToString() + " - " + message);
            streamWriter.Close();
        }
    }
}

Modifying Request Data

The following example modifies request data by searching for a known value in the data and modifying it when the request comes in:

public class ModifyContentAddin : WebSurgeAddinBase
{
    public override bool OnBeforeRequestSent(HttpRequestData data, StressTester stressTester)
    {
        data.AddHeader("x-request-time", DateTime.UtcNow.ToString("o"))

        if (!string.IsNullOrEmpty(data.RequestContent))
        {
            // do something here to get your unique value
            var userId = new Random().Next().ToString();

            // then embed it into the content
            data.RequestContent = data.RequestContent.Replace("##UserId##", "USER_" + userId);
        }
        
        // change request content conditionally
        if (data.Url.Contains("/somerequest"))
        {
            data.RequestContent = "<custom content>";
        }

        return true;
    }
}

See also

Create WebSurge .NET Addins to extend Functionality

© West Wind Technologies, 2014-2023 • Updated: 02/06/22
Comment or report problem with topic