Async support

This commit is contained in:
Yuki 2023-10-20 00:59:28 -04:00
parent 963d757ee5
commit 99907740ca
19 changed files with 208 additions and 89 deletions

View File

@ -14,15 +14,16 @@ class GeminiMediaHandler : MediaHandler
queries = new List<string>();
}
public override void Load()
public override async Task Load()
{
Title = Content.URL.AbsolutePath;
var reader = new StreamReader(Content.Content);
string line;
while((line = reader.ReadLine()) is not null)
while((line = await reader.ReadLineAsync()) is not null)
{
lines.Add(line);
}
OnLoaded();
}
public override void Render()

View File

@ -14,15 +14,16 @@ class GopherMediaHandler : MediaHandler
queries = new List<string>();
}
public override void Load()
public override async Task Load()
{
Title = Content.URL.AbsolutePath;
var reader = new StreamReader(Content.Content);
string line;
while((line = reader.ReadLine()) is not null)
while((line = await reader.ReadLineAsync()) is not null)
{
lines.Add(line);
}
OnLoaded();
}
public override void Render()
@ -67,7 +68,7 @@ class GopherMediaHandler : MediaHandler
var url = new UriBuilder(Content.URL){
Host = l[2],
Port = int.Parse(l[3]),
Path = type+l[1],
Path = type.ToString()+l[1],
}.ToString();
Content.CurrentTab.Load(url+"%09"+queries[querynum]);
});
@ -81,7 +82,7 @@ class GopherMediaHandler : MediaHandler
link = new UriBuilder(Content.URL){
Host = l[2],
Port = int.Parse(l[3]),
Path = type+l[1],
Path = type.ToString()+l[1],
}.ToString();
Gui.Link(info, link, ()=>
Content.CurrentTab.Load(link));

38
Media/GzipMediaHandler.cs Normal file
View File

@ -0,0 +1,38 @@
using System.IO.Compression;
using HeyRed.Mime;
namespace Shoko;
[MediaType("application/gzip")]
class GzipMediaHandler : MediaHandler
{
MediaHandler newHandler;
public GzipMediaHandler(ProtoHandler content)
{
Content = content;
}
public override async Task Load()
{
var stream = new GZipStream(Content.Content, CompressionMode.Decompress);
var mem = new MemoryStream();
await stream.CopyToAsync(mem);
mem.Position = 0;
Content.MediaType = MimeGuesser.GuessMimeType(mem);
Content.Content = mem;
newHandler = GetHandler(Content);
await newHandler.Load();
OnLoaded();
}
public override void Render()
{
newHandler.Render();
}
public override void MenuBar()
{
newHandler.MenuBar();
}
}

View File

@ -27,16 +27,17 @@ class ImageMediaHandler : MediaHandler
Content = content;
}
public override void Load()
public override async Task Load()
{
Title = Content.URL.AbsolutePath;
using(var memory = new MemoryStream())
{
Content.Content.CopyTo(memory);
await Content.Content.CopyToAsync(memory);
var image = Raylib.LoadImageFromMemory(MediaTypes[Content.MediaType], memory.ToArray());
Texture = Raylib.LoadTextureFromImage(image);
Raylib.UnloadImage(image);
}
OnLoaded();
}
public override void Render()

View File

@ -11,16 +11,18 @@ class MagickMediaHandler : ImageMediaHandler
Content = content;
}
public override void Load()
public override async Task Load()
{
Title = Content.URL.AbsolutePath;
using(var magic = new MagickImage(Content.Content))
using(var magic = new MagickImage())
{
await magic.ReadAsync(Content.Content);
magic.Format = MagickFormat.Png;
var image = Raylib.LoadImageFromMemory(".png", magic.ToByteArray());
Texture = Raylib.LoadTextureFromImage(image);
Raylib.UnloadImage(image);
}
OnLoaded();
}
~MagickMediaHandler()

View File

@ -18,6 +18,7 @@ class MediaHandler
{
public ProtoHandler Content;
public string Title;
public bool IsLoaded;
public MediaHandler()
{
@ -26,9 +27,17 @@ class MediaHandler
{
Content = content;
}
public virtual void Load()
public virtual Task Load()
{
OnLoaded();
return Task.CompletedTask;
}
public virtual void OnLoaded()
{
IsLoaded = true;
}
public virtual void Render()
{
Title = "Error";

View File

@ -11,15 +11,16 @@ class PlainMediaHandler : MediaHandler
lines = new List<string>();
}
public override void Load()
public override async Task Load()
{
Title = Content.URL.AbsolutePath;
var reader = new StreamReader(Content.Content);
string line;
while((line = reader.ReadLine()) is not null)
while((line = await reader.ReadLineAsync()) is not null)
{
lines.Add(line);
}
OnLoaded();
}
public override void Render()

View File

@ -17,7 +17,7 @@ class AboutProtoHandler : ProtoHandler
URL = url;
}
public override void Load()
public override Task Load()
{
var path = new UriBuilder(URL).Path;
Content = new MemoryStream(new byte[]{});
@ -41,8 +41,10 @@ class AboutProtoHandler : ProtoHandler
MediaType = "text/plain";
Status = "OK";
Loaded = true;
OnLoaded();
return Task.CompletedTask;
}
public override void Render()
{
var path = new UriBuilder(URL).Path;

View File

@ -10,7 +10,7 @@ class DataProtoHandler : ProtoHandler
URL = url;
}
public override void Load()
public override Task Load()
{
var data = new UriBuilder(URL).Path;
@ -37,7 +37,8 @@ class DataProtoHandler : ProtoHandler
MediaTypeParams = dict;
Status = "OK";
Loaded = true;
OnLoaded();
return Task.CompletedTask;
}
public override void Render()
{

View File

@ -1,6 +1,7 @@
using System.Text;
using System.Web;
using HeyRed.Mime;
using Microsoft.Win32.SafeHandles;
namespace Shoko;
@ -11,8 +12,10 @@ class FileProtoHandler : ProtoHandler
{
URL = url;
}
long _totalBytes = -1;
public override long TotalBytes => _totalBytes;
public override void Load()
public override async Task Load()
{
var file = HttpUtility.UrlDecode(URL.AbsolutePath);
@ -24,9 +27,11 @@ class FileProtoHandler : ProtoHandler
if(File.Exists(file))
{
var stream = new FileStream(file, FileMode.Open);
MediaType = MimeGuesser.GuessMimeType(stream);
Content = stream;
var fi = new FileInfo(file);
_totalBytes = fi.Length;
var stream = fi.OpenRead();
Content = await Download(stream);
MediaType = MimeGuesser.GuessMimeType(Content);
}
else if(Directory.Exists(file))
{
@ -47,8 +52,7 @@ class FileProtoHandler : ProtoHandler
Content = new MemoryStream(Encoding.UTF8.GetBytes("file not found"));
Status = "not found";
}
Loaded = true;
OnLoaded();
}
public override void Render()
{

View File

@ -12,7 +12,7 @@ class FingerProtoHandler : ProtoHandler
URL = url;
}
public override void Load()
public override async Task Load()
{
var file = URL.PathAndQuery;
@ -24,12 +24,11 @@ class FingerProtoHandler : ProtoHandler
var stream = tcp.GetStream();
stream.Write(uri);
await stream.WriteAsync(uri);
Content = stream;
Content = await Download(stream);
MediaType = "text/plain";
Loaded = true;
OnLoaded();
}
public override void Render()
{

View File

@ -13,8 +13,10 @@ class FtpProtoHandler : ProtoHandler
{
URL = url;
}
long _totalBytes = -1;
public override long TotalBytes => _totalBytes;
public override void Load()
public override async Task Load()
{
var file = URL.AbsolutePath;
@ -45,7 +47,7 @@ class FtpProtoHandler : ProtoHandler
conn.Connect();
MediaType = "text/plain"; // TODO: magic numbers
MediaType = "text/plain";
Status = "OK";
var info = conn.GetObjectInfo(file);
@ -55,12 +57,10 @@ class FtpProtoHandler : ProtoHandler
switch(info.Type)
{
case FtpObjectType.File:
//Content = conn.OpenRead(file);
if(conn.DownloadBytes(out byte[] bytes, info.FullName))
{
Content = new MemoryStream(bytes);
MediaType = MimeGuesser.GuessMimeType(Content);
}
_totalBytes = info.Size;
var stream = conn.OpenRead(file);
Content = await Download(stream);
MediaType = MimeGuesser.GuessMimeType(Content);
break;
case FtpObjectType.Directory:
MediaType = "text/gemini";
@ -107,8 +107,7 @@ class FtpProtoHandler : ProtoHandler
Status = "not found";
}
}
Loaded = true;
OnLoaded();
}
public override void Render()
{

View File

@ -12,7 +12,7 @@ class GeminiProtoHandler : ProtoHandler
URL = url;
}
public override void Load()
public override async Task Load()
{
var gemini = new Gemini(URL);
gemini.Connect();
@ -38,7 +38,7 @@ class GeminiProtoHandler : ProtoHandler
MediaTypeParams = dict;
}
Content = gemini.sslStream;
Content = await Download(gemini.sslStream);
}
else
{
@ -54,8 +54,7 @@ class GeminiProtoHandler : ProtoHandler
}
Content = new MemoryStream(content);
}
Loaded = true;
OnLoaded();
}
public override void Render()

View File

@ -12,7 +12,7 @@ class GopherProtoHandler : ProtoHandler
URL = url;
}
public override void Load()
public override async Task Load()
{
var type = "0";
var file = URL.PathAndQuery;
@ -35,15 +35,6 @@ class GopherProtoHandler : ProtoHandler
else
type = "1";
var uri = Encoding.UTF8.GetBytes(HttpUtility.UrlDecode(string.Join("/", paths))+"\r\n");
var tcp = new TcpClient(URL.Host, URL.Port < 0 ? 70 : URL.Port);
var stream = tcp.GetStream();
stream.Write(uri);
Content = stream;
switch(type)
{
case "0":
@ -72,7 +63,16 @@ class GopherProtoHandler : ProtoHandler
break;
}
Loaded = true;
var uri = Encoding.UTF8.GetBytes(HttpUtility.UrlDecode(string.Join("/", paths))+"\r\n");
var tcp = new TcpClient(URL.Host, URL.Port < 0 ? 70 : URL.Port);
var stream = tcp.GetStream();
stream.Write(uri);
Content = await Download(stream);
OnLoaded();
}
public override void Render()
{

View File

@ -1,4 +1,5 @@
using System.Reflection;
using System;
namespace Shoko;
@ -10,22 +11,26 @@ class HttpProtoHandler : ProtoHandler
{
URL = url;
}
long _totalBytes = -1;
public override long TotalBytes => _totalBytes;
public override void Load()
public override async Task Load()
{
HttpClient client = new(){
var client = new HttpClient(){
BaseAddress = URL
};
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 shoko/" + Assembly.GetExecutingAssembly().GetName().Version.ToString());
var response = client.GetAsync(URL).Result;
var response = await client.GetAsync(URL, HttpCompletionOption.ResponseHeadersRead);
Headers = response.Content.Headers;
Content = response.Content.ReadAsStream();
_totalBytes = response.Content.Headers.ContentLength ?? -1;
MediaType = response.Content.Headers.ContentType.MediaType ?? "text/html";
MediaTypeParams = response.Content.Headers.ContentType.Parameters.Select(x => new KeyValuePair<string, string>(x.Name, x.Value));
Status = string.Format("{0} {1}", (int)response.StatusCode, response.StatusCode.ToString());
Loaded = true;
var download = await response.Content.ReadAsStreamAsync();
Content = await Download(download);
OnLoaded();
}
public override void Render()
{

View File

@ -21,11 +21,13 @@ class ProtoHandler
public string MediaType;
public Stream Content;
public string Status;
public bool Loaded = false;
public int LoadedBytes = 0;
public int TotalBytes = 0;
public bool IsLoaded = false;
protected long _loadedBytes = 0;
public virtual long LoadedBytes => _loadedBytes;
public virtual long TotalBytes => -1;
public IEnumerable<KeyValuePair<string,IEnumerable<string>>> Headers;
public IEnumerable<KeyValuePair<string,string>> MediaTypeParams;
public MediaHandler Media;
public ProtoHandler()
{
@ -34,11 +36,40 @@ class ProtoHandler
{
URL = url;
}
public virtual void Load()
public virtual Task Load()
{
Content = new MemoryStream(Encoding.UTF8.GetBytes("error: no handler for this scheme"));
MediaType = "text/plain";
Loaded = true;
OnLoaded();
return Task.CompletedTask;
}
public async Task<MemoryStream> Download(Stream stream, Action<long> progress)
{
var ret = new MemoryStream();
var buf = new byte[81920];
long totalBytes = 0;
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buf, 0, buf.Length)) != 0) {
await ret.WriteAsync(buf.AsMemory(0, bytesRead));
totalBytes += bytesRead;
progress(totalBytes);
}
ret.Position = 0;
return ret;
}
public async Task<MemoryStream> Download(Stream stream)
{
return await Download(stream, b=>_loadedBytes=b);
}
public virtual void OnLoaded()
{
Media = MediaHandler.GetHandler(this);
IsLoaded = true;
Media.Load();
}
public virtual void Render()

View File

@ -1,4 +1,5 @@
using System.Reflection;
using System.Text;
using System.Web;
using HeyRed.Mime;
@ -12,21 +13,22 @@ class ResProtoHandler : ProtoHandler
URL = url;
}
public override void Load()
public override async Task Load()
{
var path = HttpUtility.HtmlDecode(new UriBuilder(URL).Path);
var path = HttpUtility.HtmlDecode(URL.AbsolutePath);
Status = "OK";
Loaded = true;
try
{
Content = Assembly.GetExecutingAssembly().GetManifestResourceStream(path);
Content = await Download(Assembly.GetExecutingAssembly().GetManifestResourceStream(path));
MediaType = MimeGuesser.GuessMimeType(Content);
}
catch
{
Content = new MemoryStream(Encoding.UTF8.GetBytes("resource not found"));
MediaType = "text/plain";
Status = "not found";
Loaded = false;
}
OnLoaded();
}
public override void Render()
{

View File

@ -12,7 +12,7 @@ class SpartanProtoHandler : ProtoHandler
URL = url;
}
public override void Load()
public override async Task Load()
{
var file = URL.AbsolutePath;
@ -28,9 +28,9 @@ class SpartanProtoHandler : ProtoHandler
var tcp = new TcpClient(URL.Host, URL.Port < 0 ? 300 : URL.Port);
var stream = tcp.GetStream();
stream.Write(uri);
await stream.WriteAsync(uri);
if(query.Length > 0)
stream.Write(query);
await stream.WriteAsync(query);
var reader = new StreamReader(stream);
var header = reader.ReadLine();
@ -56,7 +56,7 @@ class SpartanProtoHandler : ProtoHandler
MediaTypeParams = dict;
}
Content = stream;
Content = await Download(stream);
}
else
{
@ -72,7 +72,7 @@ class SpartanProtoHandler : ProtoHandler
}
Content = new MemoryStream(content);
}
Loaded = true;
OnLoaded();
}
public override void Render()

54
Tab.cs
View File

@ -1,3 +1,5 @@
using System.Numerics;
using FluentFTP.Helpers;
using ImGuiNET;
namespace Shoko;
@ -5,7 +7,6 @@ namespace Shoko;
class Tab
{
ProtoHandler Handler;
MediaHandler Document;
public bool IsOpen = true;
Exception Error = null;
Stack<Uri> History;
@ -19,7 +20,7 @@ class Tab
txtURL = url;
}
public void Load(string url)
public async Task Load(string url)
{
Error = null;
ImGui.SetScrollX(0);
@ -28,11 +29,9 @@ class Tab
{
Handler = ProtoHandler.GetHandler(url);
Handler.CurrentTab = this;
Handler.Load();
txtURL = Handler.URL.ToString();
History.Push(Handler.URL);
Document = MediaHandler.GetHandler(Handler);
Document.Load();
await Handler.Load();
}
catch(Exception ex)
{
@ -52,9 +51,9 @@ class Tab
public void Render()
{
var title = txtURL;
if(Document is not null)
if(Handler.Media is not null)
{
title = Document.Title;
title = Handler.Media.Title;
}
Gui.Window(title+"###"+GetHashCode().ToString(), ref IsOpen, ImGuiWindowFlags.MenuBar | ImGuiWindowFlags.HorizontalScrollbar, ()=>
{
@ -65,9 +64,12 @@ class Tab
if(Error is null)
{
Handler.MenuBar();
if(Handler.Loaded)
Document.MenuBar();
if(Handler.IsLoaded)
{
Handler.MenuBar();
if(Handler.Media.IsLoaded)
Handler.Media.MenuBar();
}
}
Gui.Button("<<", ()=>{
@ -79,19 +81,41 @@ class Tab
Gui.Button("Go", ()=>{
Load(txtURL);
});
if(!Handler.Loaded && Handler.TotalBytes != 0)
ImGui.ProgressBar(Handler.LoadedBytes / Handler.TotalBytes);
if(!Handler.IsLoaded)
{
ImGui.Text("Loading...");
if(Handler.TotalBytes != 0 && Handler.TotalBytes != Handler.LoadedBytes)
ImGui.ProgressBar(Handler.LoadedBytes / Handler.TotalBytes, new Vector2(200, 20),
Handler.LoadedBytes.FileSizeToString() + "/" + Handler.TotalBytes.FileSizeToString());
}
else if(!Handler.Media.IsLoaded)
{
ImGui.Text("Rendering...");
}
});
if(Error is not null)
{
ImGui.Text("error: can't load page");
ImGui.Text(Error.Message);
ImGui.Text(Error.StackTrace);
}
else
{
if(Handler.Loaded)
Document.Render();
Handler.Render();
try
{
if(Handler.IsLoaded)
{
if(Handler.Media.IsLoaded)
Handler.Media.Render();
Handler.Render();
}
}
catch(Exception ex)
{
ImGui.Text("error: can't render page");
ImGui.Text(ex.Message);
ImGui.Text(ex.StackTrace);
}
}
});
}