1 module hunt.http.routing.handler.Util;
2 
3 import hunt.http.routing.RoutingContext;
4 import hunt.http.server.HttpServerResponse;
5 import hunt.http.server.HttpServerRequest;
6 
7 import hunt.http.HttpHeader;
8 import hunt.http.HttpStatus;
9 
10 import hunt.logging;
11 import hunt.Exceptions;
12 import hunt.util.DateTime : TimeUnit;
13 import hunt.util.AcceptMimeType;
14 import hunt.util.MimeTypeUtils;
15 import hunt.util.MimeType;
16 
17 import std.algorithm;
18 import std.array;
19 import std.conv;
20 import std.datetime;
21 import std.file;
22 import std.path;
23 import std.format;
24 import std.stdio;
25 import std.string;
26 
27 /** 
28  * 
29  */
30 struct RoutingHandlerUtils {
31 
32     static void downloadFile(RoutingContext context, string requestFile) {
33         HttpServerRequest request = context.getRequest();
34         HttpServerResponse response = context.getResponse();
35         DirEntry fileInfo = DirEntry(requestFile);
36         response.header(HttpHeader.ACCEPT_RANGES, "bytes");
37 
38         ulong rangeStart = 0;
39         ulong rangeEnd = 0;
40 
41         ulong fileSize = fileInfo.size();
42 
43         if (request.headerExists(HttpHeader.RANGE))
44         {
45             // https://tools.ietf.org/html/rfc7233
46             // Range can be in form "-\d", "\d-" or "\d-\d"
47             auto range = request.header(HttpHeader.RANGE.asString()).chompPrefix("bytes=");
48             if (range.canFind(','))
49             {
50                 response.setStatus(HttpStatus.NOT_IMPLEMENTED_501);
51                 return;
52             }
53             auto s = range.split("-");
54 
55             if (s.length != 2) {
56                 response.setStatus(HttpStatus.BAD_REQUEST_400);
57                 return;
58             }
59 
60             try {
61                 if (s[0].length) {
62                     rangeStart = s[0].to!ulong;
63                     rangeEnd = s[1].length ? s[1].to!ulong : fileSize;
64                 } else if (s[1].length) {
65                     rangeEnd = fileSize;
66                     auto len = s[1].to!ulong;
67 
68                     if (len >= rangeEnd)
69                         rangeStart = 0;
70                     else
71                         rangeStart = rangeEnd - len;
72                 } else {
73                     response.setStatus(HttpStatus.BAD_REQUEST_400);
74                     return;
75                 }
76             } catch (ConvException e) {
77                 warning(e.msg);
78                 version(HUNT_DEBUG) warning(e);
79                 response.setStatus(HttpStatus.BAD_REQUEST_400);
80             }
81 
82             if (rangeEnd > fileSize)
83                 rangeEnd = fileSize;
84 
85             if (rangeStart > rangeEnd)
86                 rangeStart = rangeEnd;
87 
88             if (rangeEnd)
89                 rangeEnd--; // End is inclusive, so one less than length
90             // potential integer overflow with rangeEnd - rangeStart == size_t.max is intended. This only happens with empty files, the + 1 will then put it back to 0
91 
92             response.header(HttpHeader.CONTENT_LENGTH, to!string(rangeEnd - rangeStart + 1));
93             response.header(HttpHeader.CONTENT_RANGE, "bytes %s-%s/%s".format(rangeStart < rangeEnd ? 
94                 rangeStart : rangeEnd, rangeEnd, fileSize));
95             response.setStatus(HttpStatus.PARTIAL_CONTENT_206);
96         }
97         else
98         {
99             rangeEnd = fileSize - 1;
100             response.header(HttpHeader.CONTENT_LENGTH, fileSize.to!string);
101         }
102 
103         // write out the file contents
104         auto f = std.stdio.File(requestFile, "r");
105         scope(exit) f.close();
106 
107         f.seek(rangeStart);
108         int remainingSize = rangeEnd.to!uint - rangeStart.to!uint + 1;
109         if(remainingSize <= 0) {
110             warningf("actualSize:%d, remainingSize=%d", fileSize, remainingSize);
111         } else {
112             auto buf = f.rawRead(new ubyte[remainingSize]);
113             // response.setContent(buf);
114             context.write(cast(byte[])buf);
115         }
116     }
117 
118     static void renderFileContent(RoutingContext context, string requestFile, size_t bufferSize = 8*1024) {
119 
120         string title = "Content for: " ~ requestFile;
121 
122         ubyte[] buffer;
123         File f = File(requestFile, "r");
124         ulong total = f.size();
125 
126         scope(exit) f.close();
127 
128         ulong remaining = total;
129         while(remaining > 0 && !f.eof()) {
130 
131             if(remaining > bufferSize) 
132                 buffer = new ubyte[bufferSize];
133             else 
134                 buffer = new ubyte[cast(size_t)remaining];
135             ubyte[] data = f.rawRead(buffer);
136             
137             if(data.length > 0) {
138                 context.write(cast(byte[])data);
139                 remaining -= data.length;
140             }
141             version(HUNT_HTTP_DEBUG_MORE) {
142                 tracef("read: %s, remaining: %d, eof: %s", 
143                     data.length, remaining, f.eof());
144             }
145         }
146     }
147 
148 }