Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Http Proxy Server for multiple http servers on same IP?


This question is answered.


Permlink Replies: 6 - Last Post: Jun 16, 2016 5:28 PM Last Post By: Ahmed Sayed
Ahmed Sayed

Posts: 173
Registered: 8/9/07
Http Proxy Server for multiple http servers on same IP?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 16, 2016 2:06 PM
Hello,

I am trying to use "TIdHTTPProxyServer" component
in a server that will act as a proxy and fail-over server
for other HTTP/Rest servers, and i can't understand
how to use this component.

The server app will have a config text file with its EXE that will
look like this:

/files=https://localhost:8081/datasnap/rest/TFileServer/
/db=https://localhost:8082/datasnap/rest/TDBServer/
...


The idea is if a client made a request for instance like this:

https://www.mysite.com/files/asdadasjhda.pdf

The proxy server should extract the first segment in the
url which is "files" and look it up in the config file and then
if it is found get its remote server http command and forward
the request to that server and it should look like this:

https://localhost:8081/datasnap/rest/TFileServer/asdadasjhda.pdf

But the url on the client browser must
not be altered in any way.

I thought about using a TIdHttpServer and TIdHttp and recreate
the request to the mapped server. But i got stuck with how to copy
"TIdHTTPRequestInfo" to TIdHTTP request property and the same for
both responses.

I don't know what is the name of what I am asking for?
Whether it is a reverse proxy or tunneling i hope someone
could help me with it.

Any help will be appreciated.
--
The limits of my language mean the limits of my world
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Http Proxy Server for multiple http servers on same IP?
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 16, 2016 3:25 PM   in response to: Ahmed Sayed in response to: Ahmed Sayed
Ahmed wrote:

I thought about using a TIdHttpServer and TIdHttp and recreate
the request to the mapped server.

TIdHTTPProxyServer already handles the forwarding for you. All you need
to do is update the request criteria that is being forwarded to the target
server.

But i got stuck with how to copy "TIdHTTPRequestInfo" to TIdHTTP
request property and the same for both responses.

In the TIdHTTPProxyServer.OnHTTPBeforeCommand, you can look at, and update,
the various properties of the provided TIdHTTPProxyServerContext as needed:

- Headers will contain the full HTTP request headers. You can manipulate
these as needed (such as changing the value of the 'Host' header).

- Command will be the HTTP method (GET, POST, CONNECT, etc).

- Document will be the path and query string portion of the requested URL
(does not apply for a CONNECT request).

- the Host and Port of the OutboundClient (type-casted to TIdTCPClient) will
contain the hostname and port of the target server.

- Target will be the full requested URL (or just hostname and port for a
CONNECT request).

- TransferMode will be the value of the TIdHTTPProxyServer.DefaultTransferMode
property (tmFullDocument or tmStreaming).

- TransferSource will be tsClient.

For example:

type
  TIdHTTPProxyServerContextAccess = class(TIdHTTPProxyServerContext)
  ed;
 
procedure IdHTTPProxyServer1BeforeCommand(AContext: TIdHTTPProxyServerContext);
var
  segment, query, remote, newurl: string;
  i: Integer;
  uri: TIdURI;
begin
  if AContext.Command = 'CONNECT' then
  begin
    // AContext.Target = '<host>:<port>'
    remote := FindRemoteHostInConfig(AContext.Target);
    if remote = '' then Exit;
    TIdTCPClient(AContext.OutboundClient).Host := Fetch(remote, ':', True);
    TIdTCPClient(AContext.OutboundClient).Port := IndyStrToInt(remote, 443);
    // AContext.Headers is not forwarded in a CONNECT request, so no need 
to update a 'Host' header...
  end
  else
  begin
    // AContext.Target = '<protocol>://<host>[:<port>][/<path>][?<query>]'
    // AContext.Document = '[/<path>][?<query>]'
    segment := AContext.Document;
    i := Pos('?', segment);
    if i <> 0 then begin
      query := Copy(segment, i, MaxInt);
      SetLength(segment, i-1);
    end;
    i := PosEx('/', s, 2);
    if i <> 0 then SetLength(segment, i-1);
    remote := FindRemoteURLInConfig(segment);
    if remote = '' then Exit;
    newurl := remote + Copy(AContext.Document, Length(segment)+1, MaxInt) 
+ query;
    uri := TIdURI.Create(newurl);
    try
      TIdTCPClient(AContext.OutboundClient).Host := uri.Host;
 
      if uri.Port <> '' then
      begin
        TIdTCPClient(AContext.OutboundClient).Port := IndyStrToInt(uri.Port, 
80);
        AContext.Headers.Values['Host'] := uri.Host + ':' + uri.Port;
      end
      else if TextIsSame(uri.Protocol, 'http') then
      begin
        TIdTCPClient(AContext.OutboundClient).Port := 80;
        AContext.Headers.Values['Host'] := uri.Host;
      end
      else if TextIsSame(uri.Protocol, 'https') then
      begin
        TIdTCPClient(AContext.OutboundClient).Port := 443;
        AContext.Headers.Values['Host'] := uri.Host;
      end else begin
        raise Exception.CreateFmt('invalid protocol in config: "%s"', [uri.Protocol]);
      end;
 
      TIdHTTPProxyServerContextAccess(AContext.FDocument) := uri.GetPathAndParams;
    finally
      uri.Free;
    end;
  end;
end;


If necessary, you can use the TIdHTTPProxyServer.OnHTTPResponse event to
manipulate the response before it is forwarded to the client. For instance,
changing any URLs that refer to the target server so later requests for them
will pass though your proxy.

--
Remy Lebeau (TeamB)
Ahmed Sayed

Posts: 173
Registered: 8/9/07
Re: Http Proxy Server for multiple http servers on same IP?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 16, 2016 3:52 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks for the response that was awesome.

But i don't understand these line:

type
  TIdHTTPProxyServerContextAccess = class(TIdHTTPProxyServerContext)
  end;
 
TIdHTTPProxyServerContextAccess(AContext.FDocument) := uri.GetPathAndParams;


Why did you created a subclass from TIdHTTPProxyServerContext?

Also, I tried something as simple as this but nothing happened on the browser:

void __fastcall TForm1::IdHTTPProxyServer1HTTPBeforeCommand(TIdHTTPProxyServerContext *AContext)
{
((TIdTCPClient*)AContext->OutboundClient)->Host = "localhost";
((TIdTCPClient*)AContext->OutboundClient)->Port = 8888;
AContext->Headers->Values["Host"] = "localhost:8888";
}
//--------------------------------------------------------------------------- 

--
The limits of my language mean the limits of my world
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Http Proxy Server for multiple http servers on same IP?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 16, 2016 4:03 PM   in response to: Ahmed Sayed in response to: Ahmed Sayed
Ahmed wrote:

But i don't understand these line:

type
TIdHTTPProxyServerContextAccess = class(TIdHTTPProxyServerContext)
end;
TIdHTTPProxyServerContextAccess(AContext.FDocument) := uri.GetPathAndParams; </div>


Why did you created a subclass from TIdHTTPProxyServerContext?

The TIdHTTPProxyServerContext.Document property is read-only, but the FDocument
member is protected, so it can be accessed by a derived class. Code in a
unit has implicit access to protected members of classes declared in the
same unit, but not in other units. Using an accessor class in this manner
is a very common technique of accessing protected members of a class that
is declared in another unit.

--
Remy Lebeau (TeamB)
Ahmed Sayed

Posts: 173
Registered: 8/9/07
Re: Http Proxy Server for multiple http servers on same IP?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 16, 2016 4:11 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks again.

But i am telling you that I tried something as simple as this but nothing happened on the browser:
void __fastcall TForm1::IdHTTPProxyServer1HTTPBeforeCommand(TIdHTTPProxyServerContext *AContext)
{
((TIdTCPClient*)AContext->OutboundClient)->Host = "localhost";
((TIdTCPClient*)AContext->OutboundClient)->Port = 8888;
AContext->Headers->Values["Host"] = "localhost:8888";
}
//--------------------------------------------------------------------------- 


Is there something wrong with this code as i didn't get any response in my browser? I am just testing for
now with this.

Chrome says:
http://localhost/
 
The localhost page isn’t working
 
localhost didn’t send any data.
ERR_EMPTY_RESPONSE


--
The limits of my language mean the limits of my world
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Http Proxy Server for multiple http servers on same IP?
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 16, 2016 4:53 PM   in response to: Ahmed Sayed in response to: Ahmed Sayed
Ahmed wrote:

But i am telling you that I tried something as simple as this but
nothing happened on the browser:

There are no changes made to the browser, and that was one of your requirements
anyway:

"But the url on the client browser must not be altered in any way."

The proxy is manipulating the request that is forwarded to the target server.
The browser has no knowledge of that manipulation.

Is there something wrong with this code as i didn't get any response
in my browser?

You are telling TIdHTTPProxyServer to forward to localhost:8888. Do you
have an HTTP server running on localhost:8888? Is it receiving the forwarded
request? Is it sending a response? Is TIdHTTPProxyServer receiving the
browser's request in the first place?

I am just testing for now with this.

Chrome says:

http://localhost/
 
The localhost page isn’t working
 
localhost didn’t send any data.
ERR_EMPTY_RESPONSE

By default, the browser will connect to localhost:80 and send the request
to the server it is connected to. Do you have an HTTP server running on
localhost:80?

TIdHTTPProxyServer can only be used when the browser is explicitly configured
to send requests through an HTTP proxy (in Chrome, go to Settings > Show
advanced settings > Network > Change proxy settings > LAN settings > Use
a proxy server > Advanced > HTTP).

The resource described in an HTTP request is formatted differently depending
on whether the browser is connected directly to an actual HTTP server vs
connected to an HTTP proxy. If you want your browser to send requests through
your proxy without knowing it is connected to a proxy, then you need to act
as an actual HTTP server instead. TIdHTTPProxyServer is not the right component
for that (unless you alter the source code in TIdHTTPProxyServer.CommandPassThrough()
to handle relative URIs when it parses the TIdHTTPProxyServerContext.Target
property to pre-configure the OutboundClient before firing the OnHTTPBeforeCommand
event). Otherwise, you will have to use TIdHTTPServer instead, and implement
all of the forwarding logic manually (you can use TIdHTTP for that, but
you don't need to. See TIdHTTPProxyServer's implementation for how it forwards
requests).

--
Remy Lebeau (TeamB)
Ahmed Sayed

Posts: 173
Registered: 8/9/07
Re: Http Proxy Server for multiple http servers on same IP?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 16, 2016 5:28 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thanks i changed chrome proxy settings and it worked as expected.

--
The limits of my language mean the limits of my world
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02