Code Monkey home page Code Monkey logo

microsoft / libhttpclient Goto Github PK

View Code? Open in Web Editor NEW
273.0 25.0 126.0 4.86 MB

libHttpClient provides a platform abstraction layer for HTTP and WebSocket, and is designed for use by the Microsoft Xbox Live Service API (XSAPI) [https://github.com/Microsoft/xbox-live-api] and game devs. If you want to contribute to the project, please talk to us to avoid overlap.

License: MIT License

C 9.00% C++ 84.90% CMake 2.94% Batchfile 0.01% Objective-C 0.07% Objective-C++ 1.26% Java 1.15% Shell 0.65% Swift 0.04%

libhttpclient's Issues

Linux support?

Hi,
Is there any plan to support Linux with this API? It seems odd to offer a multi-platform library and skip probably the easiest platform to implement on.
This is blocking for some projects we are working on, and we were wondering if we have to do this ourselves or if there is already some plan or support.

Thanks a lot!
Helen

Proposal/Discussion: Propagate out android exceptions for tracing/debugging.

Java uses typed exceptions which have no error code.

However because of the c layer here.

HCHttpCallResponseSetNetworkErrorCode(_In_ HCCallHandle call, _In_ HRESULT networkErrorCode, _In_ uint32_t platformNetworkErrorCode)
HCHttpCallResponseGetNetworkErrorCode(_In_ HCCallHandle call, _Out_ HRESULT* networkErrorCode, _Out_ uint32_t* platformNetworkErrorCode)

There is nothing to propagate out for platform network error code on android.

We would like to have more visibility on the other side in c/c++ at least for tracing/debugging. So I was thinking about potential solutions.

I see 2 potential paths.

Approach 1: Add 2 new apis

HCHttpCallResponseSetNetworkErrorCode(_In_ HCCallHandle call, _In_ HRESULT networkErrorCode, _In_z_ char* platformNetworkErrorStr)
HCHttpCallResponseGetNetworkErrorCode(_In_ HCCallHandle call, _Out_ HRESULT* networkErrorCode, _Out_ char* platformNetworkErrorStr)

Alternatively make one with all (call, networkerrorcode, platformnetworkerrorcode, platformnetworkerrorstr)

Approach 2:
Make a java exception : number mapping. So that each exception is mapped to some value.
For our Java/C++ mapping in our project. We currently use this approach.

e.g.

ExceptionLookup.put(TIMEOUT.getValue(), TimeoutException.class);
...
static int translateErrorCodeValueFromThrowable(@NonNull Throwable exception) {

where TIMEOUT is just a defined value (e.g. TIMEOUT(0x80070102)). It generally matches the windows based error codes. Since Android/Java doesn't have any better numbers to choose from aside from maybe some linux ones.

Either approach I would be willing to implement a rough draft and PR it but before I did any work on it, I was hoping to have some feedback, discussion and thoughts. Just in case the libHttpClient team has given thought to this already.

Issue and proposed fix. Non ascii characters in header causes a IllegalArgumentException

No pending exception expected: java.lang.IllegalArgumentException: Unexpected char

square/okhttp#6347
square/okhttp#4008
square/okhttp#7275
square/okhttp#4296

In the case of using setHttpHeader("carrier", ""**电信"").
Note: **电信 is a Chinese telecommunications (cell phone provider carrier).

No pending exception expected: java.lang.IllegalArgumentException: Unexpected char 0x4e2d at 243 in User-Agent value

at void okhttp3.Headers$Companion.checkValue(java.lang.String, java.lang.String) (Headers.kt:450)
at void okhttp3.Headers$Companion.access$checkValue(okhttp3.Headers$Companion, java.lang.String, java.lang.String) (Headers.kt:362)
at okhttp3.Headers$Builder okhttp3.Headers$Builder.add(java.lang.String, java.lang.String) (Headers.kt:261)
at okhttp3.Request$Builder okhttp3.Request$Builder.addHeader(java.lang.String, java.lang.String) (Request.kt:210)
at <snipped>.setHttpHeader(java.lang.String, java.lang.String) (HttpClientRequest.java

The currently existing code snippet causes an issue.

 public void setHttpHeader(String name, String value) {
        this.requestBuilder = requestBuilder.addHeader(name, value);
    }

I believe the possible fix is to add a
this.headersBuilder = new Headers.Builder();
and then switch setHttpHeader to
this.headersBuilder = headersBuilder.addUnsafeNonAscii(name, value);
Then in the doRequestAsync add this.requestBuilder.headers(this.headersBuilder.build()); before the OK_Client call
or to 'build and set' in every set. Just depends if you want to do a lot of duplicate work or not.

We are running a modified version of libhttpclient, so our fix is applied there. I'll see if/when I have time to PR this change into libHttpClient proper, but if not, above should help anyone who comes across similar issue.

openssl.bash hard-codes /Applications/Xcode.app

libHttpClient.xcodeproj uses an openssl.bash helper script to build OpenSSL. However, the script hard-codes paths to /Applications/Xcode.app, which may not match the Xcode instance used to build libHttpClient.xcodeproj.

Instead the location of Xcode should be chosen by xcode-select -p.

Calling SubmitThreadpoolWork when the application is shutting down causes crash

SubmitThreadpoolWork (seems to be an alias for NTDLL.TpPostWork) cannot be called during application shutdown

It throws: Exception thrown at 0x00007FFD726AF220 (ntdll.dll) in CTA_PC.exe: 0xC000000D: An invalid parameter was passed to a service or function. occurred

Our client application that uses this crashes when we close the application using (X) close button on the upper right corner

No platform error message on Microsoft platforms?

I noticed that Microsoft platforms return empty string from HCHttpCallResponseGetPlatformNetworkErrorMessage . I suppose platErrCode can be inspected somehow, but it's value is engine specific and will be different between XDK and GDK/WIN32. What is recommended way to get human readable error regardless of the platform?

image

How to get access to PS4 and Switch platforms?

I see HC_PLATFORM_SONY_PLAYSTATION_4 and HC_PLATFORM_NINTENDO_SWITCH mentioned in the source code, but understandably implementation are not in the public repository. Is there a way to get access to them?

close func may be executed after HCWebSocketCloseHandle returned

Sadly I wasn't able to provide minimal reproducer, even smallest change, such as adjusting logging, makes bug not triggering anymore, but I hope my findings below convince you that bug is real.

Imagine following class, which creates websocket handle and passes this as the function context.

class X {
  X() {
    HCWebSocketCreate(_ws, nulltpr, nullptr, &ws_on_close, this);
  }
  ~X() {
    HCWebSocketDisconnect(_ws);
    HCWebsocketCloseHandle(_ws);
  }  

  void connect();

private:
  static void ws_on_close(HCWebsocketHandle ws, HCWebSocketCloseStatus status, void *context) {
      auto self = static_cast<X*>(context);
      self->close(status);
  }
  

  void close(HCWebSocketCloseStatus);

  HCWebsocketHandle _ws = nullptr;

}

I believe it is correct to expect, that once HCWebsocketCloseHandle has finished, then ws_on_close will never be called and as a result heap-use-after-free error will be avoided.

It is however might not be the case, due to a race between WebSocket::CloseFunc:

std::unique_lock<std::mutex> lock{ websocket->m_mutex };
if (!websocket->m_providerContext)
{
HC_TRACE_ERROR(WEBSOCKET, "Unexpected call to WebSocket::CloseFunc will be ignored!");
return;
}
// We no longer expect callbacks from Provider at this point, so cleanup m_providerContext. If there are no other
// observers of websocket, it may be destroyed now
HC_UNIQUE_PTR<ProviderContext> reclaim{ websocket->m_providerContext };
websocket->m_state = State::Disconnected;
auto callbacks{ websocket->m_eventCallbacks };
lock.unlock();

and WebSocket::UnregisterEventCallbacks (via HCWebsocketCloseHandle):

void WebSocket::UnregisterEventCallbacks(uint32_t registrationToken)
{
std::unique_lock<std::mutex> lock{ m_mutex };
m_eventCallbacks.erase(registrationToken);
}

There is possible following execution order:

T2 (WSDisconnect): initiates disconnect
T1 (CloseFunc): lock m_mutex
T2 (Unregister): waits for m_mutex
T1 (CloseFunc): copies callbacks
T1 (CloseFunc): unlocks m_mutex
T2 (Unregister): modifies callbacks and returns from `HCWebsocketCloseHandle`
T2 (~X): deletes instance, invalidating `this`
T1 (CloseFunc): calls copied callbacks , passing context (`this`) and causing use after free

Is it possible to get network failure error code on Android?

When E_HC_NO_NETWORK is returned for an Android HTTP request initialization, it seems to be swallowed by the library (when the state is extracted) and calling XAsyncGetStatus subsequently returns 0. We don't want a retry in this situation and would like to be able to act when that error occurs.
Is there a way to get the error code to stay in the XAsyncBlock?

Support big file download or upload?

Hi.
when I use this lib to upload big file to server , it was always not worked. the reasons as follows:

1.HCHttpCallRequestSetRequestBodyBytes()‘s param: requestBodyBytes is in memory. it's easy to cause oom.

2.not support upload progress callback.

Do you plan to support this feature?

Thanks.

Provide an example how to shutdown websockets safely

Callbacks passed to HCWebsocketCreate are executed on unspecified threads, which creates a problem for clean shutdown when libhttpclient is used within user object instances and therefore can't use global singletons.

Take this websocket example from Microsoft:

  1. this is passed as context in HCWebsocketCreate's callbacks:

https://github.com/microsoft/Xbox-GDK-Samples/blob/67f3650ebb5ec10e49b74cf3537ad1cdf75a5f30/Samples/Live/SimpleWebSockets/SimpleWebSockets/SimpleWebSockets.cpp#L538

  1. websocket_close callback casts pointer to the Sample * and accesses it:

https://github.com/microsoft/Xbox-GDK-Samples/blob/67f3650ebb5ec10e49b74cf3537ad1cdf75a5f30/Samples/Live/SimpleWebSockets/SimpleWebSockets/SimpleWebSockets.cpp#L538

https://github.com/microsoft/Xbox-GDK-Samples/blob/67f3650ebb5ec10e49b74cf3537ad1cdf75a5f30/Samples/Live/SimpleWebSockets/SimpleWebSockets/SimpleWebSockets.cpp#L853-L858

  1. In destructor HCWebsocketDisconnect is called:

https://github.com/microsoft/Xbox-GDK-Samples/blob/67f3650ebb5ec10e49b74cf3537ad1cdf75a5f30/Samples/Live/SimpleWebSockets/SimpleWebSockets/SimpleWebSockets.cpp#L518-L530

What I observe in my code is that destructor completes and frees this, then websocket_close is called and tries to access already freed object.

It looks like with manual Completion port dispatch it is possible to pump queue in destructor , delaying object destruction until websocket_close finishes and for other dispatch modes wait on conditional var might be an option.

It would be nice if libhttpclient repo provided complete example, showing safe use of libhttpclient without global variables.

Add option to use cpprest or json for moddern c++ (json.hpp)

I have a version of the nlohmann json for moddern C++, it would be nice if there really is a way to use that instead of cpprest's json implementation.

If possible prvide steps please (I perfer that json implementation for now that I already have as I do not have issues with it yet).

libHC wedges when it can't close Websocket connection cleanly

When libHC disconnects websocket it unconditionally sends close frame and expects acknowledgement to arrive, it fails to process case where there is no response and hangs during HCCleanup() preventing app from shutting down cleanly.

I also tried to add XTaskQueueTerminate before HCCleanup(), with and without wait, but it didn't help.

Here is a small reproducer. When it prints "Suspend/kill server now", end server task in the process explorer within 5 seconds to simulate network issue or unresponsive server. Tested on Windows 11.

disconnect.cpp
#include <httpClient/httpClient.h>
#include <httpClient/trace.h>
#include <httpClient/async.h>
#include <memory>
#include <iostream>
#include <thread>

using namespace std::chrono_literals;

HC_DEFINE_TRACE_AREA(app, HCTraceLevel::Verbose);

HRESULT init(XTaskQueueHandle* q, HCWebsocketHandle* ws) {
    HRESULT hr = XTaskQueueCreate(XTaskQueueDispatchMode::ThreadPool,
                                  XTaskQueueDispatchMode::Manual,  // callbacks from ticks
                                  q
    );
    if (FAILED(hr)) {
        HC_TRACE_ERROR(app, "Error HCWebSocketCreate(): hr=0x%08x", hr);
        return hr;
    }

    hr = HCWebSocketCreate(ws, nullptr, nullptr, nullptr, nullptr);
    if (FAILED(hr)) {
        HC_TRACE_ERROR(app, "Error HCWebSocketCreate(): hr=0x%08x", hr);
        return hr;
    }
    return S_OK;
}

const char* URL = "ws://localhost:8080/";

int main() {
    HCInitialize(nullptr);
    HCTraceSetTraceToDebugger(true);
    HCSettingsSetTraceLevel(HCTraceLevel::Verbose);
    XTaskQueueHandle q;
    HCWebsocketHandle ws;
    init(&q, &ws);

    struct {
        bool connected;
    } state{};

    XAsyncBlock *asyncBlock = new XAsyncBlock{};
    asyncBlock->context = &state;
    asyncBlock->queue = q;
    asyncBlock->callback = [](XAsyncBlock *ab) {
        const std::unique_ptr<XAsyncBlock> asyncBlock(ab);
        auto self = static_cast<decltype(&state)>(asyncBlock->context);

        WebSocketCompletionResult result;
        HRESULT hr = HCGetWebSocketConnectResult(asyncBlock.get(), &result);
        if (SUCCEEDED(hr) && SUCCEEDED(result.errorCode)) {
            HC_TRACE_INFORMATION(app, "Connected");
        } else {
            HC_TRACE_ERROR(app, "Error connecting");
        }
        self->connected = true;
    };
    HCWebSocketConnectAsync(URL, "", ws, asyncBlock);

    std::cout << "Looping" << std::endl;
    while(!state.connected) {
        while(XTaskQueueDispatch(q, XTaskQueuePort::Completion, 20));
    };

    std::cout << "Suspend/kill server now" << std::endl;
    std::this_thread::sleep_for(5s);

    //HCWebSocketDisconnect(ws);
    HCWebSocketCloseHandle(ws);

    //XTaskQueueTerminate(q, false, nullptr, nullptr);
    XTaskQueueCloseHandle(q);
    HCCleanup();

    return 0;
}

HCHttpCallResponseSetResponseBodyBytes logs bogus bodySize on Windows x32 debug

In a Windows x32 (Win32) debug configuration, I'm seeing bogus values for bodySize in the log message written by HCHttpCallResponseSetResponseBodyBytes.

Here's an example from some logs that I have handy:

HCHttpCallResponseSetStatusCode [ID 1]: statusCode=200
HCHttpCallResponseSetResponseHeader [ID 1]: Cache-Control=no-cache, no-store
...
HCHttpCallResponseSetResponseHeader [ID 1]: Content-Length=1349
HCHttpCallResponseSetResponseBodyBytes [ID 1]: bodySize=9690109650489312581

The Content-Length header here is 1349, yet the bodySize value logged here is astronomically large.

I don't have a quick way to rebuild libHttpClient inside the program that produced this log, but I was able to overwrite the format string in the debugger to be ...bodySize=%zu. After that change, I started seeing the values I expected.

The z length specifier was added to C++ in C++11. It looks like the minimum C++ supported is C++11, so that could be a fix. If not, casting bodySize to a unsigned long long int and using llu could also work.

I haven't looked to see if this problem exists elsewhere. I've not tested other platforms or configurations either.

HTTPS Struggles

I'd like to point out two limitations in the library I found while trying to deal with self-signed certificates we sometimes use for development purposes.

First of all there seem to be no way to disable SSL peer verification. I totally understand the security implications, but sometimes it's fine to accept "bad" certificates from hosts which we know. Other HTTP libraries I know expose that option (e.g. CURLOPT_SSL_VERIFYPEER), so it only makes sense to me that libHttpClient exposes it as well.

Another related limitation is that the most information the library provides about HTTPS failure (on Windows) is a WINHTTP error code. The one I was always getting is extremely generic (ERROR_WINHTTP_SECURE_FAILURE) and the only way to get more information is to use a WINHTTP_STATUS_CALLBACK. This callback slot is already used by the library and even if I wanted to hook my own callback, I couldn't find a way to retrieve the HINTERNET required to set it up.

RootTrustManager not found on Kindle Fire devices

Changes introduced with #461 don't work on Kindle Fire devices. android/security/net/config/RootTrustManager is not found causing cert chain verification to fail. The code works as expected on non amazon devices. A similar issue in cpprestsdk has an alternate solution that may work for Kindle devices as well but I haven't tested it

Deadlock on XTaskQueueTerminate?

We recently refactored a bunch of code and added
XTaskQueueTerminate(m_queueHandle, /*wait*/ true, /*callbackContext*/ nullptr, /*callback*/ nullptr);

However, sometimes it gets deadlock/hung in TaskQueue.cpp:1382-ish m_termination.cv.wait(lock);

Are there any known issues regarding using the wait in queue terminate? Or behaviors we should watch out for?

It doesn't happen often that it gets deadlocked.(It stayed in the hung state for 30+ minutes)

How to use with Xbox Durango XDK

Hi, I'm looking to use this from a DLL built against an old Durango XDK for Xbox One. Does the XDK ship with a pre-compiled version of libHttpClient somewhere? If not, any tips on how to build this library with the XDK? Alternatively, is there a different recommended library for basic web requests in the XDK? For modern GDK I use Xcurl, but that doesn't exist in the older XDKs.

Be able to query if library has been initialized

We have a bit of a maze of dependencies and devices and builds that initialize libhttpclient differently - some do it through xsapi, some do it through the gamecore GDK, some include libhttpclient separately and initialize it there - on Android it's called asynchronously from the java thread and we need to synchronize on that - if we have to track it ourselves, there are a handful of places where we'd have to flip a "we've called HCInitialize or something that calls it" bool and it would be easy to miss one.
Ideally we'd have readonly access to s_useCount from

if (!s_useCount)
- but maybe there's something else I'm not seeing that we could use. (I haven't looked that hard, to be honest.)

No way to consume ios/osx versions from CMake

I noticed it appears that the CMakeLists.txt isn't maintained when making changes to the xcodeproj. Shouldn't the CMakeLists.txt be modified to generate it and if so, would the maintainers be open to a PR making this change?

I think the workaround otherwise would be to execute_process(xcodebuild...) and link to the resulting library from CMake.

Mock calls internal compare is inadequate, fake race/thread issues

We recently ran in our unit tests some unusual behavior that presented as race conditions and other indeterminate behavior.

We built from ExampleSingleGenericMock from https://github.com/Microsoft/libHttpClient/blob/master/Tests/UnitTests/Tests/MockTests.cpp

I had queued up 50+ mock calls into our queue to then be consumed to test our multi threaded worker threads to consume the queue.

The problem presented as if data was being written into different calls.
The main differentiator in each call was by setting different response bodies. HCHttpCallResponseSetResponseBodyBytes
Essentially setting the response body it as "Mock 1", "Mock 2", "Mock #", etc...

As if were queueing the same endpoint expecting a counter to increase

It was later discovered in
https://github.com/Microsoft/libHttpClient/blob/d0737a7fcb0f563b1dd3cf2cd74977e9dd1efb7d/Source/Mock/mock.cpp#L119

And the eventual call to https://github.com/Microsoft/libHttpClient/blob/d0737a7fcb0f563b1dd3cf2cd74977e9dd1efb7d/Source/Mock/mock.cpp#L10

That the only differentiator being compared between 2 calls is requestBodyBytes and url.

But there is legitimate reasons why multiple calls should expect different results from the same url. Imagine an endpoint api that just counted how many calls to it, it'd have the same url and request but different results.

Proposed solutions:

  • Have a more robust way of identifying comparable mockcalls, by comparing more of the internal call's values.

  • Don't maintain an internal m_mocks that you have to match against to begin with, and just let the user feed in the expected call. I think this is how how traditional non mock calls occur?

[question] How do I cancel an in progress http call?

I have a scenario where we've started a call with HCHttpCallPerformAsync but before the callback has completed we'd like to tear everything down. I've attempted to use HCHttpCallCloseHandle to signal I have no interest in the result but that seems to just cause a crash while the call is processing.

[Announcement] Deprecation of the Visual Studio-based Android build

Deprecation of the Visual Studio-based Android build

The Visual Studio-based Android build of libHttpClient is being deprecated, and will be removed in the near future in favor of an Android Studio-compatible Gradle+CMake build system for libHttpClient for Android.

Who will this impact?

This will affect projects that depend on the following VS projects, located in /Build:

  • libHttpClient.141.Android.C
  • libHttpClient.141.Android.Java
  • libHttpClient.142.Android.C
  • libHttpClient.142.Android.Java
  • libssl.141.Android
  • libssl.142.Android
  • libcrypto.141.Android
  • libcrypto.142.Android

What will replace the deprecated projects?

libHttpClient already contains Gradle+CMake based Android builds for each of libHttpClient, libssl, and libcrypto, which are structured as Gradle modules that can be included by Gradle library or application projects. These builds are located in /Build, and named:

  • libHttpClient.Android
  • libssl.Android
  • libcrypto.Android

The "umbrella" project at /Build/libHttpClient.Android.Workspace provides a convenient entry point for building all three of these projects at once, for example for consumers who would like to precompile and vendor the static-library outputs of the three projects above.

How should I migrate to the new build?

The above projects will produce static libraries of the C++ code in /Binaries/Android/<ABI>/<Configuration>, and a .aar of libHttpClient's Java code in /Binaries/Android/java. This is intentionally the same set of outputs as the existing VS-based build.

If your consuming Android project already uses Gradle, you can include the new builds as modules in your build and reference the built binaries in the directories above when linking/assembling.

If your consuming Android project uses Visual Studio, you can externally build the above modules and reference the built binaries when linking/assembling.

See /Build/libHttpClient.Android.Workspace for some more details and an example.

Why/How was this decision made?

Visual Studio's support for Android projects uses very outdated tooling (e.g., old NDK, Gradle version, Android Gradle plugin) that makes the projects hard to maintain and misses out on the performance/feature improvements of the latest tools. Additionally, it contains layers of indirection (e.g., using .template versions of Gradle config files) that add complexity to the build and increase maintenance cost. Finally, emulator support through Visual Studio is poor, and testing changes can be painful.

The libHttpClient team has internally switched to the new Android builds, and as we are confident in their stability we will be removing the old builds in order to reduce overhead for our build pipelines (as the VS-based Android builds frequently require maintenance on build servers) as well as to ensure our Android product is consistent (as the old and new builds use different tooling).

We have intentionally tried to ensure this change will be minimally disruptive for consumers - for example, the outputs of the new build are identical in format (static libraries + a Java .aar) to the VS build. If this change will produce significant hardship for your project, please comment here and we will try and advise on how to ease migration for you.

Mocking IXMLHTTPRequest2?

One of the goals of the project is "Built-in API support to switch to mock layer".
I'm curious how you could mock IXMLHTTPRequest2 if it's so extremely limited or even broken at times (getting headers when handling redirects for example). I'm actually working on a very similar effort as libHttpClient and I simply cannot mock most of the stuff with IXMLHTTPRequest2. For example, I want to unit test some requests/results to/from facebook.com. Either I'd need to change host name to something else or setup dns override (which is nonsense) to be able to mock dns resolution. So, how are you planning to mock it with IXMLHTTPRequest2? Or you mock everything with different code completely which might leave IXMLHTTPRequest2-based impl untested?

I wish xbox didn't have that nonsense requirement to use this broken api in the first place so I could delete IXMLHTTPRequest2-based implementation and never hear about it again.

Double free with mock

Since we had written our own thread scheduler to consume the task. We recently started some seeing unusual results.

I started with a ExampleSingleGenericMock from https://github.com/Microsoft/libHttpClient/blob/master/Tests/UnitTests/Tests/MockTests.cpp

In the same unit test,
VERIFY_ARE_EQUAL(S_OK, HCHttpCallCloseHandle(call));
HCCleanup();
are both called but inside HCCleanup is where the http_singleton is destroyed

https://github.com/Microsoft/libHttpClient/blob/e7726a89b99b715287469e2651b9b7bff9692797/Source/Global/global.cpp#L45

HCHttpCallCloseHandle(mockCall); is called on all the mock calls.

So HCHttpCallCloseHandle is being called both by the unit test AND in the httpsingleton cleanup.

This caused some unusual behavior.

We ultimately were hitting this assert. Since it had been free'd earlier, it now contained garbage data (e.g. if (refCount was equal to like -2384234)
https://github.com/Microsoft/libHttpClient/blob/1d749c20bd522d056f339e18b01d8df70e132482/Source/HTTP/httpcall.cpp#L84

Proposed solutions:

  • Remove CallCloseHandle for mocks in the cleanup, and make it behave like non mock, where the user is responsible for cleaning it up.
  • Add additional ref counting to the mocks, so that there isn't a double free scenario.

Let me know if you need additional information and clarity.

Bad parameter from internal parameter during termination?

Anyone seen this before?

Unhandled exception at 0x00007FFFE22403B0 (ntdll.dll) in App.exe: 0xC000000D: An invalid parameter was passed to a service or function

It originated from XTaskQueueTerminate(m_queueHandle, /*wait*/ true, /*callbackContext*/ nullptr, /*callback*/ nullptr)

ntdll.dll!00007fffe22403b0()
sdk.dll!ThreadPoolImpl::Submit() Line 68
    at c:\app\windows\src\sdk\libs\libhttpclient\source\task\threadpool_win32.cpp(68)
sdk.dll!ThreadPool::Submit() Line 167
    at c:\app\windows\src\sdk\libs\libhttpclient\source\task\threadpool_win32.cpp(167)
sdk.dll!TaskQueuePortImpl::Terminate(void * token) Line 534
    at c:\app\windows\src\sdk\libs\libhttpclient\source\task\taskqueue.cpp(534)
sdk.dll!TaskQueueImpl::Terminate(bool wait, void * callbackContext, void(*)(void *) callback) Line 1377
    at c:\app\windows\src\sdk\libs\libhttpclient\source\task\taskqueue.cpp(1377)
sdk.dll!XTaskQueueTerminate(XTaskQueueObject * queue, bool wait, void * callbackContext, void(*)(void *) callback) Line 1586
    at c:\app\windows\src\sdk\libs\libhttpclient\source\task\taskqueue.cpp(1586)
sdk.dll!WebHttpClientCore::~WebHttpClientCore() Line 66

Line 66 here is from the above XTaskQueueTerminate

There should be a version of HCHttpCallRequestSetSSLValidation for web sockets

I am adding support for Web Sockets to our title, and have been testing my work with a very simple locally run echo server. Everything works fine when testing over http, but switching to https fails as I am just using a self signed certificate for local testing.

For making HTTP calls, I call HCHttpCallRequestSetSSLValidation to skip the ssl validation, but there is no equivilent for web sockets. I found HCWebSocketSetProxyDecryptsHttps, and using a debugger to skip the empty proxy check was able to get a connection open over https with the ssl check bypassed. This is not sustainable in the long term, as I can't expect other developers to stick a breakpoint in a random bit of code and set a random variable to true.

Please consider adding a new function along the lines of HCWebSocketSetSSLValidation, which when called disables the SSL validation step.

HCGetWebSocketConnectResult populates result with different handle

HCGetWebSocketConnectResult seems to populate result with a different websocket handle than one used to call HCWebSocketConnectAsync. When multiple simultaneous connections are used I wanted to use WebSocketCompletionResult::websocket to disambiguate between them, but it seems not possible.

auto* asyncBlock = new XAsyncBlock{};
asyncBlock->context = ws;
asyncBlock->queue = q;
asyncBlock->callback = [](XAsyncBlock *ab) {
    const std::unique_ptr<XAsyncBlock> asyncBlock(ab);
    auto ws = static_cast<HCWebsocketHandle>(asyncBlock->context);

    WebSocketCompletionResult result{};
    HRESULT hr = HCGetWebSocketConnectResult(asyncBlock.get(), &result);
    if (SUCCEEDED(hr)) {
       assert(ws == result.websocket);  // <<< FAILS
    }
};

HCWebSocketConnectAsync(URL, "",  ws, asyncBlock);

Log level mismatch

The internal implementation of tracing uses a different enum for log levels than the external api, we should unify this

Android native activity sample?

Thank you very much for this library, I was able to get it working in my Win32 OpenGL app without any issues. However, now I was trying to do the same for Android and I am stuck. I am very new to Android programming, so most likely it's my fault that I cannot get it to work.

I am using the OpenGL C++ Android template generated by Visual Studio. It uses NativeActivity (C++) instead of Java. The Android integration in this library did not support loading the HttpClientRequest java classes in such case, so I've added:

jclass FindClassFromNativeActivity(HCInitArgs* args, JNIEnv* jniEnv, const char* className)
{
jobject nativeActivity = args->applicationContext;
jclass acl = jniEnv->GetObjectClass(nativeActivity);
jmethodID getClassLoader = jniEnv->GetMethodID(acl, "getClassLoader", "()Ljava/lang/ClassLoader;");
jobject cls = jniEnv->CallObjectMethod(nativeActivity, getClassLoader);
jclass classLoader = jniEnv->FindClass("java/lang/ClassLoader");
jmethodID findClass = jniEnv->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
jstring strClassName = jniEnv->NewStringUTF(className);
jclass flurryClass = (jclass)(jniEnv->CallObjectMethod(cls, findClass, strClassName));
jniEnv->DeleteLocalRef(strClassName);
return flurryClass;
}

And I replaced the code to load these classes:

jclass localHttpRequest = FindClassFromNativeActivity(args, jniEnv, "com.xbox.HttpClientRequest");
if (localHttpRequest == nullptr)
{
HC_TRACE_ERROR(HTTPCLIENT, "Could not find HttpClientRequest class");
return E_FAIL;
}

This works and gets a reference to the HttpClientRequest instance. As for adapting the Win32 samples, I did:

std::vector<std::vectorstd::string> ExtractAllHeaders(In HCCallHandle call)
{
uint32_t numHeaders = 0;
HCHttpCallResponseGetNumHeaders(call, &numHeaders);

std::vector< std::vector<std::string> > headers;
for (uint32_t i = 0; i < numHeaders; i++)
{
	const char* str;
	const char* str2;
	std::string headerName;
	std::string headerValue;
	HCHttpCallResponseGetHeaderAtIndex(call, i, &str, &str2);
	if (str != nullptr) headerName = str;
	if (str2 != nullptr) headerValue = str2;
	std::vector<std::string> header;
	header.push_back(headerName);
	header.push_back(headerValue);

	headers.push_back(header);
}

return headers;

}

neosmart::neosmart_event_t g_stopRequestedHandle;
neosmart::neosmart_event_t g_workReadyHandle;
neosmart::neosmart_event_t g_completionReadyHandle;
neosmart::neosmart_event_t g_exampleTaskDone;

int g_targetNumThreads = 2;
pthread_t g_hActiveThreads[10] = { };
int g_defaultIdealProcessor = 0;
int g_numActiveThreads = 0;

void* background_thread_proc(void* lpParam)
{
neosmart::neosmart_event_t hEvents[3] =
{
g_workReadyHandle,
g_completionReadyHandle,
g_stopRequestedHandle
};

JNIEnv* jni;
JavaVM* vm = (JavaVM*)lpParam;
vm->AttachCurrentThread(&jni, NULL);

XTaskQueueHandle queue;
XTaskQueueDuplicateHandle(g_queue, &queue);

bool stop = false;
while (!stop)
{
	int index = 0;
	int dwResult = WaitForMultipleEvents(hEvents, 3, false, -1, index);
	switch (index)
	{
	case 0: // work ready
		if (XTaskQueueDispatch(queue, XTaskQueuePort::Work, 0))
		{
			// If we executed work, set our event again to check next time.
			neosmart::SetEvent(g_workReadyHandle);
		}
		break;

	case 1: // completed 
		// Typically completions should be dispatched on the game thread, but
		// for this simple XAML app we're doing it here
		if (XTaskQueueDispatch(queue, XTaskQueuePort::Completion, 0))
		{
			// If we executed a completion set our event again to check next time
			neosmart::SetEvent(g_completionReadyHandle);
		}
		break;

	default:
		stop = true;
		break;
	}
}

XTaskQueueCloseHandle(queue);
return NULL;

}

void CALLBACK HandleAsyncQueueCallback(
In void* context,
In XTaskQueueHandle queue,
In XTaskQueuePort type
)
{
//UNREFERENCED_PARAMETER(context);
//UNREFERENCED_PARAMETER(queue);

switch (type)
{
case XTaskQueuePort::Work:
	neosmart::SetEvent(g_workReadyHandle);
	break;

case XTaskQueuePort::Completion:
	neosmart::SetEvent(g_completionReadyHandle);
	break;
}

}

void StartBackgroundThread(JavaVM* vm)
{
g_stopRequestedHandle = neosmart::CreateEvent(true, false);
g_workReadyHandle = neosmart::CreateEvent(false, false);
g_completionReadyHandle = neosmart::CreateEvent(false, false);
g_exampleTaskDone = neosmart::CreateEvent(false, false);

for (uint32_t i = 0; i < g_targetNumThreads; i++)
{
	pthread_create(&g_hActiveThreads[i], NULL, background_thread_proc, (void*)vm);
}

g_numActiveThreads = g_targetNumThreads;

}

void ShutdownActiveThreads()
{
neosmart::SetEvent(g_stopRequestedHandle);

for (int i = 0; i < g_numActiveThreads; i++) {
	pthread_join(g_hActiveThreads[i], NULL);
}

neosmart::ResetEvent(g_stopRequestedHandle);

}

struct SampleHttpCallAsyncContext
{
HCCallHandle call;
bool isJson;
std::string filePath;
};

void DoHttpCall(std::string url, std::string requestBody, bool isJson, std::string filePath)
{
std::string method = "GET";
bool retryAllowed = true;
std::vector<std::vectorstd::string> headers;
std::vector< std::string > header;

header.clear();
header.push_back("TestHeader");
header.push_back("1.0");
headers.push_back(header);

HCCallHandle call = nullptr;
HCHttpCallCreate(&call);
HCHttpCallRequestSetUrl(call, method.c_str(), url.c_str());
HCHttpCallRequestSetRequestBodyString(call, requestBody.c_str());
HCHttpCallRequestSetRetryAllowed(call, retryAllowed);
for (auto& header : headers)
{
	std::string headerName = header[0];
	std::string headerValue = header[1];
	HCHttpCallRequestSetHeader(call, headerName.c_str(), headerValue.c_str(), true);
}

printf("Calling %s %s\r\n", method.c_str(), url.c_str());

SampleHttpCallAsyncContext* hcContext = new SampleHttpCallAsyncContext{ call, isJson, filePath };
XAsyncBlock* asyncBlock = new XAsyncBlock;
ZeroMemory(asyncBlock, sizeof(XAsyncBlock));
asyncBlock->context = hcContext;
asyncBlock->queue = g_queue;
asyncBlock->callback = [](XAsyncBlock* asyncBlock)
{
	const char* str;
	HRESULT networkErrorCode = S_OK;
	uint32_t platErrCode = 0;
	uint32_t statusCode = 0;
	std::string responseString;
	std::string errMessage;

	SampleHttpCallAsyncContext* hcContext = static_cast<SampleHttpCallAsyncContext*>(asyncBlock->context);
	HCCallHandle call = hcContext->call;
	bool isJson = hcContext->isJson;
	std::string filePath = hcContext->filePath;

	HRESULT hr = XAsyncGetStatus(asyncBlock, false);
	if (FAILED(hr))
	{
		// This should be a rare error case when the async task fails
		printf("Couldn't get HTTP call object 0x%0.8x\r\n", hr);
		HCHttpCallCloseHandle(call);
		return;
	}

	HCHttpCallResponseGetNetworkErrorCode(call, &networkErrorCode, &platErrCode);
	HCHttpCallResponseGetStatusCode(call, &statusCode);
	HCHttpCallResponseGetResponseString(call, &str);
	if (str != nullptr) responseString = str;
	std::vector<std::vector<std::string>> headers = ExtractAllHeaders(call);

	if (!isJson)
	{
		size_t bufferSize = 0;
		HCHttpCallResponseGetResponseBodyBytesSize(call, &bufferSize);
		uint8_t* buffer = new uint8_t[bufferSize];
		size_t bufferUsed = 0;
		HCHttpCallResponseGetResponseBodyBytes(call, bufferSize, buffer, &bufferUsed);
		/*HANDLE hFile = CreateFileA(filePath.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
		DWORD bufferWritten = 0;
		WriteFile(hFile, buffer, (DWORD)bufferUsed, &bufferWritten, NULL);
		CloseHandle(hFile);*/
		delete[] buffer;
	}

	HCHttpCallCloseHandle(call);

	printf("HTTP call done\r\n");
	printf("Network error code: 0x%0.8x\r\n", networkErrorCode);
	printf("HTTP status code: %d\r\n", statusCode);

	int i = 0;
	for (auto& header : headers)
	{
		printf("Header[%d] '%s'='%s'\r\n", i, header[0].c_str(), header[1].c_str());
		i++;
	}

	if (isJson && responseString.length() > 0)
	{
		// Returned string starts with a BOM strip it out.
		uint8_t BOM[] = { 0xef, 0xbb, 0xbf, 0x0 };
		if (responseString.find(reinterpret_cast<char*>(BOM)) == 0)
		{
			responseString = responseString.substr(3);
		}
		web::json::value json = web::json::value::parse(utility::conversions::to_string_t(responseString));;
	}

	if (responseString.length() > 200)
	{
		std::string subResponseString = responseString.substr(0, 200);
		printf("Response string:\r\n%s...\r\n", subResponseString.c_str());
	}
	else
	{
		printf("Response string:\r\n%s\r\n", responseString.c_str());
	}

	neosmart::SetEvent(g_exampleTaskDone);
	delete asyncBlock;
};

HCHttpCallPerformAsync(call, asyncBlock);

neosmart::WaitForEvent(g_exampleTaskDone, -1ul);

}

And my android_main:
void android_main(struct android_app* state) {
struct engine engine;

JNIEnv* jni;
JavaVM* vm = state->activity->vm;
vm->AttachCurrentThread(&jni, NULL);

HCInitArgs args;
args.javaVM = vm;
args.applicationContext = state->activity->clazz;

HCInitialize(&args);

XTaskQueueCreate(XTaskQueueDispatchMode::Manual, XTaskQueueDispatchMode::Manual, &g_queue);
XTaskQueueRegisterMonitor(g_queue, nullptr, HandleAsyncQueueCallback, &g_callbackToken);
HCTraceSetTraceToDebugger(true);
StartBackgroundThread(vm);

std::string url1 = "https://.../tiger.svg";
DoHttpCall(url1, "", false, "tiger.svg");`

This creates the threads as in the Win32 sample, but to be able to call into Java code I had to call JavaVM->AttachCurrentThread in my background threads.

The issue with this approach seems to be that the HCInitialize is called from one thread, but the Http requests are processed from other threads, internally sharing the HttpClientRequest java object instance from the HCInitialize call cross-threads - and the JNI apparently does not support it, so I get SIGKill and my app is terminated once I attempt to access HttpClientRequest from another thread.

Putting HCInitialize on the same thread that calls into HttpClientRequest solves the issue, but then I get a deadlock right after executing the HTTP call...

I am thinking that perhaps my approach is completely wrong and this is not how the library is supposed to be used on Android. Could you please provide some working example of an Android NativeActivity implementation using this library?

Android doesn't propagate failed scenario.

When letting a http call timeout on windows
HCHttpCallResponseGetNetworkErrorCode returns a networkErrorCode failed and platformNetworkErrorCode 12002 (timeout)

However, on android they both return as 0, which are successful responses.

There is a bug that android is not propagating any failures. (I was testing on a google xl 2)

edit:
As seen in my 2nd comment. There appears to be a bug here.

Large file support

I understand from #500 that you won't be providing an official implementation for this, but it's a requirement for my current project.
If we go ahead and implement that functionality, would you consider a PR that does so for Windows/GDK only? Android/Apple etc are out of scope for us.

networkErrorCode limitations

From the documentation of the HCHttpCallResponseGetNetworkErrorCode
// <param name="networkErrorCode">The network error code of the HTTP call. Possible values are S_OK, or E_FAIL.</param>

It is being claimed that possible return values are OK or ERROR but in reality, other states may come out such as E_HC_NO_NETWORK. Is that an error in the documentation?

Why does networkErrorCode is so limited in possible return values? It is losing its purpose if you cannot fetch a meaningful cross-platform error out of it and will have to derive the error from platformNetworkErrorCode.

Looking for more information and usages about this feature.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.