Skip to content

[Bug]: Access Violation in client when server raises exceptions in route handlers #117

@macc2010

Description

@macc2010

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

  1. 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 with RaiseError = False and without OnServerError:
    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 at RALSynopseClient.pas line 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.
  • SetAsStream should correctly handle the nil case.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    ide: DelphiIssues that impact the Delphi IDEos: WindowsIssues that happens on Windowspri: HighHighest Priority for issues that are confirmed bugsrev: OngoingWhen a reported issue was able to be reproduced and is currently being worked onsec: APIIssues that happen with the API feature of the packageseg: ClientIssues that happen on the Client side of the packageseg: ServerIssues that happen on the Server side of the packagesev: CriticalThe degree on which the issue impacts functionality of the packagevcs: ErrorWhen the issue is a bug

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions