使用 CURL 进行 GET 请求

Nil*_* J. 3 c++ curl minecraft

我的我的世界服务器程序遇到了这个令人讨厌的问题。当您尝试登录我的 minecraft 服务器时,我尝试从 minecraft.com 获得登录响应。一切都很好,设置像验证令牌密钥和共享秘密。

我只是无法获得使用 curl 的 get 请求:(

这就是我所拥有的:

    requestString.append("GET /session/minecraft/hasJoined?username=");
    requestString.append(player.Username);
    requestString.append("&");
    requestString.append("serverId=");
    requestString.append(player.loginHash);
    requestString.append(" HTTP/1.0\r\n");
    std::cout << requestString << std::endl;


    curl_easy_setopt(curl, CURLOPT_URL, "https://sessionserver.mojang.com");
    list = curl_slist_append(list, requestString.c_str());
    list = curl_slist_append(list, "User-Agent: MinecraftServerPP\r\n");
    list = curl_slist_append(list, "Connection: close\r\n");
    list = curl_slist_append(list, "\r\n");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
    curl_easy_setopt(curl, CURLOPT_CAINFO, "cacert.pem");

    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &_write_data);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
Run Code Online (Sandbox Code Playgroud)

我从服务器得到了一个 ok,但不是包含我需要的数据的 json 对象。

Rem*_*eau 6

为什么将GET请求行放在 HTTP 标头中?它根本不属于那里,事实上 libCURL 文档甚至指出:

CURLOPT_HTTPHEADER 解释

请求中的第一行(包含方法,通常是 GET 或 POST)不是标头,不能使用此选项替换。只有请求行后面的行是标题。在此标头列表中添加此方法行只会导致您的请求发送无效标头。使用 CURLOPT_CUSTOMREQUEST 更改方法。

您当前的代码正在发送GET类似于以下内容的请求:

获取/HTTP/1.1
主机:sessionserver.mojang.com
GET /session/minecraft/hasJoined?username=<username>&serverId=<loginHash> HTTP/1.0
用户代理:MinecraftServerPP
连接:关闭

这就是为什么您没有得到您期望的响应。您实际上并不是在请求/session/minecraft/hasJoined资源,而是在请求根/资源。

您应该发送这样的GET请求:

GET /session/minecraft/hasJoined?username=<username>&serverId=<loginHash> HTTP/1.0
主机:sessionserver.mojang.com
用户代理:MinecraftServerPP
连接:关闭

为此,您需要将完整的URL传递给CURLOPT_URL并让 libCURLGET为您处理该行。您可以使用CURLOPT_HTTPGET以确保GET实际使用。

此外,CURLOPT_HTTP_VERSION如果要指定特定的 HTTP 版本,则应该使用。

此外,虽然你可以使用CURLOPT_HTTPHEADERUser-AgentConnection标题,你应该是使用CURLOPT_USERAGENTCURLOPT_FORBID_REUSE替代。

试试这个:

url = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=");
url.append(player.Username); // <-- NOTE: see further below!
url.append("&serverId=");
url.append(player.loginHash); // <-- NOTE: see further below!
std::cout << url << std::endl;

curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1L); 
curl_easy_setopt(curl, CURLOPT_CAINFO, "cacert.pem");

curl_easy_setopt(curl, CURLOPT_USERAGENT, "MinecraftServerPP");

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &_write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
Run Code Online (Sandbox Code Playgroud)

现在,话虽如此,您还需要考虑其他逻辑才能正确处理:CURLOPT_URL

传递指向要使用的 URL 的指针。参数应该是一个字符 * 到零终止的字符串,该字符串必须按以下格式进行 URL 编码

方案://主机:端口/路径

有关格式的更多解释,请参阅RFC 3986

在发出传输之前,libcurl 不会验证语法或使用此变量。即使你在这里设置了一个疯狂的值,curl_easy_setopt仍然会返回CURLE_OK

特别是,RFC 的这些部分与您的代码相关:

2.1. 百分比编码

当八位字节的相应字符在允许集之外或用作组件的分隔符或组件内时,百分比编码机制用于表示组件中的数据八位字节。百分比编码的八位字节被编码为一个字符三元组,由百分比字符“%”后跟代表该八位字节数值的两个十六进制数字组成。例如,“%20”是二进制八位字节“00100000”(ABNF:%x20)的百分比编码,在 US-ASCII 中对应于空格字符 (SP)。 2.4 节描述了何时应用百分比编码和解码。

pct-encoded = "%" HEXDIG HEXDIG

大写十六进制数字“A”到“F”分别等效于小写数字“a”到“f”。如果两个 URI 仅在百分比编码的八位字节中使用的十六进制数字的情况下不同,则它们是等效的。为了一致性,URI 生成器和规范器应该对所有百分比编码使用大写的十六进制数字。

2.4. 何时编码或解码

在正常情况下,URI 中的八位字节被百分比编码的唯一时间是在从其组成部分生成 URI 的过程中。这是当实现确定哪些保留字符将用作子组件分隔符以及哪些可以安全地用作数据时。一旦生成,URI 总是采用百分比编码形式。

当取消引用 URI 时,必须先解析和分离对特定于方案的取消引用过程(如果有)重要的组件和子组件,然后才能安全地解码这些组件中的百分比编码八位字节,否则数据可能会被误认为组件分隔符。唯一的例外是与未保留集中的字符对应的百分比编码八位字节,可以随时对其进行解码。例如,与波浪号(“~”)字符对应的八位字节通常被旧的 URI 处理实现编码为“%7E”;"%7E" 可以替换为 "~" 而不会改变它的解释。

因为百分比 ("%") 字符用作百分比编码的八位字节的指示符,所以它必须被百分比编码为 "%25" 才能将该八位字节用作 URI 中的数据。实现不能对同一个字符串进行多次百分比编码或解码,因为解码一个已经解码的字符串可能会导致将百分比数据八位字节误解为百分比编码的开始,反之亦然。百分比编码的字符串。

queryURL的组成部分在第 3.4 节中定义:

查询 = *( pchar / "/" / "?" )

pchar第 3.3 节中定义:

pchar = unreserved / pct-encoded / sub-delims / ":" / "@"

unreserved第 2.3 节中定义:

未保留 = ALPHA / DIGIT / "-" / "." / "_" / "~"

sub-delims第 2.2 节中定义:

子delims =“!” / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="

因此,如果您的player.Usernameplayer.loginHash字符串中的任何字符超出了这些允许的字符,则在将它们放入 URL 的查询字符串时必须对它们进行百分比编码。例如:

std::string encodeForUrlQuery(std::string s)
{
    static const char* allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&'()*+,;=:@/?";

    // this is just an example, in practice you should first convert the
    // input string to Unicode and charset-encode it, usually to UTF-8,
    // and then percent-encode the resulting octets...

    std::string::size_type idx = s.find_first_not_of(allowed);
    while (idx != std::string::npos)
    {
        std::ostringstream oss;
        oss << '%' << std::hex << std::setw(2) << std::setfill('0') << std::uppercase << (int)s[idx];
        std::string encoded = oss.str();
        s.replace(idx, 1, encoded);
        idx = s.find_first_not_of(allowed, idx+encoded.length());
    }
    return s;
}
Run Code Online (Sandbox Code Playgroud)

url = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=");
url.append(encodeForUrlQuery(player.Username));
url.append("&serverId=");
url.append(encodeForUrlQuery(player.loginHash));
std::cout << url << std::endl;
Run Code Online (Sandbox Code Playgroud)