Skip to content

Commit e20aef8

Browse files
Craig Berrycraigberry
authored andcommitted
Expose HTTP status codes in Win32::HttpGetFile
There are no corresponding system error codes, but setting bit 29 denotes a user-defined error code. Conveniently, adding 1e9 to the HTTP status sets bit 29, and calling SetLastError() with the result gives us a value that is easy to coerce into the actual HTTP status.
1 parent 15cec35 commit e20aef8

File tree

3 files changed

+33
-21
lines changed

3 files changed

+33
-21
lines changed

Win32.pm

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1322,7 +1322,8 @@ context also returns, in addition to the boolean status, a second
13221322
value containing message text related to the status.
13231323
13241324
If the call fails, C<Win32::GetLastError()> will return a numeric
1325-
error code, which may be either a system error or a WinHttp error.
1325+
error code, which may be a system error, a WinHttp error, or a
1326+
user-defined error composed of 1e9 plus the HTTP status code.
13261327
13271328
Scalar context example:
13281329
@@ -1339,6 +1340,10 @@ List context example:
13391340
}
13401341
else {
13411342
print "Failure!: $msg\n";
1343+
my $err = Win32::GetLastError();
1344+
if ($err > 1e9) {
1345+
printf "HTTP status: %d\n", ($err - 1e9);
1346+
}
13421347
}
13431348
13441349
=item Win32::InitiateSystemShutdown

Win32.xs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,26 +1939,33 @@ XS(w32_HttpGetFile)
19391939
Safefree(hostName);
19401940
Safefree(urlPath);
19411941

1942-
/* Retrieve system and WinHttp error messages, but not if we already
1943-
* got a failed HTTP status text above.
1942+
/* Retrieve system and WinHttp error messages, or compose a user-defined
1943+
* error code if we got a failed HTTP status text above. Conveniently, adding
1944+
* 1e9 to the HTTP status sets bit 29, denoting a user-defined error code,
1945+
* and also makes it easy to lop off the upper part and just get HTTP status.
19441946
*/
1945-
if (bAborted && !bHttpError) {
1946-
DWORD msgFlags = bFileError
1947-
? FORMAT_MESSAGE_FROM_SYSTEM
1948-
: FORMAT_MESSAGE_FROM_HMODULE;
1949-
msgFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
1950-
1951-
ZeroMemory(&msgbuf, ONE_K_BUFSIZE * 2);
1952-
if (!FormatMessageW(msgFlags,
1953-
GetModuleHandleW(L"winhttp.dll"),
1954-
error,
1955-
0,
1956-
msgbuf,
1957-
ONE_K_BUFSIZE - 1, /* TCHARs, not bytes */
1958-
NULL)) {
1959-
wcsncpy(msgbuf, L"unable to format error message", ONE_K_BUFSIZE - 1);
1947+
if (bAborted) {
1948+
if (bHttpError) {
1949+
SetLastError(dwHttpStatusCode + 1000000000);
1950+
}
1951+
else {
1952+
DWORD msgFlags = bFileError
1953+
? FORMAT_MESSAGE_FROM_SYSTEM
1954+
: FORMAT_MESSAGE_FROM_HMODULE;
1955+
msgFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
1956+
1957+
ZeroMemory(&msgbuf, ONE_K_BUFSIZE * 2);
1958+
if (!FormatMessageW(msgFlags,
1959+
GetModuleHandleW(L"winhttp.dll"),
1960+
error,
1961+
0,
1962+
msgbuf,
1963+
ONE_K_BUFSIZE - 1, /* TCHARs, not bytes */
1964+
NULL)) {
1965+
wcsncpy(msgbuf, L"unable to format error message", ONE_K_BUFSIZE - 1);
1966+
}
1967+
SetLastError(error);
19601968
}
1961-
SetLastError(error);
19621969
}
19631970

19641971
if (GIMME_V == G_SCALAR) {

t/HttpGetFile.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ my $english_locale = (Win32::FormatMessage(1) eq "Incorrect function.\r\n");
1818
# We may not always have an internet connection, so don't
1919
# attempt remote connections unless the user has done
2020
# set PERL_WIN32_INTERNET_OK=1
21-
plan tests => $ENV{PERL_WIN32_INTERNET_OK} ? 19 : 7;
21+
plan tests => $ENV{PERL_WIN32_INTERNET_OK} ? 20 : 7;
2222

2323
# On Cygwin the test_harness will invoke additional Win32 APIs that
2424
# will reset the Win32::GetLastError() value, so capture it immediately.
@@ -64,6 +64,7 @@ if ($ENV{PERL_WIN32_INTERNET_OK}) {
6464

6565
my ($ok, $message) = HttpGetFileList('https://cpan.metacpan.org/authors/id/Z/ZZ/ZILCH/nonesuch.tar.gz', 'NUL:');
6666
ok($ok, '', 'Download of nonexistent file from real site should fail with 404');
67+
ok($LastError - 1e9, '404', 'Correct 404 HTTP status for not found');
6768
if ($english_locale) {
6869
ok($message, 'Not Found', 'Should get text of 404 message');
6970
}
@@ -76,7 +77,6 @@ if ($ENV{PERL_WIN32_INTERNET_OK}) {
7677
'1',
7778
"successfully downloaded a zipball via redirect");
7879

79-
$sha = undef;
8080
$sha = Digest::SHA->new('sha1');
8181
$sha->addfile($tmpfile, 'b');
8282
ok($sha->hexdigest,

0 commit comments

Comments
 (0)