HTTPRequest failing from HTML5 Export on ITCH.IO even though it works from Debug

Godot Version

4.2.1

UPDATE: I just found this in the Godot docs, do I understand correctly that I simply absolutely cannot cross-reference in HTML5 by default from Godot 4 onward?

The HTTP classes also have several restrictions on the HTML5 platform:

  • Accessing or changing the StreamPeer is not possible
  • Threaded/Blocking mode is not available
  • Cannot progress more than once per frame, so polling in a loop will freeze
  • No chunked responses
  • Host verification cannot be disabled
  • Subject to same-origin policy

Question

Hey everyone,

I hope anyone has an idea on this because I’m desperate. I have found similar questions on this but no solutions. I made a nice YouTube tutorial work, Godot to and from MySQL database tutorial by Daniel Cremers.

But unfortunately as someone in the comments mentioned, this doesn’t work in HTML5 exports. Someone then suggested to add a certificate to the website to godot and I tried that, but I don’t know if I did something wrong but it didn’t help in any way. I am certain I have header(“Access-Control-Allow-Origin: *”); pretty much anywhere that I could put it. This error is also just super cryptic and doesn’t give me much to work with. I’m hosting the zipped HTML5 export on ITCH.IO and my webserver is a free one on 000webhost, works like a charme if I request it in debug mode. When I send a request through Edge I get the following error: TypeError: Failed to fetch at Object.create […] at HTTPClientWEB::request […]

Source code:

extends Control

var http_request : HTTPRequest = HTTPRequest.new()
const SERVER_URL = "[...]"
const SERVER_HEADERS = ["Content-Type: application/x-www-form-urlencoded", "Cache-Control: max-age=0"]
const SECRET_KEY = "1234567890"
var nonce = null
var request_queue : Array = []
var is_requesting : bool = false

func _ready():
	randomize()
	
	# Connect our request handler:
	add_child(http_request)
	http_request.request_completed.connect(self._http_request_completed)
	
	$AddScore.pressed.connect(self._submit_score)
	$GetScores.pressed.connect(self._get_scores)
	
func _process(_delta):
	
	# Check if we are good to send a request:
	if is_requesting:
		return
		
	if request_queue.is_empty():
		return
		
	is_requesting = true
	if nonce == null:
		request_nonce()
	else:
		_send_request(request_queue.pop_front())
		
	
func _http_request_completed(_result, _response_code, _headers, _body):
	
	is_requesting = false
	if _result != HTTPRequest.RESULT_SUCCESS:
		printerr("Error w/ connection: " + str(_result) + " " + str(_response_code)+ " " + str(_headers) + " " + str(_body))
		return
		
	var response_body = _body.get_string_from_utf8()
	# Grab our JSON and handle any errors reported by our PHP code:
	var response = JSON.parse_string(response_body)
	if response['error'] != "none":
		printerr("We returned error: " + response['error'])
		return
	
	# Check if we were requesting a nonce:
	if response['command'] == 'get_nonce':
		nonce = response['response']['nonce']
		print("Got nonce: " + response['response']['nonce'])
		return
		
	# If not requesting a nonce, we handle all other requests here:
	print("Response Body:\n" + response_body)
	

func request_nonce():
	var client = HTTPClient.new()
	var data = client.query_string_from_dict({"data" : JSON.stringify({})})
	var body = "command=get_nonce&" + data
	
	print("Requesting nonce...")
	
	# Make request to the server:
	var err = http_request.request(SERVER_URL, SERVER_HEADERS, HTTPClient.METHOD_POST, body)
	
	# Check if there were problems:
	if err != OK:
		printerr("HTTPRequest error: " + str(err))
		return
	else:
		print("No error requesting nonce!")
	
func _send_request(request : Dictionary):
	var client = HTTPClient.new()
	var data = client.query_string_from_dict({"data" : JSON.stringify(request['data'])})
	var body = "command=" + request['command'] + "&" + data
	
	# Generate our 'response nonce'
	var cnonce = str(Crypto.new().generate_random_bytes(32)).sha256_text()
	
	# Generate our security hash:
	var client_hash = (nonce + cnonce + body + SECRET_KEY).sha256_text()
	nonce = null
	
	# Create our custom header for the request:
	var headers = SERVER_HEADERS.duplicate()
	headers.push_back("cnonce: " + cnonce)
	headers.push_back("hash: " + client_hash)
	
	# Make request to the server:
	var err = http_request.request(SERVER_URL, headers, HTTPClient.METHOD_POST, body)
	
	# Check if there were problems:
	if err != OK:
		printerr("HTTPRequest error: " + str(err))
		return
		
	# Print out request for debugging:
	print("Requesting...\n\tCommand: " + request['command'] + "\n\tBody: " + body)
	

func _submit_score():
	var score = 0
	var username = ""
	
	# Generate a random username
	var con = "bcdfghjklmnpqrstvwxyz"
	var vow = "aeiou"
	username = ""
	for _i in range(3 + randi() % 4):
		var string = con
		if _i % 2 == 0:
			string = vow
		username += string.substr(randi() % string.length(), 1)
		if _i == 0:
			username = username.capitalize()
	score = randi() % 1000
	
	var command = "add_score"
	var data = {"score" : score, "username" : username}
	request_queue.push_back({"command" : command, "data" : data})
	
	
func _get_scores():
	var command = "get_scores"
	var data = {"score_offset" : 0, "score_number" : 10}
	request_queue.push_back({"command" : command, "data" : data});


func _on_login_button_pressed():
	_get_scores()

Any help would be GREATLY appreciated!

Have you tried the request without any headers? I had CORS issues when creating this addon, and removing the headers was the solution for me.

Thanks for the answer! Unfortunately changing the requests to http_request.request(SERVER_URL, , HTTPClient.METHOD_POST, body) still throws the same error.

According to the docs, the parameters are passed in the wrong order. It should be:

var err = http_request.request(HTTPClient.METHOD_POST, SERVER_URL, SERVER_HEADERS, body)

That was really confusing for a sec, but I use HTTPRequest, not client, and apparently the order is different there:

I want to emphasize, the code absolutely WORKS. A mistake like that would surely break it. It’s just that it stops working after HTML5 Export.

Sorry, my bad, I misread the code and thought that HTTPClient was being used for the request. In that case, I have not idea what’s going on, sorry.

What error are you getting in the network inspector from browser? Or even better can you provide the link to the game on itch if it’s not private? Or make a link with password and share that somehow.

Sure, knock yourself out I just made it public:

I’m not 100% on what state it is in right now (I tried lots of things) but the main error always is the same.

Ok, you are getting a mixed block. Basically you are requesting http://my-server-123 instead of https://my-server-123.
Which you cannot do since itch.io is https, you have to also request https.

Thanks for checking that! I tried changing the link to HTTPS but then it doesn’t work even in debug mode anymore. I don’t know why.

I’ve kind of come to the conclusion that this can never work with HTTPRequest as the official documentation states that the same origin policy is always enforced. I’d be happy to be proven wrong though.

For now it feels like SilentWolf (which I found after researching alternatives to my current approach) is the best way for me to move forward. Alternatively I could try to get it running with WebSockets but I know next to nothing about that - I was only able to get this far by using a complete example from the mentioned tutorial.

I changed the code to use the https url. It fails differently, in case you want to check again.

Will check tomorrow and update this comment. Are you sure ir webserver has https configured?

Thank you! Not entirely - I’m really not very good at this stuff. I just followed that tutorial and I’m using a 000webhost domain.

This won’t work on itch.io. Here is a quote on their experimental support of shared array buffers:

So the issue is that itch does not allow your external URL. You can not change that since itch does not expose this as a setting.

Your options are:

  • don’t make web requests to external domains
  • wait until there is a solution by Godot (this PR may be related: godot#85939)
  • host on your own server, where you have full control over the headers
1 Like

Thank you very much, that was what I guessed after I found that, but it really helps to have someone confirm it. I’d say unfortunately this is actually the “solution”.

I mean you said that you use 000webhost. I think they support nginx so you could host your game there too, so that the your web requests don’t go to an external domain.

Thank you for the suggestion, I’ll keep that in mind - unfortunately I don’t really know how to do any of that. Never heard of nginx before.

I got this error when I deployed my app to Azure. In my case the problem was that on my HTTPRequest node I had “Accept Gzip” enabled. I unchecked the box and it fixed my issue.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.