通过这段代码我们就可以知道了为啥在不设置AllowSynchronousIO为true的情下读取Body会抛出异常了吧,这个是程序级别的控制,而且我们还了解到Read的本质还是在调用ReadAsync异步方法
public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default) { return ReadAsyncWrapper(destination, cancellationToken); }
ReadAsync本身并无特殊限制,所以直接操作ReadAsync不会存在类似Read的异常。
通过这个我们得出了结论Request.Body即HttpRequestStream的同步读取Read会抛出异常,而异步读取ReadAsync并不会抛出异常只和HttpRequestStream的Read方法本身存在判断AllowSynchronousIO的值有关系。
AllowSynchronousIO本质来源通过HttpRequestStream的Read方法我们可以知道AllowSynchronousIO控制了同步读取的方式。而且我们还了解到了AllowSynchronousIO有几种不同方式的去配置,接下来我们来大致看下几种方式的本质是哪一种。通过HttpRequestStream我们知道Read方法中的AllowSynchronousIO的属性是来自IHttpBodyControlFeature也就是我们上面介绍的第二种配置方式
private readonly HttpRequestPipeReader _pipeReader; private readonly IHttpBodyControlFeature _bodyControl; public HttpRequestStream(IHttpBodyControlFeature bodyControl, HttpRequestPipeReader pipeReader) { _bodyControl = bodyControl; _pipeReader = pipeReader; }
那么它和KestrelServerOptions肯定是有关系的,因为我们只配置KestrelServerOptions的是HttpRequestStream的Read是不报异常的,而HttpRequestStream的Read只依赖了IHttpBodyControlFeature的AllowSynchronousIO属性。Kestrel中HttpRequestStream初始化的地方在BodyControl[点击查看源码👈]
private readonly HttpRequestStream _request; public BodyControl(IHttpBodyControlFeature bodyControl, IHttpResponseControl responseControl) { _request = new HttpRequestStream(bodyControl, _requestReader); }
而初始化BodyControl的地方在HttpProtocol中,我们找到初始化BodyControl的InitializeBodyControl方法[点击查看源码👈]
public void InitializeBodyControl(MessageBody messageBody) { if (_bodyControl == null) { //这里传递的是bodyControl传递的是this _bodyControl = new BodyControl(bodyControl: this, this); } (RequestBody, ResponseBody, RequestBodyPipeReader, ResponseBodyPipeWriter) = _bodyControl.Start(messageBody); _requestStreamInternal = RequestBody; _responseStreamInternal = ResponseBody; }
这里我们可以看的到初始化IHttpBodyControlFeature既然传递的是this,也就是HttpProtocol当前实例。也就是说HttpProtocol是实现了IHttpBodyControlFeature接口,HttpProtocol本身是partial的,我们在其中一个分布类HttpProtocol.FeatureCollection中看到了实现关系
[点击查看源码👈]
internal partial class HttpProtocol : IHttpRequestFeature, IHttpRequestBodyDetectionFeature, IHttpResponseFeature, IHttpResponseBodyFeature, IRequestBodyPipeFeature, IHttpUpgradeFeature, IHttpConnectionFeature, IHttpRequestLifetimeFeature, IHttpRequestIdentifierFeature, IHttpRequestTrailersFeature, IHttpBodyControlFeature, IHttpMaxRequestBodySizeFeature, IEndpointFeature, IRouteValuesFeature { bool IHttpBodyControlFeature.AllowSynchronousIO { get => AllowSynchronousIO; set => AllowSynchronousIO = value; } }
通过这个可以看出HttpProtocol确实实现了IHttpBodyControlFeature接口,接下来我们找到初始化AllowSynchronousIO的地方,找到了AllowSynchronousIO = ServerOptions.AllowSynchronousIO;这段代码说明来自于ServerOptions这个属性,找到初始化ServerOptions的地方[点击查看源码👈]
private HttpConnectionContext _context; //ServiceContext初始化来自HttpConnectionContext public ServiceContext ServiceContext => _context.ServiceContext; protected KestrelServerOptions ServerOptions { get; set; } = default!; public void Initialize(HttpConnectionContext context) { _context = context; //来自ServiceContext ServerOptions = ServiceContext.ServerOptions; Reset(); HttpResponseControl = this; }