I'm trying to understand how to accomplish the following in native Delphi using the TNetHTTPClient/NetHTTPRequest component. Rather than post a bunch of embarrassing code, I'll post some C Code best I can recall that I've worked through in the past and see if I can accomplish it in Delphi. I don't mind reading but I didn't see anything that addressed this on a simple level I can understand.
TReadOnlyMemoryBufferStream
=
class
(
TCustomMemoryStream
)
public
constructor
Create
(
APtr
:
Pointer
;
ASize
:
NativeInt
);
function
Write
(
const
Buffer
;
Count
:
Longint
):
Longint
;
override
;
end
;
constructor
TReadOnlyMemoryBufferStream
.
Create
(
APtr
:
Pointer
;
ASize
:
NativeInt
);
begin
inherited
Create
;
SetPointer
(
APtr
,
ASize
);
end
;
function
TReadOnlyMemoryBufferStream
.
Write
(
const
Buffer
;
Count
:
Longint
):
Longint
;
begin
Result
:=
0
;
end
;
DataStream
:=
TReadOnlyMemoryBufferStream
.
Create
(
databuf
,
data_len
);
PostData
:=
TMultipartFormData
.
Create
;
PostData
.
AddStream
(
'data'
,
DataStream
,
'filename'
,
'application/octet-stream'
);
NetHTTPRequest1
.
Post
(
url
,
PostData
);
finally
PostData
.
Free
;
end
;
finally
DataStream
.
Free
;
end
;
Using Indy's TIdHTTP instead, you can do this:
DataStream
:=
TIdReadOnlyMemoryBufferStream
.
Create
(
databuf
,
data_len
);
PostData
:=
TIdMultiPartFormDataStream
.
Create
;
PostData
.
AddFormField
(
'data'
,
'application/octet-stream'
,
''
,
DataStream
,
'filename'
);
IdHTTP1
.
Post
(
url
,
PostData
);
finally
PostData
.
Free
;
end
;
finally
DataStream
.
Free
;
end
;
It expects that in the URL itself? That is extremely odd. But even so, that doesn't change anything I said earlier. The code I provided, for both TNetHTTPClient and TIdHTTP, sends a POST in 'multipart/form-data' format, same as curl's
CURLOPT_MIMEPOST
option does. This is a standardized format. So, what exactly is the server complaining about this when using the code I gave you?
Then please show the RAW request that curl is actually transmitting (which you can get by using the
CURLOPT_VERBOSE
and
CURLOPT_DEBUGFUNCTION
options), compared to the RAW requests that TNetHTTPClient and TIdHTTP are transmitting (not sure how to get that info with TNetHTTPClient, but for TIdHTTP you can assign any TIdLog... component to the TIdHTTP.Intercept property).
URL: string
CompressedWithBytes: string;;
CompressedLen: Cardinal; //unsigned is required by the server
URL := 'https://10.1.1.1:8443/'
ms := TMemoryStream.create
ResultString := TStringStream.create;
PostDat := TMultipartFormData.create;
CompressedWithBytes := ZipAddJSON(JSONDATA); // Returns string with 4 bytes added to header (27 02 00 00
<GZIP-JSON
data
>
)
CompressedLen := Length(CompressedWithBytes);
ms.write(CompressedWithBytes, CompressedLen);
Postdata.AddStream('data', ms, 'filename', 'application/octet-stream);
NetHttpRequest.Post(URL,PostData);
filedata = request.files.get('data');
// verifies data keyword is there prints error if missing
// verifies application/octet-stream is there prints error if missing
data = file.stream.read()
file.close()
size = int.from_bytes(data[:4], 'little) <- this should be the 4 byte size of the uncompressed data
print(f"First four bytes: {size}")
First four bytes: 88434780 <--should be 27020000 which is the first 4 bytes added to the data before sending
data = zlib.decompress(data[4:], buffsize=int.from_bytes(data[:4], "little")) <---------- fails
excep zlib.error as e:
raise HTTPError(400, f"Failed to decompress data: {e}")
//Exception Prints:
//Error in app: Failed to decompress data: Error -3 while decompressing data: incorrect header check
Why are you using 'string' for binary data? Don't do that. Use TBytes/TArray<Byte> for that instead. And then you can wrap that with TBytesStream instead of TMemoryStream (avoiding an unnecessary copy) Or, just pass your TMemoryStream into ZipAddJson() and let it Write() its bytes directly to the stream (again avoiding an unnecessary copy).