1 module served.http; 2 3 import core.thread : Fiber; 4 5 import fs = std.file; 6 import std.conv; 7 import std.datetime.stopwatch; 8 import std.exception; 9 import std.format; 10 import std.json; 11 import std.math; 12 import std.path; 13 import std.random; 14 import std.stdio; 15 import std.string; 16 import std.traits; 17 18 version (Windows) pragma(lib, "wininet"); 19 else 20 { 21 // only import in this scope 22 import requests; 23 } 24 25 struct InteractiveDownload 26 { 27 string url; 28 string title; 29 string output; 30 } 31 32 void downloadFileManual(string url, string title, string into, void delegate(string) onLog) 33 { 34 File file = File(into, "wb"); 35 36 StopWatch sw; 37 sw.start(); 38 39 version (Windows) 40 { 41 import core.sys.windows.windows : DWORD; 42 import core.sys.windows.wininet : INTERNET_FLAG_NO_UI, 43 INTERNET_OPEN_TYPE_PRECONFIG, InternetCloseHandle, InternetOpenA, 44 InternetOpenUrlA, InternetReadFile, IRF_NO_WAIT, INTERNET_BUFFERSA, 45 HttpQueryInfoA, HTTP_QUERY_CONTENT_LENGTH; 46 47 auto handle = enforce(InternetOpenA("serve-d_release-downloader/Pure-D/serve-d", 48 INTERNET_OPEN_TYPE_PRECONFIG, null, null, 0), "Failed to open internet handle"); 49 scope (exit) 50 InternetCloseHandle(handle); 51 52 auto obj = enforce(InternetOpenUrlA(handle, cast(const(char)*) url.toStringz, 53 null, 0, INTERNET_FLAG_NO_UI, 0), "Opening URL failed"); 54 scope (exit) 55 InternetCloseHandle(obj); 56 57 scope ubyte[] buffer = new ubyte[50 * 1024]; 58 59 long maxLen; 60 DWORD contentLengthLength = 16; 61 DWORD index = 0; 62 if (HttpQueryInfoA(obj, HTTP_QUERY_CONTENT_LENGTH, buffer.ptr, &contentLengthLength, &index)) 63 maxLen = (cast(char[]) buffer[0 .. contentLengthLength]).strip.to!long; 64 65 long received; 66 while (true) 67 { 68 DWORD read; 69 if (!InternetReadFile(obj, buffer.ptr, cast(DWORD) buffer.length, &read)) 70 throw new Exception("Failed to read from internet file"); 71 72 if (read == 0) 73 break; 74 75 file.rawWrite(buffer[0 .. read]); 76 received += read; 77 78 if (sw.peek >= 1.seconds) 79 { 80 sw.reset(); 81 if (maxLen > 0) 82 onLog(format!"%s %s / %s (%.1f %%)"(title, humanSize(received), 83 humanSize(maxLen), received / cast(float) maxLen * 100)); 84 else 85 onLog(format!"%s %s / ???"(title, humanSize(received))); 86 Fiber.yield(); 87 } 88 } 89 } 90 else 91 { 92 auto req = Request(); 93 req.useStreaming = true; 94 95 auto res = req.get(url); 96 97 foreach (part; res.receiveAsRange()) 98 { 99 file.rawWrite(part); 100 101 if (sw.peek >= 1.seconds) 102 { 103 sw.reset(); 104 onLog(format!"%s %s / %s (%.1f %%)"(title, humanSize(res.contentReceived), 105 humanSize(res.contentLength), res.contentReceived / cast(float) res.contentLength * 100)); 106 Fiber.yield(); 107 } 108 } 109 } 110 } 111 112 string humanSize(T)(T bytes) if (isIntegral!T) 113 { 114 static immutable string prefixes = "kMGTPE"; 115 116 if (bytes < 1024) 117 return text(bytes, " B"); 118 int exp = cast(int)(log2(bytes) / 8); // 8 = log2(1024) 119 return format!"%.1f %siB"(bytes / cast(float) pow(1024, exp), prefixes[exp - 1]); 120 }