Chapter 02

リクエストへの応答

にんにん324
にんにん324
2023.09.16に更新
  • ブラウザからのリクエストに応えられるようにします
  1. 次の要求には、index.htmlを返すようにします

     localhost GET /http://localhost/
    
  2. 次の要求には、favicon.icoを返すようにします

     localhost GET /http://localhost/favicon.ico
    

ディレクトリ構造

  • 以下のディレクトリ構造を用意してください
SimpleWebServer
├── Program.cs
├── Server.cs
├── ContentLoader.cs
└── Website
    ├── Pages
    |   ├── index.html   <--内容は何でもOK
    |   └── page1.html   <--内容は何でもOK
    └── Images
        └── favicon.ico  <--内容は何でもOK

実装

Server.cs

  • ブラウザからのリクエストへの応答をContentLoaderから受け取るようにします
        private void Respond(HttpListenerContext context)
        {
            var req = context.Request;
            var res = context.Response;

            // リクエストの内容をlogに残す
            Console.Out.WriteLine($"{req.UserHostName} {req.HttpMethod} /{req.Url.AbsoluteUri}");

-            var message = "hello browser";
-            var bytes = Encoding.UTF8.GetBytes(message);
+            var content = ContentLoader.Load(req);
+            if (content != null)
+            {
                res.StatusCode = (int)HttpStatusCode.OK;
-                res.ContentType = "text/html";
+                res.ContentType = content.ContentType;
                res.ContentEncoding = Encoding.UTF8;
-                res.ContentLength64 = bytes.Length;
-                res.OutputStream.Write(bytes, 0, bytes.Length);
+                res.ContentLength64 = content.Bytes.Length;
+                res.OutputStream.Write(content.Bytes, 0, content.Bytes.Length);
                res.OutputStream.Close();
+            }
        }
    }

ContentLoader

  1. ブラウザが要求しているデータの種類をURLの拡張子から判別
  2. サーバ上のデータのpathをデータの種類とURLから判別
  3. データをbyte列として読み込む
  4. byte列を含む必要な情報をContentクラスに入れて返す
データ種類 拡張子 データのあるフォルダ
ページ内容 html { Projectフォルダ } / Website / Pages
アイコン ico { Projectフォルダ } / Website / Images
using System;
using System.IO;
using System.Text;

namespace SimpleWebServer
{
    class Content
    {
        public string ContentType { get; set; }
        public Encoding ContentEncoding { get; set; }
        public byte[] Bytes { get; set; }
    }

    class ContentLoader
    {
        static readonly string RootDir;
        static ContentLoader()
        {
            RootDir = Path.Combine(
                Directory.GetParent(Environment.CurrentDirectory).Parent.FullName,
                "Website");
        }

        public static Content Load(HttpListenerRequest req)
        {
            var url = req.Url.AbsolutePath;
            var extension = Path.GetExtension(url).Trim('.');
            switch (extension)
            {
                case "":
                case "html":
                    return LoadPage(url);
                case "ico":
                    return LoadImage(url, extension);
                default:
                    throw new Exception("unsupported content type");
            }
        }

        static Content LoadPage(string url)
        {
            var page = (url == "/") ? "index.html" : url;
            if (!page.EndsWith(".html"))
                page += ".html";

            var path = Path.Combine(RootDir, "Pages", page.Trim('/'));
            if (!File.Exists(path))
                return null;

            var text = File.ReadAllText(path);

            return new Content
            {
                ContentType = "text/html",
                ContentEncoding = Encoding.UTF8,
                Bytes = Encoding.UTF8.GetBytes(text),
            };
        }

        static Content LoadImage(string url, string extension)
        {
            var path = Path.Combine(RootDir, "Images", url.Trim('/'));
            if (!File.Exists(path))
                return null;

            using (var fStream = new FileStream(path, FileMode.Open, FileAccess.Read))
            using (var br = new BinaryReader(fStream))
            {
                return new Content
                {
                    ContentType = @"image/{extension}",
                    Bytes = br.ReadBytes((int)fStream.Length),
                };
            }
        }
    }
}

動作確認

  • 以下の2つのアドレスを打って、正しくページが表示されること

    1. localhost
    2. localhost/page1
  • タブの左側にfaviconが表示されること

次回

  • 「localhost/hogehoge」などの適当なアドレスを打つと、エラーが表示されてしまうので、「ページが存在しません」ページへredirectするようにします