Skip to content

Conversation

@keynmol
Copy link
Contributor

@keynmol keynmol commented May 12, 2025

This PR adds a reasonable default user agent for the curl native backend, because it's a better default behaviour, see explanation below.


Consider this simple usage of sttp:

> scala run -e 'import sttp.client4.*; println(basicRequest.get(uri"https://echo.free.beeceptor.com").send(DefaultSyncBackend()).body)' --dep com.softwaremill.sttp.client4::core::4.0.3
Compiling project (Scala 3.6.4, JVM (21))
Compiled project (Scala 3.6.4, JVM (21))
Right({
  "method": "GET",
  "protocol": "https",
  "host": "echo.free.beeceptor.com",
  "path": "/",
  "ip": "195.26.120.14:57130",
  "headers": {
    "Host": "echo.free.beeceptor.com",
    "User-Agent": "Java-http-client/21.0.3",
    "Accept-Encoding": "gzip, deflate"
  },
  "parsedQueryParams": {}
})

Notice how on JVM, User-Agent is added automatically by the client implementation.

Now, if you run this on Native:

> scala run --native -e 'import sttp.client4.*; println(basicRequest.get(uri"https://echo.free.beeceptor.com").send(DefaultSyncBackend()).body)' --dep com.softwaremill.sttp.client4::core::4.0.3

...
Right({
  "method": "GET",
  "protocol": "https",
  "host": "echo.free.beeceptor.com",
  "path": "/",
  "ip": "195.26.120.14:57259",
  "headers": {
    "Host": "echo.free.beeceptor.com",
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate"
  },
  "parsedQueryParams": {}
})

There is no user agent, because libcurl does not add it by default.

Apparently there are some hosts that prohibit empty user agents – one of them being Scaladex!

# -A sets user agent
> curl -A "" 'https://index.scala-lang.org/api/project?organization=indoorvivants&repository=sn-bindgen'
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.24.0 (Ubuntu)</center>
</body>
</html>

Lack of user agent on native prevents parity between JVM and Native usage of sttp.

int sttp_curl_setopt_pointer(CURL *curl, CURLoption opt, void* arg) {return curl_easy_setopt(curl, opt, arg); }

const char* sttp_curl_get_version() {
return curl_version_info(CURLVERSION_NOW)->version;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a FFI method instead of defining the full struct for what curl_version_info returns.

}
}

private lazy val curlUserAgent = "sttp-curl/" + fromCString(CCurl.getVersion())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sttp-curl/8.7.1 is what I get on my machine

Right({
  "method": "GET",
  "protocol": "https",
  "host": "echo.free.beeceptor.com",
  "path": "/",
  "ip": "195.26.120.14:57556",
  "headers": {
    "Host": "echo.free.beeceptor.com",
    "User-Agent": "sttp-curl/8.7.1",
    "Accept": "*/*",
    "Accept-Encoding": "gzip, deflate"
  },
  "parsedQueryParams": {}
})

Comment on lines +96 to +99
curl.option(
UserAgent,
userAgent
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a reasonable default to always set the user agent – user still can circumvent that by using an empty string in the headers.

@adamw
Copy link
Member

adamw commented May 12, 2025

Looks good, thanks!

@adamw adamw merged commit fdbd0c8 into softwaremill:master May 12, 2025
14 checks passed
@keynmol keynmol deleted the curl-set-user-agent branch May 12, 2025 13:18
@keynmol
Copy link
Contributor Author

keynmol commented May 14, 2025

@adamw would it be possible to get a patch release? I see 4.0.5 was cutoff just before this commit

@adamw
Copy link
Member

adamw commented May 14, 2025

@keynmol yes, sorry, I forgot to push the tag 🤦 doing that now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants