Another time: Using CurlSMBS to POST a file

I have a file to upload to a server API and found this thread which I followed – in my case, I have to add some headers too which are working with different API methods from this server. But everything I tried – taking the direct approach as in the link
curl.FormAdd curl.kFormFilename, log.name, curl.kFormFile, log.URLPath
or being a bit more verbose
curl.FormAdd curl.kFormFilename, log.name, curl.kFormFileContent, s, curl.kFormContentsLength, s.bytes, curl.kFormContentType, "text/csv"
(where s is the content of log)
being followed by a curl.FormFinish of course
results in just a handful of bytes being transferred, and so no file arrives in the API.
Apparently I must be doing something wrong. But what?

More details here?

Well, in general you may want to move to CURLSMimePartMBS class.

See example here:
https://www.monkeybreadsoftware.net/example-curl-curlmime.shtml

Thank you, I’ll try it!
More details are like follows:

Public Sub SendErrorLog(serialnumber as string, log as FolderItem)
  Const ListId = "error_feedback"
  var url As String = makeCallURL(ListId)
  var now As DateTime = DateTime.now
  var key As EinhugurSecureString = EncryptionKey(now)
  var curl As New CurlServerTransfer(ListId)
  curl.OptionURL = url
  curl.OptionPost = True
  curl.OptionVerbose = True
  curl.DebugWithData = True
  curl.CollectDebugMessages = True
  var s As String
  If log.ReadFileMBS(s) Then
    s = s.DefineEncoding(encodings.utf8)
    AddHeaders(curl, now, key, "userid_sequence: " + serialnumber)
    curl.FormAdd curl.kFormFilename, log.name, curl.kFormFile, log.URLPath
    //curl.FormAdd curl.kFormFilename, log.name, curl.kFormFileContent, s, curl.kFormContentsLength, s.bytes, curl.kFormContentType, "text/csv"
    curl.FormFinish
    If Not Me.AddTransfer(curl) Then
      Me.RaiseError(curl, New RuntimeException("Could not send curl to server", -1))
    End If
  End If
  
End Sub

And this is what I am trying to get – the server Admin tested this successfully.

curl.FormAdd curl.kFormFilename, log.name, curl.kFormFile, log.URLPath

for this, please use native path.

Also not working, neither native path nor MIME. Curl will only upload the header and cancel transmission after expect: continue. Could it be that chunked transfer is interfering? Do I have to do something when a continue appears in CurlSMBS?
Or is it the reuse of transmissions?

What does the curl log show?

You can include “Expect:” in the HTTP Header option to disable chunked upload. It should not interfere as that has been standard for 10+ years.

If the log shows reuse of connection, you can use OptionFreshConnect = true to ask for a new connection. But in general POST connections are not reused.

Thank you for your support, Christian!
Log is like this:

MBS Xojo Plugins 25.0 with CURL 8.11.1 on macOS.

URL: https://api.serveraddress.com/admin/common/error_feedback

Hostname api.serveraddress.com was found in DNS cache

Trying xxx.xxx.xxx.xxx:443…

ALPN: curl offers http/1.1

TLSv1.3 (OUT), TLS handshake, Client hello (1):

TLSv1.3 (IN), TLS handshake, Server hello (2):

TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):

TLSv1.3 (IN), TLS handshake, Certificate (11):

TLSv1.3 (IN), TLS handshake, CERT verify (15):

TLSv1.3 (IN), TLS handshake, Finished (20):

TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):

TLSv1.3 (OUT), TLS handshake, Finished (20):

SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / RSASSA-PSS

ALPN: server accepted http/1.1

Server certificate:

subject: CN=api.serveraddress.com

start date: Dec 12 02:35:15 2024 GMT

expire date: Mar 12 02:35:14 2025 GMT

issuer: C=US; O=Let’s Encrypt; CN=R10

SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.

Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption

Certificate level 1: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption

Connected to api.serveraddress.com (xxx.xxx.xxx.xxx) port 443

using HTTP/1.x

POST /admin/common/error_feedback HTTP/1.1

Host: api.serveraddress.com

Accept: /

Transfer-Encoding: chunked

times: 2025-01-23 08:53:29

secret_key: 572FFxxxxxx

userid_sequence: 31259xxxxx

Content-Type: application/x-www-form-urlencoded

Expect: 100-continue

HTTP/1.1 100 Continue

0

upload completely sent off: 5 bytes

HTTP/1.1 200 OK

Connection: Keep-Alive

Keep-Alive: timeout=5, max=100

access-control-allow-origin: *

access-control-allow-headers: Origin, X-Requested-With, Content-Type, Accept

access-control-allow-methods: GET, POST, PUT

content-type: text/html; charset=UTF-8

content-length: 343

date: Thu, 23 Jan 2025 07:53:29 GMT

server: LiteSpeed

Array

(

[/admin/common/error_feedback] =>

)

====================Array

(

[Host] => api.serveraddress.com

[Accept] => */*

[Content-Length] => 0

[times] => 2025-01-23 08:53:29

[secret_key] => 572FFC5CFBDxxxxxxx

[userid_sequence] => 31259xxxxx

[Content-Type] => application/x-www-form-urlencoded

)

Connection #2 to host api.serveraddress.com left intact

From trying to send a log with a few 100 bytes with

Const ListId = "error_feedback"
var url As String = makeCallURL(ListId)
var now As DateTime = DateTime.now
var key As EinhugurSecureString = EncryptionKey(now)
var curl As New CurlServerTransfer(ListId)
curl.OptionFreshConnect = True
curl.OptionURL = url
curl.OptionPost = True
curl.OptionVerbose = True
curl.DebugWithData = True
curl.CollectDebugMessages = True
var s As String
If log.ReadFileMBS(s) Then
  s = s.DefineEncoding(encodings.utf8)
  AddHeaders(curl, now, key, "userid_sequence: " + serialnumber)
  var mime As CURLSMimePartMBS = curl.AddMimePart
  mime.File = log
  mime.SetHeaders(Array ("content-length: " + log.Length.ToString))
  mime.MimeType = "text/csv"
  mime.Name = log.Name
  curl.FinishMime
  curl.OptionInFileSize = log.Length
  If Not Me.AddTransfer(curl) Then
    Me.RaiseError(curl, New RuntimeException("Could not send curl to server", -1))
  End If
End If

I was not sure if I need to add MIME headers. Tried without, without InFileSize or with full headers in MIME, which gives same result.

With an “Expect:” added to the headers, I still see a

Transfer-Encoding: chunked

in the log.

Sorry for feeling kinda stupid – I wish I’d understand more of curl …

What is log there? A folder item?
Why do you use ReadFileMBS, but not use s for the content?

Why do you overwrite the curl.OptionInFileSize? Please remove!
The size is set by the mime stuff.

Why do you use SetHeaders for the CURLSMimePartMBS? You may put the wrong number there as you don’t handle the base64 encoding. Setting an option too much is often bad.

Yes. A tab separated text file.

This is from trying FormAdd. Or do I have to add Mime.Content even if I address the file directly?

As written, I tried without and got the same result. But I’ll try again without.

Same as above.

Removed all stuff, tried with just setting the MIME file or different combinations like below. Content-Length is set automatically, but still no file is being transferred. I must be missing something, but really have no clue.

AddHeaders(curl, now, key, "Expect:", "userid_sequence: " + serialnumber)
var mime As CURLSMimePartMBS = curl.AddMimePart
mime.File = log
mime.MimeType = "text/csv"
mime.FileName = log.Name
mime.Encoding = CURLSMimePartMBS.kEncodingQuotedPrintable
curl.FinishMime

Sorry, you could of course send me a test project later.

You could try DataString property with the content instead of file property.

What is the new curl messages log?