function THttpAsyncConnection.DoHeaders: TPollAsyncSocketOnReadWrite;
var
status: integer;
//!!!! fix sample
procedure DoParseRealIP(var RemoteIP:RawUtf8);
begin
if fServer.fRemoteIPHeaderUpper <> '' then
FindNameValue(fHttp.Headers, pointer(fServer.fRemoteIPHeaderUpper),
RemoteIP, {keepnotfound=}true);
end;
begin
// finalize the headers
result := soClose;
if (nfHeadersParsed in fHttp.HeaderFlags) or
not fHttp.ParseCommand then
exit;
fHttp.ParseHeaderFinalize;
//!!!!! parse proxy x-forward-forx ..realip
//!!!!! just after fHttp.Headers retrieved before fServer.OnBeforeBody get called by following DecodeHeaders
//!!!!! we need to hnow everything in business layer callback like OnBeforeBody 、OnWebSocketUpgraded、OnWebSocketConnect、
//!!!!! OnWebSocketDisconnect etc... so we wont break things
DoParseRealIP(fRemoteIP);
// immediate reject of clearly invalid requests
status := DecodeHeaders; // may handle hfConnectionUpgrade when overriden
if status <> HTTP_SUCCESS then
begin
// on fatal error (e.g. OnBeforeBody) direct reject and close the connection
DoReject(status);
exit;
end;
and the fServer.fRemoteIPHeader should set to 'X-Forwarded-For:' , because FindNameValue does not process ':'
my fix is just a sample which ignore RemoteConnID parse
]]>2) Should be fixed by
https://github.com/synopse/mORMot2/commit/2b3516e0
3) About the remote ip issue over a proxy, it is indeed a bug.
Please try https://github.com/synopse/mORMot2/commit/08a99075
function THttpAsyncConnection.DoRequest: TPollAsyncSocketOnReadWrite;
var
output: PRawByteStringBuffer;
remoteID: THttpServerConnectionID;
sent: integer;
p: PByte;
flags: THttpServerRequestFlags;
begin
// check the status
if nfHeadersParsed in fHttp.HeaderFlags then
fServer.IncStat(grBodyReceived)
else
begin
// content-length was 0, so hrsGetBody* and DoHeaders() were not called
result := DoHeaders;
if (result <> soContinue) or
(fHttp.State = hrsUpgraded) then
exit; // rejected or upgraded <<<<<!!!
<<<<--- if our server is behind a reverse proxy . even we use the (fhttpServer).RemoteIPHeader :='X-Forwarded-For'
because we exit here ,following fServer.ParseRemoteIPConnID(fHttp.Headers, fRemoteIP, remoteid); never get a chance to exec
---->>
end;
// optionaly uncompress content
if fHttp.CompressContentEncoding >= 0 then
fHttp.UncompressData;
// prepare the HTTP/REST process reusing the THttpServerRequest instance
result := soClose;
remoteid := fHandle;
fServer.ParseRemoteIPConnID(fHttp.Headers, fRemoteIP, remoteid);
<<<<--- if our server is behind a reverse proxy . even we use the (fhttpServer).RemoteIPHeader :='X-Forwarded-For'
because we exit here ,following fServer.ParseRemoteIPConnID(fHttp.Headers, fRemoteIP, remoteid); never get a chance to exec
---->>
when we need to get the Real RemoteIP from header ,
those code works fine when it handle regular http rest request ,but not work in websocket mode.
Scenario 1:client specify the protocol in reqeust header
prot := Http.HeaderGetValue('SEC-WEBSOCKET-PROTOCOL');
P := pointer(prot);
if P <> nil then
these three line identify the protocol name from request Header,
then the following code should be:
Protocol := CloneByName(subprot, prot);
NOT
Protocol := CloneByName(subprot, uri);
i think it just a type error.
-------------------------------
Scenario 2:client do NOT specify the protocol in reqeust header,
then our code detect the protocol from url,
at the end our code return 101 header by this
FormatUtf8('HTTP/1.1 101 Switching Protocols'#13#10 +
'Upgrade: websocket'#13#10 +
'Connection: Upgrade'#13#10 +
'Sec-WebSocket-Connection-ID: %'#13#10 +
'Sec-WebSocket-Protocol: %'#13#10 +
'%Sec-WebSocket-Accept: %'#13#10#13#10,
[ConnectionID, Protocol.Name, extout,
BinToBase64Short(@Digest, SizeOf(Digest))], Response)
this all work fine if client make the request by put SEC-WEBSOCKET-PROTOCOL in header,
but if js code like this
ws = new WebSocket('ws://127.0.0.1/chat');
the brower will report error :
"Response must not include ‘Sec-WebSocket-Protocol’ header if not present in request;"
u can see it in chrome console.
there are many Scenarios we can not ask the client to specify the SEC-WEBSOCKET-PROTOCOL in header, like work with 3rd party.
]]>We used this code with a JS client with success, so I am a bit afraid of breaking things.
Please provide more information, to state for the exact use case of the problem you face.
// identify the Websockets protocol
prot := Http.HeaderGetValue('SEC-WEBSOCKET-PROTOCOL');
P := pointer(prot);
if P <> nil then
begin
repeat
GetNextItemTrimed(P, ',', subprot);
Protocol := CloneByName(subprot, uri);
until (P = nil) or
(Protocol <> nil);
if (Protocol <> nil) and
(Protocol.Uri = '') and
not Protocol.ProcessHandshakeUri(prot) then
begin
Protocol.Free;
result := HTTP_NOTFOUND;
exit;
end;
end
line 2376 Protocol := CloneByName(subprot, uri); should be "Protocol := CloneByName(subprot, prot);"
and the final 101 upgrade response , if we use CloneByUri(uri) then
Response must NOT include 'Sec-WebSocket-Protocol' header if not present in request ,otherwise js client will fail(if it doesnt use the protocol header).