I have a problem that I can not resolve. I need to send a mutipart POST request to server but it rejects my tries with Freepascal.
Using command terminal and curl the server accepts upload without problems.
If I send with curl using script:
#!/bin/bash
API='api'
PASS='pass'
CALL='oh1kh'
FILE='ex.adi'
EMAIL='oh1kh@oh1kh.fi'
curl -v -i -X POST \
-F file=@${FILE} \
-F email=${EMAIL} \
-F callsign=${CALL} \
-F password=${PASS} \
-F api=${API} \
https://httpbin.org/anything
Result is:
{
"args": {},
"data": "",
"files": {
"file": "<QSO_DATE:8>20260203<TIME_ON:4>0830<CALL:5>OH1KJ<BAND:3>40M<MODE:2>CW<FREQ:5>7.025<TIME_OFF:4>0830<RST_SENT:3>599<RST_RCVD:3>599<QSL_SENT:1>N<QSL_RCVD:1>N<MY_GRIDSQUARE:6>KP01tn<TX_PWR:3>100<ITUZ:2>18<CQZ:2>15<LOTW_QSL_SENT:1>N<LOTW_QSL_RCVD:1>N<CONT:2>EU<EOR>\n<QSO_DATE:8>20260203<TIME_ON:4>0830<CALL:5>OH1KJ<BAND:3>30M<MODE:2>CW<FREQ:4>10.1<TIME_OFF:4>0830<RST_SENT:3>599<RST_RCVD:3>599<QSL_SENT:1>N<QSL_RCVD:1>N<MY_GRIDSQUARE:6>KP01tn<TX_PWR:3>100<ITUZ:2>18<CQZ:2>15<LOTW_QSL_SENT:1>N<LOTW_QSL_RCVD:1>N<CONT:2>EU<EOR>\n<QSO_DATE:8>20260203<TIME_ON:4>0831<CALL:5>OH1KJ<BAND:3>20M<MODE:2>CW<FREQ:2>14<TIME_OFF:4>0831<RST_SENT:3>599<RST_RCVD:3>599<QSL_SENT:1>N<QSL_RCVD:1>N<MY_GRIDSQUARE:6>KP01tn<TX_PWR:3>100<ITUZ:2>18<CQZ:2>15<LOTW_QSL_SENT:1>N<LOTW_QSL_RCVD:1>N<CONT:2>EU<EOR>\n"
},
"form": {
"api": "api",
"callsign": "oh1kh",
"email": "oh1kh@oh1kh.fi",
"password": "pass"
},
"headers": {
"Accept": "*/*",
"Content-Length": "1419",
"Content-Type": "multipart/form-data; boundary=------------------------191zKPATSeNPAJLX3oWkyt",
"Host": "httpbin.org",
"User-Agent": "curl/8.11.1",
"X-Amzn-Trace-Id": "Root=1-69845c03-16584d767851ee66237add89"
},
"json": null,
"method": "POST",
"origin": "84.231.202.222",
"url": "https://httpbin.org/anything"
}
And this works. File key and contents are placed in "files:{ }" part of response.
Then I use Synapse/FPC to do the same, but result from httpbin.org is not similar.
data:TStringList has the information I push to upload function :
file=/tmp/ClubStream.txt
email=oh1kh@oh1kh.fi
password=password
callsign=oh1kh
api=api
And the result from httpbin.org for this is:
{
"args": {},
"data": "",
"files": {},
"form": {
"api": "api",
"callsign": "oh1kh",
"email": "oh1kh@oh1kh.fi",
"file": "<QSO_DATE:8>20260203<TIME_ON:4>0830<CALL:5>OH1KJ<BAND:3>40M<MODE:2>CW<FREQ:5>7.025<TIME_OFF:4>0830<RST_SENT:3>599<RST_RCVD:3>599<QSL_SENT:1>N<QSL_RCVD:1>N<MY_GRIDSQUARE:6>KP01tn<TX_PWR:3>100<ITUZ:2>18<CQZ:2>15<LOTW_QSL_SENT:1>N<LOTW_QSL_RCVD:1>N<CONT:2>EU<EOR>\n<QSO_DATE:8>20260203<TIME_ON:4>0830<CALL:5>OH1KJ<BAND:3>30M<MODE:2>CW<FREQ:4>10.1<TIME_OFF:4>0830<RST_SENT:3>599<RST_RCVD:3>599<QSL_SENT:1>N<QSL_RCVD:1>N<MY_GRIDSQUARE:6>KP01tn<TX_PWR:3>100<ITUZ:2>18<CQZ:2>15<LOTW_QSL_SENT:1>N<LOTW_QSL_RCVD:1>N<CONT:2>EU<EOR>\n<QSO_DATE:8>20260203<TIME_ON:4>0831<CALL:5>OH1KJ<BAND:3>20M<MODE:2>CW<FREQ:2>14<TIME_OFF:4>0831<RST_SENT:3>599<RST_RCVD:3>599<QSL_SENT:1>N<QSL_RCVD:1>N<MY_GRIDSQUARE:6>KP01tn<TX_PWR:3>100<ITUZ:2>18<CQZ:2>15<LOTW_QSL_SENT:1>N<LOTW_QSL_RCVD:1>N<CONT",
"password": "password"
},
"headers": {
"Content-Length": "1441",
"Content-Type": "multipart/form-data; boundary=099617E5_Synapse_boundary",
"Host": "httpbin.org",
"User-Agent": "Mozilla/4.0 (X11; Linux x86_64)",
"X-Amzn-Trace-Id": "Root=1-698459c2-585a677e3a87373141193483"
},
"json": null,
"method": "POST",
"origin": "84.231.202.222",
"url": "https://httpbin.org/anything"
}
The function used for upload (part that produces key parts and upload):
for i:=0 to data.Count-1 do
begin
Key := copy(data.Strings[i],1,Pos('=',data.Strings[i])-1);
Value := copy(data.Strings[i],Pos('=',data.Strings[i])+1,Length(data.Strings[i])-Pos('=',data.Strings[i])+1);
if Key='file' then
begin
WriteStrToStream(HTTP.Document,
'--' + Bound + CRLF +
'Content-Disposition: form-data; name=' + AnsiQuotedStr(Key, '"') + CRLF +
' filename=' + AnsiQuotedStr(Value, '"') + CRLF+
'Content-Type: application/octet-string' + CRLF +
CRLF);
F:=TMemoryStream.Create;
F.LoadFromFile(Value);
HTTP.Document.CopyFrom(F, 0);
F.Destroy;
WriteStrToStream(HTTP.Document,CRLF);
end
else
begin
WriteStrToStream(HTTP.Document,
'--' + Bound + CRLF +
'Content-Disposition: form-data; name=' + AnsiQuotedStr(Key, '"') + CRLF +
'Content-Type: text/plain' + CRLF +
CRLF);
WriteStrToStream(HTTP.Document, Value);
WriteStrToStream(HTTP.Document,CRLF);
end;
end;
WriteStrToStream(HTTP.Document,'--' + Bound + '--' + CRLF);
HTTP.MimeType := 'multipart/form-data; boundary=' + Bound;
if HTTP.HTTPMethod('POST',Url) then
If I replace "WriteStrToStream(HTTP.Document," with "write(" to see what function is doing,
because I did not found the way to debug print contents of HTTP.Document, it looks like this:
(And note: the file contents are not shown in this debug)
--112ED948_Synapse_boundary
Content-Disposition: form-data; name="file"
filename="/tmp/ClubStream.txt"
Content-Type: application/octet-string
--112ED948_Synapse_boundary
Content-Disposition: form-data; name="email"
Content-Type: text/plain
oh1kh@oh1kh.fi
--112ED948_Synapse_boundary
Content-Disposition: form-data; name="password"
Content-Type: text/plain
password
--112ED948_Synapse_boundary
Content-Disposition: form-data; name="callsign"
Content-Type: text/plain
oh1kh
--112ED948_Synapse_boundary
Content-Disposition: form-data; name="api"
Content-Type: text/plain
api
--112ED948_Synapse_boundary--
It looks very good for me. I am blind for finding the error from this code!
I have tried few days with Googling and testing to resolve why the content of file goes to "form" part and not to "files" part. That seems to be the reason the server rejects my upload.
Is there anyone who could help and tell what I am doing wrong, please.