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; }
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; }
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; }
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; }
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(); }
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); }
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); }
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; }