将工作 curl 请求转换为 HTTP

时间:2021-03-19 11:22:06

标签: http curl

我正在使用 curl 来执行 POST 请求,但我不能假设我的目标平台有 curl 可用,所以我试图用 HTTP(保证可用)重写我的 curl 请求。我对 curl 和 HTTP 的了解非常有限,所以我希望有人能指出我做错了什么。

我的 curl 请求(命令行):

curl.exe POST https://xxxxxx.ingest.sentry.io/api/xxxxxxx/minidump/?sentry_key=xxxxxxxxxxxxxxxxxxxxxxx -F upload_file_minidump=@"C:\path\Minidump.dmp" -F upload_file_log=@"C:\path\program.log"

curl 输出的相关部分如下所示。这是在连接到服务器并向其发送 POST 请求之后。服务器现在让客户端知道第一个文件可以发送,curl首先通过发送文件自己的头来响应,然后是数据(这里是clamped)

<= Recv header, 23 bytes (0x17)
0000: HTTP/1.1 100 Continue
=> Send data, 175 bytes (0xaf)
0000: --------------------------f2a4a742c08bf427
002c: Content-Disposition: form-data; name="upload_file_minidump"; fil
006c: ename="UE4Minidump.dmp"
0085: Content-Type: application/octet-stream
00ad: 
=> Send data, 16384 bytes (0x4000)
0000: MDMP..a..... .......m/S`.........................;..............
0040: 8Z......T...=...........`.......8...........T................[..
0080: .........\...........]..........= ..............................
00c0: ....................................aJ.......`......Lw..........
0100: ............T........?..i/S`........ ... ... ............ ......
0140: ............G.M.T. .S.t.a.n.d.a.r.d. .T.i.m.e...................
0180: ................................G.M.T. .D.a.y.l.i.g.h.t. .T.i.m.
01c0: e...................................................1.9.0.4.1...
..etc..

通过阅读 curl 的详细输出,我创建了一个像这样的 HTTP 请求(使用虚幻引擎 4 库的 C++ 代码):

TSharedRef<IHttpRequest, ESPMode::ThreadSafe> httpRequest = FHttpModule::Get().CreateRequest();
    
httpRequest->SetURL(TEXT("https://xxxxxx.ingest.sentry.io/api/xxxxxx/minidump/?sentry_key=xxxxxxxxxxxxxxxxxxxxxxxx"));

httpRequest->SetVerb(TEXT("POST"));
const FString boundary(TEXT("------------------------f2a4a742c08bf427"));
httpRequest->SetHeader(TEXT("Content-Type"), TEXT("multipart/form-data; boundary=") + boundary);

const FString fileName(FPaths::Combine(path, crashToReport.folderName, TEXT("UE4Minidump.dmp")));

ensure(FPaths::FileExists(fileName));

const FString prefixBoundary(TEXT("\r\n--") + boundary + TEXT("\r\n"));
const FString fileHeader(TEXT("Content-Disposition: form-data; name=\"upload_file_minidump\"; filename=\"UE4Minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n"));

FString fileContents;
FFileHelper::LoadFileToString(fileContents, *fileName);

const FString suffixBoundary(TEXT("\r\n--") + boundary + TEXT("--\r\n"));

const FString content(prefixBoundary + fileHeader + fileContents + suffixBoundary);
httpRequest->SetContentAsString(content);

这在一定程度上有效,服务器现在接受了这一点,并将接收文件 - 但是该文件最终在服务器端无法读取,导致我认为我没有以正确的格式发送它。

多部分/表单数据请求中需要什么样的数据?

我注意到的一件事是 curl 请求单独发送文件的标头(第一个 175 字节的块)。我想要一些关于如何实现这一目标的信息!

1 个答案:

答案 0 :(得分:3)

我终于明白了。我不确定如何报告我做错了什么,但我认为这与幕后发生的事情有关:

httpRequest->SetContentAsString(..)

,这确实导致后端无法解释我尝试发送的二进制文件。我最终阅读了二进制文件 .. 作为二进制文件:

TArray<uint8> dumpFileData;
FFileHelper::LoadFileToArray(dumpFileData, *FPaths::Combine(path,crashToReport.folderName, TEXT("UE4Minidump.dmp")));

然后通过 POST 请求发送它,与之前类似,但将完整的表单数据部分添加为二进制数据:

TSharedRef<IHttpRequest, ESPMode::ThreadSafe> httpRequest = FHttpModule::Get().CreateRequest();
httpRequest->SetURL(TEXT("https://xxxxx.ingest.sentry.io/api/xxxxxx/minidump/?sentry_key=xxxxxxxxxxxxxxxxxxxxxxxxxxx"));

httpRequest->SetVerb(TEXT("POST"));
const FString boundary(TEXT("------------------------bb33b671b1212234"));
httpRequest->SetHeader(TEXT("Content-Type"), TEXT("multipart/form-data; boundary=") + boundary);
httpRequest->SetHeader(TEXT("Accept"), TEXT("*/*"));
httpRequest->SetHeader(TEXT("Expect"), TEXT("100-continue"));

{
    const FString prefixBoundary(TEXT("--") + boundary + TEXT("\r\n"));
    const FString fileHeader(TEXT("Content-Disposition: form-data; name=\"upload_file_minidump\"; filename=\"UE4Minidump.dmp\"\nContent-Type: application/octet-stream\r\n\r\n"));
    const FString suffixBoundary(TEXT("\r\n--") + boundary + TEXT("--\r\n"));
    
    TArray<uint8> CombinedContent;
    CombinedContent.Append(FStringToUint8(prefixBoundary + fileHeader));
    CombinedContent.Append(dumpFileData);
    CombinedContent.Append(FStringToUint8(suffixBoundary));
    
    httpRequest->SetContent(CombinedContent);
}

httpRequest->ProcessRequest();

For completeness, FStringToUint8 is defined as follows:

// Convert  FString to UTF8 and put it in a TArray
TArray<uint8> FStringToUint8(const FString& InString)
{
    TArray<uint8> OutBytes;

    // Handle empty strings
    if (InString.Len() > 0)
    {
        FTCHARToUTF8 Converted(*InString); // Convert to UTF8
        OutBytes.Append(reinterpret_cast<const uint8*>(Converted.Get()), Converted.Length());
    }

    return OutBytes;
}