Mercurial > hg > orthanc
changeset 6307:86b3ad58baa3
HttpBindAddresses
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Thu, 11 Sep 2025 18:32:39 +0200 |
parents | e6755569b9d2 |
children | 1486ee6d0a70 |
files | NEWS OrthancFramework/Resources/CodeGeneration/ErrorCodes.json OrthancFramework/Resources/CodeGeneration/GenerateErrorCodes.py OrthancFramework/Sources/Enumerations.cpp OrthancFramework/Sources/Enumerations.h OrthancFramework/Sources/HttpServer/HttpServer.cpp OrthancFramework/Sources/HttpServer/HttpServer.h OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h OrthancServer/Resources/Configuration.json OrthancServer/Sources/main.cpp |
diffstat | 10 files changed, 65 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Tue Sep 09 15:45:16 2025 +0200 +++ b/NEWS Thu Sep 11 18:32:39 2025 +0200 @@ -1,6 +1,14 @@ Pending changes in the mainline =============================== +General +------- + +* New configuration "HttpBindAddresses" to list the IP addresses on which the + HTTP server listens. By default, this list is empty and the HTTP server listens + on all network interfaces. + + Maintenance -----------
--- a/OrthancFramework/Resources/CodeGeneration/ErrorCodes.json Tue Sep 09 15:45:16 2025 +0200 +++ b/OrthancFramework/Resources/CodeGeneration/ErrorCodes.json Thu Sep 11 18:32:39 2025 +0200 @@ -393,7 +393,7 @@ { "Code": 2003, "Name": "HttpPortInUse", - "Description": "The TCP port of the HTTP server is privileged or already in use" + "Description": "The TCP port of the HTTP server is privileged or already in use or one of the HTTP bind addresses does not exist" }, { "Code": 2004,
--- a/OrthancFramework/Resources/CodeGeneration/GenerateErrorCodes.py Tue Sep 09 15:45:16 2025 +0200 +++ b/OrthancFramework/Resources/CodeGeneration/GenerateErrorCodes.py Thu Sep 11 18:32:39 2025 +0200 @@ -37,7 +37,7 @@ ## with open(os.path.join(BASE, 'OrthancFramework', 'Resources', 'CodeGeneration', 'ErrorCodes.json'), 'r') as f: - ERRORS = json.loads(re.sub('/\*.*?\*/', '', f.read())) + ERRORS = json.loads(re.sub(r'/\*.*?\*/', '', f.read())) for error in ERRORS: if error['Code'] >= START_PLUGINS: @@ -48,7 +48,7 @@ a = f.read() HTTP = {} -for i in re.findall('(HttpStatus_([0-9]+)_\w+)', a): +for i in re.findall(r'(HttpStatus_([0-9]+)_\w+)', a): HTTP[int(i[1])] = i[0] @@ -64,7 +64,7 @@ s = ',\n'.join(map(lambda x: ' ErrorCode_%s = %d /*!< %s */' % (x['Name'], int(x['Code']), x['Description']), ERRORS)) s += ',\n ErrorCode_START_PLUGINS = %d' % START_PLUGINS -a = re.sub('(enum ErrorCode\s*{)[^}]*?(\s*};)', r'\1\n%s\2' % s, a, re.DOTALL) +a = re.sub(r'(enum ErrorCode\s*{)[^}]*?(\s*};)', r'\1\n%s\2' % s, a, re.DOTALL) with open(path, 'w') as f: f.write(a) @@ -81,7 +81,7 @@ s = ',\n'.join(map(lambda x: ' OrthancPluginErrorCode_%s = %d /*!< %s */' % (x['Name'], int(x['Code']), x['Description']), ERRORS)) s += ',\n\n _OrthancPluginErrorCode_INTERNAL = 0x7fffffff\n ' -a = re.sub('(typedef enum\s*{)[^}]*?(} OrthancPluginErrorCode;)', r'\1\n%s\2' % s, a, re.DOTALL) +a = re.sub(r'(typedef enum\s*{)[^}]*?(} OrthancPluginErrorCode;)', r'\1\n%s\2' % s, a, re.DOTALL) with open(path, 'w') as f: f.write(a) @@ -99,7 +99,7 @@ a = f.read() s = '\n\n'.join(map(lambda x: ' case ErrorCode_%s:\n return "%s";' % (x['Name'], x['Description']), ERRORS)) -a = re.sub('(EnumerationToString\(ErrorCode.*?\)\s*{\s*switch \([^)]*?\)\s*{)[^}]*?(\s*default:)', +a = re.sub(r'(EnumerationToString\(ErrorCode.*?\)\s*{\s*switch \([^)]*?\)\s*{)[^}]*?(\s*default:)', r'\1\n%s\2' % s, a, re.DOTALL) def GetHttpStatus(x): @@ -107,7 +107,7 @@ return ' case ErrorCode_%s:\n return %s;' % (x['Name'], s) s = '\n\n'.join(map(GetHttpStatus, filter(lambda x: 'HttpStatus' in x, ERRORS))) -a = re.sub('(ConvertErrorCodeToHttpStatus\(ErrorCode.*?\)\s*{\s*switch \([^)]*?\)\s*{)[^}]*?(\s*default:)', +a = re.sub(r'(ConvertErrorCodeToHttpStatus\(ErrorCode.*?\)\s*{\s*switch \([^)]*?\)\s*{)[^}]*?(\s*default:)', r'\1\n%s\2' % s, a, re.DOTALL) with open(path, 'w') as f: @@ -125,10 +125,10 @@ e = list(filter(lambda x: 'SQLite' in x and x['SQLite'], ERRORS)) s = ',\n'.join(map(lambda x: ' ErrorCode_%s' % x['Name'], e)) -a = re.sub('(enum ErrorCode\s*{)[^}]*?(\s*};)', r'\1\n%s\2' % s, a, re.DOTALL) +a = re.sub(r'(enum ErrorCode\s*{)[^}]*?(\s*};)', r'\1\n%s\2' % s, a, re.DOTALL) s = '\n\n'.join(map(lambda x: ' case ErrorCode_%s:\n return "%s";' % (x['Name'], x['Description']), e)) -a = re.sub('(EnumerationToString\(ErrorCode.*?\)\s*{\s*switch \([^)]*?\)\s*{)[^}]*?(\s*default:)', +a = re.sub(r'(EnumerationToString\(ErrorCode.*?\)\s*{\s*switch \([^)]*?\)\s*{)[^}]*?(\s*default:)', r'\1\n%s\2' % s, a, re.DOTALL) with open(path, 'w') as f: @@ -145,7 +145,7 @@ a = f.read() s = '\n'.join(map(lambda x: ' PrintErrorCode(ErrorCode_%s, "%s");' % (x['Name'], x['Description']), ERRORS)) -a = re.sub('(static void PrintErrors[^{}]*?{[^{}]*?{)([^}]*?)}', r'\1\n%s\n }' % s, a, re.DOTALL) +a = re.sub(r'(static void PrintErrors[^{}]*?{[^{}]*?{)([^}]*?)}', r'\1\n%s\n }' % s, a, re.DOTALL) with open(path, 'w') as f: f.write(a)
--- a/OrthancFramework/Sources/Enumerations.cpp Tue Sep 09 15:45:16 2025 +0200 +++ b/OrthancFramework/Sources/Enumerations.cpp Thu Sep 11 18:32:39 2025 +0200 @@ -247,7 +247,7 @@ return "The specified path does not point to a directory"; case ErrorCode_HttpPortInUse: - return "The TCP port of the HTTP server is privileged or already in use"; + return "The TCP port of the HTTP server is privileged or already in use or one of the HTTP bind addresses does not exist"; case ErrorCode_DicomPortInUse: return "The TCP port of the DICOM server is privileged or already in use";
--- a/OrthancFramework/Sources/Enumerations.h Tue Sep 09 15:45:16 2025 +0200 +++ b/OrthancFramework/Sources/Enumerations.h Thu Sep 11 18:32:39 2025 +0200 @@ -192,7 +192,7 @@ ErrorCode_DirectoryOverFile = 2000 /*!< The directory to be created is already occupied by a regular file */, ErrorCode_FileStorageCannotWrite = 2001 /*!< Unable to create a subdirectory or a file in the file storage */, ErrorCode_DirectoryExpected = 2002 /*!< The specified path does not point to a directory */, - ErrorCode_HttpPortInUse = 2003 /*!< The TCP port of the HTTP server is privileged or already in use */, + ErrorCode_HttpPortInUse = 2003 /*!< The TCP port of the HTTP server is privileged or already in use or one of the HTTP bind addresses does not exist */, ErrorCode_DicomPortInUse = 2004 /*!< The TCP port of the DICOM server is privileged or already in use */, ErrorCode_BadHttpStatusInRest = 2005 /*!< This HTTP status is not allowed in a REST API */, ErrorCode_RegularFileExpected = 2006 /*!< The specified path does not point to a regular file */,
--- a/OrthancFramework/Sources/HttpServer/HttpServer.cpp Tue Sep 09 15:45:16 2025 +0200 +++ b/OrthancFramework/Sources/HttpServer/HttpServer.cpp Thu Sep 11 18:32:39 2025 +0200 @@ -1732,6 +1732,11 @@ return port_; } + void HttpServer::SetBindAddresses(const std::set<std::string>& bindAddresses) + { + bindAddresses_ = bindAddresses; + } + void HttpServer::Start() { // reset thread counter used to generate HTTP thread names. @@ -1758,11 +1763,27 @@ port += "s"; } + std::string listeningPorts; + if (bindAddresses_.size() == 0) // default behaviour till 1.12.9 and when no "HttpBindAddresses" configurations are provided + { + listeningPorts = port; + } + else + { + std::set<std::string> addresses; + for (std::set<std::string>::const_iterator it = bindAddresses_.begin(); it != bindAddresses_.end(); ++it) + { + addresses.insert(*it + ":" + port); + } + + Toolbox::JoinStrings(listeningPorts, addresses, ","); + } + std::vector<const char*> options; // Set the TCP port for the HTTP server options.push_back("listening_ports"); - options.push_back(port.c_str()); + options.push_back(listeningPorts.c_str()); // Optimization reported by Chris Hafey // https://groups.google.com/d/msg/orthanc-users/CKueKX0pJ9E/_UCbl8T-VjIJ @@ -1873,7 +1894,7 @@ else { throw OrthancException(ErrorCode_HttpPortInUse, - " (port = " + boost::lexical_cast<std::string>(port_) + ")"); + " (" + listeningPorts + ")"); } } @@ -1885,7 +1906,7 @@ } #endif - CLOG(WARNING, HTTP) << "HTTP server listening on port: " << GetPortNumber() + CLOG(WARNING, HTTP) << "HTTP server listening on : " << listeningPorts << " (HTTPS encryption is " << (IsSslEnabled() ? "enabled" : "disabled") << ", remote access is "
--- a/OrthancFramework/Sources/HttpServer/HttpServer.h Tue Sep 09 15:45:16 2025 +0200 +++ b/OrthancFramework/Sources/HttpServer/HttpServer.h Thu Sep 11 18:32:39 2025 +0200 @@ -108,6 +108,7 @@ bool sslHasCiphers_; std::string sslCiphers_; uint16_t port_; + std::set<std::string> bindAddresses_; IIncomingHttpRequestFilter* filter_; bool keepAlive_; unsigned int keepAliveTimeout_; @@ -137,6 +138,8 @@ uint16_t GetPortNumber() const; + void SetBindAddresses(const std::set<std::string>& bindAddresses); + void Start(); void Stop();
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Tue Sep 09 15:45:16 2025 +0200 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Thu Sep 11 18:32:39 2025 +0200 @@ -299,7 +299,7 @@ OrthancPluginErrorCode_DirectoryOverFile = 2000 /*!< The directory to be created is already occupied by a regular file */, OrthancPluginErrorCode_FileStorageCannotWrite = 2001 /*!< Unable to create a subdirectory or a file in the file storage */, OrthancPluginErrorCode_DirectoryExpected = 2002 /*!< The specified path does not point to a directory */, - OrthancPluginErrorCode_HttpPortInUse = 2003 /*!< The TCP port of the HTTP server is privileged or already in use */, + OrthancPluginErrorCode_HttpPortInUse = 2003 /*!< The TCP port of the HTTP server is privileged or already in use or one of the HTTP bind addresses does not exist */, OrthancPluginErrorCode_DicomPortInUse = 2004 /*!< The TCP port of the DICOM server is privileged or already in use */, OrthancPluginErrorCode_BadHttpStatusInRest = 2005 /*!< This HTTP status is not allowed in a REST API */, OrthancPluginErrorCode_RegularFileExpected = 2006 /*!< The specified path does not point to a regular file */,
--- a/OrthancServer/Resources/Configuration.json Tue Sep 09 15:45:16 2025 +0200 +++ b/OrthancServer/Resources/Configuration.json Thu Sep 11 18:32:39 2025 +0200 @@ -118,6 +118,19 @@ // HTTP port for the REST services and for the GUI "HttpPort" : 8042, + // IP addresses the HTTP server listens on. + // This is usefull when, e.g. your computer has multiple network + // interfaces and you want Orthanc to be accessible on a single + // sub-network. + // By default, the HTTP server listens on all IP addresses. + // Note that, setting "HttpBindAddresses" to ["127.0.0.1"] is + // almost equivalent to settting "RemoteAccessAllowed". In the + // first case, external HTTP clients won't be able to connect at + // all to Orthanc while, in the second case, external HTTP clients + // will be able to connect but will receive a 401 Unauthorized + // HTTP status code. + //"HttpBindAddresses": ["1.2.3.4", "127.0.0.1"] + // When the following option is "true", if an error is encountered // while calling the REST API, a JSON message describing the error // is put in the HTTP answer. This feature can be disabled if the
--- a/OrthancServer/Sources/main.cpp Tue Sep 09 15:45:16 2025 +0200 +++ b/OrthancServer/Sources/main.cpp Thu Sep 11 18:32:39 2025 +0200 @@ -881,7 +881,7 @@ PrintErrorCode(ErrorCode_DirectoryOverFile, "The directory to be created is already occupied by a regular file"); PrintErrorCode(ErrorCode_FileStorageCannotWrite, "Unable to create a subdirectory or a file in the file storage"); PrintErrorCode(ErrorCode_DirectoryExpected, "The specified path does not point to a directory"); - PrintErrorCode(ErrorCode_HttpPortInUse, "The TCP port of the HTTP server is privileged or already in use"); + PrintErrorCode(ErrorCode_HttpPortInUse, "The TCP port of the HTTP server is privileged or already in use or one of the HTTP bind addresses does not exist"); PrintErrorCode(ErrorCode_DicomPortInUse, "The TCP port of the DICOM server is privileged or already in use"); PrintErrorCode(ErrorCode_BadHttpStatusInRest, "This HTTP status is not allowed in a REST API"); PrintErrorCode(ErrorCode_RegularFileExpected, "The specified path does not point to a regular file"); @@ -1073,6 +1073,9 @@ // HTTP server httpServer.SetThreadsCount(lock.GetConfiguration().GetUnsignedIntegerParameter("HttpThreadsCount", 50)); httpServer.SetPortNumber(lock.GetConfiguration().GetUnsignedIntegerParameter("HttpPort", 8042)); + std::set<std::string> httpBindAddresses; + lock.GetConfiguration().GetSetOfStringsParameter(httpBindAddresses, "HttpBindAddresses"); + httpServer.SetBindAddresses(httpBindAddresses); httpServer.SetRemoteAccessAllowed(lock.GetConfiguration().GetBooleanParameter("RemoteAccessAllowed", false)); httpServer.SetKeepAliveEnabled(lock.GetConfiguration().GetBooleanParameter("KeepAlive", defaultKeepAlive)); httpServer.SetKeepAliveTimeout(lock.GetConfiguration().GetUnsignedIntegerParameter("KeepAliveTimeout", 1));