본문 바로가기

C#/근웹 연대기

c#으로 근본 없는 웹서버 개발기 7 : 304 Not ModiFied 구현

⚠WARNING
ASP.NET에 대한 포스팅이 아닙니다
나가실 문은 오른쪽 하단입니다

 

 

하기로 했던 것은 언제나 마음속 어딘가에 남아있기 마련이다.

전전편에서 언급하였던 Etag를 활용한 304를 써먹을 수 있도록 코드를 작성하였다.

public class Ftag
{
    public FileInfo info = null;
    public string etag = null;
    public string modiTime = null;
    public string minetype = null;
}

↕는 동일한 파일에 위치한다.

static readonly Dictionary<string, Ftag> fileDic = new Dictionary<string, Ftag>();
public static Ftag getFInfo(string fulPath)
{
    var info = new FileInfo(fulPath);
    if(info.Exists == false){
        return new Ftag(){info = info};
    }
    var modiTime = Util.utcEng(info.LastWriteTime);
    var etag = DateTime.UtcNow.Ticks.ToString("x2");
    if (fileDic.GetValueOrDefault(fulPath)?.modiTime == modiTime)
    {
        return fileDic[fulPath];
    }

    var ext = Path.GetExtension(info.Name);
    if(mineTypeDic.ContainsKey(ext) == false) ext = "?";

    fileDic[fulPath] = new Ftag()
    {
        info = info,
        etag = etag,
        modiTime = modiTime,
        minetype = mineTypeDic[ext]
    };

    return fileDic[fulPath];
}

Ftag클래스는 상속당해야 사용하기 편한데, FileInfo는 sealed 클래스라 상속을 허용하지 않았다.

 

위의 코드가 사용되는 부분

static void response(Socket client, Dictionary<string,string> dic)
{
    string fpath = Util.sPath + "/public" + dic["url"];
    var ftag =  Util.getFInfo(fpath);
    if(!ftag.info.Exists){
        resForbadrequest(client, "404 Not found");
        return;
    }  

    StringBuilder sb = new StringBuilder(100);
    bool m = ftag.etag == dic.GetValueOrDefault("If-None-Match");

    sb.AppendLine($"HTTP/1.1 {( m ? "304 Not Modified" : "200 ok")}");
    sb.AppendLine("Accept-Ranges: none");
    sb.AppendLine("Cache-Control: public, max-age=0");
    sb.AppendLine("Last-Modified: " + ftag.modiTime);
    sb.AppendLine("Etag: " + ftag.etag);
    sb.AppendLine("Date: " + Util.uTime);
    sb.AppendLine("Content-type: " + ftag.minetype);
    sb.AppendLine("Server: test server");
    sb.AppendLine("Content-Length: " + ftag.info.Length);
    sb.AppendLine("Connection: close");
    sb.AppendLine();
       
    client.Send(Encoding.UTF8.GetBytes(sb.ToString()));
    if(!m) client.Send(File.ReadAllBytes(ftag.info.FullName));
}

설명: 프로그램이 최초로 실행된 이후에, 같은 내용물의 파일을 클라이언트로부터 요청받을 경우에는 304로 응답하고 파일 전송을 하지 않는다. 

 

다음의 이미지는 이해를 돕기 위한 첫 번째, 두 번째 요청에 대한 응답 헤더의 내용이다.

첫 째

둘 째

설명: 두 번째는 파일을 전송하지 않으므로 1406에 해당하는 데이터 낭비를 줄일 수 있다.

 

Date란에 날짜의 변함이 없음을 보고 소스를 잘못 작성하였음을 깨달았다.

다음같이 수정하였다.

public static string uTime{ get {return utcEng(DateTime.Now);} }

つづく