How to use HTTP client node in Webassembly export?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By pdoc

I am using the following code taken from the godot docs on how to make a http call using godot.

func send_data_to_server(url, data):
var err = 0
var http = HTTPClient.new() # Create the Client

err = http.connect_to_host(url, 80) # Connect to host/port
assert(err == OK) # Make sure connection was OK

# Wait until resolved and connected
while http.get_status() == HTTPClient.STATUS_CONNECTING or http.get_status() == HTTPClient.STATUS_RESOLVING:
    http.poll()
    print("Connecting..")
    OS.delay_msec(500)

assert(http.get_status() == HTTPClient.STATUS_CONNECTED) # Could not connect

# Some headers
var headers = [
    "User-Agent: Pirulo/1.0 (Godot)",
    "Accept: */*", 
	"Content-Type: application/x-www-form-urlencoded"
]

err = http.request(HTTPClient.METHOD_POST, "/Post", headers, data) # Request a page from the site (this one was chunked..)
assert(err == OK) # Make sure all is OK

while http.get_status() == HTTPClient.STATUS_REQUESTING:
    # Keep polling until the request is going on
    http.poll()
    print("Requesting..")
    OS.delay_msec(500)

assert(http.get_status() == HTTPClient.STATUS_BODY or http.get_status() == HTTPClient.STATUS_CONNECTED) # Make sure request finished well.

print("response? ", http.has_response()) # Site might not have a response.

if http.has_response():
    # If there is a response..

    headers = http.get_response_headers_as_dictionary() # Get response headers
    print("code: ", http.get_response_code()) # Show response code
    print("**headers:\\n", headers) # Show headers

    # Getting the HTTP Body

    if http.is_response_chunked():
        # Does it use chunks?
        print("Response is Chunked!")
    else:
        # Or just plain Content-Length
        var bl = http.get_response_body_length()
        print("Response Length: ",bl)

    # This method works for both anyway

    var rb = PoolByteArray() # Array that will hold the data

    while http.get_status() == HTTPClient.STATUS_BODY:
        # While there is body left to be read
        http.poll()
        var chunk = http.read_response_body_chunk() # Get a chunk
        if chunk.size() == 0:
            # Got nothing, wait for buffers to fill a bit
            OS.delay_usec(1000)
        else:
            rb = rb + chunk # Append to read buffer

    # Done!

    print("bytes got: ", rb.size())
    var text = rb.get_string_from_ascii()
    print("Text: ", text)

However, I keep getting the following error:

‘HTTPClient polled multiple times in one frame, but request cannot progress more than once per frame on the HTML5 platform.’

Is anybody able to point out where I am going wrong?

:bust_in_silhouette: Reply From: Zylann

I never used HttpRequest, but the error is clear:
You are doing http.poll() more than once per frame.
For example:

while http.get_status() == HTTPClient.STATUS_REQUESTING:
    # Keep polling until the request is going on
    http.poll()
    print("Requesting..")
    OS.delay_msec(500)

This clearly isn’t timed frame by frame, you telling Godot to sleep for an arbitrary amount of time. It doesn’t even complete the frame when you do that, it just suspends the whole engine in the middle of the function.

If you want to poll once per frame, prefer using yield(get_tree(), "idle_frame") instead of delay_msec.

Ah yeah I saw the error, I just couldn’t understand how to fix it and it was exactly the same as the example given in the godot site. It worked fine on all other exports. It must need to be different for the web export however.

pdoc | 2018-06-13 12:43

The docs are probably giving a bad example and should be fixed, then^^ Or the HTML5 export should be made less strict?

Zylann | 2018-06-13 13:00

Your solution is definitely a better way of performing a HTTP request using the client script, in addition to working in the Webassembly export, it also does not block the main thread.

pdoc | 2018-06-13 14:14