diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 28bcd43295..991f85ea12 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -47,7 +47,7 @@ TimingEventType* Timing::RegisterEvent(const std::string& name, TimedCallback ca } void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, - std::uintptr_t user_data, std::size_t core_id) { + std::uintptr_t user_data, std::size_t core_id, bool thread_safe_mode) { if (event_queue_locked) { return; } @@ -61,44 +61,30 @@ void Timing::ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_ timer = timers.at(core_id).get(); } - s64 timeout = timer->GetTicks() + cycles_into_future; - if (current_timer == timer) { - // If this event needs to be scheduled before the next advance(), force one early - if (!timer->is_timer_sane) - timer->ForceExceptionCheck(cycles_into_future); + if (thread_safe_mode) { + // Events scheduled in thread safe mode come after blocking operations with + // unpredictable timings in the host machine, so there is no need to be cycle accurate. + // To prevent the event from scheduling before the next advance(), we set a minimum time + // of MAX_SLICE_LENGTH * 2 cycles into the future. + cycles_into_future = std::max(static_cast(MAX_SLICE_LENGTH * 2), cycles_into_future); - timer->event_queue.emplace_back( - Event{timeout, timer->event_fifo_id++, user_data, event_type}); - std::push_heap(timer->event_queue.begin(), timer->event_queue.end(), std::greater<>()); - } else { timer->ts_queue.Push(Event{static_cast(timer->GetTicks() + cycles_into_future), 0, user_data, event_type}); - } -} - -void Timing::ScheduleEventTS(s64 cycles_into_future, const TimingEventType* event_type, - std::uintptr_t user_data, std::size_t core_id) { - if (event_queue_locked) { - return; - } - - ASSERT(event_type != nullptr); - Timing::Timer* timer = nullptr; - if (core_id == std::numeric_limits::max()) { - timer = current_timer; } else { - ASSERT(core_id < timers.size()); - timer = timers.at(core_id).get(); + s64 timeout = timer->GetTicks() + cycles_into_future; + if (current_timer == timer) { + // If this event needs to be scheduled before the next advance(), force one early + if (!timer->is_timer_sane) + timer->ForceExceptionCheck(cycles_into_future); + + timer->event_queue.emplace_back( + Event{timeout, timer->event_fifo_id++, user_data, event_type}); + std::push_heap(timer->event_queue.begin(), timer->event_queue.end(), std::greater<>()); + } else { + timer->ts_queue.Push(Event{static_cast(timer->GetTicks() + cycles_into_future), 0, + user_data, event_type}); + } } - - // Events scheduled with this thread safe version come after blocking operations with - // unpredictable timings in the host machine, so there is no need to be cycle accurate. - // To prevent the event from scheduling before the next advance(), we set a minimum time - // of MAX_SLICE_LENGTH * 2 cycles into the future. - cycles_into_future = std::max(static_cast(MAX_SLICE_LENGTH * 2), cycles_into_future); - - timer->ts_queue.Push( - Event{static_cast(timer->GetTicks() + cycles_into_future), 0, user_data, event_type}); } void Timing::UnscheduleEvent(const TimingEventType* event_type, std::uintptr_t user_data) { diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 6b9b9cb05c..9de9011a53 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -254,13 +254,12 @@ public: */ TimingEventType* RegisterEvent(const std::string& name, TimedCallback callback); + // Make sure to use thread_safe_mode = true if called from a different thread than the + // emulator thread, such as coroutines. void ScheduleEvent(s64 cycles_into_future, const TimingEventType* event_type, std::uintptr_t user_data = 0, - std::size_t core_id = std::numeric_limits::max()); - - void ScheduleEventTS(s64 cycles_into_future, const TimingEventType* event_type, - std::uintptr_t user_data = 0, - std::size_t core_id = std::numeric_limits::max()); + std::size_t core_id = std::numeric_limits::max(), + bool thread_safe_mode = false); void UnscheduleEvent(const TimingEventType* event_type, std::uintptr_t user_data); @@ -303,13 +302,7 @@ private: void serialize(Archive& ar, const unsigned int file_version) { // event_types set during initialization of other things ar& timers; - if (file_version == 0) { - std::shared_ptr x; - ar& x; - current_timer = x.get(); - } else { - ar& current_timer; - } + ar& current_timer; if (Archive::is_loading::value) { event_queue_locked = true; } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 48a4a4d3e1..f8c4a31a70 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -278,22 +278,22 @@ private: public: /** - * Puts the game thread to sleep and calls the specified async_section from another thread. + * Puts the game thread to sleep and calls the specified async_section asynchronously. * Once the execution of the async section finishes, result_function is called. Use this * mechanism to run blocking IO operations, so that other game threads are allowed to run * while the one performing the blocking operation waits. * @param async_section Callable that takes Kernel::HLERequestContext& as argument - * and returns the amount of nanoseconds to wait before calling result_function. - * This callable is ran from a different thread. + * and returns the amount of nanoseconds to wait before calling result_function. + * This callable is ran asynchronously. * @param result_function Callable that takes Kernel::HLERequestContext& as argument - * and doesn't return anything. This callable is ran from the emulator thread + * and doesn't return anything. This callable is ran from the emulator thread * and can be used to set the IPC result. - * @param really_async If set to false, it will call async_section and result_function - * from the emulator thread without resorting to new threads. + * @param really_async If set to false, it will call both async_section and result_function + * from the emulator thread. */ template void RunAsync(AsyncFunctor async_section, ResultFunctor result_function, - bool really_async = true) { + bool really_async = true) { if (really_async) { this->SleepClientThread( @@ -301,16 +301,16 @@ public: std::make_shared>( result_function, std::move(std::async(std::launch::async, [this, async_section] { - s64 sleepfor = async_section(*this); - this->thread->WakeAfterDelayTS(sleepfor); + s64 sleep_for = async_section(*this); + this->thread->WakeAfterDelay(sleep_for, true); })))); } else { - s64 sleepfor = async_section(*this); - if (sleepfor > 0) { + s64 sleep_for = async_section(*this); + if (sleep_for > 0) { auto parallel_wakeup = std::make_shared>( result_function, std::move(std::future())); - this->SleepClientThread("RunInPool", std::chrono::nanoseconds(sleepfor), + this->SleepClientThread("RunAsync", std::chrono::nanoseconds(sleep_for), parallel_wakeup); } else { result_function(*this); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 98d6e27552..fa3f887846 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -45,16 +45,7 @@ void Thread::serialize(Archive& ar, const unsigned int file_version) { ar& tls_address; ar& held_mutexes; ar& pending_mutexes; - - // Note: this is equivalent of what is done in boost/serialization/weak_ptr.hpp, but it's - // compatible with previous versions of savestates. - // TODO(SaveStates): When the savestate version is bumped, simplify this again. - std::shared_ptr shared_owner_process = owner_process.lock(); - ar& shared_owner_process; - if (Archive::is_loading::value) { - owner_process = shared_owner_process; - } - + ar& owner_process; ar& wait_objects; ar& wait_address; ar& name; @@ -253,22 +244,15 @@ void ThreadManager::ThreadWakeupCallback(u64 thread_id, s64 cycles_late) { thread->ResumeFromWait(); } -void Thread::WakeAfterDelay(s64 nanoseconds) { +void Thread::WakeAfterDelay(s64 nanoseconds, bool thread_safe_mode) { // Don't schedule a wakeup if the thread wants to wait forever if (nanoseconds == -1) return; + size_t core = thread_safe_mode ? core_id : std::numeric_limits::max(); thread_manager.kernel.timing.ScheduleEvent(nsToCycles(nanoseconds), - thread_manager.ThreadWakeupEventType, thread_id); -} - -void Thread::WakeAfterDelayTS(s64 nanoseconds) { - // Don't schedule a wakeup if the thread wants to wait forever - if (nanoseconds == -1) - return; - - thread_manager.kernel.timing.ScheduleEventTS( - nsToCycles(nanoseconds), thread_manager.ThreadWakeupEventType, thread_id, core_id); + thread_manager.ThreadWakeupEventType, thread_id, + core, thread_safe_mode); } void Thread::ResumeFromWait() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 5b92b798a0..955962695f 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -11,9 +11,9 @@ #include #include #include -#include #include #include +#include #include "common/common_types.h" #include "common/thread_queue_list.h" #include "core/arm/arm_interface.h" @@ -238,14 +238,10 @@ public: /** * Schedules an event to wake up the specified thread after the specified delay * @param nanoseconds The time this thread will be allowed to sleep for + * @param thread_safe_mode Set to true if called from a different thread than the emulator + * thread, such as coroutines. */ - void WakeAfterDelay(s64 nanoseconds); - - /** - * Schedules an event to wake up the specified thread after the specified delay, thread safe - * @param nanoseconds The time this thread will be allowed to sleep for - */ - void WakeAfterDelayTS(s64 nanoseconds); + void WakeAfterDelay(s64 nanoseconds, bool thread_safe_mode = false); /** * Sets the result after the thread awakens (from either WaitSynchronization SVC) diff --git a/src/core/hle/service/http/http_c.cpp b/src/core/hle/service/http/http_c.cpp index c0db50f5c8..fb6b6d07c9 100644 --- a/src/core/hle/service/http/http_c.cpp +++ b/src/core/hle/service/http/http_c.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include -#include #include #include #include @@ -66,10 +65,10 @@ static std::pair SplitUrl(const std::string& url) { std::string path; if (path_index == std::string::npos) { // If no path is specified after the host, set it to "/" - host = url.substr(prefix_end); + host = url; path = "/"; } else { - host = url.substr(prefix_end, path_index - prefix_end); + host = url.substr(0, path_index); path = url.substr(path_index); } return std::make_pair(host, path); @@ -78,20 +77,37 @@ static std::pair SplitUrl(const std::string& url) { void Context::MakeRequest() { ASSERT(state == RequestState::NotStarted); + const auto& [host, path] = SplitUrl(url); + const auto client = std::make_unique(host); + SSL_CTX* ctx = client->ssl_context(); + if (ctx) { + if (auto client_cert = ssl_config.client_cert_ctx.lock()) { + SSL_CTX_use_certificate_ASN1(ctx, static_cast(client_cert->certificate.size()), + client_cert->certificate.data()); + SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, ctx, client_cert->private_key.data(), + static_cast(client_cert->private_key.size())); + } + + // TODO(B3N30): Check for SSLOptions-Bits and set the verify method accordingly + // https://www.3dbrew.org/wiki/SSL_Services#SSLOpt + // Hack: Since for now RootCerts are not implemented we set the VerifyMode to None. + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + } + + state = RequestState::InProgress; + static const std::unordered_map request_method_strings{ {RequestMethod::Get, "GET"}, {RequestMethod::Post, "POST"}, {RequestMethod::Head, "HEAD"}, {RequestMethod::Put, "PUT"}, {RequestMethod::Delete, "DELETE"}, {RequestMethod::PostEmpty, "POST"}, {RequestMethod::PutEmpty, "PUT"}, }; - - const auto& [host, path] = SplitUrl(url.c_str()); httplib::Request request; - httplib::Error error{-1}; + httplib::Error error; request.method = request_method_strings.at(method); request.path = path; - + // TODO(B3N30): Add post data body request.progress = [this](u64 current, u64 total) -> bool { // TODO(B3N30): Is there a state that shows response header are available current_download_size_bytes = current; @@ -103,75 +119,14 @@ void Context::MakeRequest() { request.headers.emplace(header.name, header.value); } - if (!post_data.empty()) { - request.headers.emplace("Content-Type", "application/x-www-form-urlencoded"); - request.body = httplib::detail::params_to_query_str(post_data); - boost::replace_all(request.body, "*", "%2A"); - } - - if (!post_data_raw.empty()) { - request.body = post_data_raw; - } - - state = RequestState::InProgress; - - const unsigned char* tmpCertPtr = clcert_data.certificate.data(); - const unsigned char* tmpKeyPtr = clcert_data.private_key.data(); - X509* cert = d2i_X509(nullptr, &tmpCertPtr, (long)clcert_data.certificate.size()); - EVP_PKEY* key = - d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &tmpKeyPtr, (long)clcert_data.private_key.size()); - // Sadly, we have to duplicate code, the class hierarchy in httplib is not very useful... - if (uses_default_client_cert) { - - std::unique_ptr client = - std::make_unique(host, 443, cert, key); - - // TODO(B3N30): Check for SSLOptions-Bits and set the verify method accordingly - // https://www.3dbrew.org/wiki/SSL_Services#SSLOpt - // Hack: Since for now RootCerts are not implemented we set the VerifyMode to None. - client->enable_server_certificate_verification(false); - - if (!client->send(request, response, error)) { - LOG_ERROR(Service_HTTP, "Request failed: {}: {}", error, httplib::to_string(error)); - state = RequestState::TimedOut; - } else { - LOG_DEBUG(Service_HTTP, "Request successful"); - // TODO(B3N30): Verify this state on HW - state = RequestState::ReadyToDownloadContent; - } + if (!client->send(request, response, error)) { + LOG_ERROR(Service_HTTP, "Request failed: {}: {}", error, httplib::to_string(error)); + state = RequestState::TimedOut; } else { - std::unique_ptr client = std::make_unique(host); - SSL_CTX* ctx = client->ssl_context(); - if (ctx) { - if (auto client_cert = ssl_config.client_cert_ctx.lock()) { - SSL_CTX_use_certificate_ASN1(ctx, static_cast(client_cert->certificate.size()), - client_cert->certificate.data()); - SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, ctx, client_cert->private_key.data(), - static_cast(client_cert->private_key.size())); - } - - // TODO(B3N30): Check for SSLOptions-Bits and set the verify method accordingly - // https://www.3dbrew.org/wiki/SSL_Services#SSLOpt - // Hack: Since for now RootCerts are not implemented we set the VerifyMode to None. - client->enable_server_certificate_verification(false); - } - - if (!client->send(request, response, error)) { - LOG_ERROR(Service_HTTP, "Request failed: {}: {}", error, httplib::to_string(error)); - state = RequestState::TimedOut; - } else { - LOG_DEBUG(Service_HTTP, "Request successful"); - // TODO(B3N30): Verify this state on HW - state = RequestState::ReadyToDownloadContent; - } + LOG_DEBUG(Service_HTTP, "Request successful"); + // TODO(B3N30): Verify this state on HW + state = RequestState::ReadyToDownloadContent; } - -#ifdef ENABLE_WEB_SERVICE - -#else - LOG_ERROR(Service_HTTP, "Tried to make request but WebServices is not enabled in this build"); - state = RequestState::TimedOut; -#endif } void HTTP_C::Initialize(Kernel::HLERequestContext& ctx) { @@ -261,7 +216,6 @@ void HTTP_C::BeginRequest(Kernel::HLERequestContext& ctx) { itr->second.request_future = std::async(std::launch::async, &Context::MakeRequest, std::ref(itr->second)); - itr->second.current_copied_data = 0; IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -289,7 +243,6 @@ void HTTP_C::BeginRequestAsync(Kernel::HLERequestContext& ctx) { itr->second.request_future = std::async(std::launch::async, &Context::MakeRequest, std::ref(itr->second)); - itr->second.current_copied_data = 0; IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -306,7 +259,7 @@ void HTTP_C::ReceiveDataTimeout(Kernel::HLERequestContext& ctx) { void HTTP_C::ReceiveDataImpl(Kernel::HLERequestContext& ctx, bool timeout) { IPC::RequestParser rp(ctx); const Context::Handle context_handle = rp.Pop(); - u32 buffer_size = rp.Pop(); + [[maybe_unused]] const u32 buffer_size = rp.Pop(); u64 timeout_nanos = 0; if (timeout) { timeout_nanos = rp.Pop(); @@ -314,8 +267,7 @@ void HTTP_C::ReceiveDataImpl(Kernel::HLERequestContext& ctx, bool timeout) { } else { LOG_WARNING(Service_HTTP, "(STUBBED) called"); } - - Kernel::MappedBuffer& buffer = rp.PopMappedBuffer(); + [[maybe_unused]] Kernel::MappedBuffer& buffer = rp.PopMappedBuffer(); if (!PerformStateChecks(ctx, rp, context_handle)) { return; @@ -324,46 +276,13 @@ void HTTP_C::ReceiveDataImpl(Kernel::HLERequestContext& ctx, bool timeout) { auto itr = contexts.find(context_handle); ASSERT(itr != contexts.end()); - Context& http_context = itr->second; - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - if (timeout) { - auto wait_res = http_context.request_future.wait_for( - std::chrono::nanoseconds(timeout_nanos)); - if (wait_res == std::future_status::timeout) { - rb.Push(ResultCode(105, ErrorModule::HTTP, ErrorSummary::NothingHappened, - ErrorLevel::Permanent)); - return; - } + itr->second.request_future.wait_for(std::chrono::nanoseconds(timeout_nanos)); + // TODO (flTobi): Return error on timeout } else { - http_context.request_future.wait(); + itr->second.request_future.wait(); } - size_t remaining_data = http_context.response.body.size() - http_context.current_copied_data; - - if (buffer_size >= remaining_data) { - buffer.Write(http_context.response.body.data() + http_context.current_copied_data, 0, - remaining_data); - http_context.current_copied_data += remaining_data; - rb.Push(RESULT_SUCCESS); - } - else { - buffer.Write(http_context.response.body.data() + http_context.current_copied_data, 0, - buffer_size); - http_context.current_copied_data += buffer_size; - rb.Push(ResultCode(43, ErrorModule::HTTP, ErrorSummary::WouldBlock, ErrorLevel::Permanent)); - } - LOG_DEBUG(Service_HTTP, "Receive: buffer_size= {}, total_copied={}, total_body={}", buffer_size, - http_context.current_copied_data, http_context.response.body.size()); - ctx.SleepClientThread("http_data", std::chrono::nanoseconds(100000), nullptr); -} - -void HTTP_C::SetProxyDefault(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx); - const Context::Handle context_handle = rp.Pop(); - - LOG_WARNING(Service_HTTP, "(STUBBED) called, handle={}", context_handle); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); } @@ -470,24 +389,6 @@ void HTTP_C::CloseContext(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } -void HTTP_C::CancelConnection(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx); - const u32 context_handle = rp.Pop(); - - LOG_WARNING(Service_HTTP, "(STUBBED) called, handle={}", context_handle); - - const auto* session_data = EnsureSessionInitialized(ctx, rp); - if (!session_data) { - return; - } - - auto itr = contexts.find(context_handle); - ASSERT(itr != contexts.end()); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); -} - void HTTP_C::AddRequestHeader(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const u32 context_handle = rp.Pop(); @@ -570,82 +471,17 @@ void HTTP_C::AddPostDataAscii(Kernel::HLERequestContext& ctx) { return; } - itr->second.post_data.emplace(name, value); + ASSERT(std::find_if(itr->second.post_data.begin(), itr->second.post_data.end(), + [&name](const Context::PostData& m) -> bool { return m.name == name; }) == + itr->second.post_data.end()); + + itr->second.post_data.emplace_back(name, value); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); rb.PushMappedBuffer(value_buffer); } -void HTTP_C::AddPostDataRaw(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx); - const u32 context_handle = rp.Pop(); - const u32 post_data_len = rp.Pop(); - auto buffer = rp.PopMappedBuffer(); - - LOG_DEBUG(Service_HTTP, "context_handle={}, post_data_len={}", context_handle, - post_data_len); - - if (!PerformStateChecks(ctx, rp, context_handle)) { - return; - } - - auto itr = contexts.find(context_handle); - ASSERT(itr != contexts.end()); - - itr->second.post_data_raw.resize(buffer.GetSize()); - buffer.Read(itr->second.post_data_raw.data(), 0, buffer.GetSize()); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(RESULT_SUCCESS); - rb.PushMappedBuffer(buffer); -} - -void HTTP_C::GetResponseHeader(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx); - const u32 context_handle = rp.Pop(); - const u32 name_len = rp.Pop(); - [[maybe_unused]] const u32 value_max_len = rp.Pop(); - auto& header_name = rp.PopStaticBuffer(); - Kernel::MappedBuffer& value_buffer = rp.PopMappedBuffer(); - std::string header_name_str(reinterpret_cast(header_name.data()), name_len); - while (header_name_str.size() && header_name_str.back() == '\0') { - header_name_str.pop_back(); - } - - if (!PerformStateChecks(ctx, rp, context_handle)) { - return; - } - - auto itr = contexts.find(context_handle); - ASSERT(itr != contexts.end()); - - itr->second.request_future.wait(); - - auto& headers = itr->second.response.headers; - u32 copied_size = 0; - - LOG_DEBUG(Service_HTTP, "header={}, max_len={}", header_name_str, value_buffer.GetSize()); - - auto header = headers.find(header_name_str); - if (header != headers.end()) { - std::string header_value = header->second; - copied_size = (u32)header_value.size(); - if (header_value.size() + 1 > value_buffer.GetSize()) { - header_value.resize(value_buffer.GetSize() - 1); - } - header_value.push_back('\0'); - value_buffer.Write(header_value.data(), 0, header_value.size()); - } else { - LOG_WARNING(Service_HTTP, "header={} not found", header_name_str); - } - - IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); - rb.Push(RESULT_SUCCESS); - rb.Push(copied_size); - rb.PushMappedBuffer(value_buffer); -} - void HTTP_C::GetResponseStatusCode(Kernel::HLERequestContext& ctx) { GetResponseStatusCodeImpl(ctx, false); } @@ -654,31 +490,6 @@ void HTTP_C::GetResponseStatusCodeTimeout(Kernel::HLERequestContext& ctx) { GetResponseStatusCodeImpl(ctx, true); } -void HTTP_C::AddTrustedRootCA(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx); - const Context::Handle context_handle = rp.Pop(); - [[maybe_unused]] const u32 root_ca_len = rp.Pop(); - auto root_ca_data = rp.PopMappedBuffer(); - - LOG_WARNING(Service_HTTP, "(STUBBED) called, handle={}", context_handle); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(RESULT_SUCCESS); - rb.PushMappedBuffer(root_ca_data); -} - -void HTTP_C::AddDefaultCert(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx); - const Context::Handle context_handle = rp.Pop(); - const u32 certificate_id = rp.Pop(); - - LOG_WARNING(Service_HTTP, "(STUBBED) called, handle={}, certificate_id={}", context_handle, - certificate_id); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); -} - void HTTP_C::GetResponseStatusCodeImpl(Kernel::HLERequestContext& ctx, bool timeout) { IPC::RequestParser rp(ctx); const Context::Handle context_handle = rp.Pop(); @@ -698,49 +509,19 @@ void HTTP_C::GetResponseStatusCodeImpl(Kernel::HLERequestContext& ctx, bool time ASSERT(itr != contexts.end()); if (timeout) { - auto wait_res = itr->second.request_future.wait_for(std::chrono::nanoseconds(timeout)); - if (wait_res == std::future_status::timeout) { - LOG_DEBUG(Service_HTTP, "Status code: {}", "timeout"); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ResultCode(105, ErrorModule::HTTP, ErrorSummary::NothingHappened, - ErrorLevel::Permanent)); - return; - } + itr->second.request_future.wait_for(std::chrono::nanoseconds(timeout)); + // TODO (flTobi): Return error on timeout } else { itr->second.request_future.wait(); } const u32 response_code = itr->second.response.status; - LOG_DEBUG(Service_HTTP, "Status code: {}, response_code={}", "good", response_code); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); rb.Push(response_code); } -void HTTP_C::SetDefaultClientCert(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx); - const Context::Handle context_handle = rp.Pop(); - // TODO(PabloMK7): There is only a single cert ID with value 64. Check it is valid and return error if not. - [[maybe_unused]] const u32 client_cert_id = rp.Pop(); - - - LOG_DEBUG(Service_HTTP, "client_cert_id={}", client_cert_id); - - if (!PerformStateChecks(ctx, rp, context_handle)) { - return; - } - - auto itr = contexts.find(context_handle); - ASSERT(itr != contexts.end()); - - itr->second.uses_default_client_cert = true; - itr->second.clcert_data = GetClCertA(); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); -} - void HTTP_C::SetClientCertContext(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const u32 context_handle = rp.Pop(); @@ -981,11 +762,10 @@ void HTTP_C::GetDownloadSizeState(Kernel::HLERequestContext& ctx) { content_length = std::stoi(it->second); } } - LOG_DEBUG(Service_HTTP, "current={}, total={}", - itr->second.current_copied_data, content_length); + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); rb.Push(RESULT_SUCCESS); - rb.Push(itr->second.current_copied_data); + rb.Push(content_length); rb.Push(content_length); } @@ -1115,7 +895,7 @@ HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) { {0x0001, &HTTP_C::Initialize, "Initialize"}, {0x0002, &HTTP_C::CreateContext, "CreateContext"}, {0x0003, &HTTP_C::CloseContext, "CloseContext"}, - {0x0004, &HTTP_C::CancelConnection, "CancelConnection"}, + {0x0004, nullptr, "CancelConnection"}, {0x0005, nullptr, "GetRequestState"}, {0x0006, &HTTP_C::GetDownloadSizeState, "GetDownloadSizeState"}, {0x0007, nullptr, "GetRequestError"}, @@ -1125,13 +905,13 @@ HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) { {0x000B, &HTTP_C::ReceiveData, "ReceiveData"}, {0x000C, &HTTP_C::ReceiveDataTimeout, "ReceiveDataTimeout"}, {0x000D, nullptr, "SetProxy"}, - {0x000E, &HTTP_C::SetProxyDefault, "SetProxyDefault"}, + {0x000E, nullptr, "SetProxyDefault"}, {0x000F, nullptr, "SetBasicAuthorization"}, {0x0010, nullptr, "SetSocketBufferSize"}, {0x0011, &HTTP_C::AddRequestHeader, "AddRequestHeader"}, {0x0012, &HTTP_C::AddPostDataAscii, "AddPostDataAscii"}, {0x0013, nullptr, "AddPostDataBinary"}, - {0x0014, &HTTP_C::AddPostDataRaw, "AddPostDataRaw"}, + {0x0014, nullptr, "AddPostDataRaw"}, {0x0015, nullptr, "SetPostDataType"}, {0x0016, nullptr, "SendPostDataAscii"}, {0x0017, nullptr, "SendPostDataAsciiTimeout"}, @@ -1141,17 +921,16 @@ HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) { {0x001B, nullptr, "SendPOSTDataRawTimeout"}, {0x001C, nullptr, "SetPostDataEncoding"}, {0x001D, nullptr, "NotifyFinishSendPostData"}, - {0x001E, &HTTP_C::GetResponseHeader, "GetResponseHeader"}, + {0x001E, nullptr, "GetResponseHeader"}, {0x001F, nullptr, "GetResponseHeaderTimeout"}, {0x0020, nullptr, "GetResponseData"}, {0x0021, nullptr, "GetResponseDataTimeout"}, {0x0022, &HTTP_C::GetResponseStatusCode, "GetResponseStatusCode"}, {0x0023, &HTTP_C::GetResponseStatusCodeTimeout, "GetResponseStatusCodeTimeout"}, - {0x0024, &HTTP_C::AddTrustedRootCA, "AddTrustedRootCA"}, - {0x0025, &HTTP_C::AddDefaultCert, "AddDefaultCert"}, + {0x0024, nullptr, "AddTrustedRootCA"}, + {0x0025, nullptr, "AddDefaultCert"}, {0x0026, nullptr, "SelectRootCertChain"}, {0x0027, nullptr, "SetClientCert"}, - {0x0028, &HTTP_C::SetDefaultClientCert, "SetDefaultClientCert"}, {0x0029, &HTTP_C::SetClientCertContext, "SetClientCertContext"}, {0x002A, &HTTP_C::GetSSLError, "GetSSLError"}, {0x002B, nullptr, "SetSSLOpt"}, @@ -1176,10 +955,6 @@ HTTP_C::HTTP_C() : ServiceFramework("http:C", 32) { DecryptClCertA(); } -std::shared_ptr GetService(Core::System& system) { - return system.ServiceManager().GetService("http:C"); -} - void InstallInterfaces(Core::System& system) { auto& service_manager = system.ServiceManager(); std::make_shared()->InstallAsService(service_manager); diff --git a/src/core/hle/service/http/http_c.h b/src/core/hle/service/http/http_c.h index 85b518488d..4ba7d2576c 100644 --- a/src/core/hle/service/http/http_c.h +++ b/src/core/hle/service/http/http_c.h @@ -21,7 +21,6 @@ #include #endif #include -#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/service.h" @@ -115,12 +114,6 @@ private: friend class boost::serialization::access; }; -struct ClCertAData { - std::vector certificate; - std::vector private_key; - bool init = false; -}; - /// Represents an HTTP context. class Context final { public: @@ -176,6 +169,22 @@ public: friend class boost::serialization::access; }; + struct PostData { + // TODO(Subv): Support Binary and Raw POST elements. + PostData(std::string name, std::string value) : name(name), value(value){}; + PostData() = default; + std::string name; + std::string value; + + private: + template + void serialize(Archive& ar, const unsigned int) { + ar& name; + ar& value; + } + friend class boost::serialization::access; + }; + struct SSLConfig { u32 options; std::weak_ptr client_cert_ctx; @@ -191,7 +200,6 @@ public: friend class boost::serialization::access; }; - ClCertAData clcert_data; Handle handle; u32 session_id; std::string url; @@ -202,14 +210,11 @@ public: SSLConfig ssl_config{}; u32 socket_buffer_size; std::vector headers; - httplib::Params post_data; - std::string post_data_raw; + std::vector post_data; std::future request_future; std::atomic current_download_size_bytes; std::atomic total_download_size_bytes; - size_t current_copied_data; - bool uses_default_client_cert{}; httplib::Response response; }; @@ -247,10 +252,6 @@ class HTTP_C final : public ServiceFramework { public: HTTP_C(); - const ClCertAData& GetClCertA() const { - return ClCertA; - } - private: /** * HTTP_C::Initialize service function @@ -287,8 +288,6 @@ private: */ void CloseContext(Kernel::HLERequestContext& ctx); - void CancelConnection(Kernel::HLERequestContext& ctx); - /** * HTTP_C::GetDownloadSizeState service function * Inputs: @@ -329,8 +328,6 @@ private: */ void BeginRequestAsync(Kernel::HLERequestContext& ctx); - void SetProxyDefault(Kernel::HLERequestContext& ctx); - /** * HTTP_C::ReceiveData service function * Inputs: @@ -392,10 +389,6 @@ private: */ void AddPostDataAscii(Kernel::HLERequestContext& ctx); - void AddPostDataRaw(Kernel::HLERequestContext& ctx); - - void GetResponseHeader(Kernel::HLERequestContext& ctx); - /** * HTTP_C::GetResponseStatusCode service function * Inputs: @@ -417,26 +410,12 @@ private: */ void GetResponseStatusCodeTimeout(Kernel::HLERequestContext& ctx); - void AddTrustedRootCA(Kernel::HLERequestContext& ctx); - - /** - * HTTP_C::AddDefaultCert service function - * Inputs: - * 1 : Context handle - * 2 : Cert ID - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ - void AddDefaultCert(Kernel::HLERequestContext& ctx); - /** * GetResponseStatusCodeImpl: * Implements GetResponseStatusCode and GetResponseStatusCodeTimeout service functions */ void GetResponseStatusCodeImpl(Kernel::HLERequestContext& ctx, bool timeout); - void SetDefaultClientCert(Kernel::HLERequestContext& ctx); - /** * HTTP_C::SetClientCertContext service function * Inputs: @@ -523,7 +502,11 @@ private: /// Global list of ClientCert contexts currently opened. std::unordered_map> client_certs; - ClCertAData ClCertA; + struct { + std::vector certificate; + std::vector private_key; + bool init = false; + } ClCertA; private: template @@ -544,8 +527,6 @@ private: friend class boost::serialization::access; }; -std::shared_ptr GetService(Core::System& system); - void InstallInterfaces(Core::System& system); } // namespace Service::HTTP