HTTP Client Connect Timeout when curl command on same device works with the same endpoint

Hey guys,

I've run into an issue that I can't really figure out with the HTTP Client. I am trying to make a simple GET call to an endpoint, which I am able to successfully make from this same device using curl on the CLI, but as soon as I try to make the call using the httpClient, I can't seem to get a valid connection to the URL.

Can anyone think of a reason that ignition would not be able to connect, even though I have confirmed a good response for the same endpoint from the same device?
image

Java Traceback:
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<module:httpGet>", line 4, in test
	at com.inductiveautomation.ignition.common.script.builtin.http.JythonHttpClient.send(JythonHttpClient.java:103)

	at com.inductiveautomation.ignition.common.script.builtin.http.JythonHttpClient.get(JythonHttpClient.java:306)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

	at java.base/java.lang.reflect.Method.invoke(Unknown Source)

java.io.IOException: java.io.IOException: Unable to GET http://192.168.1.1/api/status/system/modem_temperature


	at org.python.core.Py.JavaError(Py.java:547)

	at org.python.core.Py.JavaError(Py.java:538)

	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:192)

	at org.python.core.PyObject.__call__(PyObject.java:422)

	at org.python.core.PyMethod.instancemethod___call__(PyMethod.java:237)

	at org.python.core.PyMethod.__call__(PyMethod.java:228)

	at org.python.pycode._pyx69.test$1(<module:httpGet>:5)

	at org.python.pycode._pyx69.call_function(<module:httpGet>)

	at org.python.core.PyTableCode.call(PyTableCode.java:173)

	at org.python.core.PyBaseCode.call(PyBaseCode.java:119)

	at org.python.core.PyFunction.__call__(PyFunction.java:406)

	at org.python.pycode._pyx68.f$0(<input>:1)

	at org.python.pycode._pyx68.call_function(<input>)

	at org.python.core.PyTableCode.call(PyTableCode.java:173)

	at org.python.core.PyCode.call(PyCode.java:18)

	at org.python.core.Py.runCode(Py.java:1687)

	at org.python.core.Py.exec(Py.java:1731)

	at org.python.util.PythonInterpreter.exec(PythonInterpreter.java:277)

	at org.python.util.InteractiveInterpreter.runcode(InteractiveInterpreter.java:130)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:611)

	at com.inductiveautomation.ignition.designer.gui.tools.jythonconsole.JythonConsole$ConsoleWorker.doInBackground(JythonConsole.java:599)

	at java.desktop/javax.swing.SwingWorker$1.call(Unknown Source)

	at java.base/java.util.concurrent.FutureTask.run(Unknown Source)

	at java.desktop/javax.swing.SwingWorker.run(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

	at java.base/java.lang.Thread.run(Unknown Source)

Caused by: java.io.IOException: Unable to GET http://192.168.1.1/api/status/system/modem_temperature

	at com.inductiveautomation.ignition.common.script.builtin.http.JythonHttpClient.send(JythonHttpClient.java:103)

	at com.inductiveautomation.ignition.common.script.builtin.http.JythonHttpClient.get(JythonHttpClient.java:306)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

	at java.base/java.lang.reflect.Method.invoke(Unknown Source)

	at org.python.core.PyReflectedFunction.__call__(PyReflectedFunction.java:190)

	... 24 more

Caused by: java.net.http.HttpConnectTimeoutException: HTTP connect timed out

	at java.net.http/jdk.internal.net.http.HttpClientImpl.send(Unknown Source)

	at java.net.http/jdk.internal.net.http.HttpClientFacade.send(Unknown Source)

	at com.inductiveautomation.ignition.common.script.builtin.http.JythonHttpClient.send(JythonHttpClient.java:101)

	... 30 more

Caused by: java.net.http.HttpConnectTimeoutException: HTTP connect timed out

	at java.net.http/jdk.internal.net.http.MultiExchange.toTimeoutException(Unknown Source)

	at java.net.http/jdk.internal.net.http.MultiExchange.getExceptionalCF(Unknown Source)

	at java.net.http/jdk.internal.net.http.MultiExchange.lambda$responseAsyncImpl$7(Unknown Source)

	at java.base/java.util.concurrent.CompletableFuture.uniHandle(Unknown Source)

	at java.base/java.util.concurrent.CompletableFuture$UniHandle.tryFire(Unknown Source)

	at java.base/java.util.concurrent.CompletableFuture.postComplete(Unknown Source)

	at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(Unknown Source)

	at java.net.http/jdk.internal.net.http.Http1Exchange.lambda$cancelImpl$9(Unknown Source)

	... 3 more

Caused by: java.net.ConnectException: HTTP connect timed out

	... 11 more

Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<module:httpGet>", line 4, in test
	at com.inductiveautomation.ignition.common.script.builtin.http.JythonHttpClient.send(JythonHttpClient.java:103)

	at com.inductiveautomation.ignition.common.script.builtin.http.JythonHttpClient.get(JythonHttpClient.java:306)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)

	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

	at java.base/java.lang.reflect.Method.invoke(Unknown Source)

java.io.IOException: java.io.IOException: Unable to GET http://192.168.1.1/api/status/system/modem_temperature

You seem to be running this test from the script console, which means your script executes in the Designer, and the connection originates from the computer running the Designer. That you can curl from the CLI on that server is not relevant - can you make it work from your machine?

Try forcing a version on the httpClient instance. I've seen HTTP servers that fail to negotiate down from HTTP 2 (the default used) to HTTP 1.

Might be a classic case of me mixing up scopes for project libraries again. Am I wrong in thinking that methods defined as a Scripting Project library do not carry gateway scope into the script console?

Would I need to have this method called from a gateway event instead to make it work?

Yes. No? Not sure what you're asking.

Script libraries exist independently. They do nothing until something calls them. What that something is determines the scope.

Sorry, put a double negative in there. I incorrectly assumed the project library methods ran in gateway scope no matter where they were called from, was what I was saying. You answered my question.

I will try to get these to run with a gateway event instead. Thanks

Create a memory tag and put it in a tag change event script to test real quick.

If it works there, the issue is that the server your Gateway is running on can reach this device but the computer your Designer is running on can't.

If it also doesn't work there, then try what Paul suggested.

Fixed me up. Thanks Kevin.

My fault for continuing to mixup gateway/designer scope. Not the first time, probably not the last.

No sweat, common mixup.

I think Paul wants to put some kind of "execution scope" dropdown into the script console one day, if we can figure out how to do it without it being a scary security nightmare.

Pssst!

You may find my @system.util.runInGateway decorator helpful.

See also:

1 Like

That would be really cool/helpful!

I imagine you could piggy back off the designer roles system to deal with security. Maybe add a separate field for gateway scope access, or just use the 'save' field, since anyone with 'save' access can make changes to gateway scoped resources anyways.

I won't pretend to know what else it would impact though.

It's not so much the user facing security that is an issue, it's the idea that we'd be adding an endpoint that literally did remote code execution, which is essentially the worst possible class of security vulnerability.

The existence of such an endpoint is scary to me. I'm afraid it would make it trivial for an attacker to pivot from auth bypass to RCE, rather than also needing to find an RCE vulnerability.

2 Likes

The designer is one big auth bypass. I structured my runInGateway the way I did to ensure that any use would be auditable (project save required before the decorator works on a particular function), since you can't stop someone with designer access from actually doing Bad Things™.

1 Like