aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Net/RequestContext.cs
blob: 4c1350701c6d8713e57c115e4cb90e556f66104c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
using System;
using System.Linq;
using System.Net;
using MediaBrowser.Net.Handlers;

namespace MediaBrowser.Net
{
    public class RequestContext
    {
        public HttpListenerRequest Request { get; private set; }
        public HttpListenerResponse Response { get; private set; }

        public string LocalPath
        {
            get
            {
                return Request.Url.LocalPath;
            }
        }

        public RequestContext(HttpListenerContext context)
        {
            Response = context.Response;
            Request = context.Request;
        }

        public void Respond(BaseHandler handler)
        {
            Response.AddHeader("Access-Control-Allow-Origin", "*");

            Response.KeepAlive = true;
            
            foreach (var header in handler.Headers)
            {
                Response.AddHeader(header.Key, header.Value);
            }

            int statusCode = handler.StatusCode;
            Response.ContentType = handler.ContentType;

            TimeSpan cacheDuration = handler.CacheDuration;

            if (Request.Headers.AllKeys.Contains("If-Modified-Since"))
            {
                DateTime ifModifiedSince;

                if (DateTime.TryParse(Request.Headers["If-Modified-Since"].Replace(" GMT", string.Empty), out ifModifiedSince))
                {
                    // If the cache hasn't expired yet just return a 304
                    if (IsCacheValid(ifModifiedSince, cacheDuration, handler.LastDateModified))
                    {
                        statusCode = 304;
                    }
                }
            }

            Response.SendChunked = true;
            Response.StatusCode = statusCode;

            if (statusCode != 304)
            {
                if (handler.GzipResponse)
                {
                    Response.AddHeader("Content-Encoding", "gzip");
                }

                if (cacheDuration.Ticks > 0)
                {
                    CacheResponse(Response, cacheDuration, handler.LastDateModified);
                }
                handler.WriteStream(Response.OutputStream);
            }
            else
            {
                Response.OutputStream.Flush();
                Response.OutputStream.Close();
            }
        }

        private void CacheResponse(HttpListenerResponse response, TimeSpan duration, DateTime? dateModified)
        {
            DateTime lastModified = dateModified ?? DateTime.Now;

            response.Headers[HttpResponseHeader.CacheControl] = "Public";
            response.Headers[HttpResponseHeader.Expires] = DateTime.Now.Add(duration).ToString("r");
            response.Headers[HttpResponseHeader.LastModified] = lastModified.ToString("r");
        }

        private bool IsCacheValid(DateTime ifModifiedSince, TimeSpan cacheDuration, DateTime? dateModified)
        {
            if (dateModified.HasValue)
            {
                DateTime lastModified = NormalizeDateForComparison(dateModified.Value);
                ifModifiedSince = NormalizeDateForComparison(ifModifiedSince);

                return lastModified <= ifModifiedSince;
            }

            DateTime cacheExpirationDate = ifModifiedSince.Add(cacheDuration);

            if (DateTime.Now < cacheExpirationDate)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that
        /// </summary>
        private DateTime NormalizeDateForComparison(DateTime date)
        {
            return new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second);
        }

    }
}