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

542 views Asked by At

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;

......
1

There are 1 answers

3
Remy Lebeau On BEST ANSWER

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);
    }
}