diff --git a/src/net/mormot.net.client.pas b/src/net/mormot.net.client.pas index 20fd2899d..958973f40 100644 --- a/src/net/mormot.net.client.pas +++ b/src/net/mormot.net.client.pas @@ -47,6 +47,33 @@ interface { ************** THttpClientSocket Implementing HTTP client over plain sockets } +var + /// THttpRequest timeout default value for DNS resolution + // - only used by TWinHttp class - other clients will ignore it + // - leaving to 0 will let system default value be used + HTTP_DEFAULT_RESOLVETIMEOUT: integer = 0; + /// THttpRequest timeout default value for remote connection + // - default is 30 seconds + // - used e.g. by THttpRequest, TRestHttpClientRequest and TRestHttpClientGeneric + HTTP_DEFAULT_CONNECTTIMEOUT: integer = 30000; + /// THttpRequest timeout default value for data sending + // - default is 30 seconds + // - used e.g. by THttpRequest, TRestHttpClientRequest and TRestHttpClientGeneric + // - you can override this value by setting the corresponding parameter in + // THttpRequest.Create() constructor + HTTP_DEFAULT_SENDTIMEOUT: integer = 30000; + /// THttpRequest timeout default value for data receiving + // - default is 30 seconds + // - used e.g. by THttpRequest, TRestHttpClientRequest and TRestHttpClientGeneric + // - you can override this value by setting the corresponding parameter in + // THttpRequest.Create() constructor + HTTP_DEFAULT_RECEIVETIMEOUT: integer = 30000; + +const + /// standard text used to identify the WebSockets protocol + HTTP_WEBSOCKET_PROTOCOL: RawUtf8 = 'SEC-WEBSOCKET-PROTOCOL'; + + type /// Socket API based REST and HTTP/1.1 compatible client class // - this component is HTTP/1.1 compatible, according to RFC 2068 document diff --git a/src/net/mormot.net.http.pas b/src/net/mormot.net.http.pas index 1163c3893..657b0da08 100644 --- a/src/net/mormot.net.http.pas +++ b/src/net/mormot.net.http.pas @@ -31,56 +31,7 @@ interface { ******************** Shared HTTP Constants and Functions } -var - /// THttpRequest timeout default value for DNS resolution - // - leaving to 0 will let system default value be used - HTTP_DEFAULT_RESOLVETIMEOUT: integer = 0; - /// THttpRequest timeout default value for remote connection - // - default is 60 seconds - // - used e.g. by THttpRequest, TRestHttpClientRequest and TRestHttpClientGeneric - HTTP_DEFAULT_CONNECTTIMEOUT: integer = 60000; - /// THttpRequest timeout default value for data sending - // - default is 30 seconds - // - used e.g. by THttpRequest, TRestHttpClientRequest and TRestHttpClientGeneric - // - you can override this value by setting the corresponding parameter in - // THttpRequest.Create() constructor - HTTP_DEFAULT_SENDTIMEOUT: integer = 30000; - /// THttpRequest timeout default value for data receiving - // - default is 30 seconds - // - used e.g. by THttpRequest, TRestHttpClientRequest and TRestHttpClientGeneric - // - you can override this value by setting the corresponding parameter in - // THttpRequest.Create() constructor - HTTP_DEFAULT_RECEIVETIMEOUT: integer = 30000; - -const - /// standard text used to identify the WebSockets protocol - HTTP_WEBSOCKET_PROTOCOL: RawUtf8 = 'SEC-WEBSOCKET-PROTOCOL'; - - -/// compute the 'Authorization: Bearer ####' HTTP header of a given token value -function AuthorizationBearer(const AuthToken: RawUtf8): RawUtf8; - -/// will remove most usual HTTP headers which are to be recomputed on sending -function PurgeHeaders(P: PUtf8Char): RawUtf8; - - -{$ifndef NOXPOWEREDNAME} -const - /// pseudo-header containing the current Synopse mORMot framework version - XPOWEREDNAME = 'X-Powered-By'; - /// the full text of the current Synopse mORMot framework version - // - we don't supply full version number with build revision - // (as SYNOPSE_FRAMEWORK_VERSION), to reduce potential attack surface - XPOWEREDVALUE = SYNOPSE_FRAMEWORK_NAME + ' 2 synopse.info'; -{$endif NOXPOWEREDNAME} - - -{ ******************** THttpSocket Implementing HTTP over plain sockets } - type - /// exception class raised during HTTP process - EHttpSocket = class(ENetSock); - /// event used to compress or uncompress some data during HTTP protocol // - should always return the protocol name for ACCEPT-ENCODING: header // e.g. 'gzip' or 'deflate' for standard HTTP format, but you can add @@ -109,6 +60,48 @@ THttpSocketCompressRec = record // - filled from ACCEPT-ENCODING: header value THttpSocketCompressSet = set of 0..31; + +/// adjust HTTP body compression according to the supplied 'CONTENT-TYPE' +// - will detect most used compressible content (like 'text/*' or +// 'application/json') from OutContentType +function CompressDataAndGetHeaders(Accepted: THttpSocketCompressSet; + const Handled: THttpSocketCompressRecDynArray; const OutContentType: RawUtf8; + var OutContent: RawByteString): RawUtf8; + +/// enable a give compression function for a HTTP link +function RegisterCompressFunc(var Compress: THttpSocketCompressRecDynArray; + aFunction: THttpSocketCompress; var aAcceptEncoding: RawUtf8; + aCompressMinSize: integer): RawUtf8; + +/// decode 'CONTENT-ENCODING: ' parameter from registered compression list +function ComputeContentEncoding(const Compress: THttpSocketCompressRecDynArray; + P: PUtf8Char): THttpSocketCompressSet; + + +/// compute the 'Authorization: Bearer ####' HTTP header of a given token value +function AuthorizationBearer(const AuthToken: RawUtf8): RawUtf8; + +/// will remove most usual HTTP headers which are to be recomputed on sending +function PurgeHeaders(P: PUtf8Char): RawUtf8; + + +{$ifndef NOXPOWEREDNAME} +const + /// pseudo-header containing the current Synopse mORMot framework version + XPOWEREDNAME = 'X-Powered-By'; + /// the full text of the current Synopse mORMot framework version + // - we don't supply full version number with build revision + // (as SYNOPSE_FRAMEWORK_VERSION), to reduce potential attacker knowledge + XPOWEREDVALUE = SYNOPSE_FRAMEWORK_NAME + ' 2 synopse.info'; +{$endif NOXPOWEREDNAME} + + +{ ******************** THttpSocket Implementing HTTP over plain sockets } + +type + /// exception class raised during HTTP process + EHttpSocket = class(ENetSock); + /// map the presence of some HTTP headers for THttpSocket.HeaderFlags THttpSocketHeaderFlags = set of ( hfTransferChuked, @@ -209,20 +202,6 @@ THttpSocket = class(TCrtSocket) end; -/// adjust HTTP body compression according to the supplied 'CONTENT-TYPE' -function CompressDataAndGetHeaders(Accepted: THttpSocketCompressSet; - const Handled: THttpSocketCompressRecDynArray; const OutContentType: RawUtf8; - var OutContent: RawByteString): RawUtf8; - -/// enable a give compression function for a HTTP link -function RegisterCompressFunc(var Compress: THttpSocketCompressRecDynArray; - aFunction: THttpSocketCompress; var aAcceptEncoding: RawUtf8; - aCompressMinSize: integer): RawUtf8; - -/// decode 'CONTENT-ENCODING: ' parameter from registered compression list -function ComputeContentEncoding(const Compress: THttpSocketCompressRecDynArray; - P: PUtf8Char): THttpSocketCompressSet; - { ******************** Abstract Server-Side Types used e.g. for Client-Server Protocol } @@ -804,7 +783,7 @@ procedure THttpSocket.GetBody; SetLength(Content, ContentLength); // not chuncked: direct read SockInRead(pointer(Content), ContentLength); // works with SockIn=nil or not end - else if (ContentLength < 0) and + else if (ContentLength < 0) and // -1 means no Content-Length header IdemPChar(pointer(Command), 'HTTP/1.0 200') then begin // body = either Content-Length or Transfer-Encoding (HTTP/1.1 RFC2616 4.3) @@ -824,7 +803,8 @@ procedure THttpSocket.GetBody; if cardinal(fContentCompress) < cardinal(length(fCompress)) then if fCompress[fContentCompress].Func(Content, false) = '' then // invalid content - raise EHttpSocket.CreateFmt('%s uncompress', [fCompress[fContentCompress].Name]); + raise EHttpSocket.CreateFmt( + '%s uncompress', [fCompress[fContentCompress].Name]); ContentLength := length(Content); // update Content-Length {$ifdef SYNCRTDEBUGLOW} TSynLog.Add.Log(sllCustom2, 'GetBody sock=% pending=% sockin=% len=% %', diff --git a/src/net/mormot.net.server.pas b/src/net/mormot.net.server.pas index 1ec45b94c..fd4b6def8 100644 --- a/src/net/mormot.net.server.pas +++ b/src/net/mormot.net.server.pas @@ -524,8 +524,8 @@ THttpServer = class(THttpServerGeneric) // - this constructor will raise a EHttpServer exception if binding failed // - expects the port to be specified as string, e.g. '1234'; you can // optionally specify a server address to bind to, e.g. '1.2.3.4:1234' - // - can listed on UDS in case port is specified with 'unix:' prefix, e.g. - // 'unix:/run/myapp.sock' + // - can listed to local Unix Domain Sockets file in case port is prefixed + // with 'unix:', e.g. 'unix:/run/myapp.sock' - faster and safer than TCP // - on Linux in case aPort is empty string will check if external fd // is passed by systemd and use it (so called systemd socked activation) // - you can specify a number of threads to be initialized to handle @@ -1449,9 +1449,14 @@ destructor THttpServer.Destroy; if (fExecuteState = esRunning) and (Sock <> nil) then begin - Sock.Close; // shutdown the socket to unlock Accept() in Execute - if NewSocket('127.0.0.1', Sock.Port, nlTCP, false, 1, 1, 1, 0, callback) = nrOK then + if Sock.SocketLayer <> nlUNIX then + Sock.Close; // shutdown TCP/UDP socket to unlock Accept() in Execute + if NewSocket(Sock.Server, Sock.Port, Sock.SocketLayer, + {dobind=}false, 10, 10, 10, 0, callback) = nrOK then + // Windows TCP/UDP socket may not release Accept() until connected callback.ShutdownAndClose({rdwr=}false); + if Sock.SockIsDefined then + Sock.Close; // nlUNIX expects shutdown after accept() returned end; endtix := mormot.core.os.GetTickCount64 + 20000; EnterCriticalSection(fProcessCS); @@ -1469,7 +1474,7 @@ destructor THttpServer.Destroy; (fExecuteState <> esRunning) then break; LeaveCriticalSection(fProcessCS); - SleepHiRes(100); + SleepHiRes(10); EnterCriticalSection(fProcessCS); until mormot.core.os.GetTickCount64 > endtix; FreeAndNil(fInternalHttpServerRespList); @@ -1610,9 +1615,9 @@ procedure THttpServer.Execute; try fSock := TCrtSocket.Bind(fSockPort); // BIND + LISTEN {$ifdef OSLINUX} - // in case we started by systemd, listening socket is created by another process - // and do not interrupt while process got a signal. So we need to set a timeout to - // unblock accept() periodically and check we need terminations + // in case was started by systemd, listening socket is created by another + // process and do not interrupt while process got a signal. So we need to + // set a timeout to unlock accept() periodically and check for termination if fSockPort = '' then // external socket fSock.ReceiveTimeout := 1000; // unblock accept every second {$endif OSLINUX} @@ -2331,9 +2336,9 @@ procedure TSynThreadPoolTHttpServer.Task(aCaller: TSynThread; aContext: Pointer) grOwned: // e.g. for asynchrounous WebSockets srvsock := nil; // to ignore FreeAndNil(srvsock) below - else if Assigned(fServer.Sock.OnLog) then - fServer.Sock.OnLog(sllTrace, 'Task: close after GetRequest=% from %', - [ToText(res)^, srvsock.RemoteIP], self); + else if Assigned(fServer.Sock.OnLog) then + fServer.Sock.OnLog(sllTrace, 'Task: close after GetRequest=% from %', + [ToText(res)^, srvsock.RemoteIP], self); end; finally srvsock.Free; diff --git a/src/net/mormot.net.sock.pas b/src/net/mormot.net.sock.pas index 344f1c81d..79f2f0b55 100644 --- a/src/net/mormot.net.sock.pas +++ b/src/net/mormot.net.sock.pas @@ -529,9 +529,11 @@ TCrtSocket = class aTimeOut: cardinal = 10000; aTLS: boolean = false; aTLSContext: PNetTLSContext = nil); /// bind to an address // - aAddr='1234' - bind to a port on all interfaces, the same as '0.0.0.0:1234' - // - aAddr='IP:port' - bind to specified interface only, e.g. '1.2.3.4:1234' - // - aAddr='unix:/path/to/file' - bind to unix domain socket, e.g. 'unix:/run/mormot.sock' - // - aAddr='' - bind to systemd descriptor on linux. See + // - aAddr='IP:port' - bind to specified interface only, e.g. + // '1.2.3.4:1234' + // - aAddr='unix:/path/to/file' - bind to unix domain socket, e.g. + // 'unix:/run/mormot.sock' + // - aAddr='' - bind to systemd descriptor on linux - see // http://0pointer.de/blog/projects/socket-activation.html constructor Bind(const aAddress: RawUtf8; aLayer: TNetLayer = nlTCP; aTimeOut: integer = 10000); @@ -1078,7 +1080,11 @@ function NewSocket(const address, port: RawUtf8; layer: TNetLayer; (not dobind) and Assigned(NewSocketAddressCache) and ToCardinal(port, p, 1) then - if NewSocketAddressCache.Search(address, addr) then + if (address = '') or + (address = cLocalhost) or + (address = cAnyHost) then // for client: '0.0.0.0'->'127.0.0.1' + result := addr.SetFrom(cLocalhost, port, layer) + else if NewSocketAddressCache.Search(address, addr) then begin fromcache := true; result := addr.SetPort(p); @@ -1215,7 +1221,11 @@ function TNetSocketWrap.Accept(out clientsocket: TNetSocket; len := SizeOf(addr); sock := mormot.net.sock.accept(TSocket(@self), @addr, len); if sock = -1 then - result := NetLastError + begin + result := NetLastError; + if result = nrOk then + result := nrNotImplemented; + end else begin clientsocket := TNetSocket(sock); @@ -1582,6 +1592,32 @@ procedure TPollSockets.Terminate; const UNIX_LOW = ord('u') + ord('n') shl 8 + ord('i') shl 16 + ord('x') shl 24; +function StartWith(p, up: PUtf8Char): boolean; +// to avoid linking mormot.core.text for IdemPChar() +var + c, u: AnsiChar; +begin + result := false; + if (p = nil) or + (up = nil) then + exit; + repeat + u := up^; + if u = #0 then + break; + inc(up); + c := p^; + inc(p); + if (c >= 'a') and + (c <= 'z') then + dec(c, 32); + if c <> u then + exit; + until false; + result := true; +end; + + { TCrtSocket } function TCrtSocket.GetRawSocket: PtrInt; @@ -1625,8 +1661,17 @@ constructor TCrtSocket.Open(const aServer, aPort: RawUtf8; Create(aTimeOut); // default read timeout is 10 seconds if aTLSContext <> nil then TLS := aTLSContext^; // copy the input parameters before OpenBind() - // raise exception on error - OpenBind(aServer, aPort, {dobind=}false, aTLS, aLayer); + // OpenBind() raise an exception on error + {$ifdef OSPOSIX} + if StartWith(pointer(aServer), 'UNIX:') then + begin + // aServer='unix:/path/to/myapp.socket' + OpenBind(copy(aServer, 6, 200), '', {dobind=}false, aTLS, nlUNIX); + fServer := aServer; // keep the full server name if reused + end + else + {$endif OSPOSIX} + OpenBind(aServer, aPort, {dobind=}false, aTLS, aLayer); end; function SplitFromRight(const Text: RawUtf8; Sep: AnsiChar; @@ -1683,9 +1728,10 @@ constructor TCrtSocket.Bind(const aAddress: RawUtf8; aLayer: TNetLayer; {$ifdef OSPOSIX} if s = 'unix' then begin - aLayer := nlUNIX; - s := p; - p := ''; + // aAddress='unix:/path/to/myapp.socket' + FpUnlink(pointer(p)); // previous bind may have left the .socket file + OpenBind(p, '', {dobind=}true, {tls=}false, nlUnix, {%H-}TNetSocket(aSock)); + exit; end; {$endif OSPOSIX} end; @@ -1982,6 +2028,10 @@ procedure TCrtSocket.Close; fSock := TNetSocket(-1); // don't reset fServer/fPort/fTls/fWasBind: caller may use them to reconnect // (see e.g. THttpClientSocket.Request) + {$ifdef OSPOSIX} + if fSocketLayer = nlUnix then + FpUnlink(pointer(fServer)); // 'unix:/path/to/myapp.socket' -> delete file + {$endif OSPOSIX} end; destructor TCrtSocket.Destroy; @@ -2507,31 +2557,6 @@ function TCrtSocket.PeerPort: integer; { TUri } -function StartWith(p, up: PUtf8Char): boolean; -// to avoid linking mormot.core.text for IdemPChar() -var - c, u: AnsiChar; -begin - result := false; - if (p = nil) or - (up = nil) then - exit; - repeat - u := up^; - if u = #0 then - break; - inc(up); - c := p^; - inc(p); - if (c >= 'a') and - (c <= 'z') then - dec(c, 32); - if c <> u then - exit; - until false; - result := true; -end; - procedure TUri.Clear; begin Https := false; diff --git a/src/net/mormot.net.sock.posix.inc b/src/net/mormot.net.sock.posix.inc index 026c2e16a..a59feb8fd 100644 --- a/src/net/mormot.net.sock.posix.inc +++ b/src/net/mormot.net.sock.posix.inc @@ -144,42 +144,42 @@ type function gethostbyname(name: PAnsiChar): PHostEnt; cdecl; external clib name 'gethostbyname'; -function socket(af, struct, protocol: integer): TSocket; cdecl; +function socket(af, struct, protocol: integer): integer; cdecl; external clib name 'socket'; -function setsockopt(s: TSocket; level, optname: integer; +function setsockopt(s: integer; level, optname: integer; optval: pointer; optlen: integer): integer; cdecl; external clib name 'setsockopt'; -function ioctlsocket(s: TSocket; cmd: cardinal; arg: PCardinal): integer; cdecl; +function ioctlsocket(s: integer; cmd: cardinal; arg: PCardinal): integer; cdecl; external clib name 'ioctl'; -function shutdown(s: TSocket; how: integer): integer; cdecl; +function shutdown(s: integer; how: integer): integer; cdecl; external clib name 'shutdown'; -function closesocket(s: TSocket): integer; cdecl; +function closesocket(s: integer): integer; cdecl; external clib name 'close'; function getnameinfo(addr: PSockAddr; namelen: integer; host: PAnsiChar; hostlen: DWORD; serv: PAnsiChar; servlen: DWORD; flags: integer): integer; cdecl; external clib name 'getnameinfo'; -function bind(s: TSocket; addr: PSockAddr; namelen: integer): integer; cdecl; +function bind(s: integer; addr: PSockAddr; namelen: integer): integer; cdecl; external clib name 'bind'; -function listen(s: TSocket; backlog: integer): integer; cdecl; +function listen(s: integer; backlog: integer): integer; cdecl; external clib name 'listen'; -function accept(s: TSocket; addr: PSockAddr; var addrlen: integer): TSocket; cdecl; +function accept(s: integer; addr: PSockAddr; var addrlen: integer): integer; cdecl; external clib name 'accept'; -function connect(s: TSocket; name: PSockAddr; namelen: integer): integer; cdecl; +function connect(s: integer; name: PSockAddr; namelen: integer): integer; cdecl; external clib name 'connect'; function select(nfds: integer; readfds, writefds, exceptfds: PFDSet; timeout: PTimeVal): integer; cdecl; external clib name 'select'; -function recv(s: TSocket; Buf: Pointer; len, flags: integer): integer; cdecl; +function recv(s: integer; Buf: Pointer; len, flags: integer): integer; cdecl; external clib name 'recv'; -function recvfrom(s: TSocket; Buf: Pointer; len, flags: integer; +function recvfrom(s: integer; Buf: Pointer; len, flags: integer; from: PSockAddr; fromlen: Pinteger): integer; cdecl; external clib name 'recvfrom'; -function send(s: TSocket; Buf: Pointer; len, flags: integer): integer; cdecl; +function send(s: integer; Buf: Pointer; len, flags: integer): integer; cdecl; external clib name 'send'; -function sendto(s: TSocket; Buf: Pointer; len, flags: integer; +function sendto(s: integer; Buf: Pointer; len, flags: integer; addrto: PSockAddr; tolen: integer): integer; cdecl; external clib name 'sendto'; -function getpeername(s: TSocket; name: PSockAddr; var namelen: integer): integer; cdecl; +function getpeername(s: integer; name: PSockAddr; var namelen: integer): integer; cdecl; external clib name 'getpeername'; {$ifdef OSDARWIN} diff --git a/src/rest/mormot.rest.http.client.pas b/src/rest/mormot.rest.http.client.pas index 6f5b1e03b..8af375d33 100644 --- a/src/rest/mormot.rest.http.client.pas +++ b/src/rest/mormot.rest.http.client.pas @@ -126,6 +126,7 @@ TRestHttpClientGeneric = class(TRestClientUri) // you left the 0 default parameters, it would use global // HTTP_DEFAULT_CONNECTTIMEOUT, HTTP_DEFAULT_SENDTIMEOUT and // HTTP_DEFAULT_RECEIVETIMEOUT variable values + // - TRestHttpClientSocket handles aServer='unix:/run/mormotapp.sock' on POSIX constructor Create(const aServer, aPort: RawUtf8; aModel: TOrmModel; aHttps: boolean = false; const aProxyName: RawUtf8 = ''; const aProxyByPass: RawUtf8 = ''; aSendTimeout: cardinal = 0; @@ -246,9 +247,8 @@ TRestHttpClientRequestClass = class of TRestHttpClientRequest; { ************ TRestHttpClientSocket REST Client Class over Sockets } /// HTTP/1.1 RESTful JSON mORMot Client class using mormot.net.client socket - // - will give the best performance on a local computer, but has been found - // out to be slower over a network - // - is not able to use secure HTTPS protocol + // - can use regular HTTP/HTTPS connection, or Unix Domain Sockets supplying + // aServer='unix:/run/mormotapp.sock' to is Create() constructor // - note that, in its current implementation, this class is not thread-safe: // you need either to lock its access via a critical section, or initialize // one client instance per thread @@ -701,7 +701,8 @@ function TRestHttpClientSocket.InternalCheckOpen: boolean; on E: Exception do begin FreeAndNil(fSocket); - if started = 0 then + if (started = 0) or + (isDestroying in fInternalState) then exit; elapsed := GetTickCount64 - started; if elapsed >= fConnectRetrySeconds shl 10 then @@ -710,7 +711,7 @@ function TRestHttpClientSocket.InternalCheckOpen: boolean; if elapsed < 500 then wait := 100 else - wait := 1000; // checking every second is enough + wait := 1000; // every second retry is enough fLogClass.Add.Log(sllTrace, 'InternalCheckOpen: % on %:% after %' + ' -> wait % and retry #% up to % seconds', [E.ClassType, fServer, fPort, MicroSecToString(elapsed * 1000), @@ -725,9 +726,11 @@ function TRestHttpClientSocket.InternalCheckOpen: boolean; fSocket.UserAgent := fExtendedOptions.UserAgent; if fModel <> nil then fSocket.ProcessName := FormatUtf8('%/%', [fPort, fModel.Root]); - if fSendTimeout > 0 then + if (fSendTimeout > 0) and + (fConnectTimeout <> fSendTimeout) then fSocket.SendTimeout := fSendTimeout; - if fReceiveTimeout > 0 then + if (fReceiveTimeout > 0) and + (fConnectTimeout <> fReceiveTimeout) then fSocket.ReceiveTimeout := fReceiveTimeout; // note that first registered algo will be the prefered one {$ifndef PUREMORMOT2} diff --git a/src/rest/mormot.rest.http.server.pas b/src/rest/mormot.rest.http.server.pas index e546b8c55..37625172e 100644 --- a/src/rest/mormot.rest.http.server.pas +++ b/src/rest/mormot.rest.http.server.pas @@ -115,10 +115,12 @@ ERestHttpServer = class(ERestException); HTTP_DEFAULT_MODE = useHttpApiRegisteringURI; /// the kind of HTTP server which involves http.sys - HTTP_API_MODES = [useHttpApi .. useHttpApiRegisteringURIOnly]; + HTTP_API_MODES = + [useHttpApi .. useHttpApiRegisteringURIOnly]; /// the http.sys modes which won't have any fallback to the sockets server - HTTP_API_REGISTERING_MODES = [useHttpApiRegisteringURI, useHttpApiRegisteringURIOnly]; + HTTP_API_REGISTERING_MODES = + [useHttpApiRegisteringURI, useHttpApiRegisteringURIOnly]; {$else} HTTP_DEFAULT_MODE = useHttpSocket; @@ -615,11 +617,17 @@ constructor TRestHttpServer.Create(const aPort: RawUtf8; SetAccessControlAllowOrigin(''); // deny CORS by default fHosts.Init(false); fDomainName := aDomainName; + // aPort='publicip:port' or 'unix:/path/to/myapp.socket' or 'port' fPort := aPort; Split(RawUtf8(fPort), ':', fPublicAddress, fPublicPort); - if fPublicPort = '' then + if fPublicAddress = 'unix' then begin - // you should better set aPort='publicip:port' + // 'unix:/path/to/myapp.socket' + fPublicPort := fPort; // to be recognized by TCrtSocket.Bind() + fPublicAddress := '0.0.0.0'; + end else if fPublicPort = '' then + begin + // no publicip supplied -> bind to HostName fPublicPort := fPublicAddress; fPublicAddress := Executable.Host; end;