Using GitHub API from Indy in Delphi - rest

I'm working on a project in Delphi 10.1 Berlin which integrates with GitHub. This project intends to download repositories via ZIP files. However, I'm facing some issues.
Originally, I chose (as always) to use Indy to integrate with the GitHub API. I've always used Indy for all web API consumption. However, I'm not having any success using it with GitHub.
The API requires HTTPS. I have obtained the latest OpenSSL DLLs for use with Indy, and am using the Indy library with Delphi 10.1 Berlin.
I have setup a TIdHTTP component with a TIdSSLIOHandlerSocketOpenSSL attached. I've set that IO Handler to all the Method options available, and not one gives me a valid response. I get one of two different responses...
When using sslvSSLv3, I get: error:14094410:SSL routines: SSL3_READ_BYTES:sslv3 alert handshake failure.
When using sslvTLSv1_2, I get: error:1409442E:SSL routines:SSL_READ_BYTES:tlsv1 alert protocol version
I had to resort to TRESTClient for now just to be able to work with the API. But it doesn't handle binary properly.
The test is as simple as https://api.github.com. I can call that in Chrome and Postman, and get a response. Just not via Indy.
How can I accomplish a connection with GitHub's API via Delphi's Indy library?

This Indy HTTP subclass works with the GitHub API.
type
TIndyHttpTransport = class(TIdCustomHTTP)
public
constructor Create;
end;
implementation
uses
IdSSLOpenSSL;
{ TIndyHttpTransport }
constructor TIndyHttpTransport.Create;
var
SSLIO: TIdSSLIOHandlerSocketOpenSSL;
begin
inherited Create;
HTTPOptions := HTTPOptions + [hoNoProtocolErrorException, hoWantProtocolErrorContent];
SSLIO := TIdSSLIOHandlerSocketOpenSSL.Create(Self);
SSLIO.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
SSLIO.SSLOptions.Mode := sslmClient;
SSLIO.SSLOptions.VerifyMode := [];
SSLIO.SSLOptions.VerifyDepth := 0;
Self.IOHandler := SSLIO;
// Request.UserAgent := 'my useragent string';
end;
Maybe your code also needs to modify the user agent string (because I use this code with different service providers, not only GitHub. Some of them require a modified user agent string instead of the default).
Source: https://github.com/michaelJustin/daraja-framework/blob/master/demo/common/IndyHttpTransport.pas

Related

Updating Sharepoint metadata using REST api gives 400 bad request error

I am trying to update a Sharepoint file's metadata using the Indy TidHTTP component in my Delphi program.
Within this program I have successfully managed to do everything else I need to do with the Sharepoint files (create/rename/delete/edit/Checkout etc). But whenever I try to update some metadata property I get a "400 Bad Request" exception.
I know that my RequestDigest string is correct because I am able to upload new contents to the file.
I have seen many similar problems reported and have tried all of the suggestions without success.
(One suggestion was to access the file as a list item rather than using the file url, which I have done in the code below.)
var
str,body,MyUrl: string;
stream: TStringStream;
begin
MyUrl:=MySite+'_api/web/Lists/getByTitle(''CM Library'')/Items(2)';
body:='{"__metadata":{"type":"SP.Data.CM_x0020_LibraryItem"},"Title":"UpdatedTitle"}';
stream := TStringStream.Create(body, TEncoding.UTF8);
try
IdHTTP.Request.Accept:='application/json;odata=verbose';
IdHTTP.Request.ContentType:='application/json';
IdHTTP.Request.ContentLength:=stream.size;
IdHTTP.Request.CustomHeaders.Values['X-RequestDigest']:=RequestDigest;
IdHTTP.Request.CustomHeaders.Values['IF-MATCH']:='*';
IdHTTP.Request.CustomHeaders.Values['X-HTTP-Method']:='MERGE';
try
str := IdHTTP.Post(MyUrl,stream);
except
on E : Exception do
ShowMessage('Exception: '+E.Message);
end;
finally
stream.Free;
end;
// Displays 'Exception: HTTP/1.1 400 Bad Request'
I found it! I needed to add ";odata=verbose" to Request.ContentType (as well as Request.Accept)
Sorry to trouble the community, but I was struggling with this for several days!!

REST-Server proper place for authentication

I have a REST-Client Standalone Desktop-Application in which I can enter a URL and can choose what kind of REST Method I want to execute (POST, GET, PUT etc.)
the client primarily serves the function to send a Body-Message (XML-Format). inside the message all the necessary information to handle the message is contained.
the client also has a Basic authenticator so each Request is also sending a Username and password.
With the help of the Delphi XE8 Application Wizard I created a simple Standalone Firemonkey REST-Server with Authentication and Authorization.
When I send a Request from the Client-Application then my Server is able to recieve that Request inside the "WebModuleDefaultAction"-function which has been added by the Wizard.
What I would like to know is wether or not this is the proper place to check the Authorization of the Request for legitimicy.
It would look like this:
procedure TWebModule1.WebModuleDefaultAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
s:string;
Username:string;
Password:String;
begin
s:=Copy(Request.Authorization,7,length(Request.Authorization)-6);
s:=TNetEncoding.Base64.Decode(s);
if (Username='JohnDoe') and (Password='MyPassword') then
begin
if (Request.InternalPathInfo='') or (Request.InternalPathInfo='/') then Response.Content:=ReverseString.Content
else if (Request.InternalPathInfo='ShowContent') or (Request.InternalPathInfo='/ShowContent') then Response.Content:=PP_ShowContent.Content
else Response.SendRedirect(Request.InternalScriptName + '/');
handled:=true;
end
else
begin
Response.Content:='Unauthorized';
handled:=false;
end;
end;
The user-check is static just for now, as the authorization is meant to be presented as a proof of concept.
However, if I do Authorization like this then I expect this function will grow way too big someday and I also miss out on ServerRoles. Unfortunately I am not versed enough in the topic of REST development to know what the proper way to do anything is.
I have seen that there is also an Authentication Manager that has been added by the Wizard. But as before I don't know what to do with it.
Try to use the component TDSAuthenticationManager connected with your TDSServer.
The component TDSAuthenticationManager has a event called AuthenticationManager that is called before create user session. If your variable "valid" is true in the end, the user will authenticate.
See this https://edn.embarcadero.com/br/article/41267
and this https://www.embarcadero.com/images/dm/technical-papers/rest-servers-in-delphi-xe-using-datasnap.pdf

Connect external language server to VSCode extension

I want to implement a VSCode extension that uses the Language Server Protocol, but I want the server component to be on an actual server (in the cloud), and not a part of the VSCode extension.
Can I set the client extension to connect to a server via websockets or HTTP?
Multiple ServerOptions are supported when you initialize a LanguageClient according to the signature of ServerOptions.
you can use the StreamInfo if you want to use a real remove server as your language server. Here is a sample code to connect to your server via WebSocket and initialize a LanguageClient.
const connection = connectToServer(hostname, path);
const client = new LanguageClient(
"docfxLanguageServer",
"Docfx Language Server",
() => Promise.resolve<StreamInfo>({
reader: connection,
writer: connection,
}),
{});
private connectToServer(hostname: string, path: string): Duplex {
const ws = new WebSocket(`ws://${hostname}/${path}`);
return WebSocket.createWebSocketStream(ws);
}
I am not sure if you can control the location of the language server, but there is another option. You do not need to implement the Language Server Protocol to, for example, provide parsing help. In that case you can implement your own convenient parsing service API (tailored to the nature of the language you want to support).
Within your extension you subscribe to workspace edit events using workspace.onDidChangeTextDocument
Re-start a 1sec timeout every time the file on-change event is raised
When the timeout expires without any further file modification, gather all relevant files and send them to your parsing server
In your extension, create a DiagnosticCollection using https://code.visualstudio.com/api/references/vscode-api#languages.createDiagnosticCollection and replace populate it with the warnings/errors/hints resulting from the parsing server in the cloud.
Subscribe to other workspace events, e.g. workspace.onDidOpenTextDocument or workspace.onDidCloseTextDocument in order to keep the DiagnosticCollection content relevant

Calling REST Web API using Delphi

I have an ASP.NET MVC Web API and I need to call it using Delphi 6. I am trying to use the Indy components (version 9.0.18), I am using the TIdHttp component.
I am using the REST methods, like POST to add, PUT to update and DELETE to delete the records. I got successfully adding, updating and getting my records, but I couldn't get successfully to call the DELETE method. It raises an error "HTTP/1.1 400 Bad Request".
I tried to debug the Web API, but it appers that the request didn't come, cause it doesn't stop in the breakpoint.
The Indy version that I am using doesn't have the method DELETE, so I tried to use the DoRequest method.
My code:
IdHTTP.DoRequest(hmDelete, 'http://localhost/myapp/api/user/1', nil, nil);
If I make the request using Fiddler it works, so my Web API is working well.
As an alternative to Indy, I suggest you use "Import Type Library..." from the Project menu, and select "Microsoft XML", the highest version available (I have versions 3 through 6 on the machine I'm currently on). Disable "Generate Component Wrapper", and then use the XMLHTTP component to make REST calls. E.g.:
uses ActiveX, MSXML2_TLB;
var
r:XMLHTTP;
begin
CoInitialize(nil);
r:=CoXMLHTTP.Create;
r.open('DELETE','http://localhost/myapp/api/user/1',false,'','');
//r.setRequestHeader(...
r.send(EmptyParam);
if r.status=200 then
So another answer is simple, use a COM-Object with flexible late binding, example of a REST translate service with language detection in GET or POST, implements in maXbox script:
function getPostTranslateLibre(feedstream: string): string;
var
Url,API_KEY, source: string;
jo, locate: TJSONObject;
httpReq,hr: Olevariant;
strm: TStringStream;
begin
httpReq:= CreateOleObject('WinHttp.WinHttpRequest.5.1');
// Open the HTTPs connection.
try
hr:= httpReq.Open('POST','https://libretranslate.pussthecat.org/detect', false);
httpReq.setRequestheader('user-agent',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0');
httpReq.setRequestheader('content-type','application/x-www-form-urlencoded');
//httpReq.setRequestheader('X-RapidAPI-Host','nlp- translation.p.rapidapi.com');
//httpReq.setRequestheader('X-RapidAPI-Key','...333');
if hr= S_OK then HttpReq.Send('q='+HTTPEncode(feedstream));
/// Send HTTP Post Request & get Responses.
If HttpReq.Status = 200 Then
result:= HttpReq.responseText
Else result:= 'Failed at getting response:'+itoa(HttpReq.Status)+HttpReq.responseText;
//writeln('debug response '+HttpReq.GetAllResponseHeaders);
finally
httpreq:= unassigned;
end;
end;

set targetNameSpace using Go

I am using Go for soap web service.
When I call the webservice I do like this.
request := fmt.Sprintf(para)
cli := new(http.Client)
//http.Header["http://webservice.ei"]="notify"
resp, err := cli.Post("http://127.0.0.1:8751/WebRoot/services/SecurtyWebService/notify?wsdl&targetNamespace=http://webservice.ei","text/xml;charset=utf-8",bytes.NewBufferString(request))
I get this error.
<faultstring>namespace mismatch require http://webservice.ei found none</faultstring>
I learn that the server require a targetNameSpace para. I also know that in java. People fix this by using:
QName opAddEntry = new QName("http://webservice.ei", "notify");
But I find there's no way for me to add this parameter.
Can anyone gime me some advice on how to set the namespace when calling the soap webservice in go?