1+ module d_downloader.windows ;
2+ version (Windows ):
3+ import core.sys.windows.winhttp ;
4+
5+ bool download (string link, scope ubyte [] delegate (size_t incomingBytes) bufferSink, scope void delegate (ubyte [] buffer) onDataReceived = null )
6+ {
7+ import std.utf :toUTF16z;
8+ import core.sys.windows.windef ;
9+ HINTERNET hSession = WinHttpOpen(" D-Downloader/1.0" ,
10+ WINHTTP_ACCESS_TYPE_DEFAULT_PROXY ,
11+ WINHTTP_NO_PROXY_NAME , WINHTTP_NO_PROXY_BYPASS , 0 );
12+
13+ if (! hSession)
14+ return false ;
15+ scope (exit)
16+ WinHttpCloseHandle(hSession);
17+
18+ NetURL url = NetURL(link);
19+
20+ uint [1 ] protocols = [WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 ];
21+ WinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS , protocols.ptr, protocols.length);
22+ HINTERNET hConnect = WinHttpConnect(hSession, url.domain.toUTF16z,
23+ INTERNET_DEFAULT_HTTPS_PORT , 0 );
24+ scope (exit)
25+ WinHttpCloseHandle(hConnect);
26+
27+ HINTERNET hRequest = WinHttpOpenRequest(hConnect, " GET" , url.object.toUTF16z,
28+ null , WINHTTP_NO_REFERER , WINHTTP_DEFAULT_ACCEPT_TYPES ,
29+ WINHTTP_FLAG_SECURE );
30+
31+ if (hRequest is null )
32+ {
33+ import core.sys.windows.winbase ;
34+ DWORD code = GetLastError();
35+ string msg;
36+ switch (code)
37+ {
38+ case ERROR_WINHTTP_INCORRECT_HANDLE_TYPE :
39+ msg = ` The type of handle supplied is incorrect for this operation.` ; break ;
40+ case ERROR_WINHTTP_INTERNAL_ERROR :
41+ msg = ` An internal error has occurred.` ; break ;
42+ case ERROR_WINHTTP_INVALID_URL :
43+ msg = ` The URL is invalid.` ; break ;
44+ case ERROR_WINHTTP_OPERATION_CANCELLED :
45+ msg = ` The operation was canceled, usually because the handle on which the request was operating was closed before the operation completed.` ; break ;
46+ case ERROR_WINHTTP_UNRECOGNIZED_SCHEME :
47+ msg = ` The URL specified a scheme other than "http:" or "https:".` ; break ;
48+ case ERROR_NOT_ENOUGH_MEMORY :
49+ msg = ` Not enough memory was available to complete the requested operation. (Windows error code)` ; break ;
50+ default :
51+ wchar * buffer;
52+ DWORD msgLength = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
53+ FORMAT_MESSAGE_IGNORE_INSERTS |
54+ FORMAT_MESSAGE_ALLOCATE_BUFFER , null , code, LANG_NEUTRAL , cast (wchar * )&buffer, 0 , null );
55+ if (msgLength)
56+ {
57+ import std.conv :to;
58+ msg = buffer[0 .. msgLength].to! string ;
59+ LocalFree(buffer);
60+ }
61+ break ;
62+ }
63+ throw new Exception (msg);
64+ }
65+
66+ scope (exit)
67+ WinHttpCloseHandle(hRequest);
68+
69+ if (WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS , 0 ,
70+ WINHTTP_NO_REQUEST_DATA , 0 , 0 , 0 )
71+ && WinHttpReceiveResponse(hRequest, null ))
72+ {
73+ DWORD remainingDataSize = 0 ;
74+
75+ do {
76+ if (! WinHttpQueryDataAvailable(hRequest, &remainingDataSize))
77+ break ;
78+ if (! remainingDataSize)
79+ break ;
80+
81+ DWORD dwRead = 0 ;
82+ ubyte [] buffer = bufferSink(remainingDataSize);
83+ assert (buffer.length < DWORD .max, " Buffer sink length is too big." );
84+ WinHttpReadData(hRequest, buffer.ptr, cast (DWORD )buffer.length, &dwRead);
85+ if (onDataReceived)
86+ onDataReceived(buffer);
87+ } while (remainingDataSize > 0 );
88+ }
89+
90+ return true ;
91+ }
92+
93+ ubyte [] downloadOnBuffer (string url)
94+ {
95+ ubyte [] buffer;
96+ download(url, (size_t incomingBytes)
97+ {
98+ size_t bufferTail = buffer.length;
99+ buffer.length+= incomingBytes;
100+ return buffer[bufferTail.. $];
101+ });
102+ return buffer;
103+ }
104+
105+ void downloadOnFile (string url, string targetFile)
106+ {
107+ import std.stdio ;
108+ import std.file ;
109+ ubyte [4096 ] buffer;
110+ string newFile = targetFile;
111+ if (exists(targetFile))
112+ newFile~= " .temp" ;
113+
114+ File f = File (newFile, " wb" );
115+ download(url, (size_t incomingBytes)
116+ {
117+ return incomingBytes > buffer.length ? buffer[0 .. $] : buffer[0 .. incomingBytes];
118+ }, (ubyte [] b)
119+ {
120+ f.rawWrite(b);
121+ });
122+
123+ f.close();
124+ if (newFile != targetFile)
125+ std.file.rename (newFile, targetFile);
126+ }
127+
128+ private struct NetURL
129+ {
130+ string protocol;
131+ string domain;
132+ string object;
133+
134+ this (string url)
135+ {
136+ import std.algorithm.searching ;
137+ auto protocolIdx = countUntil(url, " ://" );
138+ if (protocolIdx != - 1 )
139+ {
140+ protocol = url[0 .. protocolIdx+ 3 ];
141+ url = url[protocolIdx+ 3 .. $];
142+ }
143+ auto objectIdx = countUntil(url, " /" );
144+ if (objectIdx == - 1 )
145+ {
146+ object = " /" ;
147+ domain = url;
148+ }
149+ else
150+ {
151+ object = url[objectIdx.. $];
152+ domain = url[0 .. objectIdx];
153+ }
154+
155+ }
156+ }
0 commit comments