diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/JT1078.Gateway.TestNormalHosting.csproj b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/JT1078.Gateway.TestNormalHosting.csproj index cde7db3..a88eb40 100644 --- a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/JT1078.Gateway.TestNormalHosting.csproj +++ b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/JT1078.Gateway.TestNormalHosting.csproj @@ -20,5 +20,11 @@ Always + + Always + + + Always + diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo.m3u8 b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo.m3u8 new file mode 100644 index 0000000..de79cca --- /dev/null +++ b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo.m3u8 @@ -0,0 +1 @@ +m3u8 demo \ No newline at end of file diff --git a/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo.ts b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo.ts new file mode 100644 index 0000000..1660b9e --- /dev/null +++ b/src/JT1078.Gateway.Tests/JT1078.Gateway.TestNormalHosting/wwwroot/demo/demo.ts @@ -0,0 +1 @@ +ts demo \ No newline at end of file diff --git a/src/JT1078.Gateway/Configurations/JT1078Configuration.cs b/src/JT1078.Gateway/Configurations/JT1078Configuration.cs index 2793fcd..57f582e 100644 --- a/src/JT1078.Gateway/Configurations/JT1078Configuration.cs +++ b/src/JT1078.Gateway/Configurations/JT1078Configuration.cs @@ -29,6 +29,10 @@ namespace JT1078.Gateway.Configurations /// Udp 60s检查一次 /// public int UdpReceiveTimeoutCheckTimeSeconds { get; set; } = 60; + /// + /// Hls根目录 + /// + public string HlsRootDirectory { get; set; } = "wwwroot"; public JT1078Configuration Value => this; } diff --git a/src/JT1078.Gateway/Extensions/JT1078HttpContextExtensions.cs b/src/JT1078.Gateway/Extensions/JT1078HttpContextExtensions.cs index 2253eb2..95f6374 100644 --- a/src/JT1078.Gateway/Extensions/JT1078HttpContextExtensions.cs +++ b/src/JT1078.Gateway/Extensions/JT1078HttpContextExtensions.cs @@ -19,6 +19,7 @@ namespace JT1078.Gateway.Extensions context.Response.ContentLength64 = b.Length; var output = context.Response.OutputStream; await output.WriteAsync(b, 0, b.Length); + context.Response.OutputStream.Close(); context.Response.Close(); } @@ -30,6 +31,7 @@ namespace JT1078.Gateway.Extensions context.Response.ContentLength64 = b.Length; var output = context.Response.OutputStream; await output.WriteAsync(b, 0, b.Length); + context.Response.OutputStream.Close(); context.Response.Close(); } @@ -37,6 +39,7 @@ namespace JT1078.Gateway.Extensions { context.Response.StatusCode = (int)HttpStatusCode.NotFound; context.Response.KeepAlive = false; + context.Response.OutputStream.Close(); context.Response.Close(); } @@ -48,6 +51,7 @@ namespace JT1078.Gateway.Extensions context.Response.ContentLength64 = b.Length; var output = context.Response.OutputStream; await output.WriteAsync(b, 0, b.Length); + context.Response.OutputStream.Close(); context.Response.Close(); } @@ -72,6 +76,7 @@ namespace JT1078.Gateway.Extensions context.Context.Response.ContentLength64 = b.Length; var output = context.Context.Response.OutputStream; await output.WriteAsync(b, 0, b.Length); + context.Context.Response.OutputStream.Close(); context.Context.Response.Close(); } diff --git a/src/JT1078.Gateway/JT1078CoordinatorHttpClient.cs b/src/JT1078.Gateway/JT1078CoordinatorHttpClient.cs new file mode 100644 index 0000000..93bd045 --- /dev/null +++ b/src/JT1078.Gateway/JT1078CoordinatorHttpClient.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; + +namespace JT1078.Gateway +{ + /// + /// 协调器客户端 + /// + public class JT1078CoordinatorHttpClient + { + private HttpClient httpClient; + + public JT1078CoordinatorHttpClient(HttpClient httpClient) + { + this.httpClient = httpClient; + } + + /// + /// 发送心跳至协调器中 + /// + /// + public async void Heartbeat(string content) + { + await httpClient.PostAsync("/heartbeat", new StringContent(content)); + } + } +} diff --git a/src/JT1078.Gateway/JT1078HttpServer.cs b/src/JT1078.Gateway/JT1078HttpServer.cs index 3b827f5..bc9c078 100644 --- a/src/JT1078.Gateway/JT1078HttpServer.cs +++ b/src/JT1078.Gateway/JT1078HttpServer.cs @@ -88,13 +88,65 @@ namespace JT1078.Gateway return Task.CompletedTask; } + private const string m3u8Mime = "application/x-mpegURL"; + private const string tsMime = "video/MP2T"; + private async ValueTask ProcessRequestAsync(HttpListenerContext context, IPrincipal principal) { if(context.Request.RawUrl.StartsWith("/favicon.ico")) { context.Http404(); + return; + } + var queryStringIndex = context.Request.RawUrl.IndexOf("?"); + string url = ""; + if (queryStringIndex > 0) + { + url = context.Request.RawUrl.Substring(1, queryStringIndex-1); + } + else + { + url = context.Request.RawUrl; + } + if (url.EndsWith(".m3u8") || url.EndsWith(".ts")) + { + string filename = Path.GetFileName(url); + string filepath = Path.Combine(Configuration.HlsRootDirectory, Path.GetFileNameWithoutExtension(filename), filename); + if (!File.Exists(filepath)) + { + context.Http404(); + return; + } + try + { + using (FileStream sr = new FileStream(filepath, FileMode.Open)) + { + context.Response.ContentLength64 = sr.Length; + await sr.CopyToAsync(context.Response.OutputStream); + } + string ext = Path.GetExtension(filename); + if (ext == ".m3u8") + { + context.Response.ContentType = m3u8Mime; + } + else if (ext == ".ts") + { + context.Response.ContentType = tsMime; + } + context.Response.StatusCode = (int)HttpStatusCode.OK; + } + catch (Exception ex) + { + Logger.LogError(ex, $"{context.Request.RawUrl}"); + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + } + finally + { + context.Response.OutputStream.Close(); + context.Response.Close(); + } + return; } - //todo:.m3u8 .ts if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace($"[http RequestTraceIdentifier]:{context.Request.RequestTraceIdentifier.ToString()}-{context.Request.RemoteEndPoint.ToString()}");