Skip to content

Commit

Permalink
rewritten low-level Windows-specific error messages support
Browse files Browse the repository at this point in the history
- mainly for client/server Windows APIs
  • Loading branch information
Arnaud Bouchez committed Oct 26, 2022
1 parent 31f4e71 commit 3319adb
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 44 deletions.
43 changes: 21 additions & 22 deletions src/lib/mormot.lib.winhttp.pas
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ interface
mormot.core.base,
mormot.core.os,
mormot.core.unicode,
mormot.core.text,
mormot.net.sock;


{ ******************** WinINet API Additional Wrappers }

/// retrieve extended error information text after a WinINet API call
function SysErrorMessageWinInet(error: integer): string;
function SysErrorMessageWinInet(error: integer): RawUtf8;

/// low-level retrieval of a Domain User from a transmitted Token
procedure GetDomainUserNameFromToken(UserToken: THandle; var result: RawUtf8);
Expand Down Expand Up @@ -1223,7 +1224,7 @@ HTTP_PROTECTION_LEVEL_INFO = record

type
/// exception raised during http.sys HTTP/1.1 process
EHttpApiServer = class(ExceptionWithProps)
EHttpApiServer = class(ESynException)
protected
fLastApiError: integer;
fLastApi: THttpApis;
Expand Down Expand Up @@ -1636,7 +1637,7 @@ TProxyInfo = record
/// if the Proxy settings were auto-detected by Internet Explorer
AutoDetected: Boolean;
/// detailed error message, if GetProxyInfo() returned a non 0 error code
ErrorMessage: string;
ErrorMessage: RawUtf8;
end;

/// use WinHttp to retrieve the proxy information needed to access a given URI
Expand Down Expand Up @@ -1784,7 +1785,7 @@ WEB_SOCKET_BUFFER_CLOSE_STATUS = record
hSend);

/// exception raised during http.sys WebSockets process
EWebSocketApi = class(ExceptionWithProps)
EWebSocketApi = class(ESynException)
protected
fLastError: integer;
fLastApi: TWebSocketApis;
Expand Down Expand Up @@ -2099,8 +2100,8 @@ constructor EHttpApiServer.Create(api: THttpApis; Error: integer);
begin
fLastApiError := Error;
fLastApi := api;
inherited CreateFmt('%s failed: %s (%d=0x%x)', [HttpNames[api],
SysErrorMessagePerModule(Error, HTTPAPI_DLL), Error, Error])
inherited CreateUtf8('% failed: % (%)',
[HttpNames[api], WinErrorText(Error, HTTPAPI_DLL), Error])
end;


Expand Down Expand Up @@ -2256,22 +2257,20 @@ function HTTP_RESPONSE.AddCustomHeader(P: PUtf8Char;

{ ******************** WinINet API Additional Wrappers }

function SysErrorMessageWinInet(error: integer): string;
function SysErrorMessageWinInet(error: integer): RawUtf8;
var
dwError, tmpLen: DWORD;
tmp: string;
tmp: array[0..511] of WideChar;
begin
result := SysErrorMessagePerModule(error, 'wininet.dll');
if error = ERROR_INTERNET_EXTENDED_ERROR then
begin
InternetGetLastResponseInfo({$ifdef FPC}@{$endif}dwError, nil, tmpLen);
if tmpLen > 0 then
begin
SetLength(tmp, tmpLen);
InternetGetLastResponseInfo({$ifdef FPC}@{$endif}dwError, PChar(tmp), tmpLen);
result := result + ' [' + tmp + ']';
end;
end;
result := WinErrorText(error, 'wininet.dll');
if error <> ERROR_INTERNET_EXTENDED_ERROR then
exit;
InternetGetLastResponseInfoW(dwError, nil, tmpLen);
if (tmpLen = 0) or
(tmplen > SizeOf(tmp)) then
exit;
InternetGetLastResponseInfoW(dwError, @tmp, tmpLen);
result := FormatUtf8('% [%]', [result, PWideChar(@tmp)]);
end;

procedure GetDomainUserNameFromToken(UserToken: THandle; var result: RawUtf8);
Expand Down Expand Up @@ -2432,7 +2431,7 @@ function WinHttpGetProxyInfo(const URL: SynUnicode;
result := GetLastError;
end;
if result <> 0 then
ProxyInfo.ErrorMessage := SysErrorMessagePerModule(result, winhttpdll);
ProxyInfo.ErrorMessage := WinErrorText(result, winhttpdll);
end;


Expand Down Expand Up @@ -2607,8 +2606,8 @@ constructor EWebSocketApi.Create(api: TWebSocketApis; Error: integer);
begin
fLastError := Error;
fLastApi := api;
inherited CreateFmt('%s failed: %s (%d)', [WebSocketNames[api],
SysErrorMessagePerModule(Error, WEBSOCKET_DLL), Error])
inherited CreateUtf8('% failed: % (%)', [WebSocketNames[api],
WinErrorText(Error, WEBSOCKET_DLL), Error])
end;

const
Expand Down
2 changes: 1 addition & 1 deletion src/mormot.commit.inc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
'2.0.4236'
'2.0.4237'
45 changes: 29 additions & 16 deletions src/net/mormot.net.client.pas
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,11 @@ TWinHttp = class(TWinHttpApi)
end;

/// WinHttp exception type
EWinHttp = class(ESynException);
EWinHttp = class(ESynException)
public
/// create and raise a EWinHttp exception, with the error message as text
class procedure RaiseFromLastError;
end;

/// establish a client connection to a WebSocket server using the Windows API
// - used by TWinWebSocketClient class
Expand Down Expand Up @@ -1184,6 +1188,7 @@ function SendEmailSubject(const Text: string): RawUtf8;
implementation



{ ******************** THttpMultiPartStream for multipart/formdata HTTP POST }

{ THttpMultiPartStream }
Expand Down Expand Up @@ -2409,26 +2414,26 @@ procedure TWinHttp.InternalConnect(ConnectionTimeOut, SendTimeout, ReceiveTimeou
pointer(Utf8ToSynUnicode(fProxyName)),
pointer(Utf8ToSynUnicode(fProxyByPass)), 0);
if fSession = nil then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
// cf. http://msdn.microsoft.com/en-us/library/windows/desktop/aa384116
if not WinHttpApi.SetTimeouts(fSession, HTTP_DEFAULT_RESOLVETIMEOUT,
ConnectionTimeOut, SendTimeout, ReceiveTimeout) then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
if fHTTPS then
begin
protocols := InternalGetProtocols;
if not WinHttpApi.SetOption(fSession, WINHTTP_OPTION_SECURE_PROTOCOLS,
@protocols, SizeOf(protocols)) then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
Callback := WinHttpApi.SetStatusCallback(fSession,
WinHttpSecurityErrorCallback, WINHTTP_CALLBACK_FLAG_SECURE_FAILURE, nil);
if CallbackRes = WINHTTP_INVALID_STATUS_CALLBACK then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
end;
fConnection := WinHttpApi.Connect(
fSession, pointer(Utf8ToSynUnicode(fServer)), fPort, 0);
if fConnection = nil then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
end;

procedure TWinHttp.InternalCreateRequest(const aMethod, aUrl: RawUtf8);
Expand All @@ -2446,13 +2451,13 @@ procedure TWinHttp.InternalCreateRequest(const aMethod, aUrl: RawUtf8);
fRequest := WinHttpApi.OpenRequest(fConnection, pointer(Utf8ToSynUnicode(aMethod)),
pointer(Utf8ToSynUnicode(aUrl)), nil, nil, ACCEPT_TYPES[fNoAllAccept], Flags);
if fRequest = nil then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
if fKeepAlive = 0 then
begin
Flags := WINHTTP_DISABLE_KEEP_ALIVE;
if not WinHttpApi.SetOption(
fRequest, WINHTTP_OPTION_DISABLE_FEATURE, @Flags, SizeOf(Flags)) then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
end;
end;

Expand All @@ -2470,7 +2475,7 @@ procedure TWinHttp.InternalAddHeader(const hdr: RawUtf8);
if (hdr <> '') and
not WinHttpApi.AddRequestHeaders(FRequest,
Pointer(Utf8ToSynUnicode(hdr)), length(hdr), WINHTTP_ADDREQ_FLAG_COALESCE) then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
end;

procedure TWinHttp.InternalSendRequest(const aMethod: RawUtf8;
Expand Down Expand Up @@ -2499,7 +2504,7 @@ procedure TWinHttp.InternalSendRequest(const aMethod: RawUtf8;
Bytes := Max;
if not WinHttpApi.WriteData(fRequest, @PByteArray(aData)[Current],
Bytes, BytesWritten) then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
inc(Current, BytesWritten);
if not fOnUpload(Self, Current, L) then
raise EWinHttp.CreateUtf8('%: OnUpload canceled %', [self, aMethod]);
Expand Down Expand Up @@ -2531,13 +2536,13 @@ procedure TWinHttp.InternalSendRequest(const aMethod: RawUtf8;
end;
if not WinHttpApi.SetCredentials(fRequest, WINHTTP_AUTH_TARGET_SERVER,
winAuth, pointer(AuthUserName), pointer(AuthPassword), nil) then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
end;
if fHTTPS and
IgnoreTlsCertificateErrors then
if not WinHttpApi.SetOption(fRequest, WINHTTP_OPTION_SECURITY_FLAGS,
@SECURITY_FLAT_IGNORE_CERTIFICATES, SizeOf(SECURITY_FLAT_IGNORE_CERTIFICATES)) then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
L := length(aData);
if _SendRequest(L) and
WinHttpApi.ReceiveResponse(fRequest, nil) then
Expand All @@ -2553,7 +2558,7 @@ procedure TWinHttp.InternalSendRequest(const aMethod: RawUtf8;
WinHttpApi.ReceiveResponse(fRequest, nil) then
exit; // success with no certificate validation
// if we reached here, an error occured
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
end;

function TWinHttp.InternalGetInfo(Info: cardinal): RawUtf8;
Expand Down Expand Up @@ -2594,14 +2599,14 @@ function TWinHttp.InternalGetInfo32(Info: cardinal): cardinal;
function TWinHttp.InternalQueryDataAvailable: cardinal;
begin
if not WinHttpApi.QueryDataAvailable(fRequest, result) then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
end;

function TWinHttp.InternalReadData(var Data: RawByteString; Read: integer;
Size: cardinal): cardinal;
begin
if not WinHttpApi.ReadData(fRequest, @PByteArray(Data)[Read], Size, result) then
RaiseLastModuleError(winhttpdll, EWinHttp);
EWinHttp.RaiseFromLastError;
end;

destructor TWinHttp.Destroy;
Expand All @@ -2614,6 +2619,14 @@ destructor TWinHttp.Destroy;
end;


{ EWinHttp }

class procedure EWinHttp.RaiseFromLastError;
begin
RaiseLastModuleError(winhttpdll, EWinHttp);
end;


{ EWinINet }

class procedure EWinINet.RaiseFromLastError;
Expand All @@ -2623,7 +2636,7 @@ class procedure EWinINet.RaiseFromLastError;
begin
// see http://msdn.microsoft.com/en-us/library/windows/desktop/aa383884
err := GetLastError;
E := CreateFmt('%s (%x)', [SysErrorMessageWinInet(err), err]);
E := CreateUtf8('% (%)', [SysErrorMessageWinInet(err), err]);
E.fLastError := err;
raise E;
end;
Expand Down
11 changes: 6 additions & 5 deletions src/net/mormot.net.server.pas
Original file line number Diff line number Diff line change
Expand Up @@ -3099,7 +3099,8 @@ procedure THttpApiServer.Execute;
logdata: PHTTP_LOG_FIELDS_DATA;
contrange: ShortString;

procedure SendError(StatusCode: cardinal; const ErrorMsg: string; E: Exception = nil);
procedure SendError(StatusCode: cardinal; const ErrorMsg: RawUtf8;
E: Exception = nil);
var
msg: RawUtf8;
begin
Expand All @@ -3110,7 +3111,7 @@ procedure THttpApiServer.Execute;
[StatusCode, outstat], msg);
if E <> nil then
msg := FormatUtf8('%% Exception raised:<br>', [msg, E]);
msg := msg + HtmlEscapeString(ErrorMsg) + ('</p><p><small>' + XPOWEREDVALUE);
msg := msg + HtmlEscape(ErrorMsg) + ('</p><p><small>' + XPOWEREDVALUE);
reps^.SetContent(datachunkmem, msg, 'text/html; charset=utf-8');
Http.SendHttpResponse(fReqQueue, req^.RequestId, 0, reps^, nil,
bytessent, nil, 0, nil, fLogData);
Expand Down Expand Up @@ -3180,7 +3181,7 @@ procedure THttpApiServer.Execute;
fmOpenRead or fmShareDenyNone);
if not ValidHandle(filehandle) then
begin
SendError(HTTP_NOTFOUND, SysErrorMessage(GetLastError));
SendError(HTTP_NOTFOUND, WinErrorText(GetLastError, nil));
result := false; // notify fatal error
end;
try // http.sys will serve then close the file from kernel
Expand Down Expand Up @@ -3409,7 +3410,7 @@ procedure THttpApiServer.Execute;
until incontlenread = incontlen;
if err <> NO_ERROR then
begin
SendError(HTTP_NOTACCEPTABLE, SysErrorMessagePerModule(err, HTTPAPI_DLL));
SendError(HTTP_NOTACCEPTABLE, WinErrorText(err, HTTPAPI_DLL));
continue;
end;
// optionally uncompress input body
Expand Down Expand Up @@ -3446,7 +3447,7 @@ procedure THttpApiServer.Execute;
if not respsent then
if not E.InheritsFrom(EHttpApiServer) or // ensure still connected
(EHttpApiServer(E).LastApiError <> HTTPAPI_ERROR_NONEXISTENTCONNECTION) then
SendError(HTTP_SERVERERROR, E.Message, E);
SendError(HTTP_SERVERERROR, StringToUtf8(E.Message), E);
end;
finally
reqid := 0; // reset Request ID to handle the next pending request
Expand Down

0 comments on commit 3319adb

Please sign in to comment.