# New in 8.0.6: system.net.httpClient()

**URL:** <https://forum.inductiveautomation.com/t/new-in-8-0-6-system-net-httpclient/29297>\
**Category:** Ignition Early Access\
**Created:** [October 22, 2019, 7:11pm UTC](https://forum.inductiveautomation.com/t/new-in-8-0-6-system-net-httpclient/29297 "2019-10-22T19:11:13Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 22, 2019, 7:11pm UTC](https://forum.inductiveautomation.com/t/new-in-8-0-6-system-net-httpclient/29297/1 "2019-10-22T19:11:13Z")

</div>

Coming in 8.0.6, and available in nightly builds as of today, a not-as-pretty but hopefully pretty exciting new feature: `system.net.httpClient`. This is a new function that will deprecate the existing `system.net.http*` functions. I’m excited for users to try it out. It wraps the new-to-Java-11 HttpClient with some convenience stuff for usage from Python. Full details are below, but basic usage is pretty similar to the existing functions, just much more powerful:

```python
client = system.net.httpClient()
promise = client.getAsync("https://httpbin.org/get", params={"a": 1, "b": 2})
print "doing something while waiting"
response = promise.get()
if response.good:
    print response.json['args']['a']

```

A key feature are asynchronous requests - each common HTTP verb (get, post, put, patch, etc) is covered with a matching asynchronous variant, plus there are generic fallback `request()/requestAsync()` methods to enable custom HTTP verbs to be used.

> ****
>
> ## Usage Examples
> 
> ### Synchronous (Blocking)
> 
> ```python
> client = system.net.httpClient()
> print client
> >>> <JythonHttpClient@1072310501 timeout=10000, bypass_cert_validation=False, has_credentials=False, proxy=None, cookie_policy='ACCEPT_ORIGINAL_SERVER', redirect_policy='NEVER'>
> 
> ```
> 
> Create a new JythonHttpClient instance with default parameters. This client accepts most common HTTP methods (GET, POST, PATCH, etc) directly, as well as arbitrary HTTP methods (.request())
> 
> ```python
> response = client.get("https://httpbin.org/get")
> print response
> >>> <Response@1848533253 'https://httpbin.org/get' [200]>
> 
> ```
> 
> Run a simple GET request against a URL. Returns a Response object, which has various convenience methods, such as .text, .json, etc.
> 
> ```python
> print response.json
> >>> {'headers': {'User-Agent': u'Ignition', 'Host': u'httpbin.org'}, 'origin': u'70.96.196.62, 70.96.196.62', 'url': u'https://httpbin.org/get', 'args': {}}
> 
> ```
> 
> Return the JSON body of a response directly as a Python object (most likely, a dictionary containing other dictionaries). If the body can’t be decoded as JSON, throws an error.
> 
> #### Complete Example
> 
> ```python
> client = system.net.httpClient()
> response = client.get("https://httpbin.org/get", params={"a": 1, "b": 2})
> if response.good:
> print response.json['args']['a']
> 
> ```
> 
> ### Asynchronous (Non-Blocking)
> 
> ```python
> def callback(arg):
> ## do something out of band
> print 'asyncHandler', arg
> 
> ```
> 
> First, define a callback function, which will be executed with the result of the HTTP request (or the error, if an error was thrown during evaluation). All httpClient functions return a Promise that contains a Response object.
> 
> ```python
> c = system.net.httpClient()
> p = c.getAsync("https://httpbin.org/status/404")
> 
> ```
> 
> Then, create your client and call the async variant of an HTTP method. This will directly return a Promise object.
> 
> ```python
> p.whenComplete(callback)
> 
> ```
> 
> Add your callback to the Promise object. The whenComplete(callback) handler will fire whenever the Promise resolves, but is not expected to return anything, in contrast with .then(callback), which is expected to return a new value which will itself be wrapped in a Promise, allowing for chaining.
> 
> ```python
> print p
> print p.get()
> >>> <Promise@1427747612 isDone=false>
> <Response@1299364187 'https://httpbin.org/status/404' [404]>
> 
> ```
> 
> Finally, you can also directly return the result by calling .get([timeout]). If not specified, the timeout defaults to 60 seconds. This is optional - you can fire off asynchronous requests and they will be automatically executed, even if you don’t ever ask them to block.
> 
> #### Complete Example
> 
> ```python
> client = system.net.httpClient()
> promise = client.getAsync("https://httpbin.org/get", params={"a": 1, "b": 2})
> print "doing something while waiting"
> response = promise.get()
> if response.good:
> print response.json['args']['a']
> 
> ```
> 
> ## `system.net.httpClient()`
> 
> Creates a new JythonHttpClient instance.
> 
> ### Parameters
> 
> | Parameter | Type | Default Value | Description |
> | --- | --- | --- | --- |
> | timeout | int | 60000 | A value, in milliseconds, to set the client’s connect timeout setting to. |
> | bypass\_cert\_validation | bool | False | A boolean indicating whether to bypass SSL verification on the target host. Pass `True` to skip verification. |
> | username | str | None | A string indicating the username to use for authentication if the remote server requests authentication; specifically, by responding with a `WWW-Authenticate` or `Proxy-Authenticate` header. Only supports Basic authorization. If username is present but not password, an empty string will be used for password. |
> | password | str | None | A string indicating the password to use for authentication. |
> | proxy | str | None | The address of a proxy, which will be used for HTTP and HTTPS traffic. If the port is not specified, it will be assumed from the protocol in the URL, i.e. 80/443. |
> | cookie\_policy | str | “ACCEPT\_ORIGINAL\_SERVER” | A string representing this client’s cookie policy. Accepts values `ACCEPT_ALL`, `ACCEPT_NONE`, and `ACCEPT_ORIGINAL_SERVER`. |
> | redirect\_policy | str | “NORMAL” | A string representing this client’s redirect policy. Accepts values `NEVER`, `ALWAYS`, and `NORMAL` (allows redirects, except HTTPS -\> HTTP downgrades. |
> | customizer | Callable | None | A reference to a callable function. This function will be called with one argument (an instance of HttpClient.Builder). The function should operate on the builder class and allows for arbitrary customization of the created HTTP client. |
> 
> ## JythonHttpClient
> 
> A wrapper around Java’s `HttpClient`.
> 
> ### Methods
> 
> #### `.get(), .post(), .put(), .delete(), .head(), .patch(), .options(), .trace() -> Response`
> 
> Dispatch an HTTP request. Returns a `Response` object.
> 
> ##### Parameters
> 
> | Parameter | Type | Default Value | Description |
> | --- | --- | --- | --- |
> | url | str | Required | The URL to connect to. |
> | parameters | str, dict | None | Parameters to append to the URL. If supplied as a string, will be directly appended to the URL. If supplied as a dictionary, key/value pairs will be automatically URL encoded. |
> | data\* | str | None | Data to send in the request. String data will be sent with a `Content-Type` of `text/plain; charset=UTF-8`, unless the `Content-Type` header was already specified. |
> | data\* | dict | None | Data to send in the request. Will be automatically encoded into JSON, and set the Content-Type header to application/json;charset=UTF-8 unless the Content-Type header was already specified. |
> | data\* | byte[] | None | Data to send in the request. A byte array will be sent directly, and set the Content-Type header to application/octet-stream unless the Content-Type header was already specified. |
> | file | str | None | The path to a file relative to the HTTP client. If specified, and the path is valid, will directly send the data in the file. The file attribute overrides any value set in data; only the file’s data will be sent. |
> | headers | dict | None | A dictionary of headers to add to the request. |
> | username | str | None | Username to add to a Basic Authorization header in the outgoing request. If username is specified, but not password, password is assumed to be an empty string. |
> | password | str | None | Password to add to a Basic Authorization header in the outgoing request. |
> | timeout | int | 60000 | Timeout for this request, in milliseconds. |
> 
> #### `.request() -> Response`
> 
> Same as the above functions, but with an additional required argument, `method`:
> 
> ##### Parameters
> 
> | Parameter | Type | Default Value | Description |
> | --- | --- | --- | --- |
> | url | str | Required | The URL to connect to. |
> | method | str | Required | The method to use in the request. |
> | parameters | str, dict | None | Parameters to append to the URL. If supplied as a string, will be directly appended to the URL. If supplied as a dictionary, key/value pairs will be automatically URL encoded. |
> | data\* | str | None | Data to send in the request. String data will be sent with a `Content-Type` of `text/plain; charset=UTF-8`, unless the `Content-Type` header was already specified. |
> | data\* | dict | None | Data to send in the request. Will be automatically encoded into JSON, and set the Content-Type header to application/json;charset=UTF-8 unless the Content-Type header was already specified. |
> | data\* | byte[] | None | Data to send in the request. A byte array will be sent directly, and set the Content-Type header to application/octet-stream unless the Content-Type header was already specified. |
> | file | str | None | The path to a file relative to the HTTP client. If specified, and the path is valid, will directly send the data in the file. The file attribute overrides any value set in data; only the file’s data will be sent. |
> | headers | dict | None | A dictionary of headers to add to the request. |
> | username | str | None | Username to add to a Basic Authorization header in the outgoing request. If username is specified, but not password, password is assumed to be an empty string. |
> | password | str | None | Password to add to a Basic Authorization header in the outgoing request. |
> | timeout | int | 60000 | Timeout for this request, in milliseconds. |
> 
> #### `.getAsync(), .postAsync(), .putAsync(), .deleteAsync(), .headAsync(), .patchAsync(), .optionsAsync(), .traceAsync() -> Promise`
> 
> Returns a new Promise instance.
> 
> ##### Parameters
> 
> | Parameter | Type | Default Value | Description |
> | --- | --- | --- | --- |
> | url | str | Required | The URL to connect to. |
> | parameters | str, dict | None | Parameters to append to the URL. If supplied as a string, will be directly appended to the URL. If supplied as a dictionary, key/value pairs will be automatically URL encoded. |
> | data\* | str | None | Data to send in the request. String data will be sent with a `Content-Type` of `text/plain; charset=UTF-8`, unless the `Content-Type` header was already specified. |
> | data\* | dict | None | Data to send in the request. Will be automatically encoded into JSON, and set the Content-Type header to application/json;charset=UTF-8 unless the Content-Type header was already specified. |
> | data\* | byte[] | None | Data to send in the request. A byte array will be sent directly, and set the Content-Type header to application/octet-stream unless the Content-Type header was already specified. |
> | file | str | None | The path to a file relative to the HTTP client. If specified, and the path is valid, will directly send the data in the file. The file attribute overrides any value set in data; only the file’s data will be sent. |
> | headers | dict | None | A dictionary of headers to add to the request. |
> | username | str | None | Username to add to a Basic Authorization header in the outgoing request. If username is specified, but not password, password is assumed to be an empty string. |
> | password | str | None | Password to add to a Basic Authorization header in the outgoing request. |
> | timeout | int | 60000 | Timeout for this request, in milliseconds. |
> 
> #### .requestAsync() -\> Promise
> 
> Same as the above functions, but with an additional required argument, method:
> 
> ##### Parameters
> 
> | Parameter | Type | Default Value | Description |
> | --- | --- | --- | --- |
> | url | str | Required | The URL to connect to. |
> | method | str | Required | The method to use in the request. |
> | parameters | str, dict | None | Parameters to append to the URL. If supplied as a string, will be directly appended to the URL. If supplied as a dictionary, key/value pairs will be automatically URL encoded. |
> | data\* | str | None | Data to send in the request. String data will be sent with a `Content-Type` of `text/plain; charset=UTF-8`, unless the `Content-Type` header was already specified. |
> | data\* | dict | None | Data to send in the request. Will be automatically encoded into JSON, and set the Content-Type header to application/json;charset=UTF-8 unless the Content-Type header was already specified. |
> | data\* | byte[] | None | Data to send in the request. A byte array will be sent directly, and set the Content-Type header to application/octet-stream unless the Content-Type header was already specified. |
> | file | str | None | The path to a file relative to the HTTP client. If specified, and the path is valid, will directly send the data in the file. The file attribute overrides any value set in data; only the file’s data will be sent. |
> | headers | dict | None | A dictionary of headers to add to the request. |
> | username | str | None | Username to add to a Basic Authorization header in the outgoing request. If username is specified, but not password, password is assumed to be an empty string. |
> | password | str | None | Password to add to a Basic Authorization header in the outgoing request. |
> | timeout | int | 60000 | Timeout for this request, in milliseconds. |
> 
> ### Attributes
> 
> #### `.javaClient/.getJavaClient() -> HttpClient`
> 
> Returns the underlying Java HTTPClient.
> 
> #### `.cookieManager/.getCookieManager()`
> 
> Returns a CookieManager, which can be used to override the cookie storage policy (setCookiePolicy(policy), where policy is a java.net.CookiePolicy), or to retrieve the CookieStore directly via (getCookieStore()). The CookieStore will itself have various methods to add or retrieve cookies, as detailed on the CookieStore interface.
> 
> ## Response
> 
> A wrapper around a Java `HttpResponse`.
> 
> ### Methods
> 
> #### `.body/.getBody() -> byte[]`
> 
> Returns the response content directly.
> 
> #### `.json/.getJson([encoding]) -> dict`
> 
> Return the response content, with the encoding specified by the response (or the encoding specified by the encoding argument, decoded into Python (dictionary, list, etc). If response can’t be decoded to JSON, throws an error.
> 
> #### `.text/.getText([encoding]) -> str`
> 
> Return the response content decoded into a string - either with the charset specified in the response (or UTF-8 if not specified), or using the encoding specified in the function call.
> 
> ### Attributes
> 
> #### `.statusCode/.getStatusCode() -> int`
> 
> Return the status code of the response object, i.e. 200 or 404.
> 
> #### `.good/.isGood() -> bool`
> 
> Returns True if the response was good (i.e. 200) or False if it was a client or server error (status code between 400 and 599).
> 
> #### `.clientError/.isClientError() -> bool`
> 
> Returns True if the response was a client error, as in an HTTP 4XX response.
> 
> #### `.serverError/.isServerError() -> bool`
> 
> Returns True if the response was a server error, as in an HTTP 5XX response.
> 
> #### `.url/.getUrl() -> str`
> 
> Returns the URL this Response connected to.
> 
> #### `.headers/.getHeaders() -> dict`
> 
> Returns a case-insensitive “dictionary” of headers present on the response. Values will be in a list, even if only a single value is present.
> 
> #### `.javaResponse/.getJavaResponse() -> HttpResponse`
> 
> Returns the underlying Java HttpResponse behind this Response.
> 
> #### `.cookieManager/.getCookieManager() -> CookieManager`
> 
> Returns a CookieManager, which can be used to override the cookie storage policy (setCookiePolicy(policy), where policy is a java.net.CookiePolicy), or to retrieve the CookieStore directly via (getCookieStore()). The CookieStore will itself have various methods to add or retrieve cookies, as detailed on the CookieStore interface.
> 
> #### `.request/.getRequest() -> RequestWrapper`
> 
> Returns a RequestWrapper object, that thinly wraps a java.net.http.HttpRequest.
> 
> ## Promise
> 
> A wrapper around a Java CompletableFuture that returns some type
> 
> ### Methods
> 
> #### `.get([timeout]) -> <T>`
> 
> Block for timeout until a result is available. The result object can technically be any type, if chaining, but will be a Response object if calling one of the HTTPClient methods directly. If the timeout is met without a result, throws an exception. The default timeout is 60s, just like existing system.net.http\* functions.
> 
> #### `.then(callback) -> Promise`
> 
> Allows chaining; returns a new Promise, wrapping callback. Callback should either accept two arguments (result, error) or have a single argument but be able to accept exceptions as well as valid values.
> 
> #### `.handleException(callback) -> Promise`
> 
> In the event of an exception in a potential chain of promises, handleException will be called with one argument (the thrown error) and is expected to return a new fallback value for the next step in the promise chain.
> 
> #### `.whenComplete(callback) -> void`
> 
> Call the provided callback when this promise finishes evaluating. Callback will be called with return value as the first argument, and any thrown error as the second argument. Any return value will be ignored.
> 
> #### `.cancel() -> bool`
> 
> Attempt to cancel the wrapped Java future. Returns `True` if the cancellation succeeded.
> 
> ### Attributes
> 
> #### `.future/.getFuture()`
> 
> Returns the underlying Java CompletableFuture object that this Promise contains.
> 
> #### `.done/.isDone()`
> 
> Returns `True` if the underlying future has completed - with a good result, or an exception.
> 
> ## RequestWrapper
> 
> A wrapper around a base Java `HttpRequest`.
> 
> ### Attributes
> 
> #### `.url/.getUrl() -> str`
> 
> Returns the actual URL that was contacted in the request.
> 
> #### `.method/.getMethod() -> str`
> 
> Return the HTTP method used in this request; `GET`, `POST`, `PATCH`, etc.
> 
> #### `.headers/.getHeaders() -> dict`
> 
> Returns a case-insensitive “dictionary” of headers present on the request. Values will always be in a list, even if only a single value is present.
> 
> #### `.timeout/.getTimeout() -> int`
> 
> Returns the timeout this query was set to, or -1 if timeout was invalid.
> 
> #### `.version/.getVersion() -> str`
> 
> Returns the HTTP version used for this request; either `HTTP_1_1` or `HTTP_2`
> 
> #### `.javaRequest/.getJavaRequest() -> HttpRequest`
> 
> Returns the underlying `HttpRequest` object directly.

---

<div class="post-metadata">

**Author:** ![awalker](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/awalker/32/6874_2.png) [@awalker](https://forum.inductiveautomation.com/u/awalker)\
**Post date:** [October 22, 2019, 9:00pm UTC](https://forum.inductiveautomation.com/t/new-in-8-0-6-system-net-httpclient/29297/2 "2019-10-22T21:00:03Z")

</div>



---

<div class="post-metadata">

**Author:** ![Kyle\_Chase](https://avatars.discourse-cdn.com/v4/letter/k/9de0a6/32.png) [@Kyle\_Chase](https://forum.inductiveautomation.com/u/Kyle_Chase)\
**Post date:** [October 23, 2019, 6:46pm UTC](https://forum.inductiveautomation.com/t/new-in-8-0-6-system-net-httpclient/29297/3 "2019-10-23T18:46:59Z")

</div>

Now this is exciting. Was the existing [system.net](http://system.net) features written in FPMI? Looks like a 10+ year jump!

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [October 24, 2019, 3:39pm UTC](https://forum.inductiveautomation.com/t/new-in-8-0-6-system-net-httpclient/29297/4 "2019-10-24T15:39:50Z")

</div>

Close to, yeah. Oldest commit in our git history is 2011, and it has the original implementation of the functions, which mostly hasn’t changed since then.
