/// <summary> /// Asynchronously parses a request body in <c>application/x-www-form-urlencoded</c> format. /// </summary> /// <param name="this">The <see cref="IHttpContext"/> on which this method is called.</param> /// <returns>A <see cref="Task{TResult}">Task</see>, representing the ongoing operation, /// whose result will be a read-only <see cref="NameValueCollection"/>of form field names and values.</returns> /// <exception cref="NullReferenceException"><paramref name="this"/> is <see langword="null"/>.</exception> /// <remarks> /// <para>This method may safely be called more than once for the same <see cref="IHttpContext"/>: /// it will return the same collection instead of trying to parse the request body again.</para> /// </remarks> public static async Task <NameValueCollection> GetRequestFormDataAsync(this IHttpContext @this) { if ([email protected](FormDataKey, out var previousResult)) { NameValueCollection result; try { using var reader = @this.OpenRequestText(); result = UrlEncodedDataParser.Parse(await reader.ReadToEndAsync().ConfigureAwait(false), false); } catch (Exception e) { @this.Items[FormDataKey] = e; throw; } @this.Items[FormDataKey] = result; return(result); } switch (previousResult) { case NameValueCollection collection: return(collection); case Exception exception: throw exception.RethrowPreservingStackTrace(); case null: throw SelfCheck.Failure($"Previous result of {nameof(HttpContextExtensions)}.{nameof(GetRequestFormDataAsync)} is null."); default: throw SelfCheck.Failure($"Previous result of {nameof(HttpContextExtensions)}.{nameof(GetRequestFormDataAsync)} is of unexpected type {previousResult.GetType().FullName}"); } }
/// <summary> /// Parses a request URL query. Note that this is different from getting the <see cref="IHttpRequest.QueryString"/> property, /// in that fields without an equal sign are treated as if they have an empty value, instead of their keys being grouped /// as values of the <c>null</c> key. /// </summary> /// <param name="this">The <see cref="IHttpContext"/> on which this method is called.</param> /// <returns>A read-only <see cref="NameValueCollection"/>.</returns> /// <exception cref="NullReferenceException"><paramref name="this"/> is <see langword="null"/>.</exception> /// <remarks> /// <para>This method may safely be called more than once for the same <see cref="IHttpContext"/>: /// it will return the same collection instead of trying to parse the request body again.</para> /// </remarks> public static NameValueCollection GetRequestQueryData(this IHttpContext @this) { if ([email protected](QueryDataKey, out var previousResult)) { NameValueCollection result; try { result = UrlEncodedDataParser.Parse(@this.Request.Url.Query, false); } catch (Exception e) { @this.Items[FormDataKey] = e; throw; } @this.Items[FormDataKey] = result; return(result); } switch (previousResult) { case NameValueCollection collection: return(collection); case Exception exception: throw exception.RethrowPreservingStackTrace(); case null: throw SelfCheck.Failure($"Previous result of {nameof(HttpContextExtensions)}.{nameof(GetRequestQueryData)} is null."); default: throw SelfCheck.Failure($"Previous result of {nameof(HttpContextExtensions)}.{nameof(GetRequestQueryData)} is of unexpected type {previousResult.GetType().FullName}"); } }
protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var serverRequest = new TestRequest(Validate.NotNull(nameof(request), request)); var cookiesFromContainer = CookieContainer.GetCookieHeader(serverRequest.Url); if (!string.IsNullOrEmpty(cookiesFromContainer)) { serverRequest.Headers.Add(HttpHeaderNames.Cookie, cookiesFromContainer); } var context = new TestContext(serverRequest) { CancellationToken = cancellationToken, Route = RouteMatch.UnsafeFromRoot(UrlPath.Normalize(serverRequest.Url.AbsolutePath, false)), }; await _handler.HandleContextAsync(context).ConfigureAwait(false); var serverResponse = context.TestResponse; var responseCookies = serverResponse.Headers.Get(HttpHeaderNames.SetCookie); if (!string.IsNullOrEmpty(responseCookies)) { CookieContainer.SetCookies(serverRequest.Url, responseCookies); } var response = new HttpResponseMessage((HttpStatusCode)serverResponse.StatusCode) { RequestMessage = request, Version = serverResponse.ProtocolVersion, ReasonPhrase = serverResponse.StatusDescription, Content = serverResponse.Body == null ? null : new ByteArrayContent(serverResponse.Body), }; foreach (var key in serverResponse.Headers.AllKeys) { var responseHeaderType = GetResponseHeaderType(key); switch (responseHeaderType) { case ResponseHeaderType.Content: response.Content?.Headers.Add(key, serverResponse.Headers.GetValues(key)); break; case ResponseHeaderType.Response: response.Headers.Add(key, serverResponse.Headers.GetValues(key)); break; case ResponseHeaderType.None: break; default: throw SelfCheck.Failure("Unexpected response header type {responseHeaderType}."); } } return(response); }
public byte[]? GetContent(CompressionMethod compressionMethod) { // If there are both entity tag and content, use them. switch (compressionMethod) { case CompressionMethod.Deflate: if (_deflatedContent != null) { return(_deflatedContent); } break; case CompressionMethod.Gzip: if (_gzippedContent != null) { return(_gzippedContent); } break; case CompressionMethod.None: if (_uncompressedContent != null) { return(_uncompressedContent); } break; default: throw SelfCheck.Failure($"Unexpected compression method {compressionMethod}."); } // Try to convert existing content, if any. byte[]? content; if (_uncompressedContent != null) { content = CompressionUtility.ConvertCompression(_uncompressedContent, CompressionMethod.None, compressionMethod); } else if (_gzippedContent != null) { content = CompressionUtility.ConvertCompression(_gzippedContent, CompressionMethod.Gzip, compressionMethod); } else if (_deflatedContent != null) { content = CompressionUtility.ConvertCompression(_deflatedContent, CompressionMethod.Deflate, compressionMethod); } else { // No content whatsoever. return(null); } return(SetContent(compressionMethod, content)); }
private TResolver GetResolver(RouteMatcher matcher) { var resolver = _resolvers.FirstOrDefault(r => r.Matcher.Equals(matcher)); if (resolver != null) { return(resolver); } resolver = CreateResolver(matcher); _resolvers.Add(resolver ?? throw SelfCheck.Failure($"{nameof(CreateResolver)} returned null.")); return(resolver); }
public async Task ListDirectoryAsync( MappedResourceInfo info, string absolutePath, IEnumerable <MappedResourceInfo> entries, Stream stream, CancellationToken cancellationToken) { const int MaxEntryLength = 50; const int SizeIndent = -20; // Negative for right alignment if (!info.IsDirectory) { throw SelfCheck.Failure($"{nameof(HtmlDirectoryLister)}.{nameof(ListDirectoryAsync)} invoked with a file, not a directory."); } var encodedPath = WebUtility.HtmlEncode(absolutePath); using var text = new StreamWriter(stream, Encoding.UTF8); text.Write("<html><head><title>Index of "); text.Write(encodedPath); text.Write("</title></head><body><h1>Index of "); text.Write(encodedPath); text.Write("</h1><hr/><pre>"); if (encodedPath.Length > 1) { text.Write("<a href='../'>../</a>\n"); } entries = entries.ToArray(); foreach (var directory in entries.Where(m => m.IsDirectory).OrderBy(e => e.Name)) { text.Write($"<a href=\"{Uri.EscapeDataString(directory.Name)}{Path.DirectorySeparatorChar}\">{WebUtility.HtmlEncode(directory.Name)}</a>"); text.Write(new string(' ', Math.Max(1, MaxEntryLength - directory.Name.Length + 1))); text.Write(HttpDate.Format(directory.LastModifiedUtc)); text.Write('\n'); await Task.Yield(); } foreach (var file in entries.Where(m => m.IsFile).OrderBy(e => e.Name)) { text.Write($"<a href=\"{Uri.EscapeDataString(file.Name)}{Path.DirectorySeparatorChar}\">{WebUtility.HtmlEncode(file.Name)}</a>"); text.Write(new string(' ', Math.Max(1, MaxEntryLength - file.Name.Length + 1))); text.Write(HttpDate.Format(file.LastModifiedUtc)); text.Write($" {file.Length.ToString("#,###", CultureInfo.InvariantCulture),SizeIndent}\n"); await Task.Yield(); } text.Write("</pre><hr/></body></html>"); }
/// <inheritdoc /> protected override async Task OnRequestAsync([ValidatedNotNull] IHttpContext context) { var result = await _resolvers.UnsafeResolveAsync(context).ConfigureAwait(false); switch (result) { case RouteResolutionResult.RouteNotMatched: case RouteResolutionResult.NoHandlerSuccessful: await OnPathNotFoundAsync(context).ConfigureAwait(false); break; case RouteResolutionResult.NoHandlerSelected: await OnMethodNotAllowedAsync(context).ConfigureAwait(false); break; case RouteResolutionResult.Success: return; default: throw SelfCheck.Failure($"Internal error: unknown route resolution result {result}."); } }
/// <summary> /// <para>Gets the underlying <see cref="IHttpContextImpl"/> interface of an <see cref="IHttpContext"/>.</para> /// <para>This API mainly supports the EmbedIO infrastructure; it is not intended to be used directly from your code, /// unless to fulfill very specific needs in the development of plug-ins (modules, etc.) for EmbedIO.</para> /// </summary> /// <param name="this">The <see cref="IHttpContext"/> interface on which this method is called.</param> /// <returns>The underlying <see cref="IHttpContextImpl"/> interface representing /// the HTTP context implementation.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="this"/> is <see langword="null"/>. /// </exception> /// <exception cref="EmbedIOInternalErrorException"> /// <paramref name="this"/> does not implement <see cref="IHttpContextImpl"/>. /// </exception> public static IHttpContextImpl GetImplementation(this IHttpContext @this) => Validate.NotNull(nameof(@this), @this) as IHttpContextImpl ?? throw SelfCheck.Failure($"{@this.GetType().FullName} does not implement {nameof(IHttpContextImpl)}.");