Skip to main content

Command Palette

Search for a command to run...

Building WebSessionForge: A Multi-Session WebView2 Browser & Proxy Testing Platform in .NET 8

Updated
•15 min read•View as Markdown
R
### About Hey! I’m **Rithvik Shaga**, a cybersecurity graduate and developer from India. I’m passionate about **cybersecurity, ethical hacking, AI, automation, and software development**. I enjoy building practical projects, experimenting with new technologies, participating in CTFs, and exploring how systems work — especially from a security perspective. I’m here to **learn, build, share, and connect** with other developers and security enthusiasts. 🚀🔐 Currently exploring: **Cybersecurity • AI • Automation • Web Development • Security Research**

C# · .NET 8 · WPF · Microsoft Edge WebView2 · Proxy Management · Browser Testing

I wanted to build something that would let me experiment with multiple isolated browser sessions, while also giving me control over the network environment behind each session.

That led to WebSessionForge — a Windows desktop application built with C# and .NET 8 that combines WebView2 browser sessions, proxy management, proxy health monitoring, proxy rotation, failure recovery, and structured runtime logging.

GitHub: https://github.com/shagarithvik/WebSessionForge


What Is WebSessionForge?

WebSessionForge is a browser-session and network testing platform.

It provides a central desktop interface for creating and managing multiple WebView2 sessions while independently managing their network assignments.

The core idea is:

                 WebSessionForge
                       │
          ┌────────────┼────────────┐
          │            │            │
          ▼            ▼            ▼
      Session 1    Session 2    Session N
          │            │            │
          ▼            ▼            ▼
       WebView2      WebView2      WebView2
          │            │            │
          ▼            ▼            ▼
       Proxy A      Proxy B      Proxy N

The interesting engineering challenge isn't simply opening multiple browsers.

It is coordinating:

  • Browser lifecycle

  • Session isolation

  • Proxy allocation

  • Proxy health

  • Proxy rotation

  • Failure recovery

  • Concurrent operations

  • Runtime observability


Why I Built It

Browser automation becomes complicated when network infrastructure is part of the test.

A session can fail because:

  • A proxy becomes unavailable

  • A provider returns malformed data

  • A connection times out

  • A browser session encounters an error

  • Multiple sessions compete for the same proxy

  • Network conditions change during execution

I wanted these responsibilities separated instead of putting everything into one large automation class.

The resulting architecture became:

Browser Sessions
       │
       ├── Session lifecycle
       │
       ├── Proxy assignment
       │
       ├── Proxy health
       │
       ├── Failure recovery
       │
       └── Logging

Technology Stack

Component Technology
Language C#
Framework .NET 8
Desktop UI WPF
Browser Microsoft Edge WebView2
Configuration JSON
Proxy input CSV / Provider endpoints
Logging JSONL
Platform Windows 10/11

Architecture

The application is organized around several major components:

                         ┌─────────────────────┐
                         │   WebSessionForge   │
                         │     WPF Dashboard   │
                         └──────────┬──────────┘
                                    │
              ┌─────────────────────┼─────────────────────┐
              │                     │                     │
              ▼                     ▼                     ▼
       Session Manager        Proxy Manager           Logger
              │                     │                     │
              │              ┌──────┴──────┐              │
              │              │             │              │
              ▼              ▼             ▼              ▼
        WebView2        CSV Import    Providers       JSONL Logs
        Sessions             │             │
              │              └──────┬──────┘
              │                     │
              └──────────────┬──────┘
                             ▼
                       Proxy Pool

The main principle is separation of responsibilities.


🔧 Concrete Implementation Walkthrough

This is where the architecture becomes practical.

The following walkthrough shows how the major pieces fit together conceptually in C#.

The exact class names and implementation details may evolve as the project develops; the snippets illustrate the intended implementation pattern.


1. Define a Proxy Model

Everything starts with a common representation for a proxy.

Instead of passing strings throughout the application, represent a proxy as an object.

public sealed class ProxyInfo
{
    public string Host { get; init; } = string.Empty;
    public int Port { get; init; }

    public string Scheme { get; init; } = "http";

    public string? Username { get; init; }
    public string? Password { get; init; }

    public string? Provider { get; init; }

    public TimeSpan? Latency { get; set; }

    public bool IsHealthy { get; set; }

    public int FailureCount { get; set; }
}

Now every subsystem can work with the same structure.

For example:

CSV
 │
 ▼
ProxyInfo
 │
 ├── Health Checker
 ├── Proxy Pool
 ├── Session Manager
 └── Logger

This is much cleaner than passing raw strings between services.


2. Normalize Proxy Input

Different proxy sources can produce different formats.

For example:

http://host:8080

or:

host,8080,http,user,password

The importer converts these into the same internal model.

Conceptually:

ProxyInfo ParseProxy(string value)
{
    // Parse scheme, host, port and optional credentials.
    // Normalize the result into ProxyInfo.
}

The important design principle is:

Normalize at the boundary.

Once a proxy enters the application, the rest of the system should not care where it came from.


3. Build the Proxy Pool

The proxy manager maintains the available proxies.

Conceptually:

private readonly List<ProxyInfo> _proxies = new();

Adding a proxy becomes:

public void Add(ProxyInfo proxy)
{
    if (!_proxies.Any(p =>
        p.Host == proxy.Host &&
        p.Port == proxy.Port &&
        p.Scheme == proxy.Scheme))
    {
        _proxies.Add(proxy);
    }
}

This gives the application a central location for:

  • Deduplication

  • Health state

  • Assignment

  • Release

  • Replacement


4. Health Check the Proxy

A proxy shouldn't automatically become available just because it was parsed successfully.

The health-check layer tests connectivity.

A simplified conceptual implementation is:

public async Task<bool> CheckAsync(
    ProxyInfo proxy,
    CancellationToken cancellationToken)
{
    try
    {
        using var client = CreateClient(proxy);

        using var response = await client.GetAsync(
            TestEndpoint,
            cancellationToken);

        proxy.IsHealthy = response.IsSuccessStatusCode;

        return proxy.IsHealthy;
    }
    catch
    {
        proxy.IsHealthy = false;
        proxy.FailureCount++;

        return false;
    }
}

The actual implementation needs to account for the proxy protocol and appropriate timeout behavior.

The important flow is:

Proxy
  │
  ▼
Connectivity Test
  │
 ┌┴─────────┐
 ▼          ▼
Healthy    Failed
 │          │
 ▼          ▼
Pool       Remove/Retry

5. Reserve a Proxy for a Session

Once healthy proxies exist, sessions need to obtain them safely.

A simple conceptual abstraction is:

public ProxyInfo? Acquire()
{
    lock (_sync)
    {
        var proxy = _proxies
            .FirstOrDefault(p =>
                p.IsHealthy &&
                !IsReserved(p));

        if (proxy == null)
            return null;

        Reserve(proxy);

        return proxy;
    }
}

The important part here is synchronization.

Without it, two sessions could perform:

Session A → find Proxy A
Session B → find Proxy A

before either session marks it as reserved.

With synchronized allocation:

Proxy Pool
    │
    ├── Proxy A → Session 1
    ├── Proxy B → Session 2
    └── Proxy C → Available

This becomes particularly important as the number of concurrent sessions increases.


6. Create a WebView2 Session

A session manager can then create a browser environment for the assigned session.

Conceptually:

public sealed class BrowserSession
{
    public string Id { get; }
    public ProxyInfo? Proxy { get; }

    public BrowserSession(
        string id,
        ProxyInfo? proxy)
    {
        Id = id;
        Proxy = proxy;
    }
}

The lifecycle becomes:

Create Session
      │
      ▼
Acquire Proxy
      │
      ▼
Create WebView2 Environment
      │
      ▼
Initialize Browser
      │
      ▼
Navigate

WebView2 itself then becomes the browser execution layer, while the session manager controls the higher-level lifecycle.


7. Keep Session State Separate

Each session should have an identity.

For example:

var sessionId = $"session-{index}";

The session can then be associated with:

Session ID
Proxy
Browser Environment
Status
Created Time
Log File

That makes it possible to answer questions like:

Which proxy was assigned to Session 4 when the failure occurred?

This becomes extremely useful during debugging.


8. Start Multiple Sessions

The dashboard can coordinate multiple sessions.

Conceptually:

for (int i = 0; i < sessionCount; i++)
{
    var session = await sessionManager.CreateAsync(
        $"session-{i + 1}");

    sessions.Add(session);
}

In a production implementation, these operations need proper asynchronous lifecycle management rather than blindly creating everything at once.

The overall flow becomes:

Dashboard
    │
    ▼
Session Manager
    │
    ├── Session 1
    ├── Session 2
    ├── Session 3
    └── Session N

9. Handle Proxy Failure

Suppose Session 2 is currently using Proxy B.

A network operation fails.

The session manager can perform:

Session 2
   │
   ▼
Network Failure
   │
   ▼
Mark Proxy B Failed
   │
   ▼
Release Proxy B
   │
   ▼
Acquire Replacement
   │
   ▼
Proxy C

Conceptually:

public async Task<bool> ReplaceProxyAsync(
    BrowserSession session)
{
    Release(session.Proxy);

    var replacement = Acquire();

    if (replacement == null)
        return false;

    session.SetProxy(replacement);

    return true;
}

The exact WebView2 reconfiguration strategy depends on the session lifecycle and browser environment implementation.

The important architectural idea is that proxy failure is handled by the session/proxy layers rather than the UI.


10. Implement Rotation

Rotation can be represented as an asynchronous background operation.

Conceptually:

while (!cancellationToken.IsCancellationRequested)
{
    await Task.Delay(rotationInterval, cancellationToken);

    await RotateSessionAsync(session);
}

The rotation process is:

Wait
 │
 ▼
Rotation Due
 │
 ▼
Release Current Proxy
 │
 ▼
Acquire Replacement
 │
 ▼
Update Session
 │
 ▼
Log Event
 │
 ▼
Continue

A jitter value can be applied around the configured interval when the testing scenario requires variation.


11. Structured Logging

Instead of writing arbitrary strings:

"proxy failed"

the application can record structured events.

Conceptually:

await logger.WriteAsync(new
{
    Timestamp = DateTimeOffset.UtcNow,
    Event = "ProxyFailure",
    SessionId = session.Id,
    Proxy = proxy.Host,
    Port = proxy.Port
});

The result can be stored as JSONL:

{
  "timestamp": "2026-10-02T12:00:00Z",
  "event": "ProxyFailure",
  "sessionId": "session-2",
  "proxy": "proxy.example",
  "port": 8080
}

Now logs can be filtered programmatically.

For example:

All failures
All events for Session 2
All proxy rotations
All health-check failures

12. Connect the UI

The WPF dashboard acts as the orchestration layer.

Conceptually:

MainWindow
    │
    ├── Start
    │      ↓
    │   SessionManager
    │
    ├── Stop
    │      ↓
    │   SessionManager
    │
    ├── Proxy Status
    │      ↓
    │   ProxyManager
    │
    ├── Browser Matrix
    │      ↓
    │   Active Sessions
    │
    └── Logs
           ↓
        Logger

The UI should display state rather than contain the business logic itself.

That distinction becomes increasingly important as the project grows.


13. Configuration-Driven Behavior

Runtime settings belong in configuration rather than hardcoded constants.

For example:

{
  "SessionCount": 6,
  "RotationMinutes": 5,
  "RotationJitterSeconds": 30,
  "ProxyHealthTimeoutSeconds": 10,
  "ProviderRefreshMinutes": 5,
  "ProviderUrls": [],
  "Headless": false
}

The application can load this during startup:

var json = await File.ReadAllTextAsync(
    "config/config.json");

var config = JsonSerializer.Deserialize<AppConfig>(json);

The result is passed to the relevant services.

Configuration
      │
      ├── Session Manager
      ├── Proxy Manager
      ├── Health Checker
      └── Provider Manager

This makes behavior reproducible and easier to modify.


14. Complete Runtime Flow

Putting everything together:

                    START
                      │
                      ▼
               Load Configuration
                      │
                      ▼
              Load Proxy Sources
                      │
                      ▼
              Normalize Proxies
                      │
                      ▼
                Health Checks
                      │
             ┌────────┴────────┐
             ▼                 ▼
          Healthy             Failed
             │                 │
             ▼                 ▼
        Proxy Pool          Excluded
             │
             ▼
       Create Session
             │
             ▼
       Acquire Proxy
             │
             ▼
       Create WebView2
             │
             ▼
          Navigate
             │
             ▼
           Monitor
             │
       ┌─────┴──────┐
       ▼            ▼
    Healthy       Failure
       │            │
       │            ▼
       │       Mark Proxy Failed
       │            │
       │            ▼
       │       Acquire Replacement
       │
       ▼
   Rotation Due
       │
       ▼
   Rotate Proxy
       │
       ▼
     Continue

This is the core of WebSessionForge.


Proxy Provider Pipeline

Provider integration follows the same architecture.

Provider URL
     │
     ▼
HTTP Request
     │
     ▼
Response
     │
     ▼
Format Detection
     │
 ┌───┼──────────┐
 ▼   ▼          ▼
JSON Text     Object
 │    │          │
 └────┴──────────┘
          │
          ▼
      Proxy Parser
          │
          ▼
      ProxyInfo[]
          │
          ▼
      Proxy Manager

The provider layer therefore doesn't need to know anything about browser sessions.

It simply supplies normalized proxy objects.


Failure Isolation

One of the most important architectural goals is preventing one failure from cascading into everything else.

For example:

Proxy A fails
     │
     ▼
Session 1 affected
     │
     ├── Proxy A marked failed
     ├── Proxy A removed
     └── Replacement requested

Instead of:

Proxy A fails
     │
     ▼
Entire application crashes

The system treats failures as local events whenever possible.


Browser Matrix Implementation Concept

The browser matrix is essentially a presentation layer over the active sessions.

Conceptually:

Observable Session Collection
          │
          ▼
      Browser Matrix
          │
    ┌─────┼─────┐
    ▼     ▼     ▼
 Session Session Session
    1      2      3

Each visual container can host the corresponding WebView2 control.

This provides an easy way to inspect the state of multiple sessions without losing the central dashboard.


Observability Architecture

The logging architecture can be visualized as:

Session Manager ──────┐
                      │
Proxy Manager ────────┤
                      ├──► Logger ───► JSONL
Health Checker ───────┤
                      │
Provider Manager ─────┘

This is useful because logging remains centralized.

A session doesn't need to know where logs are stored.

It simply emits an event.

The logger handles persistence.


Performance Considerations

Multiple WebView2 instances are considerably more expensive than simple HTTP clients.

Resource consumption depends on:

Session Count
      +
Page Complexity
      +
JavaScript
      +
Network Activity
      +
Proxy Latency
      +
CPU
      +
RAM

For that reason, the recommended development workflow is:

1 session
   ↓
2 sessions
   ↓
4 sessions
   ↓
6 sessions
   ↓
Scale gradually

This makes it easier to identify the point where resource consumption becomes problematic.


Project Structure

The project follows a modular layout:

WebSessionForge/
│
├── Models/
│
├── Providers/
│
├── Services/
│
├── config/
│
├── logs/
│
├── App.xaml
├── App.xaml.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── BrowserWindow.xaml
├── BrowserWindow.xaml.cs
├── WebSessionForge.csproj
├── DOCUMENTATION.md
├── README.md
└── LICENSE

The intended responsibility boundaries are:

Directory Responsibility
Models/ Domain/application models
Providers/ External proxy sources
Services/ Session, proxy, health and logging logic
config/ Runtime configuration
logs/ Runtime event data
*.xaml WPF interface

Configuration

The main configuration file is:

config/config.json

Example:

{
  "SessionCount": 6,
  "RotationMinutes": 5,
  "RotationJitterSeconds": 30,
  "ProxyHealthTimeoutSeconds": 10,
  "ProviderRefreshMinutes": 5,
  "ProviderUrls": [],
  "Headless": false
}

Important options:

Setting Purpose
SessionCount Number of browser sessions
RotationMinutes Proxy rotation interval
RotationJitterSeconds Rotation variation
ProxyHealthTimeoutSeconds Health-check timeout
ProviderRefreshMinutes Provider refresh
ProviderUrls Provider endpoints
Headless Headless configuration

Logging

WebSessionForge uses JSON Lines.

Application logs:

logs/application-YYYY-MM-DD.jsonl

Session logs:

logs/session-1-YYYY-MM-DD.jsonl
logs/session-2-YYYY-MM-DD.jsonl
logs/session-3-YYYY-MM-DD.jsonl

Events can include:

  • Session creation

  • Session termination

  • Proxy assignment

  • Proxy rotation

  • Health checks

  • Failures

  • Retries

  • Recovery

  • Runtime errors

Structured logs make concurrent browser testing much easier to investigate.


Running the Project

Requirements:

  • Windows 10/11

  • .NET 8 SDK

  • Microsoft Edge WebView2 Runtime

Clone:

git clone https://github.com/shagarithvik/WebSessionForge.git

Build:

cd WebSessionForge
dotnet restore
dotnet build

Run:

dotnet run

Publish:

dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None

Testing Scenarios

WebSessionForge is useful for controlled testing of:

Session Isolation

Verify independent browser state.

Proxy Reliability

Observe behavior when a network endpoint becomes unavailable.

Proxy Rotation

Test session behavior when network assignments change.

Failure Recovery

Verify replacement and recovery workflows.

Provider Reliability

Test malformed or unavailable provider responses.

Observability

Reconstruct a test run from structured JSONL events.


What I Learned

Browser sessions are expensive

A browser environment is much heavier than an HTTP client.

Network failures are normal

Proxy infrastructure should be designed around failure.

Health checks are not guarantees

A successful check only tells us that a proxy worked at that particular moment.

Logging matters

Concurrent systems become difficult to debug without structured events.

Separation matters

Keeping the UI, session manager, proxy manager, providers, and logger independent makes the project easier to maintain.


Responsible Use

WebSessionForge is intended for authorized testing and experimentation.

Use it with systems and infrastructure where you have permission.

It should not be used to:

  • Manipulate platform metrics

  • Generate artificial engagement

  • Circumvent access controls

  • Evade anti-bot systems

  • Bypass rate limits

  • Access systems without authorization

  • Abuse third-party infrastructure

The project is intended as a browser-session and QA/testing platform.


Roadmap

Architecture

  • [ ] Further MVVM adoption

  • [ ] Dependency injection

  • [ ] More interfaces

  • [ ] Improved testability

Proxy System

  • [ ] Provider health scoring

  • [ ] Advanced proxy statistics

  • [ ] Better failure classification

  • [ ] Improved validation

Browser Sessions

  • [ ] Improved WebView2 lifecycle

  • [ ] Configurable profiles

  • [ ] Session telemetry

  • [ ] Performance metrics

Testing

  • [ ] Unit tests

  • [ ] Integration tests

  • [ ] Mock proxy provider

  • [ ] Local test infrastructure

Developer Experience

  • [ ] CI/CD

  • [ ] Automated releases

  • [ ] Configuration validation

  • [ ] Exportable reports


Why WebSessionForge?

The name reflects the direction of the project.

WebSession represents the primary unit of the platform.

Forge represents creating and managing controlled browser-session environments.

The project started around proxy-backed browser experimentation, but the architecture is broader:

Browser Sessions
       +
Network Conditions
       +
Testing
       +
Observability
       +
Automation

Final Thoughts

WebSessionForge started as an experiment around multiple browser sessions and proxy management.

It has evolved into a small platform for studying how:

  • Browser environments

  • Network conditions

  • Failure recovery

  • Concurrency

  • Observability

interact in a real desktop application.

The most important lesson has been simple:

Reliable automation isn't just about making something work. It's about making failures understandable and recoverable.

That's the direction I want to continue taking WebSessionForge.


🔗 Project

GitHub: https://github.com/shagarithvik/WebSessionForge

Author: Rithvik Shaga

Stack: C# · .NET 8 · WPF · WebView2

License: MIT


⭐ Get Involved

The repository is open source.

Feedback and contributions around:

  • Architecture

  • WebView2

  • Session management

  • Proxy infrastructure

  • Reliability

  • Performance

  • Testing

  • Observability

are welcome.

GitHub: https://github.com/shagarithvik/WebSessionForge


Built with C# • .NET 8 • WPF • WebView2