diff --git a/client/src/com/mirth/connect/connectors/http/HttpSender.java b/client/src/com/mirth/connect/connectors/http/HttpSender.java index a049a8183..fac40106e 100644 --- a/client/src/com/mirth/connect/connectors/http/HttpSender.java +++ b/client/src/com/mirth/connect/connectors/http/HttpSender.java @@ -1,11 +1,5 @@ -/* - * Copyright (c) Mirth Corporation. All rights reserved. - * - * http://www.mirthcorp.com - * - * The software in this package is published under the terms of the MPL license a copy of which has - * been included with this distribution in the LICENSE.txt file. - */ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: Mirth Corporation package com.mirth.connect.connectors.http; @@ -130,7 +124,7 @@ public void changedUpdate(DocumentEvent e) { checkContentEnabled(); } }); - + initToolTips(); initLayout(); } @@ -148,6 +142,8 @@ public ConnectorProperties getProperties() { properties.setUseProxyServer(useProxyServerYesRadio.isSelected()); properties.setProxyAddress(proxyAddressField.getText()); properties.setProxyPort(proxyPortField.getText()); + properties.setOverrideLocalBinding(overrideLocalBindingYesRadio.isSelected()); + properties.setLocalAddress(localAddressField.getText()); if (postButton.isSelected()) { properties.setMethod("post"); @@ -190,7 +186,7 @@ public ConnectorProperties getProperties() { properties.setParametersMap(getProperties(queryParametersTable)); properties.setUseParametersVariable(useQueryParamsVariableRadio.isSelected()); properties.setParametersVariable(queryParamsVariableField.getText()); - + properties.setHeadersMap(getProperties(headersTable)); properties.setUseHeadersVariable(useHeadersVariableRadio.isSelected()); properties.setHeadersVariable(headersVariableField.getText()); @@ -219,6 +215,15 @@ public void setProperties(ConnectorProperties properties) { proxyAddressField.setText(props.getProxyAddress()); proxyPortField.setText(props.getProxyPort()); + if (props.isOverrideLocalBinding()) { + overrideLocalBindingYesRadio.setSelected(true); + overrideLocalBindingYesRadioActionPerformed(); + } else { + overrideLocalBindingNoRadio.setSelected(true); + overrideLocalBindingNoRadioActionPerformed(); + } + localAddressField.setText(props.getLocalAddress()); + if (props.getMethod().equalsIgnoreCase("post")) { postButton.setSelected(true); postButtonActionPerformed(null); @@ -656,6 +661,15 @@ public boolean checkProperties(ConnectorProperties properties, boolean highlight } } + if (props.isOverrideLocalBinding()) { + if (props.getLocalAddress().length() <= 3) { + valid = false; + if (highlight) { + localAddressField.setBackground(UIConstants.INVALID_COLOR); + } + } + } + return valid; } @@ -670,6 +684,7 @@ public void resetInvalidProperties() { headersVariableField.setBackground(null); contentTypeField.setBackground(null); contentTextArea.setBackground(null); + localAddressField.setBackground(null); } @Override @@ -889,6 +904,12 @@ private void initComponents() { responseBinaryMimeTypesField = new MirthTextField(); responseBinaryMimeTypesRegexCheckBox = new MirthCheckBox(); patchButton = new MirthRadioButton(); + overrideLocalBindingLabel = new JLabel(); + overrideLocalBindingButtonGroup = new ButtonGroup(); + overrideLocalBindingYesRadio = new MirthRadioButton(); + overrideLocalBindingNoRadio = new MirthRadioButton(); + localAddressLabel = new JLabel(); + localAddressField = new MirthIconTextField(); setBackground(new Color(255, 255, 255)); setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); @@ -926,13 +947,13 @@ public void actionPerformed(ActionEvent evt) { useQueryParamsTableRadio.addActionListener(event -> { useQueryParamsVariableFieldsEnabled(false); }); - + useQueryParamsVariableRadio.setText("Use Map:"); useQueryParamsVariableRadio.setBackground(new Color(255, 255, 255)); useQueryParamsVariableRadio.addActionListener(event -> { useQueryParamsVariableFieldsEnabled(true); }); - + ButtonGroup queryParamsButtonGroup = new ButtonGroup(); queryParamsButtonGroup.add(useQueryParamsTableRadio); queryParamsButtonGroup.add(useQueryParamsVariableRadio); @@ -989,17 +1010,17 @@ public void actionPerformed(ActionEvent evt) { useHeadersTableRadio.addActionListener(event -> { useHeadersVariableFieldsEnabled(false); }); - + useHeadersVariableRadio.setText("Use Map:"); useHeadersVariableRadio.setBackground(new Color(255, 255, 255)); useHeadersVariableRadio.addActionListener(event -> { useHeadersVariableFieldsEnabled(true); }); - + ButtonGroup headersButtonGroup = new ButtonGroup(); headersButtonGroup.add(useHeadersTableRadio); headersButtonGroup.add(useHeadersVariableRadio); - + responseContentLabel.setText("Response Content:"); responseContentXmlBodyRadio.setBackground(new Color(255, 255, 255)); @@ -1203,8 +1224,38 @@ public void actionPerformed(ActionEvent evt) { patchButtonActionPerformed(evt); } }); + + localAddressLabel.setText("Local Address:"); + + localAddressField.setToolTipText("The local address that the client socket will be bound to, if Override Local Binding is set to Yes.
"); + + overrideLocalBindingLabel.setText("Override Local Binding:"); + + overrideLocalBindingYesRadio.setBackground(new Color(255, 255, 255)); + overrideLocalBindingYesRadio.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + overrideLocalBindingButtonGroup.add(overrideLocalBindingYesRadio); + overrideLocalBindingYesRadio.setText("Yes"); + overrideLocalBindingYesRadio.setToolTipText("Select Yes to override the local address that the client socket will be bound to.
Select No to use the default values of 0.0.0.0:0.
"); + overrideLocalBindingYesRadio.setMargin(new Insets(0, 0, 0, 0)); + overrideLocalBindingYesRadio.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + overrideLocalBindingYesRadioActionPerformed(); + } + }); + + overrideLocalBindingNoRadio.setBackground(new Color(255, 255, 255)); + overrideLocalBindingNoRadio.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + overrideLocalBindingButtonGroup.add(overrideLocalBindingNoRadio); + overrideLocalBindingNoRadio.setText("No"); + overrideLocalBindingNoRadio.setToolTipText("Select Yes to override the local address that the client socket will be bound to.
Select No to use the default values of 0.0.0.0:0.
"); + overrideLocalBindingNoRadio.setMargin(new Insets(0, 0, 0, 0)); + overrideLocalBindingNoRadio.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + overrideLocalBindingNoRadioActionPerformed(); + } + }); } - + private void initToolTips() { urlField.setToolTipText("Enter the URL of the HTTP server to send each message to."); queryParametersTable.setToolTipText("Query parameters are encoded as x=y pairs as part of the request URL, separated from it by a '?' and from each other by an '&'."); @@ -1243,15 +1294,15 @@ private void initToolTips() { patchButton.setToolTipText("Selects the HTTP operation used to send each message."); useQueryParamsTableRadio.setToolTipText("The table below will be used to populate query parameters."); useQueryParamsVariableRadio.setToolTipText("The Java map specified by the following variable will be used to populate query parameters.
The map must have String keys and either String or List<String> values."); - queryParamsVariableField.setToolTipText("The variable of a Java map to use to populate query parameters.
The map must have String keys and either String or List<String> values."); + queryParamsVariableField.setToolTipText("The variable of a Java map to use to populate query parameters.
The map must have String keys and either String or List<String> values."); useHeadersTableRadio.setToolTipText("The table below will be used to populate headers."); useHeadersVariableRadio.setToolTipText("The Java map specified by the following variable will be used to populate headers.
The map must have String keys and either String or List<String> values."); headersVariableField.setToolTipText("The variable of a Java map to use to populate headers.
The map must have String keys and either String or List<String> values."); } - + private void initLayout() { setLayout(new MigLayout("insets 0 8 0 8, novisualpadding, hidemode 3, gap 12 6", "[][]6[]", "[][][][][][][][][][][][][][][][][grow][][grow][][][][grow]")); - + add(urlLabel, "right"); add(urlField, "w 312!, sx, split 2"); add(testConnection, "gapbefore 6"); @@ -1262,6 +1313,11 @@ private void initLayout() { add(proxyAddressField, "w 202!, sx"); add(proxyPortLabel, "newline, right"); add(proxyPortField, "w 56!, sx"); + add(overrideLocalBindingLabel, "newline, right"); + add(overrideLocalBindingYesRadio, "split 2"); + add(overrideLocalBindingNoRadio); + add(localAddressLabel, "newline, right"); + add(localAddressField, "w 200!, sx"); add(methodLabel, "newline, right"); add(postButton, "split 5"); add(getButton); @@ -1476,21 +1532,31 @@ private void dataTypeTextRadioActionPerformed(ActionEvent evt) { charsetEncodingCombobox.setEnabled(true); } } - + private void useHeadersVariableFieldsEnabled(boolean useVariable) { headersVariableField.setEnabled(useVariable); headersTable.setEnabled(!useVariable); headersNewButton.setEnabled(!useVariable); headersDeleteButton.setEnabled(!useVariable && headersTable.getSelectedRow() > -1); } - + private void useQueryParamsVariableFieldsEnabled(boolean useVariable) { queryParamsVariableField.setEnabled(useVariable); queryParametersTable.setEnabled(!useVariable); queryParametersNewButton.setEnabled(!useVariable); queryParametersDeleteButton.setEnabled(!useVariable && queryParametersTable.getSelectedRow() > -1); } - + + private void overrideLocalBindingYesRadioActionPerformed() { + localAddressField.setEnabled(true); + localAddressLabel.setEnabled(true); + } + + private void overrideLocalBindingNoRadioActionPerformed() { + localAddressField.setEnabled(false); + localAddressLabel.setEnabled(false); + } + private ButtonGroup authenticationButtonGroup; private JLabel authenticationLabel; private MirthRadioButton authenticationNoRadio; @@ -1570,4 +1636,10 @@ private void useQueryParamsVariableFieldsEnabled(boolean useVariable) { private MirthRadioButton useProxyServerYesRadio; private MirthTextField usernameField; private JLabel usernameLabel; + private JLabel overrideLocalBindingLabel; + private ButtonGroup overrideLocalBindingButtonGroup; + private MirthRadioButton overrideLocalBindingNoRadio; + private MirthRadioButton overrideLocalBindingYesRadio; + private JLabel localAddressLabel; + private MirthIconTextField localAddressField; } diff --git a/server/src/com/mirth/connect/connectors/http/HttpConnectorServlet.java b/server/src/com/mirth/connect/connectors/http/HttpConnectorServlet.java index ff6684845..766bbe597 100644 --- a/server/src/com/mirth/connect/connectors/http/HttpConnectorServlet.java +++ b/server/src/com/mirth/connect/connectors/http/HttpConnectorServlet.java @@ -1,11 +1,5 @@ -/* - * Copyright (c) Mirth Corporation. All rights reserved. - * - * http://www.mirthcorp.com - * - * The software in this package is published under the terms of the MPL license a copy of which has - * been included with this distribution in the LICENSE.txt file. - */ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: Mirth Corporation package com.mirth.connect.connectors.http; @@ -15,8 +9,6 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.SecurityContext; -import org.apache.commons.lang3.StringUtils; - import com.mirth.connect.client.core.api.MirthApiException; import com.mirth.connect.server.api.MirthServlet; import com.mirth.connect.server.util.ConnectorUtil; @@ -36,9 +28,22 @@ public HttpConnectorServlet(@Context HttpServletRequest request, @Context Securi public ConnectionTestResponse testConnection(String channelId, String channelName, HttpDispatcherProperties properties) { try { URL url = new URL(replacer.replaceValues(properties.getHost(), channelId, channelName)); - int port = url.getPort(); // If no port was provided, default to port 80 or 443. - return ConnectorUtil.testConnection(url.getHost(), (port == -1) ? (StringUtils.equalsIgnoreCase(url.getProtocol(), "https") ? 443 : 80) : port, TIMEOUT); + final int port; + if (url.getPort() != -1) { + port = url.getPort(); + } else if ("https".equalsIgnoreCase(url.getProtocol())) { + port = 443; + } else { + port = 80; + } + + if (!properties.isOverrideLocalBinding()) { + return ConnectorUtil.testConnection(url.getHost(), port, TIMEOUT); + } else { + String localAddr = replacer.replaceValues(properties.getLocalAddress(), channelId, channelName); + return ConnectorUtil.testConnection(url.getHost(), port, TIMEOUT, localAddr); + } } catch (Exception e) { throw new MirthApiException(e); } diff --git a/server/src/com/mirth/connect/connectors/http/HttpDispatcher.java b/server/src/com/mirth/connect/connectors/http/HttpDispatcher.java index 8bd865700..91bbd2af0 100644 --- a/server/src/com/mirth/connect/connectors/http/HttpDispatcher.java +++ b/server/src/com/mirth/connect/connectors/http/HttpDispatcher.java @@ -1,15 +1,10 @@ -/* - * Copyright (c) Mirth Corporation. All rights reserved. - * - * http://www.mirthcorp.com - * - * The software in this package is published under the terms of the MPL license a copy of which has - * been included with this distribution in the LICENSE.txt file. - */ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: Mirth Corporation package com.mirth.connect.connectors.http; import java.io.File; +import java.net.InetAddress; import java.net.URI; import java.nio.charset.Charset; import java.util.ArrayList; @@ -27,6 +22,7 @@ import javax.mail.util.ByteArrayDataSource; import com.mirth.connect.client.core.BrandingConstants; +import com.mirth.connect.util.TcpUtil; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.map.CaseInsensitiveMap; import org.apache.commons.fileupload.FileUploadBase; @@ -192,6 +188,7 @@ public void replaceConnectorProperties(ConnectorProperties connectorProperties, httpDispatcherProperties.setHost(replacer.replaceValues(httpDispatcherProperties.getHost(), connectorMessage)); httpDispatcherProperties.setProxyAddress(replacer.replaceValues(httpDispatcherProperties.getProxyAddress(), connectorMessage)); httpDispatcherProperties.setProxyPort(replacer.replaceValues(httpDispatcherProperties.getProxyPort(), connectorMessage)); + httpDispatcherProperties.setLocalAddress(replacer.replaceValues(httpDispatcherProperties.getLocalAddress(), connectorMessage)); httpDispatcherProperties.setResponseBinaryMimeTypes(replacer.replaceValues(httpDispatcherProperties.getResponseBinaryMimeTypes(), connectorMessage)); httpDispatcherProperties.setHeadersMap(replacer.replaceKeysAndValuesInMap(httpDispatcherProperties.getHeadersMap(), connectorMessage)); httpDispatcherProperties.setHeadersVariable(replacer.replaceValues(httpDispatcherProperties.getHeadersVariable(), connectorMessage)); @@ -258,9 +255,9 @@ public Response send(ConnectorProperties connectorProperties, ConnectorMessage c /* * If a charset is set in the Content Type field, use that. - * + * * If the charset is NONE, keep it as null. - * + * * Otherwise, use the charset from the Character Encoding drop-down menu. */ Charset charset = null; @@ -312,8 +309,14 @@ public Response send(ConnectorProperties connectorProperties, ConnectorMessage c logger.debug("using authentication with credentials: " + credentials); } - RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(socketTimeout).setSocketTimeout(socketTimeout).setStaleConnectionCheckEnabled(true).build(); - context.setRequestConfig(requestConfig); + final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom() + .setConnectTimeout(socketTimeout) + .setSocketTimeout(socketTimeout) + .setStaleConnectionCheckEnabled(true); + if (httpDispatcherProperties.isOverrideLocalBinding()) { + requestConfigBuilder.setLocalAddress(InetAddress.getByName(TcpUtil.getFixedHost(httpDispatcherProperties.getLocalAddress()))); + } + context.setRequestConfig(requestConfigBuilder.build()); // Set proxy information if (httpDispatcherProperties.isUseProxyServer()) { @@ -444,7 +447,7 @@ public boolean isBinaryContentType(ContentType contentType) { return new Response(responseStatus, responseData, responseStatusMessage, responseError, validateResponse); } - + protected boolean shouldParseMultipart(HttpDispatcherProperties httpDispatcherProperties, String mimeType) { // Only parse multipart if XML Body is selected and Parse Multipart is enabled return httpDispatcherProperties.isResponseXmlBody() && httpDispatcherProperties.isResponseParseMultipart() && mimeType.startsWith(FileUploadBase.MULTIPART); diff --git a/server/src/com/mirth/connect/connectors/http/HttpDispatcherProperties.java b/server/src/com/mirth/connect/connectors/http/HttpDispatcherProperties.java index 5689c2e4d..8892dda27 100644 --- a/server/src/com/mirth/connect/connectors/http/HttpDispatcherProperties.java +++ b/server/src/com/mirth/connect/connectors/http/HttpDispatcherProperties.java @@ -1,11 +1,6 @@ -/* - * Copyright (c) Mirth Corporation. All rights reserved. - * - * http://www.mirthcorp.com - * - * The software in this package is published under the terms of the MPL license a copy of which has - * been included with this distribution in the LICENSE.txt file. - */ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: Mirth Corporation + package com.mirth.connect.connectors.http; @@ -32,6 +27,8 @@ public class HttpDispatcherProperties extends ConnectorProperties implements Des private boolean useProxyServer; private String proxyAddress; private String proxyPort; + private boolean overrideLocalBinding; + private String localAddress; private String method; private Map> headers; private Map> parameters; @@ -63,6 +60,8 @@ public HttpDispatcherProperties() { this.useProxyServer = false; this.proxyAddress = ""; this.proxyPort = ""; + this.overrideLocalBinding = false; + this.localAddress = "0.0.0.0"; this.method = "post"; this.headers = new LinkedHashMap>(); this.useHeadersVariable = false; @@ -96,6 +95,8 @@ public HttpDispatcherProperties(HttpDispatcherProperties props) { useProxyServer = props.isUseProxyServer(); proxyAddress = props.getProxyAddress(); proxyPort = props.getProxyPort(); + overrideLocalBinding = props.isOverrideLocalBinding(); + localAddress = props.getLocalAddress(); method = props.getMethod(); headers = new LinkedHashMap>(); @@ -162,6 +163,22 @@ public void setProxyPort(String proxyPort) { this.proxyPort = proxyPort; } + public boolean isOverrideLocalBinding() { + return overrideLocalBinding; + } + + public void setOverrideLocalBinding(boolean overrideLocalBinding) { + this.overrideLocalBinding = overrideLocalBinding; + } + + public String getLocalAddress() { + return localAddress; + } + + public void setLocalAddress(String localAddress) { + this.localAddress = localAddress; + } + public String getMethod() { return method; } @@ -173,7 +190,7 @@ public void setMethod(String method) { public Map> getHeadersMap() { return headers; } - + public boolean isUseHeadersVariable() { return useHeadersVariable; } @@ -189,7 +206,7 @@ public void setHeadersMap(Map> headers) { public String getHeadersVariable() { return this.headersVariable; } - + public void setHeadersVariable(String headerVariable) { this.headersVariable = headerVariable; } @@ -209,11 +226,11 @@ public boolean isUseParametersVariable() { public void setUseParametersVariable(boolean useParametersVariable) { this.useParametersVariable = useParametersVariable; } - + public String getParametersVariable() { return this.parametersVariable; } - + public void setParametersVariable(String variableName) { this.parametersVariable = variableName; } @@ -513,8 +530,8 @@ public void migrate3_2_0(DonkeyElement element) { @Override public void migrate3_6_0(DonkeyElement element) {} @Override public void migrate3_7_0(DonkeyElement element) {} @Override public void migrate3_9_0(DonkeyElement element) {} - @Override public void migrate3_11_0(DonkeyElement element) {} - @Override public void migrate3_11_1(DonkeyElement element) {} + @Override public void migrate3_11_0(DonkeyElement element) {} + @Override public void migrate3_11_1(DonkeyElement element) {} @Override public void migrate3_12_0(DonkeyElement element) {}// @formatter:on @Override diff --git a/server/src/com/mirth/connect/server/util/ConnectorUtil.java b/server/src/com/mirth/connect/server/util/ConnectorUtil.java index 3ffef5a53..820711985 100644 --- a/server/src/com/mirth/connect/server/util/ConnectorUtil.java +++ b/server/src/com/mirth/connect/server/util/ConnectorUtil.java @@ -1,11 +1,5 @@ -/* - * Copyright (c) Mirth Corporation. All rights reserved. - * - * http://www.mirthcorp.com - * - * The software in this package is published under the terms of the MPL license a copy of which has - * been included with this distribution in the LICENSE.txt file. - */ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: Mirth Corporation package com.mirth.connect.server.util; @@ -22,6 +16,10 @@ public static ConnectionTestResponse testConnection(String host, int port, int t return testConnection(host, port, timeout, null, 0); } + public static ConnectionTestResponse testConnection(String host, int port, int timeout, String localAddr) throws Exception { + return testConnection(host, port, timeout, localAddr, 0); + } + public static ConnectionTestResponse testConnection(String host, int port, int timeout, String localAddr, int localPort) throws Exception { Socket socket = null; InetSocketAddress address = null;