Indy 10 TIdHttpServer missing MaxConnectionReply property - delphi

I am using Indy10 and have created a Web server using a class derived from TIdHttpServer. In my subclass I override the DoMaxConnectionsExceeded method. And this seems to be properly firing when MaxConnections is exceeded.
In earlier Indy versions, at least according to Remy Lebeau's comment here, there was MaxConnectionReply property on TIdHttpServer. This could be used to create custom messages if MaxConnections was exceeded. This doesn't seem to be the case with Indy 10.
Is there a way with Indy10 that you can create custom messages when MaxConnections is exceeded?

As I stated in the thread you linked to, MaxConnectionReply is implemented by TIdCmdTCPServer, which TIdHTTPServer does not derive from. Since you are overriding DoMaxConnectionsExceeded(), you will have to send your own reply to the client manually, and make sure it is properly HTTP-formatted, for example:
procedure TMyHttpServer.DoMaxConnectionsExceeded(AIOHandler: TIdIOHandler);
var
Html: TIdBytes;
begin
Html := ToBytes('<html><body>500 - Too many connections</body></html>');
AIOHandler.WriteLn('HTTP/1.0 500 Too many connections');
AIOHandler.WriteLn('Content-Type: text/html');
AIOHandler.WriteLn('Content-Length: ' + IntToStr(Html));
AIOHandler.WriteLn('Connection: close');
AIOHandler.WriteLn;
AIOHandler.Write(Html);
end;

Related

Delphi Indy 10 IdHTTPProxyServer Modifying Headers

I am trying to develop a CORS proxy server in Delphi XE2 using Indy 10 so that I can get around the issue of embedding sites into an IFrame where sites have added X-Frame-Options to the response headers.
Can anyone give me some example code as to how I can use IdHTTPProxyServer to do this?
When using TIdHTTPProxyServer, you can modify HTTP headers for GET/POST/HEAD requests only, in the following events:
OnHTTPBeforeCommand event (client headers)
OnHTTPResponse event (server headers)
OnHTTPDocument event (client or server headers, depending on the TIdHTTPProxyServerContext.TransferSource property) when the proxy's DefaultTransferMode property is set to tmFullDocument.
The headers are stored in the Headers property of the TIdHTTPProxyServerContext object provided to each event.
For example, using the OnHTTPResponse event, you can easily remove an X-Frame-Options header, eg:
procedure TMyForm.IdHTTPProxyServer1HTTPResponse(AContext: TIdHTTPProxyServerContext);
var
I: Integer;
begin
I := AContext.Headers.IndexOfName('X-Frame-Options');
if I <> -1 then
AContext.Headers.Delete(I);
end;
Or:
procedure TMyForm.IdHTTPProxyServer1HTTPResponse(AContext: TIdHTTPProxyServerContext);
begin
AContext.Headers.Values['X-Frame-Options'] := '';
end;
For the ProcessHTTPRequest - Resp: TIdHTTPResponseInfo - you can add this line to set the http header response:
Resp.CustomHeaders.Values['X-Frame-Options'] := 'Deny';
Works with the normal indy http server, should work with the proxy as well.
In the same way you can set it to empty, or other values.
I came upon your post looking for a way to "add" it - i'm hoping this answer helps others who are looking for similar information, even if the actual answer isn't exactly what you're looking for in the OP.

Access Request Header in Delphi XE3 DataSnap Server

I am implementing a REST server API in Delphi XE3 (first time using Delphi in about a decade so am a bit rusty). Currently it is using Indy server for debug purposes, but eventually it will be an ISAPI dll.
Now I have implemented a number of TDSServerClass classes and want to access the request header within the class methods. So for example when the user requests mysite.com/datasnap/rest/foo/bar I want to be able to read the header within the foo class method called bar. Is this possible?
If not, is it possible to create a global filter of incoming requests before they get to the REST class method? I need to check the API key and user authentication on incoming requests and not sure the best way to implement. Thanks.
I don't know if anything changed in XE3, but in XE2 you can do the following:
uses
Web.HTTPApp,
Datasnap.DSHTTPWebBroker;
function TServerMethods1.EchoString(Value: string): string;
var
Module: TWebModule;
begin
Module := GetDataSnapWebModule;
Result := Module.Request.RemoteIP + ': ' + Value;
end;

DataSnap Limits Inbound Request to 16k

I built a DataSnap server with Delphi XE2 that implements TDSHTTPService. When the inbound request comes in, TIdIOHandler.InitComponent is called in a thread before execution is handed to the method called in TServerMethods. I do not have any Indy components in the server, so DataSnap is using Indy 10 under-the-hood.
.InitComponent() sets the IO handler's max line length to a hard-coded value (FMaxLineLength := IdMaxLineLengthDefault;), which is 16384. I can't find a way to increase the value. I even tried copying the IdIOHandler Unit to the project folder and changing the constant value. But it still picks up the IdIOHandler.dcu from the Indy 10 build, and ignores the copied file in my project folder. I also tried adding a TIdIOHandlerStream component to the server project and setting its MaxLineLength to no avail.
Plan A = Properly set the MaxLineLength value in the DataSnap server.
Plan B = Somehow compile a modified IdIOHandler.pas file into my project.
Are either of these possible? I've been working on this for hours and can't find anything similar in all my searching, and can't seem to make any headway by experimenting.
After recompiling all Indy packages in Delphi XE3, having changed the IdMaxLineLengthDefault constant to 512 * 1024, and working as expected after that, I began searching for the simplest solution to this problem. So, I found out this an easy workaround for this limit.
You can implement a procedure for the OnContextCreated event of the TIdHTTPWebBrokerBridge object used in the main unit of the DataSnap REST Server project. In that event, the AContext object is received, which is created for each request to the DataSnap server. So, in the code for this procedure you just have to override the default value for this property as follows:
procedure TForm1.FormCreate(Sender: TObject);
begin
FServer := TIdHTTPWebBrokerBridge.Create(Self);
{Here you assign the new procedure for this event}
FServer.OnContextCreated:= OnContextCreated;
end;
procedure TForm1.OnContextCreated(AContext: TIdContext);
begin
AContext.Connection.IOHandler.MaxLineLength:= 512*1024 {or whatever value you need);
end;
Short of removing the Delphi XE2 install of Indy 10 and downloading the source, tweaking the constant values and compiling / maintaining my own build forever going forward..., I solve the issue.
I created an additional method in the DataSnap server so that I could create a record in the database with a call to the first method, and then incrementally stream the rest of the data by passing it to the second method 16k at a time -- buffering it in the DataSnap server until all parts are received. Then I update the record in the database with the fully buffered value from the DataSnap server.
Maybe not the most effective solution, but it works and it will scale as needed.

TIdHTTP in Indy 10

I used to use Indy back in the Delphi 6 days, and I am playing with Indy 10 now. What I want to do is incredibly simple, but I don't see a simple way of doing it, so I must be missing something.
What I want to do is something like this:
Here is the actual code I am using:
procedure TForm1.btnGetURLClick(Sender: TObject);
begin
moHeader.Lines.Clear;
moBody.Lines.Clear;
try
moBody.text := IdHttp1.Get(edURL.text);
finally
end;
end;
When the request is complete, the http_result should contain the HTML from the URL specified. This doesn't seem to work however, so I get the feeling I should perhaps be using the IOHandler property or the OnWork event of the component - however the usage doesn't seem obvious to me, and I couldn't find any working examples with google. I am sure this is something that has been done before, so any help would be appreciated.
Additional Information:
In the spirit of being more specific, I want to know:
1. Am I doing this right to begin with (or did I miss something?).
2. If so, why might it not be working.
3. It is always possible that there is a bug in the combination of compiler/os/Indy I am using. (Although it should be working).
I should mention, I always get a popup "Connection Closed Gracefully". This seems to be an exception, and it could be interfering with the result of the function. I attempted to trap this with a TRY...FINALLY, but it doesn't work. Probably because Indy is triggering the exception in the background after the Get method runs I suppose.
Finally, here is a screencast of the program running to clear up any confusion:
http://screencast.com/t/NDMzNTQ5
I expect the HTML to fill the second memo box.
i think you have the TIdHTTP.HandleRedirects property set to false, if you get the error "HTTP/1.1 302 Found" you can try this
var
http_result:string;
Begin
IdHTTP1.HandleRedirects:=True;
http_result := IdHTTP1.Get('http://www.google.com');
End;
Another option, would be to use synapse. This is all that is needed to retrieve a webpage using this library:
uses
...,HTTPSEND;
var
Result : TStrings;
if HTTPGetText('http://www.google.com',Result) then
// do something with result
Synapse is a lightweight TCPIP library. The library is being actively maintained and the current version runs fine in Delphi 2009/2010. It is NOT a component based framework, so it is very easy to use with other threading techniques (OmniThreadLibrary or AsyncCalls for example).
You have to set the property HandleRedirects to true.
There's no need for a form, using GExperts components to code I got this:
var
IdHTTP: TIdHTTP;
IdHTTP := TIdHTTP.Create(Self);
with IdHTTP do
begin
Name := 'IdHTTP';
AllowCookies := True;
HandleRedirects := True;
HTTPOptions := [hoForceEncodeParams];
end;
Just paste this in your unit, it should be all you need.
Iirc if the website redirects, you also need to override some handler (onredirect or so). But this was also the case in indy9 iirc.
This question has lingered open for quite some time, so I am closing it out. My solution was to just use Synapse, as one of the posters suggested. It works on windows/Linux/Mac OS with minimal modifications, and works fine in libraries/threads.

Delphi Indy IdTcpClient read operation returning truncated data for one specific request

This is an interesting problem that I’ve not been able to solve yet.
I am writing a client that communicates across the Internet to a server. I am using the TIdTcpClient Internet Direct (Indy) component in Indy 10 using RAD Studio 2007 native personality.
To get data from the server, I issue an HTTP request using SSL over port 443 where my request details are contained in the HTTP message body. So far, so good. The code works like charm, with one exception.
There is one request that I am submitting that should produce a response of about 336 KB from the server (the HTTP response header contains Content-Length: 344795). The problem is that I am getting only 320KB back. The response, which is in XML, is clearly truncated in the middle of an XML element.
For what it’s worth, the XML is simple text. There are no special characters that can account for the truncation. My TIdTcpClient component is simply reporting that, after receiving the partial response, that the server closed the connection gracefully (which is how every response is expected to be completed, even those that are not truncated, so this is not a problem).
I can make nearly identical calls to the same server where the response is also more than a few K bytes, and all of these work just fine. One request I make returns about 850 KB, another returns about 300 KB, and so on.
In short, I encounter this problem only with the one specific request. All other requests, of which there are many, receive a complete response.
I have talked to the creator of the service, and have supplied examples of my request. He reported that the request is correct. He also told me that when he issues my same request to his server that he gets a complete response.
I’m at a loss. Either the creator of the service is mistaken, and there is actually a problem with the response on that end, or there is something peculiar about my request.
Is there a solution here that I'm missing? Note that I’ve also used a number of other read mechanisms (ReadString, ReadStrings, ReadBytes, etc) and all produce the same result, a truncation of this one specific response at the 320KB mark.
The code is probably not relevant, but I’ll include it anyway. Sorry, but I cannot include the XML request, as it includes proprietary information. (ReadTimeout is set to 20 seconds, but the request returns in about 1 second, so it's not a timeout issue.)
function TClient.GetResponse(PayloadCDS: TClientDataSet): String;
var
s: String;
begin
try
try
s := GetBody(PayloadCDS);
IdTcpClient1.Host := Host;
IdTcpClient1.Port := StrToInt(Port);
IdTcpClient1.ReadTimeout := ReadTimeout;
IdTcpClient1.Connect;
IdTcpClient1.IOHandler.LargeStream := True;
//IdTcpClient1.IOHandler.RecvBufferSize := 2000000;
IdTcpClient1.IOHandler.Write(s);
Result := IdTcpClient1.IOHandler.AllData;
except
on E: EIdConnClosedGracefully do
begin
//eat the exception
end;
on e: Exception do
begin
raise;
end;
end;
finally
if IdTcpClient1.Connected then
IdTcpClient1.Disconnect;
end;
end;
Since you are sending an HTTP request, you should be using the TIdHTTP component instead of the TIdTCPClient component directly. There are a lot of details about the HTTP protocol that TIdHTTP manages for you that you would have to handle manually if you continue using TIdTCPClient directly.
If you are going to continue using TIdTCPClient directly, then you should at least stop using the TIdIOHandler.AllData() method. Extract the 'Content-Length' reply header (you can Capture() the headers into a TStringList or TIdHeaderList and then use its Values[] property) and then pass the actual reported byte count to either TIdIOHandler.ReadString() or TIdIOHandler.ReadStream(). That will help ensure that the reading I/O does not stop reading prematurely because of the server's disconnect at the end of the reply.
As I mentioned in my original question, this connection uses SSL. This requires that you use an IdSSLIOHandlerSocketOpenSSL component, which you assign to the IOHandler property of the IdTcpClient component. All of this was in my original design, and as I mentioned, many of my requests were being responded to correctly.
Over the weekend I discovered another request which was returning an incomplete response. I captured that original HTTP request, and using a tool called SOAP UI, I submitted that request to the server. Using SOAP UI, the server returned a complete answer. Clearly, something was wrong with my client, and not the server.
I finally found the solution by just fiddling around with various properties. The property that finally corrrected the problem was in the SSLOptions property of the IdSSLIOHandlerSocketOpenSSL class. The default value of SSLOptions.Method is sslvSSLv2. When I changed Method to sslvSSLv23, all responses returned complete.
Why I was able to retrieve some responses in full and not all before I made this change is still a mystery to me. Nonetheless, setting IdSSLIOHandlerSocketOpenSSL.SSLOptions.Method to sslvSSLv23 solved my problem.
Thank you Remy Lebeau, for your suggestions.

Resources