ErikMinekus / sm-ripext

SourceMod REST in Pawn Extension
https://forums.alliedmods.net/showthread.php?t=298024
GNU General Public License v3.0
136 stars 38 forks source link

HTTPResponse.Data throws exception "Invalid JSON" #63

Open roby2014 opened 3 years ago

roby2014 commented 3 years ago

Hello. Im trying something and I cannot understand if its my code or the extension that is not working as it should. Im doing an API request where it should return me some JSON data. The problem is, Im getting some errors:

L 09/16/2021 - 12:41:21: [SM] Exception reported: Invalid JSON in line 1, column 86: ':' expected near end of file
L 09/16/2021 - 12:41:21: [SM] Blaming: csgopanel_gametracker.smx
L 09/16/2021 - 12:41:21: [SM] Call stack trace:
L 09/16/2021 - 12:41:21: [SM]   [0] HTTPResponse.Data.get
L 09/16/2021 - 12:41:21: [SM]   [1] Line 47, h:\coding\csgopanel_sm\scripting\include\gametracker/httpcallbacks.sp::httpcallback_getgametracker 

(but sometimes error message is different) :

L 09/16/2021 - 13:59:14: [SM] Exception reported: Invalid JSON in line 1, column 85: premature end of input near '"points' 
L 09/16/2021 - 13:59:14: [SM] Blaming: csgopanel_gametracker.smx 
L 09/16/2021 - 13:59:15: [SM] Call stack trace: 
L 09/16/2021 - 13:59:15: [SM]   [0] HTTPResponse.Data.get
L 09/16/2021 - 13:59:15: [SM]   [1] Line 47, h:\coding\csgopanel_sm\scripting\include\gametracker/httpcallbacks.sp::httpcallback_getgametracker     

This is my code (where the exception occurs).

public void httpcallback_getgametracker(HTTPResponse response, any userid, const char[] error) {
    int client = GetClientOfUserId(userid);
    if (!(IsClientConnected(client) && !IsFakeClient(client) && !IsClientSourceTV(client))) {
        return;
    }

    if (response.Status != HTTPStatus_OK && response.Status != HTTPStatus_Created) {
        LogError("\nError on request \" get/gametracker \" (%i)\nError: %s", response.Status, error);
        return;
    }

    JSONObject user_data = view_as<JSONObject>(response.Data); // ERROR HERE
// ...
}

I do not understand the error because, testing this endpoint on a software like Postman, it gives me valid JSON Example:

Setup:

Thanks in advance.

CrazyHackGUT commented 3 years ago

Show the code and full endpoint. It looks like you incorrect build the link. You can check manually web server logs.

roby2014 commented 3 years ago

@CrazyHackGUT Hello, thanks for answering.

Full code here, with screenshots also with the JSON sent to the endpoint.

void get_player_info(int client) {
    GetClientName(client, g_players[client].name, sizeof(CSGOPanelUser::name));
    GetClientAuthId(client, AuthId_SteamID64, g_players[client].steam_id, sizeof(CSGOPanelUser::steam_id));

    JSONObject json = new JSONObject();
    json.SetString("token", TOKEN);
    json.SetInt("server_id", SERVERID);
    json.SetString("steamid", g_players[client].steam_id);

        if (DEBUG) {
        char debugjson[MAX_DEBUG_LENGTH];
        json.ToString(debugjson, sizeof(debugjson), JSON_INDENT(5));
        LogMessage("\n@ get_player_info @ sending api request to \"%s\":\n%s\n", "https://api.csgopanel.com/get/gametracker", debugjson);
    }

    HTTPRequest req = new HTTPRequest("https://api.csgopanel.com/get/gametracker");
    req.SetHeader("Content-Type", "application/json");
    req.Post(json, httpcallback_getgametracker, GetClientUserId(client));

    delete json;
}

public void httpcallback_getgametracker(HTTPResponse response, any userid, const char[] error) {
    int client = GetClientOfUserId(userid);
    if (!(IsClientConnected(client) && !IsFakeClient(client) && !IsClientSourceTV(client))) {
        return;
    }

    if (response.Status != HTTPStatus_OK && response.Status != HTTPStatus_Created) {
        LogError("\nError on request \" get/gametracker \" (%i)\nError: %s", response.Status, error);
        return;
    }

    LogMessage("\n client exists... lets get his data %i \n", response.Status);

    JSONObject user_data = view_as<JSONObject>(response.Data); // ERROR, function stops here.

    if (DEBUG) {
        char debugjson[MAX_DEBUG_LENGTH];
        user_data.ToString(debugjson, sizeof(debugjson), JSON_INDENT(5));
        LogMessage("\n@ httpcallback_getgametracker @ received player data:\n%s\n", debugjson);
    }
}

json debug:

roby2014 commented 3 years ago

Also, as you can see, using another API testing tool like Insomnia and sending the same JSON to the endpoint as the plugin, it gives me a valid JSON response...

ErikMinekus commented 3 years ago

Could you try the debug build posted here, and post the output from the server console?

roby2014 commented 3 years ago

Hello @ErikMinekus , yes sure. I have attached a .txt file with the console output, and also there is the plugin source code:

public void OnClientAuthorized(int client, const char[] auth) {
    if (!IsFakeClient(client)) {
        get_player_info(client);
    }
}

void get_player_info(int client) {
    char name[64];
    char steam_id[64];
    GetClientName(client, name, sizeof(name));
    GetClientAuthId(client, AuthId_SteamID64, steam_id, sizeof(steam_id));

    JSONObject json = new JSONObject();
    json.SetString("token", TOKEN);
    json.SetInt("server_id", SERVERID);
    json.SetString("steamid", steam_id);

    if (DEBUG) {
        char debugjson[MAX_DEBUG_LENGTH];
        json.ToString(debugjson, sizeof(debugjson), JSON_INDENT(5));
        LogMessage("\n@ get_player_info @ sending api request to \"%s\":\n%s\n", "https://api.csgopanel.com/get/gametracker", debugjson);
    }

    HTTPRequest req = new HTTPRequest("https://api.csgopanel.com/get/gametracker");
    req.SetHeader("Content-Type", "application/json");
    req.Post(json, httpcallback_getgametracker, GetClientUserId(client));

    delete json;
}

public void httpcallback_getgametracker(HTTPResponse response, any userid, const char[] error) {
    int client = GetClientOfUserId(userid);
    if (response.Status != HTTPStatus_OK && response.Status != HTTPStatus_Created) {
        LogError("\nError on request \" get/gametracker \" (%i)\nError: %s", response.Status, error);
        return;
    }

    JSONObject user_data = view_as<JSONObject>(response.Data); // CRASH, function stops here.

    if (DEBUG) {
        char debugjson[MAX_DEBUG_LENGTH];
        user_data.ToString(debugjson, sizeof(debugjson), JSON_INDENT(5));
        LogMessage("\n@ httpcallback_getgametracker @ received player data:\n%s\n", debugjson);
    }
}

errors.txt

ESK0 commented 3 years ago

Hi @ErikMinekus,

we do have a similar issue (https://github.com/ESK0/eItems/blob/main/scripting/files/parse.sp#L43)

basically we have found out that this happen on Windows only and only if the endpoint is https, http works just fine.

L 11/09/2021 - 05:17:57: [SM] Exception reported: Invalid JSON in line 1, column 2014: unexpected token near end of file
L 11/09/2021 - 05:17:57: [SM] Blaming: eitems.smx
L 11/09/2021 - 05:17:57: [SM] Call stack trace:
L 11/09/2021 - 05:17:57: [SM]   [0] HTTPResponse.Data.get
L 11/09/2021 - 05:17:57: [SM]   [1] Line 43, files\parse.sp::ParseItemsDownloaded

=== Info: h2_process_pending_input: 2715724417014431744 bytes left in connection buffer

=== Info: http2_recv returns AGAIN for stream 1

=== Info: http2_recv: easy 0x25b77d90 (stream 1) win 33543474/33543474

=== Info: Use data left in connection buffer, nread=0
ErikMinekus commented 3 years ago

Yes, it’s an issue with HTTP/2 on Windows. HTTP/2 is only attempted over HTTPS.

ESK0 commented 3 years ago

Yes, it’s an issue with HTTP/2 on Windows. HTTP/2 is only attempted over HTTPS.

Is there a workaround?

Wend4r commented 3 years ago

Yes, it’s an issue with HTTP/2 on Windows. HTTP/2 is only attempted over HTTPS.

Is there a workaround?

Use SteamWorks extension and convert body to JSON via JSONArray.FromString/JSONObject.FromString

ESK0 commented 3 years ago

Yes, it’s an issue with HTTP/2 on Windows. HTTP/2 is only attempted over HTTPS.

Is there a workaround?

Use SteamWorks extension and convert body to JSON via JSONArray.FromString/JSONObject.FromString

This can be inconvenient for many people (install another extension)... but probably the fastest solution at the moment.