Search code examples
delphic++builderindyindy10indy-9

Write the Indy 10 of Delphi Codes's to C ++ Builder's Indy 10


I am new to learn C ++ Builder. Three days ago, I installed Embarcadero®. C++Builder® 2010. This language is very interesting for me to learn.

In Delphi, I generally write a simple proxy-server using TIdMappedPortTCP of Indy 9 and 10. I usually use its OnExecute and OnOutboundData events to modify data as it passes through the proxy.

Since I'm new in C ++ Builder, so I don't know how to convert my Delphi code to the exactly right C ++ Builder code.

I've tried and tried many ways, including reading several books, one of which is Borland C ++ Builder - The Complete Reference, by Herbert Schildt, as well as to increase knowledge. Unfortunately, in the book was not discussed at all very important things related to my condition. Also, I find references on google, but I've not found.

So, I ventured to ask for your help. I really need it. Please help! Thank you very much.

The following is my Indy 10's Delphi code that I want to write to C ++ Builder.

......

procedure TForm.IdMappedPortTCP1Execute(AContext: TIdContext);
var
Mydata, NetData: string;
begin
  if (Pos('HTTP',netstring(AContext)) <> 0) or (Pos('GET',netstring(AContext)) <> 0) then begin
   NetData := netstring(AContext);
   TIdMappedPortContext(AContext).OutboundClient.IOHandler.Write(AddHeader(netstring(AContext),'Connection: Keep-Alive'));
     Sleep(1000);
     Mydata  := 'GET http://website.com/ HTTP/1.1'+#13#10+'Host: website.com'#13#10;
     NetData := Mydata + Netdata;
   TIdMappedPortContext(AContext).NetData := netbyte(Netdata);
   TIdMappedPortContext(AContext).OutboundClient.IOHandler.Write(netbyte(Mydata + NetData));
  end;
end;

......

Solution

  • A literal translation to C++Builder would look like this:

    ......
    
    String __fastcall AddHeader(String S, String Header)
    {
        S = StringReplace(S, "\r\n\r\n", "\r\n" + Header + "\r\n\r\n", TReplaceFlags() << rfReplaceAll);
        return S;
    }
    
    void __fastcall TForm::IdMappedPortTCP1Execute(TIdContext *AContext)
    {
        String Mydata, NetData;
    
        if ((netstring(AContext).Pos("HTTP") != 0) || (netstring(AContext).Pos("GET") != 0))
        {
            NetData = netstring(AContext);
            TIdMappedPortContext(AContext)->OutboundClient->IOHandler->Write(AddHeader(netstring(AContext), "Connection: Keep-Alive"));
            Sleep(1000);
            Mydata = "GET http://website.com/ HTTP/1.1\r\nHost: website.com\r\n";
            NetData = Mydata + Netdata;
            static_cast<TIdMappedPortContext*>(AContext)->NetData = netbyte(Netdata);
            static_cast<TIdMappedPortContext*>(AContext)->OutboundClient->IOHandler->Write(netbyte(Mydata + NetData));
        }
    }
    
    ......
    

    Here is a slightly condensed version:

    ......
    
    String __fastcall AddHeader(String S, String Header)
    {
        return StringReplace(S, "\r\n\r\n", "\r\n" + Header + "\r\n\r\n", TReplaceFlags() << rfReplaceAll);
    }
    
    void __fastcall TForm::IdMappedPortTCP1Execute(TIdContext *AContext)
    {
        String NetData = netstring(AContext);
        if ((NetData.Pos("HTTP") != 0) || (NetData.Pos("GET") != 0))
        {
            Sleep(1000);
            String Mydata = "GET http://website.com/ HTTP/1.1\r\nHost: website.com\r\n" + AddHeader(NetData, "Connection: Keep-Alive");
            static_cast<TIdMappedPortContext*>(AContext)->NetData = netbyte(Mydata);
        }
    }
    
    ......
    

    But either way, this is definitely NOT a reliable way to implement a viable HTTP proxy in Indy. In fact, Indy 10 introduced a specific TIdHTTPProxyServer component for that very purpose. You should seriously consider using that instead of TIdMappedPortTCP. For example, the above can be done in TIdHTTPProxyServer like this:

    class TIdHTTPProxyServerContextAccess : public TIdHTTPProxyServerContext
    {
    public:
        void SetCommand(String Value) { FCommand = Value; }
        void SetDocument(String Value) { FDocument = Value; }
        void SetTarget(String Value) { FTarget = Value; }
    };
    
    void __fastcall TForm1.IdHTTPProxyServer1HTTPBeforeCommand(TIdHTTPProxyServerContext *AContext)
    {
        static_cast<TIdHTTPProxyServerContextAccess*>(AContext)->SetCommand("GET");
        static_cast<TIdHTTPProxyServerContextAccess*>(AContext)->SetTarget ("http://website.com/");
        static_cast<TIdHTTPProxyServerContextAccess*>(AContext)->SetDocument("/");
    
        AContext->Headers->Values["Host"] = "website.com";
        AContext->Headers->Values["Connection"] = "Keep-Alive";
    
        /*
        the original code was not changing the Host/Port where the
        HTTP request was being sent to.  But if you needed to,
        you can do it like this... 
    
        static_cast<TIdTCPClient*>(AContext->OutboundClient)->Host = "website.com";
        static_cast<TIdTCPClient*>(AContext->OutboundClient)->Port = 80;
        */
    }
    

    Update: the netstring() and netbyte() functions you linked to have syntax errors, and have unnecessary overhead (there is no need to involve MIME just to convert a String into a byte array and vice versa, Indy has functions specifically for that purpose). Here are the corrected versions:

    String __fastcall netstring(TIdMappedPortContext* AContext)
    {
        return BytesToStringRaw(AContext->NetData);
    }
    
    TIdBytes __fastcall netbyte(String S)
    {
        return ToBytes(S, IndyTextEncoding_8Bit());
    }
    

    So, you could actually just eliminate the functions altogether:

    void __fastcall TForm::IdMappedPortTCP1Execute(TIdContext *AContext)
    {
        TIdMappedPortContext *ctx = static_cast<TIdMappedPortContext*>(AContext)
        String NetData = BytesToStringRaw(ctx->NetData);
        if ((NetData.Pos("HTTP") != 0) || (NetData.Pos("GET") != 0))
        {
            Sleep(1000);
            String Mydata = "GET http://website.com/ HTTP/1.1\r\nHost: website.com\r\n" + AddHeader(NetData, "Connection: Keep-Alive");
            ctx->NetData = ToBytes(Mydata);
        }
    }