.NET本身就是一个基于中间件(middleware)的框架,它通过一系列的中间件组件来处理HTTP请求和响应。因此,本篇文章主要描述从用户键入请求到服务器响应的大致流程,并深入探讨.NET通过kestrel将HTTP报文转换为HttpContext对象。

通过本文,您可以了解以下内容:

  • http的数据流转流程
  • 源码解读kestrel服务器的运作流程及生成HttpContext对象

一、HTTP请求的数据流转过程

1. 数据流转

HTTP 请求的数据流转过程非常复杂,涉及多个协议层次和网络设备。通过数据流转示意图可以简要了解该流程:

  1. DNS 解析

客户端浏览器会首先尝试从本地缓存中查找目标服务器的 IP 地址。如果缓存中没有该域名对应的 IP 地址,则会向本地 DNS 服务器发起 DNS 查询请求。

DNS 服务器会根据域名信息向上级 DNS 服务器发送递归查询请求,直到找到能够返回该域名对应 IP 地址的 DNS 服务器为止。最终,DNS 服务器将目标服务器的 IP 地址返回给客户端浏览器。

2.TCP 连接

TCP 连接需要经过三次握手的过程:

  • 第一次握手:客户端发送 SYN 包,表示请求建立连接。
  • 第二次握手:服务器返回 SYN+ACK 包,表示同意建立连接。
  • 第三次握手:客户端发送 ACK 包,表示确认连接已建立。

当客户端和服务器完成三次握手后,TCP 连接就建立成功了。

  1. 应用层发送HTTP请求

用户在浏览器中输入URL后,浏览器会向应用层发送HTTP请求。请求报文包含请求方法、URI、协议版本和请求头信息等。

  1. 传输层封装TCP协议数据段

传输层负责将HTTP请求报文分成若干个数据段进行传输,并使用TCP协议对这些数据段进行封装。

  1. 网络层路由选择和寻址

网络层负责对TCP数据段进行分组,并通过IP协议进行路由选择和寻址,以便将数据包从本地网络送到目标服务器。

  1. 数据链路层封装数据帧

数据链路层将IP数据包封装为数据帧,并添加源和目标MAC地址,以便在物理层上进行传输。

  1. 物理层传输比特流

物理层将数据帧转换为比特流,并通过物理介质(如网线、无线电波等)将数据发送到目标服务器。

  1. 服务器接收HTTP请求

当数据包到达目标服务器后,网络协议栈会解析数据包,并将HTTP请求报文交给Web服务器处理。

  1. .NET服务器处理HTTP请求

Web服务器处理HTTP请求,包括解析HTTP请求报文、映射URL到相应的处理器、执行请求处理程序,并生成HTTP响应报文等。

  1. 传输层封装TCP协议数据段

Web服务器生成HTTP响应报文之后,通过TCP协议将响应数据分成若干个数据段进行封装。

  1. 数据链路层封装数据帧

数据链路层将TCP数据段封装为数据帧,并添加源和目标MAC地址。

  1. 物理层传输比特流

物理层将数据帧转换为比特流,并通过物理介质(如网线、无线电波等)将数据发送回客户端浏览器。

  1. 应用层接收HTTP响应

客户端浏览器收到HTTP响应报文后,会交给应用层进行解析和处理。响应报文包含状态行、响应头和响应体等信息。

通过上文,我们已经了解了 HTTP 请求数据流转的基本过程。下图展示了数据从 HTTP 数据开始,逐层添加 TCP、IP、以太网头部,然后在每个层次进行解析,最终抵达目标服务器。

2. 报文数据格式

下边贴一张网络包的报文数据格式图:

想深入了解更多计算机网络知识的同学,可以自行查阅书籍和资料,这里有位博主总结的很好,地址:小林coding

二、认识kestrel和HttpContext

1. kestrel的作用

Kestrel 是一个基于libuv的跨平台Web 服务器,是.NET中默认启用的 Web 服务器,可以处理来自客户端的 HTTP 请求和响应。

图一 内网访问程序

图二 反向代理访问程序

2. 什么是HttpContext?

HttpContext保存有关 Http 请求的当前信息。它包含授权,身份验证,请求,响应,会话,项目,用户,表单选项等信息。收到每个 HTTP 请求时,HttpContext都会初始化一个包含当前信息的新对象。

想要了解更多HttpContext对象的属性和方法,请直接参阅官方文档

3. .NET中如何访问HttpContext

  • 使用 .NET Core 内置依赖项注入容器注册依赖项,如下所示的 Startup.cs配置服务类方法:
// 注入IHttpContextAccessor服务
builder.Services.AddHttpContextAccessor(); // 自定义服务中访问HttpContext
public class UserRepository : IUserRepository
{
private readonly IHttpContextAccessor _httpContextAccessor; public UserRepository(IHttpContextAccessor httpContextAccessor) =>
_httpContextAccessor = httpContextAccessor; public void LogCurrentUser()
{
var username = _httpContextAccessor.HttpContext.User.Identity.Name; // ...
}
}

更多的访问方式请自行查阅官方文档

三、源码解读kestrel创建HttpContext对象

以下是源代码的部分删减和修改,以便于更好地理解

1. 创建主机构建器

我们从Program开始,使用CreateBuilder方法创建一个默认的主机构建器,配置应用程序的默认设置以及注入基础服务。

// 在Program.cs文件中调用
var builder = WebApplication.CreateBuilder(args); // CreateBuilder方法返回了WebApplicationBuilder实例
public static WebApplicationBuilder CreateBuilder(string[] args) =>
new WebApplicationBuilder(new WebApplicationOptions(){ Args = args });

在WebApplicationBuilder 类的构造函数中,关于配置Configuration和IOC容器相关的已经在历史文章中做过解读。本文在看下几个主机构建器的关系和作用:

  • BootstrapHostBuilder 是一个基本的主机构建器,构建默认的主机(Host)和服务容器(Service Container)

  • IHostBuilder 定义了一组用于配置主机的方法,并返回一个IHost实例。使用IHostBuilder可以自定义应用程序的配置信息,如应用程序的环境、日志记录、配置文件等

  • ConfigureHostBuilder 扩展了 IHostBuilder 接口,并添加了一些特定主机的配置选项,例如应用程序名称、配置文件路径、日志、依赖注入等,可以根据需要进行扩展和定制。

  • ConfigureWebHostBuilder 是 ConfigureHostBuilder 的子类,主要用于处理与 Web 主机相关的配置,例如 Kestrel 服务器选项、HTTPS 配置、Web 根目录等

这几个的关系简单来讲就是通过BootstrapHostBuilder和IHostBuilder创建主机构建器,然后使用ConfigureHostBuilder和ConfigureWebHostBuilder扩展方法设置所需的选项,最终创建主机和服务容器实例

internal WebApplicationBuilder(WebApplicationOptions options, Action<IHostBuilder>? configureDefaults = null)
{
// configuration将在后续的配置中提供应用程序选项和参数
var configuration = new ConfigurationManager();
configuration.AddEnvironmentVariables(prefix: "ASPNETCORE_"); // 创建一个 HostApplicationBuilder 对象,并将其中包含的设置初始化为从 WebApplicationOptions 对象中获取的值
_hostApplicationBuilder = new HostApplicationBuilder(new HostApplicationBuilderSettings
{
Args = options.Args,
ApplicationName = options.ApplicationName,
EnvironmentName = options.EnvironmentName,
ContentRootPath = options.ContentRootPath,
Configuration = configuration,
}); // 创建BootstrapHostBuilder实例
var bootstrapHostBuilder = new BootstrapHostBuilder(_hostApplicationBuilder); // bootstrapHostBuilder 上调用 ConfigureWebHostDefaults 方法,以进行特定于 Web 主机的配置
bootstrapHostBuilder.ConfigureWebHostDefaults(webHostBuilder =>
{
//......
}); var webHostContext = (WebHostBuilderContext)bootstrapHostBuilder.Properties[typeof(WebHostBuilderContext)];
Environment = webHostContext.HostingEnvironment; Host = new ConfigureHostBuilder(bootstrapHostBuilder.Context, Configuration, Services);
WebHost = new ConfigureWebHostBuilder(webHostContext, Configuration, Services);
}

使用Kestrel构建默认主机

internal static void ConfigureWebDefaults(IWebHostBuilder builder)
{
ConfigureWebDefaultsWorker(
builder.UseKestrel(ConfigureKestrel),
services =>
{
services.AddRouting();
});
} public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, KestrelServerOptions> configureOptions)
{
return hostBuilder.UseKestrel().ConfigureKestrel(configureOptions);
}

配置WebHost在Kestrel服务器上运行,并通过QUIC协议实现高效数据传输的方式

public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder)
{
return hostBuilder
.UseKestrelCore()
.UseKestrelHttpsConfiguration()
.UseQuic(options =>
{
// Configure server defaults to match client defaults.
// https://github.com/dotnet/runtime/blob/a5f3676cc71e176084f0f7f1f6beeecd86fbeafc/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs#L118-L119
options.DefaultStreamErrorCode = (long)Http3ErrorCode.RequestCancelled;
options.DefaultCloseErrorCode = (long)Http3ErrorCode.NoError;
});
}

重点看下UseKestrelCore方法,该方法将Kestrel服务器应用到主机构建器的上下文中,并配置相关的服务

  • IConnectionListenerFactory:负责创建和管理传输连接
  • KestrelServerOptions:负责配置Kestrel服务器选项,例如端口号、连接数等
  • IHttpsConfigurationService:负责HTTPS支持,例如配置证书、加密算法等
  • IServer:指定KestrelServerImpl作为其实现类。这个服务是Kestrel服务器的核心实现,它接收来自客户端的请求并返回响应
  • KestrelMetrics:收集和报告有关Kestrel服务器运行状况的数据
public static IWebHostBuilder UseKestrelCore(this IWebHostBuilder hostBuilder)
{
hostBuilder.ConfigureServices(services =>
{
// Don't override an already-configured transport
services.TryAddSingleton<IConnectionListenerFactory, SocketTransportFactory>(); services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>();
services.AddSingleton<IHttpsConfigurationService, HttpsConfigurationService>();
services.AddSingleton<IServer, KestrelServerImpl>();
services.AddSingleton<KestrelMetrics>();
});
return hostBuilder;
}

2. 启动主机,并侦听HTTP请求

从Program中app.Run()开始,启动主机,最终会调用IHost的StartAsync方法。

app.Run();

public void Run([StringSyntax(StringSyntaxAttribute.Uri)] string? url = null)
{
Listen(url);
HostingAbstractionsHostExtensions.Run(this);
} public static async Task RunAsync(this IHost host, CancellationToken token = default)
{
try
{
await host.StartAsync(token).ConfigureAwait(false); await host.WaitForShutdownAsync(token).ConfigureAwait(false);
}
finally
{
if (host is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
}
else
{
host.Dispose();
}
}
}

将中间件和StartupFilters扩展传入HostingApplication主机,并进行启动

public async Task StartAsync(CancellationToken cancellationToken)
{
// ...省略了从配置中获取服务器监听地址和端口... // 这个东西就是中间件,下篇文章再重点解读
RequestDelegate? application = null;
try
{
IApplicationBuilder builder = ApplicationBuilderFactory.CreateBuilder(Server.Features); foreach (var filter in StartupFilters.Reverse())
{
configure = filter.Configure(configure);
}
configure(builder);
// Build the request pipeline
application = builder.Build();
}
catch (Exception ex)
{
Logger.ApplicationError(ex);
} /*
* application:中间件
* DiagnosticListener:事件监听器
* HttpContextFactory:HttpContext对象的工厂
*/
HostingApplication httpApplication = new HostingApplication(application, Logger, DiagnosticListener, ActivitySource, Propagator, HttpContextFactory, HostingEventSource.Log, HostingMetrics); await Server.StartAsync(httpApplication, cancellationToken); }

KestrelServerImpl类中实现Server.StartAsync方法,用于在指定地址和端口上开启HTTP服务。本篇文章只会解读http2的实现流程,http3的如果您感兴趣,请自行查阅源码。

public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken) where TContext : notnull
{
// 用于处理与绑定事件相关的逻辑
async Task OnBind(ListenOptions options, CancellationToken onBindCancellationToken)
{
// ...省略 获取是否支持Http1/2/3/协议及TLS加密,及判断至少支持一种协议... if (hasHttp1 || hasHttp2
|| options.Protocols == HttpProtocols.None)
{
// 调用UseHttpServer方法,为HTTP连接配置中间件、应用程序请求处理成中间件
options.UseHttpServer(ServiceContext, application, options.Protocols, addAltSvcHeader);
ConnectionDelegate connectionDelegate = options.Build(); // 添加连接限制中间件
connectionDelegate = EnforceConnectionLimit(connectionDelegate, Options.Limits.MaxConcurrentConnections, Trace, ServiceContext.Metrics); // 开始监听指定地址和端口上的HTTP请求
options.EndPoint = await _transportManager.BindAsync(configuredEndpoint, connectionDelegate, options.EndpointConfig, onBindCancellationToken).ConfigureAwait(false);
} //...省略http3...
} AddressBindContext = new AddressBindContext(_serverAddresses, Options, Trace, OnBind); await BindAsync(cancellationToken).ConfigureAwait(false);
}

UseHttpServer方法是将创建连接,解析等功能创建成委托中间件。在_transportManager.BindAsync方法中,启动监听后执行。我们先跳过UseHttpServer方法,先看下启动监听的方法。

public async Task<EndPoint> BindAsync(EndPoint endPoint, ConnectionDelegate connectionDelegate, EndpointConfig? endpointConfig, CancellationToken cancellationToken)
{
// 遍历所有的ITransportFactory对象,并查找可以对指定地址和端口进行绑定的工厂对象
foreach (var transportFactory in _transportFactories)
{
var selector = transportFactory as IConnectionListenerFactorySelector;
if (CanBindFactory(endPoint, selector))
{
// 调用其BindAsync方法,在指定地址和端口上启动传输通道(Transport)
var transport = await transportFactory.BindAsync(endPoint, cancellationToken).ConfigureAwait(false); // 启动循环接收传入连接。对于每个新连接请求,ConnectionListener都会创建一个新的ConnectionContext对象,并将其传递给连接处理委托(ConnectionDelegate)进行处理
StartAcceptLoop(new GenericConnectionListener(transport), c => connectionDelegate(c), endpointConfig);
return transport.EndPoint;
}
}
}

该方法使用IConnectionListener接口创建一个新的连接监听器(ConnectionListener),并启动一个循环以便不断接收传入的连接请求。对于每个新连接请求,它都会创建一个新的BaseConnectionContext对象,并将其传递给连接处理委托进行相应的操作

private void StartAcceptLoop<T>(IConnectionListener<T> connectionListener, Func<T, Task> connectionDelegate, EndpointConfig? endpointConfig) where T : BaseConnectionContext
{
var transportConnectionManager = new TransportConnectionManager(_serviceContext.ConnectionManager); var connectionDispatcher = new ConnectionDispatcher<T>(_serviceContext, connectionDelegate, transportConnectionManager); var acceptLoopTask = connectionDispatcher.StartAcceptingConnections(connectionListener); _transports.Add(new ActiveTransport(connectionListener, acceptLoopTask, transportConnectionManager, endpointConfig));
}

线程池中通过while循环不断监听连接请求

public Task StartAcceptingConnections(IConnectionListener<T> listener)
{
ThreadPool.UnsafeQueueUserWorkItem(StartAcceptingConnectionsCore, listener, preferLocal: false);
return _acceptLoopTcs.Task;
}
private void StartAcceptingConnectionsCore(IConnectionListener<T> listener)
{
// REVIEW: Multiple accept loops in parallel?
_ = AcceptConnectionsAsync(); async Task AcceptConnectionsAsync()
{
try
{
while (true)
{
var connection = await listener.AcceptAsync();
if (connection == null)
{
// We're done listening
break;
}
// 创建一个新的连接Id
var id = _transportConnectionManager.GetNewConnectionId(); var metricsContext = Metrics.CreateContext(connection); var kestrelConnection = new KestrelConnection<T>(
id, _serviceContext, _transportConnectionManager, _connectionDelegate, connection, Log, metricsContext); _transportConnectionManager.AddConnection(id, kestrelConnection); Metrics.ConnectionQueuedStart(metricsContext); ThreadPool.UnsafeQueueUserWorkItem(kestrelConnection, preferLocal: false);
}
}
}
}

IThreadPoolWorkItem执行方法就是调用了我们上文中,先跳过的委托部分

void IThreadPoolWorkItem.Execute()
{
using (BeginConnectionScope(connectionContext))
{
try
{
await _connectionDelegate(connectionContext);
}
catch (Exception ex)
{
}
}
}

回到上文中的UseHttpServer方法,该方法中创建HttpConnectionMiddleware对象,用于封装处理HTTP连接和请求的中间件

public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, ServiceContext serviceContext, IHttpApplication<TContext> application, HttpProtocols protocols, bool addAltSvcHeader) where TContext : notnull
{
var middleware = new HttpConnectionMiddleware<TContext>(serviceContext, application, protocols, addAltSvcHeader);
return builder.Use(next =>
{
// 实际的请求处理
return middleware.OnConnectionAsync;
});
}

创建HttpConnection对象,并调用ProcessRequestsAsync处理传入的请求

public Task OnConnectionAsync(ConnectionContext connectionContext)
{
var httpConnectionContext = new HttpConnectionContext(); var connection = new HttpConnection(httpConnectionContext); return connection.ProcessRequestsAsync(_application);
}

创建Http2Connection连接对象,并注册停止清理事件,调用ProcessRequestsAsync方法处理请求

public async Task ProcessRequestsAsync<TContext>(IHttpApplication<TContext> httpApplication) where TContext : notnull
{
IRequestProcessor? requestProcessor = new Http2Connection((HttpConnectionContext)_context); if (requestProcessor != null)
{
// 注册停止处理请求事件
using var shutdownRegistration = connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state!).StopProcessingNextRequest(), this); // 注册执行清理操作事件
using var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((HttpConnection)state!).OnConnectionClosed(), this); await requestProcessor.ProcessRequestsAsync(httpApplication);
} }

从ProcessRequestsAsync方法就进入核心解析环节了,该方法负责读取和解析传入的HTTP/2帧,并执行相应的操作来处理请求。为了保证性能和可靠性,该方法中还使用了心跳检测、流量控制和超时控制等技巧。

通过循环读取数据并使用 ProcessFrameAsync方法处理传入的HTTP/2帧,直到收到终止连接的帧或者出现错误。

private Task ProcessFrameAsync<TContext>(IHttpApplication<TContext> application, in ReadOnlySequence<byte> payload) where TContext : notnull
{
// 请求流标识符必须是奇数
if (_incomingFrame.StreamId != 0 && (_incomingFrame.StreamId & 1) == 0)
{
throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdEven(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.PROTOCOL_ERROR);
} // 根据帧类型分发到不同的处理方法中
return _incomingFrame.Type switch
{
Http2FrameType.DATA => ProcessDataFrameAsync(payload),
Http2FrameType.HEADERS => ProcessHeadersFrameAsync(application, payload),
Http2FrameType.PRIORITY => ProcessPriorityFrameAsync(),
Http2FrameType.RST_STREAM => ProcessRstStreamFrameAsync(),
Http2FrameType.SETTINGS => ProcessSettingsFrameAsync(payload),
Http2FrameType.PUSH_PROMISE => throw new Http2ConnectionErrorException(CoreStrings.Http2ErrorPushPromiseReceived, Http2ErrorCode.PROTOCOL_ERROR),
Http2FrameType.PING => ProcessPingFrameAsync(payload),
Http2FrameType.GOAWAY => ProcessGoAwayFrameAsync(),
Http2FrameType.WINDOW_UPDATE => ProcessWindowUpdateFrameAsync(),
Http2FrameType.CONTINUATION => ProcessContinuationFrameAsync(payload),
_ => ProcessUnknownFrameAsync(),
};
}

读取ProcessHeadersFrameAsync头部数据时,如果是新的数据,就开启新的数据流

private Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext> application, in ReadOnlySequence<byte> payload) where TContext : notnull
{
// ...... // 开始一个新的Stream
_currentHeadersStream = GetStream(application); _headerFlags = _incomingFrame.HeadersFlags; // 荷载数据
var headersPayload = payload.Slice(0, _incomingFrame.HeadersPayloadLength); // 解析请求头部数据
return DecodeHeadersAsync(_incomingFrame.HeadersEndHeaders, headersPayload);
} private Task DecodeHeadersAsync(bool endHeaders, in ReadOnlySequence<byte> payload)
{
_highestOpenedStreamId = _currentHeadersStream.StreamId; // 解码数据
_hpackDecoder.Decode(payload, endHeaders, handler: this); // 当头部信息解码完成,开启新的数据流并重置处理状态,迎接下一个请求
if (endHeaders)
{
_currentHeadersStream.OnHeadersComplete();
StartStream();
ResetRequestHeaderParsingState();
} return Task.CompletedTask;
}

Decode解码方法中使用HPACK算法和状态机算法对HTTP/2请求头部进行解码。本篇文章中就不继续深究了......

StartStream方法用于处理 HTTP/2 的流开始,并进行一些相关的检查和操作,如添加到流字典、计数增加、验证标头等。在做了诸多校验工作后,进行执行。

private void StartStream()
{
// _scheduleInline 仅在测试中为 true
if (!_scheduleInline)
{
// 不能让应用程序代码阻塞连接处理循环。
ThreadPool.UnsafeQueueUserWorkItem(_currentHeadersStream, preferLocal: false);
}
else
{
_currentHeadersStream.Execute();
}
}

Execute方法在处理请求之前进行一些日志记录和度量统计操作,并调用异步方法 ProcessRequestsAsync() 来处理请求

public override void Execute()
{
KestrelEventSource.Log.RequestQueuedStop(this, AspNetCore.Http.HttpProtocol.Http2);
ServiceContext.Metrics.RequestQueuedStop(MetricsContext, AspNetCore.Http.HttpProtocol.Http2); // REVIEW: Should we store this in a field for easy debugging?
_ = ProcessRequestsAsync(_application);
}

ProcessRequests是异步处理请求的方法。使用循环来处理多个请求,并在每个请求处理的不同阶段执行相应的操作,如解析请求、运行应用程序代码、发送响应等。同时,它还处理了各种异常情况,并记录日志。循环会一直执行,直到保持连接的标志 _keepAlive 被设置为 false 或需要结束连接。并在此处创建了HttpContext对象

private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application) where TContext : notnull
{
while (_keepAlive)
{
BeginRequestProcessing(); // 尝试解析请求,直到成功解析请求或者需要结束连接
var result = default(ReadResult);
bool endConnection;
do
{
if (BeginRead(out var awaitable))
{
result = await awaitable;
}
} while (!TryParseRequest(result, out endConnection)); if (endConnection)
{
// 连接已经结束,停止处理请求
return;
} // 创建消息体
var messageBody = CreateMessageBody();
if (!messageBody.RequestKeepAlive)
{
_keepAlive = false;
} // 初始化请求体控制器
InitializeBodyControl(messageBody); // 创建上下文对象
var context = application.CreateContext(this); // 运行应用程序对该请求的处理代码
await application.ProcessRequestAsync(context); // 方法停止请求体控制器
await _bodyControl.StopAsync(); // 释放上下文对象
application.DisposeContext(context, _applicationException); // 回到 while 循环的开头,继续处理下一个请求 }
}

该方法接受一个 IFeatureCollection 类型的参数,并返回一个 HttpContext 对象

public HttpContext CreateContext(IFeatureCollection contextFeatures)
{
return _httpContextFactory?.Create(contextFeatures) ?? new DefaultHttpContext(contextFeatures);
}

初始化 DefaultHttpContext 对象的 _features、_request 和 _response 成员变量,并创建与当前上下文相关联的默认的请求和响应对象

public DefaultHttpContext(IFeatureCollection features)
{
_features.Initalize(features);
_request = new DefaultHttpRequest(this);
_response = new DefaultHttpResponse(this);
}

四、小结

通过本篇文章可以深入了解了HTTP请求的数据流转过程。了解了数据在客户端和服务器之间的流动方式,以及HTTP报文的结构。

此外,我们还对Kestrel进行了源码解读,并了解了如何创建和管理HttpContext。Kestrel作为高性能的Web服务器,扮演着连接客户端和应用程序的桥梁,而HttpContext则提供了对请求和响应的上下文信息和处理能力。

通过深入研究和理解HTTP请求的数据流转过程以及Kestrel和HttpContext的工作原理,我们可以清晰的认知到整个运作流程。当然还有很多细节没有表述,在以后遇见问题的时候,可以快速定位问题或者查阅相关模块代码。以及了解如何去定制想要的扩展功能。

题外话:

由于我阅读时喜欢一次性阅读完整篇文章,因此我写文章时常常会花费很长时间,这也导致我的文章变得相对较长。我也会考虑你是否有足够的耐心和时间来阅读整篇文章,如果你有好写作技巧,请指教。总之,完成一篇长文后,我会感到非常舒适和满足,很有成就感!

如果您觉得这篇文章有所收获,还请点个赞并关注。如果您有宝贵建议,欢迎在评论区留言,非常感谢您的支持!

(也可以关注我的公众号噢:Broder,万分感谢_)

.NET源码解读kestrel服务器及创建HttpContext对象流程的更多相关文章

  1. openfire源码解读之将cache和session对象移入redis以提升性能

    原文:http://blog.csdn.net/jinzhencs/article/details/50522322 前言: 目前我们的openfire服务器只能支撑单机2W 集群4W.(估测在线用户 ...

  2. pdb 源码索引符号服务器创建过程

    pdb是调试程序必不可少的东西,它保存着一个exe或dll的调试信息,对pdb进行源码索引可以快速找到软件对应该版本的代码,本文以subversion版本控制服务器进行介绍 一.需要安装的软件 win ...

  3. Prism 源码解读1-Bootstrapper和Region的创建

    介绍 之前也研究过Prism框架但是一直没有深入理解,现在项目上想把一个Winform的桌面应用程序改造成WPF程序,同时我希望程序是可测试可维护架构良好的,Prism的这些设计理念正好符合我的需求, ...

  4. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  5. AFNetworking 3.0 源码解读 总结(干货)(下)

    承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...

  6. AFNetworking 3.0 源码解读 总结(干货)(上)

    养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...

  7. AFNetworking 3.0 源码解读(八)之 AFImageDownloader

    AFImageDownloader 这个类对写DownloadManager有很大的借鉴意义.在平时的开发中,当我们使用UIImageView加载一个网络上的图片时,其原理就是把图片下载下来,然后再赋 ...

  8. AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization

    这篇就讲到了跟请求相关的类了 关于AFNetworking 3.0 源码解读 的文章篇幅都会很长,因为不仅仅要把代码进行详细的的解释,还会大概讲解和代码相关的知识点. 上半篇: URI编码的知识 关于 ...

  9. AFNetworking 3.0 源码解读(四)之 AFURLResponseSerialization

    本篇是AFNetworking 3.0 源码解读的第四篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...

  10. AFNetworking 3.0 源码解读(五)之 AFURLSessionManager

    本篇是AFNetworking 3.0 源码解读的第五篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...

随机推荐

  1. 全网最详细中英文ChatGPT-GPT-4示例文档-智能编写Python注释文档字符串从0到1快速入门——官网推荐的48种最佳应用场景(附python/node.js/curl命令源代码,小白也能学)

    目录 Introduce 简介 setting 设置 Prompt 提示 Sample response 回复样本 API request 接口请求 python接口请求示例 node.js接口请求示 ...

  2. blender资源库 【自用】

    1 https://www.threedscans.com A Website with a lot of photo-scanned sculptures which are free to use ...

  3. [Java SE]Unicode解码

    文由 将ASCII等其他非Unicode字符与Unicode混合的"脏的.不规范的"编码文本转为正常文本. 源码 unicodetoString(String unicodeTex ...

  4. sorted、返回函数、匿名函数、装饰器、偏函数

    1.sorted()排序方法,它可已经一个列表按照升序排序,也可以按照反序排序 1)如果要进行反序排序时,需要在函数里面设置reverse = True 2)sorted是一个高阶函数,它接受函数作为 ...

  5. 【LeetCode回溯算法#extra01】集合划分问题【火柴拼正方形、划分k个相等子集、公平发饼干】

    火柴拼正方形 https://leetcode.cn/problems/matchsticks-to-square/ 你将得到一个整数数组 matchsticks ,其中 matchsticks[i] ...

  6. DG:windows密码文件

    问题描述:搭建DG,找不到密码文件的位置,就给备库重新生成了一个密码文件,传到了备库,但是拉到了备库以后,恢复过程中,trace日志在报错,后来才知道windows下的密码文件跟linux平台下的面文 ...

  7. Nvidia GPU池化-远程GPU

    1 背景 Nvidia GPU得益于在深度学习领域强大的计算能力,使其在数据中心常年处于绝对的统治地位.尽管借助GPU虚拟化实现多任务混布,提高了GPU的利用率,缓解了长尾效应,但是GPU利用率的绝对 ...

  8. Redis性能瓶颈揭秘:如何优化大key问题?

    1. 什么是Redis大key问题 Redis大key问题指的是某个key对应的value值所占的内存空间比较大,导致Redis的性能下降.内存不足.数据不均衡以及主从同步延迟等问题. 到底多大的数据 ...

  9. cocos2dx返回Android游戏黑屏解决办法

    用来解决返回Android游戏加载资源时黑屏的问题.帖子过些日子估计就沉了,所以转出来,以供后面查询. 需要修改三个文件: 1) cocos2dx/platform/CCPlatformMacros. ...

  10. C# 自定义并动态切换光标

    系统有很多光标类型 :Cursors 类 (System.Windows.Input) | Microsoft Docs 本章介绍如何自定义光标.并动态切换光标类型. 动态切换光标类型 以白板书写为例 ...