Skip to content
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

expose HttpConnection #66223

Open
wfurt opened this issue Mar 4, 2022 · 1 comment
Open

expose HttpConnection #66223

wfurt opened this issue Mar 4, 2022 · 1 comment
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-System.Net.Http
Milestone

Comments

@wfurt
Copy link
Member

wfurt commented Mar 4, 2022

This is somewhat related to #525 and it aims to solve some of the problem Low-level HTTP API is trying to solve.
At this moment it is direct copy from prototype @scalablecory was working on.

The basic idea here is that we would API that allows to connect to given server and expose operations similar to HttpClient.
There would be no connection pool or any hidden magic and caller would have direct control over object lifetime.

// This class tries to have everything needed for an efficient
// version-agnostic load balancing connection pool to be built on top.
//
// As part of this, we'll will be proving out that such a version-agnostic
// pool is possible. If not, the callbacks probably make more sense directly
// on the HTTP/2 and HTTP/3 classes, not as virtuals.
//
// Solves https://github.com/dotnet/runtime/issues/45246,
// which just news up one of these connections manually.
public abstract class HttpConnection : HttpMessageHandler
{
    // used by pool to disqualify this connection or (for latency purposes)
    // to preemptively spin up a new connection before a new request is made.
    //
    // called on GOAWAY.
    public abstract Action<HttpConnection> ConnectionShuttingDownCallback { get; set; }
    
    // used to know when a new request can be made: barring a GOAWAY
    // race, SendIfStreamAvailableAsync will not return null.
    //
    // used for:
    // https://github.com/microsoft/reverse-proxy/issues/394
    //
    // this puts the job of queuing requests on the caller, and enables them to
    // route request among load balanced (possibly not same host/port, for YARP)
    // connections.
    //
    // called on:
    // HTTP/1 - a response finished.
    // HTTP/2 - a response finished, or new SETTINGS frame gave us additional streams.
    // HTTP/3 - a MAX_STREAMS frame gave us additional streams.
    public abstract Action<HttpConnection, int> StreamsAvailableCallback { get; set; }
    
    // if a stream is not available, throw.
    // inherited and sealed: all implementation funnels through the Core method below.
    protected internal sealed override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken);
    protected internal sealed override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
    
    // only sends if a stream is available, otherwise return null.
    // Just a non-throwing version. "Try" is inappropriate because it can't
    // follow the "bool TryFoo(out T)" pattern -- no out params on async.
    public ValueTask<HttpResponseMessage?> SendIfStreamAvailableAsync(HttpRequestMessage request, CancellationToken cancellationToken = default);
    protected abstract ValueTask<HttpResponseMessage?> SendIfStreamAvailableAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken);
}

public sealed class Http1Connection : HttpConnection
{
    public Http1Connection(Stream stream, HttpConnectionSettings settings);
}

public sealed class Http2Connection : HttpConnection
{   
    // used to upgrade protocol versions, or for server-directed load balancing.
    //
    // called when an ALTSVC extension frame is received.
    public abstract Action<HttpConnection, AltSvcValue> AltSvcCallback { get; set; }
    
    public Http2Connection(Stream stream, HttpConnectionSettings settings);

    // rather than be a config value, caller must PingAsync().
    // this enables smarter load balancing.
    //
    // note for H3, pings are a QUIC feature, not a HTTP feature.
    // we could still reasonably support this for H3 by forwarding the API call, if we wanted consistency of APIs.
    public ValueTask PingAsync(CancellationToken cancellationToken);
}

public sealed class Http3Connection : HttpConnection
{
    public Http3Connection(QuicConnection connection, HttpConnectionSettings settings);
}

// SocketsHttpHandler settings -- decide which of these we want to handle at a connection level.
public sealed class HttpConnectionSettings
{
    public CookieContainer? CookieContainer { get; init; }
    public ICredentials? Credentials { get; init; }
    public TimeSpan Expect100ContinueTimeout { get; init; }
    public int MaxResponseDrainSize { get; init; }
    public int MaxResponseHeadersLength { get; init; }
    public bool PreAuthenticate { get; init; }
    public HeaderEncodingSelector<HttpRequestMessage>? RequestHeaderEncodingSelector { get; init; }
    public TimeSpan ResponseDrainTimeout { get; init; }
    public HeaderEncodingSelector<HttpRequestMessage>? ResponseHeaderEncodingSelector { get; init; }
}
@wfurt wfurt added api-needs-work API needs work before it is approved, it is NOT ready for implementation api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.Net.Http labels Mar 4, 2022
@ghost
Copy link

ghost commented Mar 4, 2022

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Issue Details

This is somewhat related to #525 and it aims to solve some of the problem Low-level HTTP API is trying to solve.
At this moment it is direct copy from prototype @scalablecory was working on.

The basic idea here is that we would API that allows to connect to given server and expose operations similar to HttpClient.
There would be no connection pool or any hidden magic and caller would have direct control over object lifetime.

// This class tries to have everything needed for an efficient
// version-agnostic load balancing connection pool to be built on top.
//
// As part of this, we'll will be proving out that such a version-agnostic
// pool is possible. If not, the callbacks probably make more sense directly
// on the HTTP/2 and HTTP/3 classes, not as virtuals.
//
// Solves https://github.com/dotnet/runtime/issues/45246,
// which just news up one of these connections manually.
public abstract class HttpConnection : HttpMessageHandler
{
    // used by pool to disqualify this connection or (for latency purposes)
    // to preemptively spin up a new connection before a new request is made.
    //
    // called on GOAWAY.
    public abstract Action<HttpConnection> ConnectionShuttingDownCallback { get; set; }
    
    // used to know when a new request can be made: barring a GOAWAY
    // race, SendIfStreamAvailableAsync will not return null.
    //
    // used for:
    // https://github.com/microsoft/reverse-proxy/issues/394
    //
    // this puts the job of queuing requests on the caller, and enables them to
    // route request among load balanced (possibly not same host/port, for YARP)
    // connections.
    //
    // called on:
    // HTTP/1 - a response finished.
    // HTTP/2 - a response finished, or new SETTINGS frame gave us additional streams.
    // HTTP/3 - a MAX_STREAMS frame gave us additional streams.
    public abstract Action<HttpConnection, int> StreamsAvailableCallback { get; set; }
    
    // if a stream is not available, throw.
    // inherited and sealed: all implementation funnels through the Core method below.
    protected internal sealed override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken);
    protected internal sealed override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
    
    // only sends if a stream is available, otherwise return null.
    // Just a non-throwing version. "Try" is inappropriate because it can't
    // follow the "bool TryFoo(out T)" pattern -- no out params on async.
    public ValueTask<HttpResponseMessage?> SendIfStreamAvailableAsync(HttpRequestMessage request, CancellationToken cancellationToken = default);
    protected abstract ValueTask<HttpResponseMessage?> SendIfStreamAvailableAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken);
}

public sealed class Http1Connection : HttpConnection
{
    public Http1Connection(Stream stream, HttpConnectionSettings settings);
}

public sealed class Http2Connection : HttpConnection
{   
    // used to upgrade protocol versions, or for server-directed load balancing.
    //
    // called when an ALTSVC extension frame is received.
    public abstract Action<HttpConnection, AltSvcValue> AltSvcCallback { get; set; }
    
    public Http2Connection(Stream stream, HttpConnectionSettings settings);

    // rather than be a config value, caller must PingAsync().
    // this enables smarter load balancing.
    //
    // note for H3, pings are a QUIC feature, not a HTTP feature.
    // we could still reasonably support this for H3 by forwarding the API call, if we wanted consistency of APIs.
    public ValueTask PingAsync(CancellationToken cancellationToken);
}

public sealed class Http3Connection : HttpConnection
{
    public Http3Connection(QuicConnection connection, HttpConnectionSettings settings);
}

// SocketsHttpHandler settings -- decide which of these we want to handle at a connection level.
public sealed class HttpConnectionSettings
{
    public CookieContainer? CookieContainer { get; init; }
    public ICredentials? Credentials { get; init; }
    public TimeSpan Expect100ContinueTimeout { get; init; }
    public int MaxResponseDrainSize { get; init; }
    public int MaxResponseHeadersLength { get; init; }
    public bool PreAuthenticate { get; init; }
    public HeaderEncodingSelector<HttpRequestMessage>? RequestHeaderEncodingSelector { get; init; }
    public TimeSpan ResponseDrainTimeout { get; init; }
    public HeaderEncodingSelector<HttpRequestMessage>? ResponseHeaderEncodingSelector { get; init; }
}
Author: wfurt
Assignees: -
Labels:

api-needs-work, api-suggestion, area-System.Net.Http

Milestone: -

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Mar 4, 2022
@wfurt wfurt removed the untriaged New issue has not been triaged by the area owner label Mar 8, 2022
@wfurt wfurt added this to the 7.0.0 milestone Mar 8, 2022
@karelz karelz removed the api-suggestion Early API idea and discussion, it is NOT ready for implementation label May 12, 2022
@wfurt wfurt modified the milestones: 7.0.0, Future May 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-System.Net.Http
Projects
None yet
Development

No branches or pull requests

2 participants