Unspecified certificate from client when using TRESTRequest - rest

When calling one of our Internal servers with SSL URL, TRESTClient and TRESTRequest gives and error
first from Exception Class ENetHTTPCertificateException and then from ERESTException with the message 'Unspecified certificate from client'
I know that WinInet Api is being used behind, but here is no native way in the Delphi Rest Library to set the connection to Ignore Certificate Errors
- How can I code that ?
or
Does this mean the server is requesting a client certificate ?
function TdmoRestApi.GetSaLogin(var sErrorText: String): TLoginSvar;
var
LoginRoot: TLoginRootClass;
begin
Result := lsErrVilla;
RESTRequestLogin.Params.ParameterByName('TOKEN').value := FLoginToken;
RESTRequestLogin.Params.ParameterByName('X-CSRF-Token').value := 'Fetch';
try
RESTRequestLogin.Execute; //This call fails
except
on e: Exception do begin
sErrorText := e.ClassName + ' ' + e.message;
if RESTResponseLogin.StatusCode = 0 then
Exit(lsErrVilla);
end;
end;
case RESTResponseLogin.StatusCode of
200: begin
....
400: begin
....
end;
else begin
....
end;
FCsrfToken := RESTResponseLogin.Headers.Values['X-CSRF-Token'];
FLoginSvar := Result;
end;

I found the solution to your problem. I had the same problem, but manage to ignore the certificate validation with this:
In the RESTClient1 component, set the following flag to false inside the event:
procedure TForm1.RESTClient1AuthEvent(const Sender: TObject; AnAuthTarget:
AuthTargetType; const ARealm, AURL: string; var AUserName, APassword:
string; ar AbortAuth: Boolean; var Persistence: TAuthPersistenceType);
begin
AbortAuth = True;
end;

Related

TRestRequest.Execute exits thread when responses code is 300 or higher

I need to check specific status codes from a RestResponse however the RestRequest.Execute seems to generate a EHTTPProtocolException when the status codes are not 200.
I did take a look at the documentation and it states that The request execution does not raise HTTP protocol exceptions. To verify the response status, check the TCustomRESTResponse.StatusCode property.
I'm having some trouble understanding why a EHTTPProtocolException gets raised. How can I ignore this or suppress it? I did check if it's just the debugger but the code stops at execute even in a Release build and when running standalone.
My code is as follows:
procedure TdmWebsockets.GetRustMapURL(const Connection: TsgcWSConnection; const Data: string);
begin
var jdata := TJSONObject.ParseJSONValue(Data);
var restClient := TRESTClient.Create(Self);
var restResponse := TRESTResponse.Create(Self);
var restRequest := TRESTRequest.Create(Self);
try
var isStaging := jdata.GetValue<Boolean>('data.staging');
var isBarren := jdata.GetValue<Boolean>('data.barren');
var seed := jdata.GetValue<Int32>('data.seed');
var size := jdata.GetValue<Int32>('data.size');
restRequest.Client := restClient;
restRequest.Response := restResponse;
restClient.BaseURL := 'https://rustmaps.com/api/v2/maps/' + seed.ToString + '/' + size.ToString;
restClient.RaiseExceptionOn500 := False;
restRequest.AddParameter('staging', BoolToStr(isStaging, True));
restRequest.AddParameter('barren', BoolToStr(isBarren, True));
restRequest.Params.AddHeader('X-API-Key', 'REDACTED');
restRequest.Method := TRESTRequestMethod.rmPOST;
Writeln('before');
restRequest.Execute;
Writeln('after');
var Writer: TJsonTextWriter;
var StringWriter: TStringWriter;
try
StringWriter := TStringWriter.Create();
Writer := TJsonTextWriter.Create(StringWriter);
Writer.Formatting := TJsonFormatting.Indented;
Writer.WriteStartObject;
/////////////////////////////////////////////
Writer.WritePropertyName('command');
Writer.WriteValue(COMMAND_GETMAP);
/////////////////////////////////////////////
if restResponse.Status.SuccessOK_200 or restResponse.Status.ClientErrorDuplicate_409 then
begin
Writeln('200 ok');
Writer.WritePropertyName('success');
Writer.WriteValue(True);
/////////////////////////////////////////////
var mapID := restResponse.JSONValue.GetValue<string>('mapId');
Writer.WritePropertyName('mapID');
Writer.WriteValue(mapID);
/////////////////////////////////////////////
end
else if restResponse.Status.ClientErrorBadRequest_400 then
begin
Writeln('400 ok');
/////////////////////////////////////////////
Writer.WritePropertyName('success');
Writer.WriteValue(False);
/////////////////////////////////////////////
var reason := restResponse.JSONValue.GetValue<string>('reason');
Writer.WritePropertyName('reason');
Writer.WriteValue(reason);
/////////////////////////////////////////////
end;
Writer.WriteEndObject;
Writeln(StringWriter.ToString);
Connection.WriteData(StringWriter.ToString);
finally
StringWriter.Free;
Writer.Free;
end;
finally
restRequest.Free;
restResponse.Free;
restClient.Free;
jdata.Free;
end;
end;
EDIT:
When clicking on the "Break" button on the exception window it takes me to the Execute method in Rest.HttpClient despite it not being added to the uses. Stepping it shows that it's executing in Rest.HttpClient and not in Rest.Client.
When trying to use try .. except I am required to Rest.HttpClient to my uses to get the exception type EHTTPProtocolException. When using try .. except it still throws out the exception. I understand the debugger will still catch it but the code never runs in the try .. except and just stops after the exception thows.
IF the exception is escaping TRestRequest.Execute() into your code, then just catch it with a try..except. EHTTPProtocolException has an ErrorCode property you can look at, if needed.
A try..except does not prevent an exception from being raised at all. The debugger will still show the exception being raised, unless you configure the debugger to ignore it. But your code will be able to catch the exception and process it as needed. If the debugger breaks on the exception, simply press the "Run" button on the IDE, or F9 on the keyboard, to continue execution, and the exception will be passed to the nearest matching exception handler in your code.
For example:
try
restRequest.Execute;
except
// swallow ALL exceptions...
end;
And just move on, no need to update the uses clause - unless you want the code to ignore non-EHTTPProtocolException exceptions and let them propagate up the call stack (as you should), in which case you can use this instead:
uses
..., REST.HttpClient;
try
restRequest.Execute;
except
on E: Exception do begin
if not (E is EHTTPProtocolException) then begin
// DON'T swallow unknown non-HTTP errors...
raise;
end;
// swallow HTTP errors and let subsequent code check the status...
end;
end;
Or:
uses
..., REST.HttpClient;
try
restRequest.Execute;
except
on E: EHTTPProtocolException do begin
// swallow HTTP errors and let subsequent code check the status...
end;
on E: Exception do begin
// DON'T swallow unknown non-HTTP errors...
raise;
end;
end;
Or:
uses
..., REST.HttpClient;
try
restRequest.Execute;
except
on E: EHTTPProtocolException do;
// swallow only HTTP errors and let subsequent code check the status...
end;
However, IF the exception is not escaping TRestRequest.Execute() into your code, then the issue is moot. The exception will be handled inside of Execute(), no need for a try..except in your code at all. If you don't want to see the exception while debugging your code, then simply configure the debugger to ignore EHTTPProtocolException and move on.
I misunderstood what the issue was and was focused on the HTTPException being thrown instead of the real issue. It seems that since my application is a console app and the NotifyEvent is noSync for the thread in sgcWebsockets the thread ended up exiting every time TCustomRESTClient.HandleEvent was called. In this case it got called and actually went through as restRequest.SynchronizedEvents is set to true by default but should be false.
So the solution was to simply do:
restRequest.SynchronizedEvents := False;

TStream with Datasnap Rest and Close-session

I build a datasnap REST application and I can't close the session.
For example if I call my URL 4 times in my web browser:
http://127.0.0.1:4445/REST/image?json=false
There is 4 sessions opened.
Here is my function:
function TV1.TestImage:TStream;
var
ContentType : String;
FileStream : TFileStream;
begin
FileStream := TFileStream.Create('C:\santiago.jpg', fmOpenRead or fmShareDenyNone);
ContentType := 'image/jpg';
GetInvocationMetadata.ResponseContentType := ContentType;
ContentTypeHeaderToUse := ContentType;
GetInvocationMetadata().ResponseCode := 200;
result := FileStream;
//Can't call CloseSession with TStream, pointer exception
//GetInvocationMetaData.CloseSession := True;
end;
And I use the WebModuleAfterDispatch like that:
procedure TWebModule2.WebModuleAfterDispatch(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var session : TDSSession;
begin
response.FreeContentStream := true;
if ContentTypeHeaderToUse<>'' then begin
Response.ContentType := ContentTypeHeaderToUse;
ContentTypeHeaderToUse := ''; // Reset global variable
end;
end;
As you can see, GetInvocationMetaData.CloseSession := True; can't be used here.
Do you know how to close session after sending the TStream?
On a REST server I prefer to return a base64 string, it's easier to deal for the client app than the JSON representation of a Delphi TStream (an array of bytes).
The EncodeBase64 function on the Soap.EncdDecd unit does the transformation very easily :
function TV1.TestImage:string;
var MemoryStream: TMemoryStream;
begin
MemoryStream := TMemoryStream.Create;
MemoryStream.LoadFromFile('C:\santiago.jpg');
Result := EncodeBase64(MemoryStream.Memory, MemoryStream.Size);
MemoryStream.Free; // Now you can free the Stream yourself
end;
An additional benefit is that you can now release your Stream and not depend on Datasnap doing it. So you should be able to manually close your connection without that pointer exception and solve the problem of multiple sessions remaining open.
You can directly use that base64 image string on your web application. You only need to add their type at the start of an < img > tag.
Example :
<img src="data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MEVBMTczNDg3QzA5MTFFNjk3ODM5NjQyRjE2RjA3QTkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MEVBMTczNDk3QzA5MTFFNjk3ODM5NjQyRjE2RjA3QTkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowRUExNzM0NjdDMDkxMUU2OTc4Mzk2NDJGMTZGMDdBOSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowRUExNzM0NzdDMDkxMUU2OTc4Mzk2NDJGMTZGMDdBOSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PjjUmssAAAGASURBVHjatJaxTsMwEIbpIzDA6FaMMPYJkDKzVYU+QFeEGPIKfYU8AETkCYI6wANkZQwIKRNDB1hA0Jrf0rk6WXZ8BvWkb4kv99vn89kDrfVexBSYgVNwDA7AN+jAK3gEd+AlGMGIBFDgFvzouK3JV/lihQTOwLtOtw9wIRG5pJn91Tbgqk9kSk7GViADrTD4HCyZ0NQnomi51sb0fUyCMQEbp2WpU67IjfNjwcYyoUDhjJVcZBjYBy40j4wXgaobWoe8Z6Y80CJBwFpunepIzt2AUgFjtXXshNXjVmMh+K+zzp/CMs0CqeuzrxSRpbOKfdCkiMTS1VBQ41uxMyQR2qbrXiiwYN3ACh1FDmsdK2Eu4J6Tlo31dYVtCY88h5ELZIJJ+IRMzBHfyJINrigNkt5VsRiub9nXICdsYyVd2NcVvA3ScE5t2rb5JuEeyZnAhmLt9NK63vX1O5Pe8XaPSuGq1uTrfUgMEp9EJ+CQvr+BJ/AAKvAcCiAR+bf9CjAAluzmdX4AEIIAAAAASUVORK5CYII=">

how to view postgresql "raise notice" from go app

I'm writting small app in go. And now I need to log postgres "raise notice" message in app console.
I was not found where postgresql raised messages stored in *sql.Conn (I'm use go/sql with lib/pq driver)
example,
create function show_mes() returns int as $$
begin
raise notice 'test message from pg';
return 1;
end
$$ language plpgsql;
from go app I call this function and can get the result.
But how can i access to that messages "test message from pg" from go app?
In current version written in Node we log messages in console for debbugging:
thank you!
If you're using lib/pq you can use its LISTEN/NOTIFY support to achieve what you want.
On the Go side you can use something like this:
package main
import (
"log"
"time"
"github.com/lib/pq"
)
func main() {
dburl := "postgres:///?sslmode=disable"
li := pq.NewListener(dburl, 10*time.Second, time.Minute, func(event pq.ListenerEventType, err error) {
if err != nil {
log.Fatal(err)
}
})
if err := li.Listen("raise_notice"); err != nil {
panic(err)
}
for {
select {
case n := <-li.Notify:
// n.Extra contains the payload from the notification
log.Println("notification:", n.Extra)
case <-time.After(5 * time.Minute):
if err := li.Ping(); err != nil {
panic(err)
}
}
}
}
Then your postgres function would look like this:
CREATE FUNCTION show_mes() RETURNS int AS $$
BEGIN
PERFORM pg_notify('raise_notice', 'test message from pg');
RETURN 1;
END
$$ LANGUAGE plpgsql;
I have created example:
https://github.com/jackc/pgx/issues/838
with pgx driver
Call back function in connection for postgres messages

How to handle Socket error 10053 in SOAPServerwithINDYVCL in Delphi XE

I have created a SOAPServerwithINDYVCL in Delphi XE. When a clients gets disconnected from server due to any reason like timeout at client side or net connection dropped.
I am getting error at server side as Socket Error 10053 - software caused connection abort.
this error popsup and blocking my thread of server.
I also checked this withe the demo of SOAPDMServerIndy. Here also if i start server, connect client and before server responds disconnect the client I get socket error.
Anyone knows how to handle this exception?
Even at this time this issue still exists. It's because of this code:
procedure TWebRequestHandler.HandleException(Sender: TObject);
var
Handled: Boolean;
Intf: IWebExceptionHandler;
begin
Handled := False;
if ExceptObject is Exception and
Supports(Sender, IWebExceptionHandler, Intf) then
try
Intf.HandleException(Exception(ExceptObject), Handled);
except
Handled := True;
System.SysUtils.ShowException(ExceptObject, ExceptAddr);
end;
if (not Handled) then
System.SysUtils.ShowException(ExceptObject, ExceptAddr);
end;
The line Intf.HandleException(Exception(ExceptObject), Handled) calls the TCustomWebDispatcher.HandleException procedure. This procedure checks for the existance of a OnException event handler in the TWebModule descendant in your application. If it exists (I would recommend it) the event handler procedure is called with the var argument 'Handled' set to true.
procedure TCustomWebDispatcher.HandleException(E: Exception; var Handled: Boolean);
begin
Handled := False;
if Assigned(FOnException) then
begin
Handled := True;
if Response <> nil then
Response.StatusCode := 500; { Allow the user to override the StatusCode }
FOnException(Self, E, Handled);
if Handled and (Response <> nil) then
Response.SendResponse;
end;
if not Handled then
if Response <> nil then
begin
Response.Content := Format(sInternalApplicationError, [E.Message, Request.PathInfo]);
if not Response.Sent then
begin
Response.StatusCode := 500;
Response.SendResponse;
end;
Handled := True;
end;
end;
When the handler returns there are two situations:
Handled is True; The response will be sent which leads to an new Exception. This one will be propagated back to the calling code (the procedure in the first block) so ShowException will be called.
Handled is False; Response <> nil will evaluate to True so when Response.Sent is False the same thing will happen.
I solved this by trying to send the response from the OnException event handler. When it fails an exception is raised. In that case I'm setting the Field 'FResponse' of the read only property 'Response' of my TWebModule's descendant to 'nil'. However, this field is a private field so I used some rtti to be able to access it.
procedure TwbmWebBrokerServerModule.UnlinkResponseObject;
begin
inherited;
TRTTIContext.Create.GetType(ClassInfo).GetField('FResponse').SetValue(Self, TValue.From(nil));
end;
procedure TwbmWebBrokerServerModule.WebModuleException(Sender: TObject; E: Exception; var Handled: Boolean);
begin
uVMI_Tools.AppendToLog(E.Message + ' (' + Request.PathInfo + ')');
if Request.Accept.Contains(CONTENTTYPE_APPLICATION_JSON) then
begin
Response.Content := StringToJSON(E.Message);
Response.ContentType := CONTENTTYPE_APPLICATION_JSON;
end
else
Response.Content := E.Message;
try
Response.SendResponse;
except
UnlinkResponseObject;
end;
end;
So when an exception occurs while sending the response, the property 'Response' is set to nil so there will be no attempt to send the response again.
There is no need to destroy the response object, assinging nil to it will suffice. It's because the Response object is created within TIdCustomHTTPServer class which just passed it to the TWebModule descendant. (Actually to the TWebModule's parent class TCustomWebDispatcher)

Connection to PostgreSQL using PL/Java in a trigger fails

I defined a trigger in PostgreSQL 9.1 that should fire whenever a record is updated using:
CREATE OR REPLACE FUNCTION check() RETURNS TRIGGER AS $$
BEGIN
SELECT doubletEngine.checkForDoublet(NEW.id);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS check_upd on people;
CREATE TRIGGER check_upd AFTER UPDATE ON people
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE PROCEDURE check();
The method doubletEngine.checkForDoublet() was introduced to PL/Java 1.4.3 using
CREATE OR REPLACE FUNCTION doubletEngine.checkForDoublet(varchar) RETURNS void AS 'DoubletSearcher.checkForDoublet' LANGUAGE java;
Inside the method checkForDoublet(), I try to connect to the database using
Class.forName("org.postgresql.Driver");
String url = "jdbc:postgresql://127.0.0.1:5432/db";
c = DriverManager.getConnection(url, "postgres", "postgres");
if ( c == null ) {
throw new RuntimeException("Could not connect to server");
}
...but c is still null. Calling this code directly from my IDE works perfectly, but when the trigger fires, it only throws the RuntimeException. Is there anything I miss...???
According to the PL/Java documentation the connection should be obtained as follows:
Connection conn = DriverManager.getConnection("jdbc:default:connection");