HTTP Client Post Call

Hi All,

I am pretty new to using the Post Call method to post data. While I tried to use the httpClient function to post a request on the Boomi server, I got the following error saying the existing connection is forcibly closed by the remote host. Can anyone point me in the direction of why this error occurred? Here is my script:

    user = "user" 
	password = "password"
	info = {"ProductPartNumber" : " **** ", "FacilityPrefix" : "***", "SqlWhere": "*****"}
	url = "https://****.com/ws/rest/Ignition?messageType=GET_PRODUCT_SPEC"
	# Create the JythonHttpClient.
	client = system.net.httpClient(username=user, password=password, bypass_cert_validation = True)
	response = client.post(url, info)

Error:

	... 38 common frames omitted
Caused by: com.inductiveautomation.ignition.common.GenericTransferrableException: Unable to POST https://bil-boomi01dv.entegris.com/ws/rest/Ignition?messageType=GET_PRODUCT_SPEC?ProductPartNumber=+AEC010M1G3000T+&FacilityPrefix=LSP&SqlWhere=and+PRODUCT_SPEC.SPEC_TYPE+in+%28%27IC%27%2C%27TOC%27%2C%27LPC%27%29
	at com.inductiveautomation.ignition.common.script.builtin.http.JythonHttpClient.send(JythonHttpClient.java:103)
	at com.inductiveautomation.ignition.common.script.builtin.http.InsecureJythonHttpClient.send(InsecureJythonHttpClient.java:37)
	at com.inductiveautomation.ignition.common.script.builtin.http.JythonHttpClient.post(JythonHttpClient.java:318)
	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)
	... 35 common frames omitted
Caused by: com.inductiveautomation.ignition.common.GenericTransferrableException: An existing connection was forcibly closed by the remote host
	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)
	... 42 common frames omitted
Caused by: com.inductiveautomation.ignition.common.GenericTransferrableException: An existing connection was forcibly closed by the remote host
	at java.base/sun.nio.ch.SocketDispatcher.read0(Native Method)
	at java.base/sun.nio.ch.SocketDispatcher.read(Unknown Source)
	at java.base/sun.nio.ch.IOUtil.readIntoNativeBuffer(Unknown Source)
	at java.base/sun.nio.ch.IOUtil.read(Unknown Source)
	at java.base/sun.nio.ch.IOUtil.read(Unknown Source)
	at java.base/sun.nio.ch.SocketChannelImpl.read(Unknown Source)
	at java.net.http/jdk.internal.net.http.SocketTube.readAvailable(Unknown Source)
	at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$InternalReadSubscription.read(Unknown Source)
	at java.net.http/jdk.internal.net.http.SocketTube$SocketFlowTask.run(Unknown Source)
	at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(Unknown Source)
	at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(Unknown Source)
	at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(Unknown Source)
	at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$InternalReadSubscription.signalReadable(Unknown Source)
	at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$ReadEvent.signalEvent(Unknown Source)
	at java.net.http/jdk.internal.net.http.SocketTube$SocketFlowEvent.handle(Unknown Source)
	at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.handleEvent(Unknown Source)
	at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.lambda$run$3(Unknown Source)
	at java.base/java.util.ArrayList.forEach(Unknown Source)
	at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(Unknown Source)

Please make sure you format code correctly when posting using the </> button.
Try this

user = "user" 
password = "password" 
data = {"ProductPartNumber" : " **** ", "FacilityPrefix" : "***", "SqlWhere": "*****"} 
url = "https://****.com/ws/rest/Ignition?messageType=GET_PRODUCT_SPEC" 
# Create the JythonHttpClient. 
client = system.net.httpClient(username=user, password=password, bypass_cert_validation = True) 
response = client.post(url, data=data)

got the same error.

The exact same error? The first error you posted looks like this:
https://xxx/ws/rest/Ignition?messageType=GET_PRODUCT_SPEC?ProductPartNumber=+AEC010M1G3000T+&FacilityPrefix=LSP&SqlWhere=and+PRODUCT_SPEC.SPEC_TYPE+in+%28%27IC%27%2C%27TOC%27%2C%27LPC%27%29

Which means you're sending your data dictionary as URL parameters, which is probably not what your server is expecting; typically POST routes expect data in the request body.

Can you post the actual error message (you may want to sanitize it, if you consider your URL sensitive) you get if you execute something like this:

user = "user" 
password = "password" 
client = system.net.httpClient(username=user, password=password, bypass_cert_validation = True) 

url = "https://****.com/ws/rest/Ignition" 
params = { "messageType": "GET_PRODUCT_SPEC" }
data = { "ProductPartNumber" : " **** ", "FacilityPrefix" : "***", "SqlWhere": "*****"} 
# Create the JythonHttpClient. 
response = client.post(url, params=params, data=data)

Note that the only error here is this: An existing connection was forcibly closed by the remote host, which is pretty clearly indicating the server isn't accepting your request; instead of returning an error code it's forcibly closing the socket.

As an aside:

:grimacing:
Accepting arbitrary strings as SQL input is super dangerous - I hope you know what you're doing.

3 Likes

This is the error after implementing what you recommended. It seems to me that they are the exact same error.

com.inductiveautomation.ignition.common.script.JythonExecException
Traceback (most recent call last):
  File "<function:runAction>", line 2, in runAction
  File "<custom-method specRuleTest>", line 13, in specRuleTest
java.io.IOException: java.io.IOException: Unable to POST https://****.com/ws/rest/Ignition?messageType=GET_PRODUCT_SPEC

	caused by org.python.core.PyException
Traceback (most recent call last):
  File "<function:runAction>", line 2, in runAction
  File "<custom-method specRuleTest>", line 13, in specRuleTest
java.io.IOException: java.io.IOException: Unable to POST https://****.com/ws/rest/Ignition?messageType=GET_PRODUCT_SPEC

	caused by IOException: Unable to POST https://****.com/ws/rest/Ignition?messageType=GET_PRODUCT_SPEC
	caused by IOException: An existing connection was forcibly closed by the remote host
	caused by IOException: An existing connection was forcibly closed by the remote host


Thanks for pointing out the arbitrary strings as SQL input is dangerous, but the arbitrary strings as SQL input are developed by another team that manages the database. I was just asked to provide the data shown above to get the product info.

If I had to guess, since I do because there’s not enough information available to us, I’d say this means either:

  • the server doesn’t like the data you’re providing
  • network or firewall issue

Usually you’d get an HTTP error code when a server doesn’t like something about the data, but who knows.

1 Like

Revisiting this topic, the issue above was due to connectivity to the destination server (Boomi). Although our team resolved the issue above, we would get HTTP code 500 from time to time. Should Ignition continue to post to the server until the post call returns code 200? Is there a better way to address the occasion where post call fails?

Thanks!

Certainly not.

Check the result and retry yourself.

This is supposed to be an automatic process to post the live process data to the server, which triggers another post call to store the data. In the case of the destination server being down or unavailable, is manually triggering the post call the only option? Does Ignition provide a similar method like store and forward for API calls?

No.

HTTP is a low level protocol. If you want to layer retry semantics on top of it, you can, but it's not something that there's any general standard for, so you have to create the logic yourself.

just ran into another issue, and Here is the script:

	client = system.net.httpClient(username=user, password=password, bypass_cert_validation = True)
	system.perspective.print(client)
	now = system.date.now()
	meas = [524, 124,24,4,2,1,0]
	for i in range(7):
		data =  {"run_start_time": "03-24-2023 11:01" ,
			 "meas_time": now,
			 "reported_meas":  meas[i] , 
			 "analysis": "LPC", 
			 "component": "ch"+str(i+1), 
			 "upw_tool_id":  233 ,
			 "instrument_id": "LPC_001", 
			 "mfc_number": "A",
			 "filter_pos": 1,
			 "catalog_number": self.parent.custom.catalog,
			 "lot_id": "300KT02",
			 "serial_number": "004",
			 "sequence_number": self.parent.custom.seq,
			 "run_end_time": "",
			 "results": "Pass",
			 "collection_method": "Auto",
			 "injection_type": "DATA",
			 "run_id": 1299, 
			 "facility": "LSP" 
		}
		
		response = client.post(boomiURL, data = data)

this is a test script for the post call. During one of the testings, the end server missed the last four messages from Ignition, but I could see on the designer console that the responses were good. However, the subsequent tests showed that all 7 messaged went through successfully to the end server.

Is it okay to post messages continuously without delay? or Is it better to have delay between each post?

That's up to your server. Best practice would be to change or add a new route on the server so that it can accept multiple result sets in one transaction; that way you're only going over the network once. You certainly can artificially slow down your requests, if your server needs it. I can pretty much guarantee that the issue isn't the client, here.