Exemple #1
0
godot_pool_byte_array GDAPI godot_variant_as_pool_byte_array(const godot_variant *p_self) {
	godot_pool_byte_array raw_dest;
	const Variant *self = (const Variant *)p_self;
	PoolByteArray *dest = (PoolByteArray *)&raw_dest;
	memnew_placement(dest, PoolByteArray(self->operator PoolByteArray())); // operator = is overloaded by PoolByteArray
	*dest = *self;
	return raw_dest;
}
Exemple #2
0
Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const String &p_request_data) {

	ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
	if (requesting) {
		ERR_EXPLAIN("HTTPRequest is processing a request. Wait for completion or cancel it before attempting a new one.");
		ERR_FAIL_V(ERR_BUSY);
	}

	method = p_method;

	Error err = _parse_url(p_url);
	if (err)
		return err;

	validate_ssl = p_ssl_validate_domain;

	bool has_user_agent = false;
	bool has_accept = false;
	headers = p_custom_headers;

	request_data = p_request_data;

	for (int i = 0; i < headers.size(); i++) {

		if (headers[i].findn("user-agent:") == 0)
			has_user_agent = true;
		if (headers[i].findn("Accept:") == 0)
			has_accept = true;
	}

	if (!has_user_agent) {
		headers.push_back("User-Agent: GodotEngine/" + String(VERSION_MKSTRING) + " (" + OS::get_singleton()->get_name() + ")");
	}

	if (!has_accept) {
		headers.push_back("Accept: */*");
	}

	requesting = true;

	if (use_threads) {

		thread_done = false;
		thread_request_quit = false;
		client->set_blocking_mode(true);
		thread = Thread::create(_thread_func, this);
	} else {
		client->set_blocking_mode(false);
		err = _request();
		if (err != OK) {
			call_deferred("_request_done", RESULT_CANT_CONNECT, 0, PoolStringArray(), PoolByteArray());
			return ERR_CANT_CONNECT;
		}

		set_process_internal(true);
	}

	return OK;
}
Exemple #3
0
void HTTPRequest::_thread_func(void *p_userdata) {

	HTTPRequest *hr = (HTTPRequest *)p_userdata;

	Error err = hr->_request();

	if (err != OK) {
		hr->call_deferred("_request_done", RESULT_CANT_CONNECT, 0, PoolStringArray(), PoolByteArray());
	} else {
		while (!hr->thread_request_quit) {

			bool exit = hr->_update_connection();
			if (exit)
				break;
			OS::get_singleton()->delay_usec(1);
		}
	}

	hr->thread_done = true;
}
Exemple #4
0
Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, bool p_ssl_validate_domain, HTTPClient::Method p_method, const String &p_request_data) {

	ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
	if (requesting) {
		ERR_EXPLAIN("HTTPRequest is processing a request. Wait for completion or cancel it before attempting a new one.");
		ERR_FAIL_V(ERR_BUSY);
	}

	method = p_method;

	Error err = _parse_url(p_url);
	if (err)
		return err;

	validate_ssl = p_ssl_validate_domain;

	headers = p_custom_headers;

	request_data = p_request_data;

	requesting = true;

	if (use_threads) {

		thread_done = false;
		thread_request_quit = false;
		client->set_blocking_mode(true);
		thread = Thread::create(_thread_func, this);
	} else {
		client->set_blocking_mode(false);
		err = _request();
		if (err != OK) {
			call_deferred("_request_done", RESULT_CANT_CONNECT, 0, PoolStringArray(), PoolByteArray());
			return ERR_CANT_CONNECT;
		}

		set_process_internal(true);
	}

	return OK;
}
PoolByteArray HTTPClient::read_response_body_chunk() {

	ERR_FAIL_COND_V(status != STATUS_BODY, PoolByteArray());

	int to_read = MIN(read_limit, polled_response.size() - response_read_offset);
	PoolByteArray chunk;
	chunk.resize(to_read);
	PoolByteArray::Write write = chunk.write();
	PoolByteArray::Read read = polled_response.read();
	memcpy(write.ptr(), read.ptr() + response_read_offset, to_read);
	write = PoolByteArray::Write();
	read = PoolByteArray::Read();
	response_read_offset += to_read;

	if (response_read_offset == polled_response.size()) {
		status = STATUS_CONNECTED;
		polled_response.resize(0);
		polled_response_code = 0;
		polled_response_header = String();
		godot_xhr_reset(xhr_id);
	}

	return chunk;
}
Exemple #6
0
PoolByteArray HTTPClient::read_response_body_chunk() {

	ERR_FAIL_COND_V(status != STATUS_BODY, PoolByteArray());

	Error err = OK;

	if (chunked) {

		while (true) {

			if (chunk_left == 0) {
				// Reading length
				uint8_t b;
				int rec = 0;
				err = _get_http_data(&b, 1, rec);

				if (rec == 0)
					break;

				chunk.push_back(b);

				if (chunk.size() > 32) {
					ERR_PRINT("HTTP Invalid chunk hex len");
					status = STATUS_CONNECTION_ERROR;
					return PoolByteArray();
				}

				if (chunk.size() > 2 && chunk[chunk.size() - 2] == '\r' && chunk[chunk.size() - 1] == '\n') {

					int len = 0;
					for (int i = 0; i < chunk.size() - 2; i++) {
						char c = chunk[i];
						int v = 0;
						if (c >= '0' && c <= '9')
							v = c - '0';
						else if (c >= 'a' && c <= 'f')
							v = c - 'a' + 10;
						else if (c >= 'A' && c <= 'F')
							v = c - 'A' + 10;
						else {
							ERR_PRINT("HTTP Chunk len not in hex!!");
							status = STATUS_CONNECTION_ERROR;
							return PoolByteArray();
						}
						len <<= 4;
						len |= v;
						if (len > (1 << 24)) {
							ERR_PRINT("HTTP Chunk too big!! >16mb");
							status = STATUS_CONNECTION_ERROR;
							return PoolByteArray();
						}
					}

					if (len == 0) {
						// End reached!
						status = STATUS_CONNECTED;
						chunk.clear();
						return PoolByteArray();
					}

					chunk_left = len + 2;
					chunk.resize(chunk_left);
				}
			} else {

				int rec = 0;
				err = _get_http_data(&chunk[chunk.size() - chunk_left], chunk_left, rec);
				if (rec == 0) {
					break;
				}
				chunk_left -= rec;

				if (chunk_left == 0) {

					if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') {
						ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)");
						status = STATUS_CONNECTION_ERROR;
						return PoolByteArray();
					}

					PoolByteArray ret;
					ret.resize(chunk.size() - 2);
					{
						PoolByteArray::Write w = ret.write();
						copymem(w.ptr(), chunk.ptr(), chunk.size() - 2);
					}
					chunk.clear();

					return ret;
				}

				break;
			}
		}

	} else {

		int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size;
		PoolByteArray ret;
		ret.resize(to_read);
		int _offset = 0;
		while (read_until_eof || to_read > 0) {
			int rec = 0;
			{
				PoolByteArray::Write w = ret.write();
				err = _get_http_data(w.ptr() + _offset, to_read, rec);
			}
			if (rec < 0) {
				if (to_read > 0) // Ended up reading less
					ret.resize(_offset);
				break;
			} else {
				_offset += rec;
				if (!read_until_eof) {
					body_left -= rec;
					to_read -= rec;
				} else {
					if (rec < to_read) {
						ret.resize(_offset);
						err = ERR_FILE_EOF;
						break;
					}
					ret.resize(_offset + to_read);
				}
			}
		}
		if (!read_until_eof) {
			if (body_left == 0) {
				status = STATUS_CONNECTED;
			}
			return ret;
		} else {
			if (err == ERR_FILE_EOF) {
				err = OK; // EOF is expected here
				close();
				return ret;
			}
		}
	}

	if (err != OK) {

		close();

		if (err == ERR_FILE_EOF) {

			status = STATUS_DISCONNECTED; // Server disconnected
		} else {

			status = STATUS_CONNECTION_ERROR;
		}
	} else if (body_left == 0 && !chunked) {

		status = STATUS_CONNECTED;
	}

	return PoolByteArray();
}
Exemple #7
0
MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) {
	switch (p_type.type_encoding) {
		case MONO_TYPE_BOOLEAN: {
			MonoBoolean val = p_var->operator bool();
			return BOX_BOOLEAN(val);
		}

		case MONO_TYPE_I1: {
			char val = p_var->operator signed char();
			return BOX_INT8(val);
		}
		case MONO_TYPE_I2: {
			short val = p_var->operator signed short();
			return BOX_INT16(val);
		}
		case MONO_TYPE_I4: {
			int val = p_var->operator signed int();
			return BOX_INT32(val);
		}
		case MONO_TYPE_I8: {
			int64_t val = p_var->operator int64_t();
			return BOX_INT64(val);
		}

		case MONO_TYPE_U1: {
			char val = p_var->operator unsigned char();
			return BOX_UINT8(val);
		}
		case MONO_TYPE_U2: {
			short val = p_var->operator unsigned short();
			return BOX_UINT16(val);
		}
		case MONO_TYPE_U4: {
			int val = p_var->operator unsigned int();
			return BOX_UINT32(val);
		}
		case MONO_TYPE_U8: {
			uint64_t val = p_var->operator uint64_t();
			return BOX_UINT64(val);
		}

		case MONO_TYPE_R4: {
			float val = p_var->operator float();
			return BOX_FLOAT(val);
		}
		case MONO_TYPE_R8: {
			double val = p_var->operator double();
			return BOX_DOUBLE(val);
		}

		case MONO_TYPE_STRING: {
			return (MonoObject *)mono_string_from_godot(p_var->operator String());
		} break;

		case MONO_TYPE_VALUETYPE: {
			GDMonoClass *tclass = p_type.type_class;

			if (tclass == CACHED_CLASS(Vector2))
				RETURN_BOXED_STRUCT(Vector2, p_var);

			if (tclass == CACHED_CLASS(Rect2))
				RETURN_BOXED_STRUCT(Rect2, p_var);

			if (tclass == CACHED_CLASS(Transform2D))
				RETURN_BOXED_STRUCT(Transform2D, p_var);

			if (tclass == CACHED_CLASS(Vector3))
				RETURN_BOXED_STRUCT(Vector3, p_var);

			if (tclass == CACHED_CLASS(Basis))
				RETURN_BOXED_STRUCT(Basis, p_var);

			if (tclass == CACHED_CLASS(Quat))
				RETURN_BOXED_STRUCT(Quat, p_var);

			if (tclass == CACHED_CLASS(Transform))
				RETURN_BOXED_STRUCT(Transform, p_var);

			if (tclass == CACHED_CLASS(Rect3))
				RETURN_BOXED_STRUCT(Rect3, p_var);

			if (tclass == CACHED_CLASS(Color))
				RETURN_BOXED_STRUCT(Color, p_var);

			if (tclass == CACHED_CLASS(Plane))
				RETURN_BOXED_STRUCT(Plane, p_var);

			if (mono_class_is_enum(tclass->get_raw())) {
				int val = p_var->operator signed int();
				return BOX_ENUM(tclass->get_raw(), val);
			}
		} break;

		case MONO_TYPE_ARRAY:
		case MONO_TYPE_SZARRAY: {
			MonoArrayType *array_type = mono_type_get_array_type(GDMonoClass::get_raw_type(p_type.type_class));

			if (array_type->eklass == CACHED_CLASS_RAW(MonoObject))
				return (MonoObject *)Array_to_mono_array(p_var->operator Array());

			if (array_type->eklass == CACHED_CLASS_RAW(uint8_t))
				return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray());

			if (array_type->eklass == CACHED_CLASS_RAW(int32_t))
				return (MonoObject *)PoolIntArray_to_mono_array(p_var->operator PoolIntArray());

			if (array_type->eklass == REAL_T_MONOCLASS)
				return (MonoObject *)PoolRealArray_to_mono_array(p_var->operator PoolRealArray());

			if (array_type->eklass == CACHED_CLASS_RAW(String))
				return (MonoObject *)PoolStringArray_to_mono_array(p_var->operator PoolStringArray());

			if (array_type->eklass == CACHED_CLASS_RAW(Vector2))
				return (MonoObject *)PoolVector2Array_to_mono_array(p_var->operator PoolVector2Array());

			if (array_type->eklass == CACHED_CLASS_RAW(Vector3))
				return (MonoObject *)PoolVector3Array_to_mono_array(p_var->operator PoolVector3Array());

			if (array_type->eklass == CACHED_CLASS_RAW(Color))
				return (MonoObject *)PoolColorArray_to_mono_array(p_var->operator PoolColorArray());

			ERR_EXPLAIN(String() + "Attempted to convert Variant to a managed array of unmarshallable element type.");
			ERR_FAIL_V(NULL);
		} break;

		case MONO_TYPE_CLASS: {
			GDMonoClass *type_class = p_type.type_class;

			// GodotObject
			if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
				return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
			}

			if (CACHED_CLASS(NodePath) == type_class) {
				return GDMonoUtils::create_managed_from(p_var->operator NodePath());
			}

			if (CACHED_CLASS(RID) == type_class) {
				return GDMonoUtils::create_managed_from(p_var->operator RID());
			}
		} break;
		case MONO_TYPE_OBJECT: {
			// Variant
			switch (p_var->get_type()) {
				case Variant::BOOL: {
					MonoBoolean val = p_var->operator bool();
					return BOX_BOOLEAN(val);
				}
				case Variant::INT: {
					int val = p_var->operator signed int();
					return BOX_INT32(val);
				}
				case Variant::REAL: {
#ifdef REAL_T_IS_DOUBLE
					double val = p_var->operator double();
					return BOX_DOUBLE(val);
#else
					float val = p_var->operator float();
					return BOX_FLOAT(val);
#endif
				}
				case Variant::STRING:
					return (MonoObject *)mono_string_from_godot(p_var->operator String());
				case Variant::VECTOR2:
					RETURN_BOXED_STRUCT(Vector2, p_var);
				case Variant::RECT2:
					RETURN_BOXED_STRUCT(Rect2, p_var);
				case Variant::VECTOR3:
					RETURN_BOXED_STRUCT(Vector3, p_var);
				case Variant::TRANSFORM2D:
					RETURN_BOXED_STRUCT(Transform2D, p_var);
				case Variant::PLANE:
					RETURN_BOXED_STRUCT(Plane, p_var);
				case Variant::QUAT:
					RETURN_BOXED_STRUCT(Quat, p_var);
				case Variant::RECT3:
					RETURN_BOXED_STRUCT(Rect3, p_var);
				case Variant::BASIS:
					RETURN_BOXED_STRUCT(Basis, p_var);
				case Variant::TRANSFORM:
					RETURN_BOXED_STRUCT(Transform, p_var);
				case Variant::COLOR:
					RETURN_BOXED_STRUCT(Color, p_var);
				case Variant::NODE_PATH:
					return GDMonoUtils::create_managed_from(p_var->operator NodePath());
				case Variant::_RID:
					return GDMonoUtils::create_managed_from(p_var->operator RID());
				case Variant::OBJECT: {
					return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
				}
				case Variant::DICTIONARY:
					return Dictionary_to_mono_object(p_var->operator Dictionary());
				case Variant::ARRAY:
					return (MonoObject *)Array_to_mono_array(p_var->operator Array());
				case Variant::POOL_BYTE_ARRAY:
					return (MonoObject *)PoolByteArray_to_mono_array(p_var->operator PoolByteArray());
				case Variant::POOL_INT_ARRAY:
					return (MonoObject *)PoolIntArray_to_mono_array(p_var->operator PoolIntArray());
				case Variant::POOL_REAL_ARRAY:
					return (MonoObject *)PoolRealArray_to_mono_array(p_var->operator PoolRealArray());
				case Variant::POOL_STRING_ARRAY:
					return (MonoObject *)PoolStringArray_to_mono_array(p_var->operator PoolStringArray());
				case Variant::POOL_VECTOR2_ARRAY:
					return (MonoObject *)PoolVector2Array_to_mono_array(p_var->operator PoolVector2Array());
				case Variant::POOL_VECTOR3_ARRAY:
					return (MonoObject *)PoolVector3Array_to_mono_array(p_var->operator PoolVector3Array());
				case Variant::POOL_COLOR_ARRAY:
					return (MonoObject *)PoolColorArray_to_mono_array(p_var->operator PoolColorArray());
				default:
					return NULL;
			}
			break;
			case MONO_TYPE_GENERICINST: {
				if (CACHED_RAW_MONO_CLASS(Dictionary) == p_type.type_class->get_raw()) {
					return Dictionary_to_mono_object(p_var->operator Dictionary());
				}
			} break;
		} break;
	}

	ERR_EXPLAIN(String() + "Attempted to convert Variant to an unmarshallable managed type. Name: \'" +
				p_type.type_class->get_name() + "\' Encoding: " + itos(p_type.type_encoding));
	ERR_FAIL_V(NULL);
}
Exemple #8
0
bool HTTPRequest::_update_connection() {

	switch (client->get_status()) {
		case HTTPClient::STATUS_DISCONNECTED: {
			call_deferred("_request_done", RESULT_CANT_CONNECT, 0, PoolStringArray(), PoolByteArray());
			return true; //end it, since it's doing something
		} break;
		case HTTPClient::STATUS_RESOLVING: {
			client->poll();
			//must wait
			return false;
		} break;
		case HTTPClient::STATUS_CANT_RESOLVE: {
			call_deferred("_request_done", RESULT_CANT_RESOLVE, 0, PoolStringArray(), PoolByteArray());
			return true;

		} break;
		case HTTPClient::STATUS_CONNECTING: {
			client->poll();
			//must wait
			return false;
		} break; //connecting to ip
		case HTTPClient::STATUS_CANT_CONNECT: {

			call_deferred("_request_done", RESULT_CANT_CONNECT, 0, PoolStringArray(), PoolByteArray());
			return true;

		} break;
		case HTTPClient::STATUS_CONNECTED: {

			if (request_sent) {

				if (!got_response) {

					//no body

					bool ret_value;

					if (_handle_response(&ret_value))
						return ret_value;

					call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, PoolByteArray());
					return true;
				}
				if (got_response && body_len < 0) {
					//chunked transfer is done
					call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body);
					return true;
				}

				call_deferred("_request_done", RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PoolByteArray());
				return true;
				//request migh have been done
			} else {
				//did not request yet, do request

				Error err = client->request(method, request_string, headers, request_data);
				if (err != OK) {
					call_deferred("_request_done", RESULT_CONNECTION_ERROR, 0, PoolStringArray(), PoolByteArray());
					return true;
				}

				request_sent = true;
				return false;
			}
		} break; //connected: { } break requests only accepted here
		case HTTPClient::STATUS_REQUESTING: {
			//must wait, it's requesting
			client->poll();
			return false;

		} break; // request in progress
		case HTTPClient::STATUS_BODY: {

			if (!got_response) {

				bool ret_value;

				if (_handle_response(&ret_value))
					return ret_value;

				if (!client->is_response_chunked() && client->get_response_body_length() == 0) {

					call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, PoolByteArray());
					return true;
				}

				if (client->is_response_chunked()) {
					body_len = -1; //no body len because chunked, change your webserver configuration if you want body len
				} else {
					body_len = client->get_response_body_length();

					if (body_size_limit >= 0 && body_len > body_size_limit) {
						call_deferred("_request_done", RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PoolByteArray());
						return true;
					}
				}

				if (download_to_file != String()) {
					file = FileAccess::open(download_to_file, FileAccess::WRITE);
					if (!file) {

						call_deferred("_request_done", RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PoolByteArray());
						return true;
					}
				}
			}

			//print_line("BODY: "+itos(body.size()));
			client->poll();

			PoolByteArray chunk = client->read_response_body_chunk();
			downloaded += chunk.size();

			if (file) {
				PoolByteArray::Read r = chunk.read();
				file->store_buffer(r.ptr(), chunk.size());
				if (file->get_error() != OK) {
					call_deferred("_request_done", RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PoolByteArray());
					return true;
				}
			} else {
				body.append_array(chunk);
			}

			if (body_size_limit >= 0 && downloaded > body_size_limit) {
				call_deferred("_request_done", RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PoolByteArray());
				return true;
			}

			if (body_len >= 0) {

				if (downloaded == body_len) {
					call_deferred("_request_done", RESULT_SUCCESS, response_code, response_headers, body);
					return true;
				}
				/*if (body.size()>=body_len) {
					call_deferred("_request_done",RESULT_BODY_SIZE_MISMATCH,response_code,response_headers,ByteArray());
					return true;
				}*/
			}

			return false;

		} break; // request resulted in body: { } break which must be read
		case HTTPClient::STATUS_CONNECTION_ERROR: {
			call_deferred("_request_done", RESULT_CONNECTION_ERROR, 0, PoolStringArray(), PoolByteArray());
			return true;
		} break;
		case HTTPClient::STATUS_SSL_HANDSHAKE_ERROR: {
			call_deferred("_request_done", RESULT_SSL_HANDSHAKE_ERROR, 0, PoolStringArray(), PoolByteArray());
			return true;
		} break;
	}

	ERR_FAIL_V(false);
}
Exemple #9
0
bool HTTPRequest::_handle_response(bool *ret_value) {

	if (!client->has_response()) {
		call_deferred("_request_done", RESULT_NO_RESPONSE, 0, PoolStringArray(), PoolByteArray());
		*ret_value = true;
		return true;
	}

	got_response = true;
	response_code = client->get_response_code();
	List<String> rheaders;
	client->get_response_headers(&rheaders);
	response_headers.resize(0);
	downloaded = 0;
	for (List<String>::Element *E = rheaders.front(); E; E = E->next()) {
		//print_line("HEADER: "+E->get());
		response_headers.push_back(E->get());
	}

	if (response_code == 301 || response_code == 302) {
		//redirect
		if (max_redirects >= 0 && redirections >= max_redirects) {

			call_deferred("_request_done", RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PoolByteArray());
			*ret_value = true;
			return true;
		}

		String new_request;

		for (List<String>::Element *E = rheaders.front(); E; E = E->next()) {
			if (E->get().findn("Location: ") != -1) {
				new_request = E->get().substr(9, E->get().length()).strip_edges();
			}
		}

		//print_line("NEW LOCATION: "+new_request);

		if (new_request != "") {
			//process redirect
			client->close();
			int new_redirs = redirections + 1; //because _request() will clear it
			Error err;
			if (new_request.begins_with("http")) {
				//new url, request all again
				err = _parse_url(new_request);
			} else {
				request_string = new_request;
			}

			err = _request();

			//print_line("new connection: "+itos(err));
			if (err == OK) {
				request_sent = false;
				got_response = false;
				body_len = -1;
				body.resize(0);
				downloaded = 0;
				redirections = new_redirs;
				*ret_value = false;
				return true;
			}
		}
	}

	return false;
}