How to redirect http to https in TIdCustomHTTPServer - ermesoml - 06-08-2018
Hello Guys,
I am trying to do a service that works under ssl, but that I redirect the http connections to https and I have not succeeded. Is it possible to do that?
I need to use port 9001 so I also can not bind on two different ports, so I need only redirect https:\\example.com to https:\\example.com.
My implementation:
Code: sslHandler := TIdServerIOHandlerSSLOpenSSL.Create(nil);
sslHandler.SSLOptions.KeyFile := GetCurrentDir + '\certificados\private.key';
sslHandler.SSLOptions.CertFile := GetCurrentDir + '\certificados\certificate.crt';
sslHandler.SSLOptions.RootCertFile := GetCurrentDir + '\certificados\ca_bundle.crt';
sslHandler.SSLOptions.Mode := sslmServer;
sslHandler.SSLOptions.SSLVersions := [sslvTLSv1, sslvSSLv2, sslvSSLv23, sslvSSLv3, sslvTLSv1_1, sslvTLSv1_2];
sslHandler.SSLOptions.VerifyDepth := 0;
sslHandler.SSLOptions.VerifyMode := [];
sslHandler.OnGetPassword := IdServerIOHandlerSSLOpenSSLGetPassword;
Server.OnConnect := onConect;
Server.IOHandler := sslHandler;
and
Code: if (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase) then
TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough:= False;
Thank you!
RE: How to redirect http to https in TIdCustomHTTPServer - rlebeau - 06-08-2018
(06-08-2018, 02:56 PM)ermesoml Wrote: I am trying to do a service that works under ssl, but that I redirect the http connections to https and I have not succeeded. Is it possible to do that?
Of course. In your OnCommand... event handler(s), if you detect a request for an HTTP url, you can use the AResponseInfo.Redirect() method to redirect to an HTTPS url.
This would be very easy to do if you were using separate ports for HTTP and HTTPS, eg:
Code: const
cHTTPSPort: TIdPort = ...; // HTTPS port
// TIdHTTPServer.OnQuerySSLPort event handler
procedure TMyForm.onQuerySslPort(APort: TIdPort; var VUseSSL: Boolean);
begin
VUseSSL := (Port = cHTTPSPort);
end;
// TIdHTTPServer.OnCommandGet event handler
procedure TMyForm.onCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
Uri: TIdURI;
begin
if AContext.Binding.Port <> cHTTPSPort then
begin
Uri := TIdURI.Create;
try
Uri.Protocol := 'https';
Uri.Host := iif(ARequestInfo.Host <> '', ARequestInfo.Host, 'myserver');
Uri.Port := cHTTPSPort;
Uri.Document := TIdURI.PathEncode(ARequestInfo.Document);
Uri.Params := ARequestInfo.QueryParams;
AResponseInfo.Redirect(Uri.URI);
finally
Uri.Free;
end;
Exit;
end;
// process request normally ...
end;
(06-08-2018, 02:56 PM)ermesoml Wrote: I need to use port 9001
Why? HTTP is normally on port 80, and HTTPS is normally on port 443. Those are standardized.
(06-08-2018, 02:56 PM)ermesoml Wrote: I also can not bind on two different ports, so I need only redirect https:\\example.com to https:\\example.com.
It is not advisable (but not impossible) to run both HTTP and HTTPS on a single port.
That being said, the ONLY way to make this work is to peek (not read!) the first few bytes from a new connection, and if those bytes are an SSL/TLS client handshake then you can activate SSL/TLS. For example:
Code: uses
..., IdStackConsts, IdStackBSDBase;
// unfortunately, Indy does not currently provide a *public* interface for calling
// the underlying platform's socket recv() function with custom flags, see
// https://github.com/IndySockets/Indy/issues/214. But it does have a *protected*
// interface for it, but only on platforms that use BSD-style sockets APIs
// (Windows, POSIX, etc)...
type
TIdStackBSDBaseAccess = class(TIdStackBSDBase)
end;
// adapted from http://cboard.cprogramming.com/networking-device-communication/166336-detecting-ssl-tls-client-handshake.html
function SslTlsHandshakeDetected(ASocket: TIdStackSocketHandle): Boolean;
var
Buffer: array[0..5] of Byte;
NumBytes, Len: Integer;
MsgType: Byte;
begin
Result := False;
// sniff the first few bytes from the socket directly, do not remove them from
// its inbound buffer, in case the SSLIOHandler needs to read them again later ...
NumBytes := GStack.CheckForSocketError(TIdStackBSDBaseAccess(GBSDStack).WSRecv(ASocket, Buffer, SizeOf(Buffer), MSG_PEEK));
// client disconnected?
if NumBytes = 0 then Exit;
if NumBytes < 3 then Exit;
// SSL v3 or TLS v1.x ?
if (Buffer[0] = $16) and // type (22 = handshake)
(Buffer[1] = $3) then // protocol major version (3.0 = SSL v3, 3.x = TLS v1.x)
begin
Result := True;
Exit;
end;
// SSL v2 ?
if (Buffer[0] and $80) <> 0 then
begin
// no padding, 2-byte header
Len := (Integer(buffer[0] and $7F) shl 8) + buffer[1];
MsgType := buffer[2];
end else
begin
// padding, 3-byte header
Len := (Integer(buffer[0] and $3F) shl 8) + buffer[1];
MsgType := Buffer[3];
end;
Result := (Len > 9) and
(MsgType = $1); // msg type (1 = client hello)
end;
// TIdHTTPServer.OnQuerySSLPort event handler
procedure TMyForm.onQuerySslPort(APort: TIdPort; var VUseSSL: Boolean);
begin
// do not activate SSL/TLS automatically based on port, need to sniff the socket data...
VUseSSL := False;
end;
// TIdHTTPServer.OnConnect event handler
procedure TMyForm.onConnect(AContext: TIdContext);
begin
if not (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase) then
raise Exception.Create('SSL IOHandler not assigned!');
TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough := not SslTlsHandshakeDetected(AContext.Binding.Handle);
end;
// TIdHTTPServer.OnCommandGet event handler
procedure TMyForm.onCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
if TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough then
begin
// not using SSL/TLS, redirect to HTTPS...
Exit;
end;
// process request normally ...
end;
RE: How to redirect http to https in TIdCustomHTTPServer - ermesoml - 06-08-2018
Thank you rlebeau, only god knows how much time i spend searching this!
|