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

Replace HttpListener for .NET 6.0+ #142

Open
wants to merge 2 commits into
base: version-six
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"sdk": {
"version": "7.0.400",
"rollForward": "minor"
}
}
9 changes: 4 additions & 5 deletions src/Grapeseed/ContentFolder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
Expand Down Expand Up @@ -162,18 +162,17 @@ public void Dispose()
Watcher?.Dispose();
}

public async override Task SendFileAsync(IHttpContext context)
public override async Task SendFileAsync(IHttpContext context)
{
await SendFileAsync(context, null);
}

public async override Task SendFileAsync(IHttpContext context, string filename)
public override async Task SendFileAsync(IHttpContext context, string filename)
{
PopulateDirectoryListing();

if (DirectoryMapping.ContainsKey(context.Request.Endpoint))
if (DirectoryMapping.TryGetValue(context.Request.Endpoint, out var filepath))
{
var filepath = DirectoryMapping[context.Request.Endpoint];
context.Response.StatusCode = HttpStatusCode.Ok;

var lastModified = File.GetLastWriteTimeUtc(filepath).ToString("R");
Expand Down
140 changes: 98 additions & 42 deletions src/Grapeseed/ContentType.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
Expand All @@ -10,64 +10,96 @@ public class ContentType
{
#region Static Implementations

public static ContentType Binary { get; } = new ContentType("application/octet-stream");
/// <summary>
/// This is also the fallback
/// </summary>
public static ContentType Binary { get; } = new ContentType("application/octet-stream", "exe");

public static ContentType Css { get; } = new ContentType("text/css", false, "UTF-8");
public static ContentType Css { get; } = new ContentType("text/css", "css", false, "UTF-8");

public static ContentType FormUrlEncoded { get; } = new ContentType("application/x-www-form-urlencoded");
public static ContentType FormUrlEncoded { get; } = new ContentType("application/x-www-form-urlencoded", "");

public static ContentType Gif { get; } = new ContentType("image/gif");
public static ContentType Gif { get; } = new ContentType("image/gif", "gif");

public static ContentType Html { get; } = new ContentType("text/html", false, "UTF-8");
public static ContentType Html { get; } = new ContentType("text/html", "html", false, "UTF-8");

public static ContentType Icon { get; } = new ContentType("image/x-icon");
public static ContentType Htm { get; } = new ContentType("text/html", "htm", false, "UTF-8");

public static ContentType JavaScript { get; } = new ContentType("application/javascript", false, "UTF-8");
public static ContentType Icon { get; } = new ContentType("image/x-icon", "ico");

public static ContentType Json { get; } = new ContentType("application/json", false, "UTF-8");
public static ContentType JavaScript { get; } = new ContentType("application/javascript", "js", false, "UTF-8");

public static ContentType Jpg { get; } = new ContentType("image/jpeg");
public static ContentType Json { get; } = new ContentType("application/json", "json", false, "UTF-8");

public static ContentType Mp3 { get; } = new ContentType("audio/mpeg");
public static ContentType Jpg { get; } = new ContentType("image/jpeg", "jpg");

public static ContentType Mp4 { get; } = new ContentType("video/mp4");
public static ContentType Mp3 { get; } = new ContentType("audio/mpeg", "mp3");

public static ContentType MultipartFormData { get; } = new ContentType("multipart/form-data");
public static ContentType Mp4 { get; } = new ContentType("video/mp4", "mp4");

public static ContentType Pdf { get; } = new ContentType("application/pdf");
public static ContentType MultipartFormData { get; } = new ContentType("multipart/form-data", "");

public static ContentType Png { get; } = new ContentType("image/png");
public static ContentType Pdf { get; } = new ContentType("application/pdf", "pdf");

public static ContentType Png { get; } = new ContentType("image/png", "png");

public static IProblemDetailsContentTypes ProblemDetails { get; } = new ProblemDetailsContentTypes();

public static ContentType Svg { get; } = new ContentType("image/svg+xml", false, "UTF-8");
public static ContentType Svg { get; } = new ContentType("image/svg+xml", "svg", false, "UTF-8");

public static ContentType Text { get; } = new ContentType("text/plain", false, "UTF-8");
public static ContentType Text { get; } = new ContentType("text/plain", "txt", false, "UTF-8");

public static ContentType Xml { get; } = new ContentType("application/xml", false, "UTF-8");
public static ContentType Xml { get; } = new ContentType("application/xml", "xml", false, "UTF-8");

public static ContentType Zip { get; } = new ContentType("application/zip");
public static ContentType Zip { get; } = new ContentType("application/zip", "zip");

#endregion

#region Static Initialization

private static readonly Dictionary<string, ContentType> _contentTypes = new Dictionary<string, ContentType>();
private static readonly Dictionary<string, ContentType> _contentTypes;

private static readonly Dictionary<string, ContentType> _extensions = new Dictionary<string, ContentType>();
private static readonly Dictionary<string, ContentType> _extensions;

static ContentType()
{
var ct = typeof(ContentType);
var fields = typeof(ContentType).GetFields(BindingFlags.Public | BindingFlags.Static).ToList();
_contentTypes = new Dictionary<string, ContentType>();
_extensions = new Dictionary<string, ContentType>();
var contentTypes = new List<ContentType>()
{
Binary,
Css,
FormUrlEncoded,
Gif,
Html,
Htm,
Icon,
JavaScript,
Jpg,
Json,
Mp3,
Mp4,
Xml,
Zip,
Png,
Pdf,
Svg,
Text,
MultipartFormData,
};

foreach (var field in fields)
foreach (var c in contentTypes)
{
var contentType = field.GetValue(null) as ContentType;
if (contentType == null) return;
if (!_contentTypes.ContainsKey(c.Value))
{
_contentTypes.Add(c.Value, c); // Ignore if it's already there
}

_contentTypes.Add(contentType, contentType);
_extensions.Add(field.Name.ToLower(), contentType);
if (!string.IsNullOrEmpty(c.Extension))
{
// Some types cannot be derived from their extension
_extensions.Add(c.Extension, c);
}
}
}

Expand All @@ -83,9 +115,19 @@ static ContentType()

public string Boundary { get; protected set; }

public ContentType(string value, bool isBinary = true, string charSet = "")
public string Extension { get; }

/// <summary>
/// Declares a mime type
/// </summary>
/// <param name="value">The mime type</param>
/// <param name="extension">The extension to associate. Can be empty if this type has no defined extension</param>
/// <param name="isBinary">True if this is a binary file</param>
/// <param name="charSet">Character set of the data</param>
public ContentType(string value, string extension, bool isBinary = true, string charSet = "")
{
Value = value;
Extension = extension;
IsBinary = isBinary;
CharSet = charSet;
}
Expand Down Expand Up @@ -132,29 +174,34 @@ public static implicit operator string(ContentType value)

public static ContentType Find(string value)
{
Add(value);
return _contentTypes[value];
return Add(value);
}

public static ContentType FindKey(string key)
{
var k = key.ToLower();
return (_extensions.ContainsKey(k))
? _extensions[k]
return _extensions.TryGetValue(k, out var extension)
? extension
: ContentType.Binary;
}

public static void Add(string value, bool isBinary = true, string charSet = "")
public static ContentType Add(string value, bool isBinary = true, string charSet = "")
{
if (_contentTypes.ContainsKey(value)) return;
if (_contentTypes.TryGetValue(value, out ContentType result))
{
return result;
}

var key = (value.Contains(';') && string.IsNullOrWhiteSpace(charSet))
? value
: string.IsNullOrWhiteSpace(charSet)
? value
: $"{value}; charset={charSet}";

if (_contentTypes.ContainsKey(key)) return;
if (_contentTypes.TryGetValue(key, out result))
{
return result;
}

if (value.Contains(';') && string.IsNullOrWhiteSpace(charSet))
{
Expand All @@ -163,22 +210,31 @@ public static void Add(string value, bool isBinary = true, string charSet = "")
charSet = parts[1]?.Replace("charset=", "").Trim();
}

var contentType = new ContentType(value, isBinary, charSet);
_contentTypes.Add(contentType, contentType);
if (_contentTypes.TryGetValue(value, out result))
{
return result;
}

var contentType = new ContentType(value, "", isBinary, charSet);
_contentTypes.Add(contentType.Value, contentType);
return contentType;
}

public static void Add(string key, ContentType contentType)
public static void Add(ContentType contentType)
{
_contentTypes.Add(contentType, contentType);
_extensions.Add(key, contentType);
_contentTypes.Add(contentType.Value, contentType);
if (!string.IsNullOrWhiteSpace(contentType.Extension))
{
_extensions.Add(contentType.Extension, contentType);
}
}

public static ContentType MultipartContent(Multipart multipart = default, string boundary = null)
{
if (string.IsNullOrWhiteSpace(boundary))
boundary = MultiPartBoundary.Generate();

return new ContentType($"multipart/{multipart.ToString().ToLower()}", false, "")
return new ContentType($"multipart/{multipart.ToString().ToLower()}", "", false, "")
{
Boundary = boundary.Substring(0, 70).TrimEnd()
};
Expand Down
2 changes: 1 addition & 1 deletion src/Grapeseed/Grapeseed.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<Version>6.0.0-beta</Version>
<Version>6.0.0-beta2</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/scottoffen/grapevine</RepositoryUrl>
Expand Down
27 changes: 15 additions & 12 deletions src/Grapeseed/HttpMethod.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
Expand All @@ -19,18 +19,21 @@ public partial class HttpMethod
public static HttpMethod Connect { get; } = new HttpMethod("CONNECT");

// for implicit conversions
private static readonly Dictionary<string, HttpMethod> _methods = new Dictionary<string, HttpMethod>();
private static readonly Dictionary<string, HttpMethod> _methods;

static HttpMethod()
{
var mtype = typeof(HttpMethod);
var staticMethods = typeof(HttpMethod).GetFields(BindingFlags.Public | BindingFlags.Static)
.Select(f => f.GetValue(null))
.Where(f => f.GetType() == mtype)
.Cast<HttpMethod>()
.ToList();

foreach (var method in staticMethods) _methods.Add(method.ToString(), method);
_methods = new Dictionary<string, HttpMethod>();
_methods.Add("POST", Post);
_methods.Add("PUT", Put);
_methods.Add("DELETE", Delete);
_methods.Add("HEAD", Head);
_methods.Add("GET", Get);
_methods.Add("ANY", Any);
_methods.Add("OPTIONS", Options);
_methods.Add("TRACE", Trace);
_methods.Add("PATCH", Patch);
_methods.Add("CONNECT", Connect);
}

public static bool operator ==(HttpMethod left, HttpMethod right)
Expand Down Expand Up @@ -63,11 +66,12 @@ public static implicit operator HttpMethod(string value)

public partial class HttpMethod : IEquatable<HttpMethod>
{
private int _hashcode;
private readonly int _hashcode;

public HttpMethod(string method)
{
Method = method.Trim().ToUpper();
_hashcode = StringComparer.OrdinalIgnoreCase.GetHashCode(Method);
}

public string Method { get; }
Expand All @@ -92,7 +96,6 @@ public override bool Equals(object obj)

public override int GetHashCode()
{
if (_hashcode == 0) StringComparer.OrdinalIgnoreCase.GetHashCode(Method);
return _hashcode;
}

Expand Down
13 changes: 6 additions & 7 deletions src/Grapeseed/IHttpResponse.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

Expand Down Expand Up @@ -32,12 +31,12 @@ public interface IHttpResponse
/// <summary>
/// Gets or sets the collection of cookies returned with the response
/// </summary>
CookieCollection Cookies { get; set; }
System.Net.CookieCollection Cookies { get; set; }

/// <summary>
/// Gets or sets the collection of header name/value pairs returned by the server
/// </summary>
WebHeaderCollection Headers { get; set; }
System.Net.WebHeaderCollection Headers { get; set; }

string RedirectLocation { get; set; }

Expand Down Expand Up @@ -77,7 +76,7 @@ public interface IHttpResponse
/// Adds the specified Cookie to the collection of cookies for this response
/// </summary>
/// <param name="cookie"></param>
void AppendCookie(Cookie cookie);
void AppendCookie(System.Net.Cookie cookie);

/// <summary>
/// Appends a value to the specified HTTP header to be sent with this response
Expand All @@ -102,7 +101,7 @@ public interface IHttpResponse
/// Adds or updates a Cookie in the collection of cookies sent with this response
/// </summary>
/// <param name="cookie"></param>
void SetCookie(Cookie cookie);
void SetCookie(System.Net.Cookie cookie);
}

public static class IHttpResponseExtensions
Expand Down Expand Up @@ -162,4 +161,4 @@ public static async Task SendResponseAsync(this IHttpResponse response, HttpStat
await response.SendResponseAsync(content);
}
}
}
}
Loading