-
Notifications
You must be signed in to change notification settings - Fork 7
Description
RAL Version:
1.0
IDE:
Delphi 10.4 Sydney
Operating System:
Windows
Bug Description
Bug Report: Access Violation in client when server raises exceptions in route handlers
Summary
When a route handler on the server raises an exception, the client receives an Access Violation instead of a valid HTTP error response. There are 3 related bugs causing this behavior.
Version
PascalRAL v0.12.2-1 beta
Problem Description
If a route handler (TRALRequest/TRALResponse) raises an exception and OnServerError is not assigned and RaiseError is false ( as default ),, the server does not send a valid HTTP response to the client. The client attempts to process an incomplete response and causes an Access Violation.
If I change RaiseError to True ( not default value ), the access violation dissapear.
Identified Bugs
Bug 1: Server does not send error response (RALSynopseServer.pas)
File: src/engine/synopse/RALSynopseServer.pas
Lines: 302-308
Current code:
except
on e: exception do
if Assigned(OnServerError) then
OnServerError(e)
else if RaiseError then
raise;
end;Problem: If OnServerError is not assigned and RaiseError = False, the exception is swallowed and no HTTP response is sent to the client.
Impact: The client waits for a response that never arrives.
Bug 2: Client does not check nil before accessing parameters (RALSynopseClient.pas)
File: src/engine/synopse/RALSynopseClient.pas
Line: 148
Current code:
AResponse.Params.AddParam('Stream', AResponse.ParamByName('ral_body').AsStream, rpkBODY);Problem: It does not check if ParamByName('ral_body') is nil before calling AsStream. If the server sends an error response without a body, this causes an Access Violation.
Impact: Access Violation on the client when the server responds without a body.
Bug 3: SetAsStream does not handle nil (RALParams.pas)
File: src/base/RALParams.pas
Lines: 419-428
Current code:
procedure TRALParam.SetAsStream(const AValue: TStream);
begin
if FContent <> nil then
FreeAndNil(FContent);
AValue.Position := 0; // ← Access Violation if AValue is nil
FContent := TRALStringStream.Create(AValue);
FContent.Position := 0;
end;Problem: It does not check if AValue is nil before accessing AValue.Position.
Impact: Access Violation when attempting to assign a nil stream.
Steps to Reproduce
- Create a route handler that raises an exception:scal
procedure TFormServer.FServer_Handle_Route_Test(ARequest: TRALRequest; AResponse: TRALResponse);
begin
raise Exception.Create('Test error');
end;2. Configure the server withRaiseError = Falseand withoutOnServerError:
FServer.RaiseError := False;
// FServer.OnServerError := nil; (by default)3. Call from the client:
FClient.Post('api/test', nil);4. Result: Access Violation on the client atRALSynopseClient.pasline 148.
Expected Behavior
- The server should send an HTTP 500 (Internal Server Error) response with a JSON error when an exception occurs.
- The client should handle error responses without a body without causing an Access Violation.
SetAsStreamshould correctly handle thenilcase.
Proposed Solution
Fix 1: RALSynopseServer.pas
except
on e: exception do
begin
if Assigned(OnServerError) then
OnServerError(e)
else if RaiseError then
raise
else
begin
// Send HTTP 500 error response to client
if vResponse <> nil then
begin
vResponse.StatusCode := HTTP_InternalError;
vResponse.ContentType := rctAPPLICATIONJSON;
vResponse.ResponseText := Format('{"Status":"ERROR","Message":"%s"}',
[StringReplace(e.Message, '"', '"', [rfReplaceAll])]);
AContext.OutContent := vResponse.ResponseText;
AContext.OutContentType := vResponse.ContentType;
AContext.OutCustomHeaders := 'Content-Type: ' + string(vResponse.ContentType) + HTTPLineBreak;
Result := HTTP_InternalError;
end;
end;
end;
end
;### Fix 2: RALSynopseClient.pas
var vBodyParam := AResponse.ParamByName('ral_body');
if vBodyParam <> nil then
AResponse.Params.AddParam('Stream', vBodyParam.AsStream, rpkBODY);
Fix 3: RALParams.pas
procedure TRALParam.SetAsStream(const AValue: TStream);
begin
if FContent <> nil then
FreeAndNil(FContent);
if AValue <> nil then
begin
AValue.Position := 0;
FContent := TRALStringStream.Create(AValue);
FContent.Position := 0;
end
else
FContent := nil;
end;---
Environment
- Delphi: D10
- Engine: mORMot2 (Synopse)
- OS: Windows 10
Priority
High — causes Access Violation and blocks production use.
Steps to reproduce the bug
In bug description
Expected behavior
No response
Pictures of the issue
No response
Referral
No response