.NET CIL Browser - Back to table of contents

Source file: SourceServer.cs

Files in CilBrowser.Core directory:

AssemblyServer.cs

CilBrowser.Core.csproj

CilBrowserOptions.cs

FileUtils.cs

HtmlAttribute.cs

HtmlBuilder.cs

HtmlGenerator.cs

ServerBase.cs

SourceServer.cs

Utils.cs

WebsiteGenerator.cs

/* CIL Browser (https://github.com/MSDN-WhiteKnight/CilBrowser)
 * Copyright (c) 2023,  MSDN.WhiteKnight 
 * License: BSD 3-Clause */
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;

namespace CilBrowser.Core
{
    /// <summary>
    /// Provides a server that dynamically generates HTML for source code files in the specified directory 
    /// and returns it via HTTP
    /// </summary>
    public sealed class SourceServer : ServerBase
    {
        string _baseDirectory;
        HtmlGenerator _gen;
        CilBrowserOptions _options;
        HashSet<string> _sourceExtensions;
        Dictionary<string, byte[]> _resources;

        public SourceServer(string baseDirectory, CilBrowserOptions options, string urlHost, string urlPrefix) : 
            base(urlHost, urlPrefix)
        {
            this._baseDirectory = baseDirectory;
            this._options = options;
            this._gen = new HtmlGenerator();
            this._sourceExtensions = this._options.SourceExtensions;
            
            this._resources = new Dictionary<string, byte[]>(2);
            Assembly ass = typeof(WebsiteGenerator).Assembly;
            byte[] imageData = FileUtils.ReadFromResource(ass, "CilBrowser.Core.Images", "dir.png");
            this._resources["dir.png"] = imageData;
            imageData = FileUtils.ReadFromResource(ass, "CilBrowser.Core.Images", "file.png");
            this._resources["file.png"] = imageData;
        }

        protected override void OnStart()
        {
            Console.WriteLine("Base source directory: " + this._baseDirectory);
        }

        void RenderToc(string dir, bool topLevel, HttpListenerResponse response)
        {
            if (!Directory.Exists(dir))
            {
                SendErrorResponse(response, 404, "Not found");
                return;
            }
            
            // Render table of contents
            response.ContentType = "text/html; charset=utf-8";
            StreamWriter wr = new StreamWriter(response.OutputStream);
            HtmlBuilder toc = new HtmlBuilder(wr);

            using (wr)
            {
                string dirName = Utils.GetDirectoryNameFromPath(dir);
                HtmlGenerator.WriteTocStart(toc, dirName);

                // Index subdirectories
                string[] dirs = Directory.GetDirectories(dir);
                Array.Sort(dirs);

                if (dirs.Length > 0) toc.WriteTag("h2", "Subdirectories");

                if (!topLevel)
                {
                    toc.StartParagraph();
                    toc.WriteHyperlink("../index.html", "(go to parent directory)");
                    toc.EndParagraph();
                }

                WebsiteGenerator.RenderDirsList(dirs, "dir.png", toc);

                // Index files in directory
                string[] files = Directory.GetFiles(dir);
                Array.Sort(files);

                if (files.Length > 0) toc.WriteTag("h2", "Files");

                toc.WriteTagStart("table", HtmlBuilder.OneAttribute("cellpadding", "2px"));

                for (int i = 0; i < files.Length; i++)
                {
                    if (!FileUtils.IsSourceFile(files[i], this._sourceExtensions)) continue;

                    string name = Path.GetFileName(files[i]);
                    string pageName = FileUtils.FileNameToPageName(name);
                    WebsiteGenerator.RenderTocEntry(name, pageName, "file.png", toc);
                }

                toc.WriteTagEnd("table");

                this._gen.WriteFooter(toc);
                toc.EndDocument();
            }
        }

        protected override void RenderFrontPage(HttpListenerResponse response)
        {
            try
            {
                this.RenderToc(this._baseDirectory, true, response);
            }
            catch (Exception ex)
            {
                Console.WriteLine("SourceServer error when rendering frontpage!");
                Console.WriteLine(ex.ToString());
                Console.WriteLine();
                SendErrorResponse(response, 500, "Internal server error");
            }
        }

        static bool IsUrlInvalid(string url)
        {
            string[] parts = url.Split('/');

            for (int i = 0; i < parts.Length; i++)
            {
                if (Utils.StrEquals(parts[i], "..")) return true;

                if (Utils.StrEquals(parts[i], "\\")) return true;
            }

            return false;
        }

        protected override void RenderPage(string url, HttpListenerResponse response)
        {
            try
            {
                url = WebUtility.UrlDecode(url);
                string relativePath = StripURL(url);

                if (IsUrlInvalid(relativePath))
                {
                    SendErrorResponse(response, 404, "Not found");
                    return;
                }

                if (url.EndsWith("index.html", StringComparison.OrdinalIgnoreCase))
                {
                    //directory table of contents
                    if (relativePath.Length < 5)
                    {
                        SendErrorResponse(response, 404, "Not found");
                        return;
                    }

                    string dir = relativePath.Substring(0, relativePath.Length - 5);
                    this.RenderToc(Path.Combine(this._baseDirectory, dir), false, response);
                }
                else if (url.Length > 0 && url[url.Length - 1] == '/')
                {
                    //directory table of contents
                    this.RenderToc(Path.Combine(this._baseDirectory, StripPrefix(url)), false, response);
                }
                else if (url.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
                {
                    //image
                    string imageName = Path.GetFileName(StripPrefix(url)).ToLower();
                    byte[] imageContent;

                    if (this._resources.TryGetValue(imageName, out imageContent))
                    {
                        response.ContentType = "image/png";
                        response.OutputStream.Write(imageContent, 0, imageContent.Length);
                        response.Close();
                    }
                    else
                    {
                        SendErrorResponse(response, 404, "Not found");
                    }
                }
                else
                {
                    //regular page
                    string filepath = Path.Combine(this._baseDirectory, relativePath);

                    if (!File.Exists(filepath))
                    {
                        SendErrorResponse(response, 404, "Not found");
                        return;
                    }

                    string src = File.ReadAllText(filepath, this._options.GetEncoding());
                    string filename = Path.GetFileName(relativePath);

                    //render left navigation panel
                    string navigation = WebsiteGenerator.RenderNavigationPanel(filepath, filename, this._sourceExtensions);

                    //render page body
                    string html = this._gen.VisualizeSourceFile(src, filename, navigation, string.Empty);

                    this.AddToCache(url, html);
                    SendHtmlResponse(response, html);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("SourceServer error when rendering page!");
                Console.WriteLine(ex.ToString());
                Console.WriteLine();
                SendErrorResponse(response, 500, "Internal server error");
            }
        }
    }
}
View in source control

Back to table of contents


Generated by CIL Browser