protected static async Task WriteFileAsync(MazeContext context, Stream fileStream, RangeItemHeaderValue range, long rangeLength) { var outputStream = context.Response.Body; using (fileStream) { try { if (range == null) { await StreamCopyOperation.CopyToAsync(fileStream, outputStream, count : null, bufferSize : BufferSize, cancel : context.RequestAborted); } else { fileStream.Seek(range.From.Value, SeekOrigin.Begin); await StreamCopyOperation.CopyToAsync(fileStream, outputStream, rangeLength, BufferSize, context.RequestAborted); } } catch (OperationCanceledException) { // Don't throw this exception, it's most likely caused by the client disconnecting. // However, if it was cancelled for any other reason we need to prevent empty responses. context.Abort(); } } }
/// <summary> /// Creates a new <see cref="OutputFormatterWriteContext" />. /// </summary> /// <param name="context">The <see cref="MazeContext" /> for the current request.</param> /// <param name="writerFactory">The delegate used to create a <see cref="TextWriter" /> for writing the response.</param> /// <param name="objectType">The <see cref="Type" /> of the object to write to the response.</param> /// <param name="object">The object to write to the response.</param> public OutputFormatterWriteContext(MazeContext context, Func <Stream, Encoding, TextWriter> writerFactory, Type objectType, object @object) : base(context) { WriterFactory = writerFactory ?? throw new ArgumentNullException(nameof(writerFactory)); ObjectType = objectType; Object = @object; }
/// <summary> /// Creates a new instance of <see cref="InputFormatterContext" />. /// </summary> /// <param name="httpContext"> /// The <see cref="Microsoft.AspNetCore.Http.HttpContext" /> for the current operation. /// </param> /// <param name="modelName">The name of the model.</param> /// <param name="modelState"> /// The <see cref="Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary" /> for recording errors. /// </param> /// <param name="metadata"> /// The <see cref="ModelMetadata" /> of the model to deserialize. /// </param> /// <param name="readerFactory"> /// A delegate which can create a <see cref="TextReader" /> for the request body. /// </param> public InputFormatterContext( MazeContext httpContext, string modelName, ModelStateDictionary modelState, ModelMetadata metadata, Func <Stream, Encoding, TextReader> readerFactory) : this(httpContext, modelName, modelState, metadata, readerFactory, false) { }
private Task WriteError(MazeContext context, RestError error, int statusCode) { var actionContext = new DefaultActionContext(context, null, ImmutableDictionary <string, object> .Empty); return(new ObjectResult(new[] { error }) { StatusCode = statusCode }.ExecuteResultAsync(actionContext)); }
/// <summary> /// Creates a new instance of <see cref="InputFormatterContext" />. /// </summary> /// <param name="httpContext"> /// The <see cref="Microsoft.AspNetCore.Http.HttpContext" /> for the current operation. /// </param> /// <param name="modelName">The name of the model.</param> /// <param name="modelState"> /// The <see cref="Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary" /> for recording errors. /// </param> /// <param name="metadata"> /// The <see cref="ModelMetadata" /> of the model to deserialize. /// </param> /// <param name="readerFactory"> /// A delegate which can create a <see cref="TextReader" /> for the request body. /// </param> /// <param name="treatEmptyInputAsDefaultValue"> /// A value for the <see cref="TreatEmptyInputAsDefaultValue" /> property. /// </param> public InputFormatterContext( MazeContext httpContext, string modelName, ModelStateDictionary modelState, ModelMetadata metadata, Func <Stream, Encoding, TextReader> readerFactory, bool treatEmptyInputAsDefaultValue) { MazeContext = httpContext ?? throw new ArgumentNullException(nameof(httpContext)); ModelName = modelName ?? throw new ArgumentNullException(nameof(modelName)); ModelState = modelState ?? throw new ArgumentNullException(nameof(modelState)); Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); ReaderFactory = readerFactory ?? throw new ArgumentNullException(nameof(readerFactory)); TreatEmptyInputAsDefaultValue = treatEmptyInputAsDefaultValue; ModelType = metadata.ModelType; }
public ResolveResult Resolve(MazeContext context) { var pathDecoded = HttpUtility.UrlDecode(context.Request.Path); var results = _routeResolverTrie.GetMatches(context.Request.Method, pathDecoded); if (!results.Any()) { return(new ResolveResult(false)); } var matchResult = results.OrderByDescending(x => x.Score).First(); return(new ResolveResult { RouteDescription = matchResult.RouteDescription, Parameters = matchResult.Parameters }); }
public MazeGeneratorController(ILogger <MazeGeneratorController> logger, MazeContext context) { _logger = logger; _context = context; }
/// <summary> /// Returns the normalized form of the requested range if the Range Header in the <see cref="MazeContext.Request" /> /// is valid. /// </summary> /// <param name="context">The <see cref="MazeContext" /> associated with the request.</param> /// <param name="requestHeaders">The <see cref="RequestHeaders" /> associated with the given <paramref name="context" />.</param> /// <param name="length">The total length of the file representation requested.</param> /// <param name="logger">The <see cref="ILogger" />.</param> /// <returns> /// A boolean value which represents if the <paramref name="requestHeaders" /> contain a single valid /// range request. A <see cref="RangeItemHeaderValue" /> which represents the normalized form of the /// range parsed from the <paramref name="requestHeaders" /> or <c>null</c> if it cannot be normalized. /// </returns> /// <remark> /// If the Range header exists but cannot be parsed correctly, or if the provided length is 0, then the range request /// cannot be satisfied (status 416). /// This results in (<c>true</c>,<c>null</c>) return values. /// </remark> public static (bool isRangeRequest, RangeItemHeaderValue range) ParseRange( MazeContext context, RequestHeaders requestHeaders, long length, ILogger logger) { var rawRangeHeader = context.Request.Headers[HeaderNames.Range]; if (StringValues.IsNullOrEmpty(rawRangeHeader)) { logger.LogTrace("Range header's value is empty."); return(false, null); } // Perf: Check for a single entry before parsing it if (rawRangeHeader.Count > 1 || rawRangeHeader[0].IndexOf(',') >= 0) { logger.LogDebug("Multiple ranges are not supported."); // The spec allows for multiple ranges but we choose not to support them because the client may request // very strange ranges (e.g. each byte separately, overlapping ranges, etc.) that could negatively // impact the server. Ignore the header and serve the response normally. return(false, null); } var rangeHeader = requestHeaders.Range; if (rangeHeader == null) { logger.LogDebug("Range header's value is invalid."); // Invalid return(false, null); } // Already verified above Debug.Assert(rangeHeader.Ranges.Count == 1); var ranges = rangeHeader.Ranges; if (ranges == null) { logger.LogDebug("Range header's value is invalid."); return(false, null); } if (ranges.Count == 0) { return(true, null); } if (length == 0) { return(true, null); } // Normalize the ranges var range = NormalizeRange(ranges.SingleOrDefault(), length); // Return the single range return(true, range); }
/// <summary> /// Creates a new <see cref="OutputFormatterCanWriteContext" />. /// </summary> /// <param name="context">The <see cref="MazeContext" /> for the current request.</param> protected OutputFormatterCanWriteContext(MazeContext context) { MazeContext = context ?? throw new ArgumentNullException(nameof(context)); }
public MazesController(MazeContext context) { _context = context; }
public DefaultActionContext(MazeContext context, Route route, IReadOnlyDictionary <string, object> routeData) { Context = context; Route = route; RouteData = routeData; }
/// <inheritdoc /> public async Task Execute(MazeContext context, IChannelServer channelServer) { _logger.LogDebug($"Resolve Maze path {context.Request.Path}"); var result = _routeResolver.Resolve(context); if (!result.Success) { _logger.LogDebug("Path not found"); await WriteError(context, BusinessErrors.Commander.RouteNotFound(context.Request.Path), StatusCodes.Status404NotFound); return; } _logger.LogDebug( $"Route resolved (package: {result.RouteDescription.PackageIdentity}). Get cached route info."); var route = _routeCache.Routes[result.RouteDescription]; var actionContext = new DefaultActionContext(context, route, result.Parameters.ToImmutableDictionary()); _logger.LogDebug($"Invoke method {route.RouteMethod}"); IActionResult actionResult; try { switch (route.RouteType) { case RouteType.Http: actionResult = await route.ActionInvoker.Value.Invoke(actionContext); break; case RouteType.ChannelInit: _logger.LogDebug("Create channel {channelName}", actionContext.Route.ControllerType.FullName); var channel = await route.ActionInvoker.Value.InitializeChannel(actionContext, channelServer); context.Response.Headers.Add(HeaderNames.Location, "ws://channels/" + channel.ChannelId); actionResult = new StatusCodeResult(StatusCodes.Status201Created); break; case RouteType.Channel: var channelId = int.Parse(actionContext.Context.Request.Headers["ChannelId"]); _logger.LogDebug("Request channel with id {channelId}", channelId); var foundChannel = channelServer.GetChannel(channelId); actionResult = await route.ActionInvoker.Value.InvokeChannel(actionContext, (MazeChannel)foundChannel); break; default: throw new ArgumentOutOfRangeException(); } } catch (Exception e) { if (context.RequestAborted.IsCancellationRequested) { return; } _logger.LogError(e, $"Error occurred when invoking method {route.RouteMethod} of package {result.RouteDescription.PackageIdentity} (path: {context.Request.Path})"); await WriteError(context, BusinessErrors.Commander.ActionError(e.GetType().Name, route.RouteMethod.Name, e.Message), StatusCodes.Status500InternalServerError); return; } if (context.RequestAborted.IsCancellationRequested) { return; } try { await actionResult.ExecuteResultAsync(actionContext); } catch (Exception e) { if (context.RequestAborted.IsCancellationRequested) { return; } _logger.LogError(e, $"Error occurred when executing action result {route.RouteMethod} of package {result.RouteDescription.PackageIdentity} (path: {context.Request.Path})"); await WriteError(context, BusinessErrors.Commander.ResultExecutionError(e.GetType().Name, actionResult?.GetType().Name, e.Message), StatusCodes.Status500InternalServerError); return; } _logger.LogDebug("Request successfully executed."); }