Skip to content

[dotnet] [bidi] Combine network interception to apply rules (breaking change) #15603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 11, 2025

Conversation

nvborisenko
Copy link
Member

@nvborisenko nvborisenko commented Apr 9, 2025

User description

Fixes #15592

💥 What does this PR do?

Before:

public async Task<Intercept> InterceptRequestAsync(Func<BeforeRequestSentEventArgs, Task> handler, AddInterceptOptions? interceptOptions = null, SubscriptionOptions? options = null)

After:

public async Task<Intercept> InterceptRequestAsync(Func<BeforeRequestSentEventArgs, Task> handler, InterceptRequestOptions? options = null)

Actually it is high level API, following the pattern: public Task<T> DoSomethingAsync(DoSomethingOptions? = null)

🔧 Implementation Notes

💡 Additional Considerations

🔄 Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)

PR Type

Enhancement, Tests


Description

  • Refactored network interception APIs for BiDi in .NET bindings.

  • Introduced high-level API methods for request, response, and auth interception.

  • Simplified RequestConverter by removing dependency on BiDi.

  • Added new test cases to validate updated interception functionality.


Changes walkthrough 📝

Relevant files
Enhancement
6 files
Broker.cs
Updated `RequestConverter` usage to reflect refactoring   
+1/-1     
RequestConverter.cs
Simplified `RequestConverter` by removing `BiDi` dependency
+1/-8     
BrowsingContextNetworkModule.cs
Refactored network interception methods for high-level API
+21/-9   
NetworkModule.HighLevel.cs
Added high-level API for network interception in `NetworkModule`
+106/-0 
NetworkModule.cs
Updated `NetworkModule` to support high-level interception APIs
+1/-28   
Request.cs
Removed `BiDi` dependency from `Request` class                     
+1/-41   
Tests
1 files
NetworkTest.cs
Updated and added tests for new interception APIs               
+17/-17 

Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • @selenium-ci selenium-ci added the C-dotnet .NET Bindings label Apr 9, 2025
    Copy link
    Contributor

    qodo-merge-pro bot commented Apr 9, 2025

    PR Reviewer Guide 🔍

    (Review updated until commit ed35158)

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
    🧪 PR contains tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Code Duplication

    The InterceptRequestAsync, InterceptResponseAsync, and InterceptAuthAsync methods in both NetworkModule.HighLevel.cs and BrowsingContextNetworkModule.cs contain nearly identical code. Consider extracting common functionality to reduce duplication.

    public async Task<Intercept> InterceptRequestAsync(Func<InterceptedRequest, Task> handler, InterceptRequestOptions? options = null)
    {
        var intercept = await AddInterceptAsync([InterceptPhase.BeforeRequestSent], options).ConfigureAwait(false);
    
        await intercept.OnBeforeRequestSentAsync(async req => await handler(new(req.BiDi, req.Context, req.IsBlocked, req.Navigation, req.RedirectCount, req.Request, req.Timestamp, req.Initiator))).ConfigureAwait(false);
    
        return intercept;
    }
    
    public async Task<Intercept> InterceptResponseAsync(Func<InterceptedResponse, Task> handler, InterceptResponseOptions? options = null)
    {
        var intercept = await AddInterceptAsync([InterceptPhase.ResponseStarted], options).ConfigureAwait(false);
    
        await intercept.OnResponseStartedAsync(async res => await handler(new(res.BiDi, res.Context, res.IsBlocked, res.Navigation, res.RedirectCount, res.Request, res.Timestamp, res.Response))).ConfigureAwait(false);
    
        return intercept;
    }
    
    public async Task<Intercept> InterceptAuthAsync(Func<InterceptedAuth, Task> handler, InterceptAuthOptions? options = null)
    {
        var intercept = await AddInterceptAsync([InterceptPhase.AuthRequired], options).ConfigureAwait(false);
    
        await intercept.OnAuthRequiredAsync(async auth => await handler(new(auth.BiDi, auth.Context, auth.IsBlocked, auth.Navigation, auth.RedirectCount, auth.Request, auth.Timestamp, auth.Response))).ConfigureAwait(false);
    
        return intercept;
    }
    Error Handling

    The new high-level API methods don't include explicit error handling for cases where the handler might throw exceptions. Consider adding try/catch blocks to ensure proper error propagation.

    public async Task<Intercept> InterceptRequestAsync(Func<InterceptedRequest, Task> handler, InterceptRequestOptions? options = null)
    {
        var intercept = await AddInterceptAsync([InterceptPhase.BeforeRequestSent], options).ConfigureAwait(false);
    
        await intercept.OnBeforeRequestSentAsync(async req => await handler(new(req.BiDi, req.Context, req.IsBlocked, req.Navigation, req.RedirectCount, req.Request, req.Timestamp, req.Initiator))).ConfigureAwait(false);
    
        return intercept;
    }
    
    public async Task<Intercept> InterceptResponseAsync(Func<InterceptedResponse, Task> handler, InterceptResponseOptions? options = null)
    {
        var intercept = await AddInterceptAsync([InterceptPhase.ResponseStarted], options).ConfigureAwait(false);
    
        await intercept.OnResponseStartedAsync(async res => await handler(new(res.BiDi, res.Context, res.IsBlocked, res.Navigation, res.RedirectCount, res.Request, res.Timestamp, res.Response))).ConfigureAwait(false);
    
        return intercept;
    }
    
    public async Task<Intercept> InterceptAuthAsync(Func<InterceptedAuth, Task> handler, InterceptAuthOptions? options = null)
    {
        var intercept = await AddInterceptAsync([InterceptPhase.AuthRequired], options).ConfigureAwait(false);
    
        await intercept.OnAuthRequiredAsync(async auth => await handler(new(auth.BiDi, auth.Context, auth.IsBlocked, auth.Navigation, auth.RedirectCount, auth.Request, auth.Timestamp, auth.Response))).ConfigureAwait(false);
    
        return intercept;
    }

    Copy link
    Contributor

    qodo-merge-pro bot commented Apr 9, 2025

    PR Code Suggestions ✨

    Latest suggestions up to ed35158
    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Learned
    best practice
    Add null parameter validation to prevent potential NullReferenceExceptions

    The constructor accepts a string parameter id which is directly assigned to the
    Id property without any null check. Since the parameter could be null, add a
    null check using ArgumentNullException.ThrowIfNull() to prevent potential
    NullReferenceExceptions when the Id property is accessed later.

    dotnet/src/webdriver/BiDi/Modules/Network/Request.cs [24-27]

     internal Request(string id)
     {
    +    ArgumentNullException.ThrowIfNull(id, nameof(id));
         Id = id;
     }
    • Apply this suggestion
    Suggestion importance[1-10]: 6
    Low
    General
    Simplify lambda expression

    The lambda expression is unnecessarily complex with nested awaits. Since you're
    just passing through the handler call, you can simplify this by directly using
    the handler as the delegate.

    dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextNetworkModule.cs [37-39]

     await intercept.OnBeforeRequestSentAsync(
    -    async req => await handler(new(req.BiDi, req.Context, req.IsBlocked, req.Navigation, req.RedirectCount, req.Request, req.Timestamp, req.Initiator)),
    +    req => handler(new(req.BiDi, req.Context, req.IsBlocked, req.Navigation, req.RedirectCount, req.Request, req.Timestamp, req.Initiator)),
         new BrowsingContextsSubscriptionOptions(null) { Contexts = [context] }).ConfigureAwait(false);
    • Apply this suggestion
    Suggestion importance[1-10]: 5

    __

    Why: The suggestion correctly identifies an unnecessarily complex lambda expression with nested awaits. The proposed simplification removes the redundant async/await keywords while maintaining the same functionality, which improves code readability and reduces overhead.

    Low
    • Update

    Previous suggestions

    Suggestions up to commit 02ecaed
    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Missing subscription options parameter

    The OnBeforeRequestSentAsync method is being called without passing the
    subscription options, but the original implementation in NetworkModule.cs passed
    these options. This could lead to different behavior in the new implementation.

    dotnet/src/webdriver/BiDi/Modules/Network/NetworkModule.HighLevel.cs [27-34]

     public async Task<Intercept> InterceptRequestAsync(Func<BeforeRequestSentEventArgs, Task> handler, InterceptRequestOptions? options = null)
     {
         var intercept = await AddInterceptAsync([InterceptPhase.BeforeRequestSent], options).ConfigureAwait(false);
     
    -    await intercept.OnBeforeRequestSentAsync(handler).ConfigureAwait(false);
    +    await intercept.OnBeforeRequestSentAsync(handler, options).ConfigureAwait(false);
     
         return intercept;
     }
    Suggestion importance[1-10]: 8

    __

    Why: The suggestion correctly identifies a critical issue where the new implementation omits the subscription options parameter when calling OnBeforeRequestSentAsync, which was present in the original code. This could cause different behavior and potentially break existing functionality.

    Medium
    Learned
    best practice
    Add null validation for required parameters to prevent potential NullReferenceExceptions

    Add a null check for the handler parameter before using it. Since this parameter
    is not marked as nullable and is required for the method to function properly,
    you should validate that it's not null before proceeding to prevent potential
    NullReferenceExceptions.

    dotnet/src/webdriver/BiDi/Modules/Network/NetworkModule.HighLevel.cs [27-34]

     public async Task<Intercept> InterceptRequestAsync(Func<BeforeRequestSentEventArgs, Task> handler, InterceptRequestOptions? options = null)
     {
    +    ArgumentNullException.ThrowIfNull(handler, nameof(handler));
    +    
         var intercept = await AddInterceptAsync([InterceptPhase.BeforeRequestSent], options).ConfigureAwait(false);
     
         await intercept.OnBeforeRequestSentAsync(handler).ConfigureAwait(false);
     
         return intercept;
     }
    Suggestion importance[1-10]: 6
    Low

    @nvborisenko
    Copy link
    Member Author

    This is the first attempt to split "extensions" and "low-level".

    @nvborisenko nvborisenko marked this pull request as draft April 10, 2025 09:03
    @nvborisenko nvborisenko marked this pull request as ready for review April 11, 2025 07:21
    @nvborisenko nvborisenko changed the title [dotnet] [bidi] Combine network interception to apply rules [dotnet] [bidi] Combine network interception to apply rules (breaking change) Apr 11, 2025
    @nvborisenko nvborisenko merged commit 12a1593 into SeleniumHQ:trunk Apr 11, 2025
    10 checks passed
    @nvborisenko nvborisenko deleted the bidi-intercept-highlevel branch April 11, 2025 19:52
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    [🚀 Feature]: [dotnet] [bidi] Revisit InterceptRequestAsync method
    2 participants