Skip to content

Commit

Permalink
Fix native on ARM (Macs) (#2082)
Browse files Browse the repository at this point in the history
  • Loading branch information
szymon-rd authored May 10, 2024
1 parent e1f4aa0 commit 1dab59a
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 35 deletions.
9 changes: 9 additions & 0 deletions core/src/main/resources/scala-native/ffi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#if defined(STTP_CURL_FFI)
#include <curl/curl.h>

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

int sttp_curl_getinfo_pointer(CURL *curl, CURLINFO info, void* arg) {return curl_easy_getinfo(curl, info, arg); }
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ abstract class AbstractCurlBackend[F[_]](_monad: MonadError[F], verbose: Boolean
curl.option(HeaderData, spaces.headersResp)
curl.option(Url, request.uri.toString)
setMethod(curl, request.method)
setRequestBody(curl, request.body)
setRequestBody(curl, request.body, request.method)
monad.flatMap(perform(curl)) { _ =>
curl.info(ResponseCode, spaces.httpCode)
val responseBody = fromCString((!spaces.bodyResp)._1)
Expand Down Expand Up @@ -157,7 +157,7 @@ abstract class AbstractCurlBackend[F[_]](_monad: MonadError[F], verbose: Boolean
curl.option(WriteData, outputFilePtr)
curl.option(Url, request.uri.toString)
setMethod(curl, request.method)
setRequestBody(curl, request.body)
setRequestBody(curl, request.body, request.method)
monad.flatMap(perform(curl)) { _ =>
curl.info(ResponseCode, spaces.httpCode)
val httpCode = StatusCode((!spaces.httpCode).toInt)
Expand Down Expand Up @@ -195,7 +195,7 @@ abstract class AbstractCurlBackend[F[_]](_monad: MonadError[F], verbose: Boolean
lift(m)
}

private def setRequestBody(curl: CurlHandle, body: GenericRequestBody[R])(implicit
private def setRequestBody(curl: CurlHandle, body: GenericRequestBody[R], method: Method)(implicit
ctx: Context
): F[CurlCode] = {
implicit val z = ctx.zone
Expand Down Expand Up @@ -223,7 +223,9 @@ abstract class AbstractCurlBackend[F[_]](_monad: MonadError[F], verbose: Boolean
case StreamBody(_) =>
monad.error(new IllegalStateException("CurlBackend does not support stream request body"))
case NoBody =>
monad.unit(CurlCode.Ok)
// POST with empty body might wait for the input on stdin
if (method.is(Method.POST)) lift(curl.option(PostFields, c""))
else monad.unit(CurlCode.Ok)
}
}

Expand Down
25 changes: 14 additions & 11 deletions core/src/main/scalanative/sttp/client4/curl/internal/CCurl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ private[curl] trait Mime {}
private[curl] trait MimePart {}

private[curl] object libcurlPlatformCompat {
@extern @link("libcurl") @link("crypt32")
@extern @link("libcurl") @link("crypt32") @define("STTP_CURL_FFI")
private object libcurlWin64 extends CCurl

@extern @link("curl")
@extern @link("curl") @define("STTP_CURL_FFI")
private object libcurlDefault extends CCurl

val instance: CCurl =
Expand All @@ -25,24 +25,27 @@ private[curl] object libcurlPlatformCompat {

@extern
private[curl] trait CCurl {
@name("sttp_curl_setopt_int")
def setoptInt(handle: Ptr[Curl], option: CInt, parameter: Int): CInt = extern

@name("sttp_curl_setopt_long")
def setoptLong(handle: Ptr[Curl], option: CInt, parameter: Long): CInt = extern

@name("sttp_curl_setopt_pointer")
def setoptPtr(handle: Ptr[Curl], option: CInt, parameter: Ptr[_]): CInt = extern

@name("sttp_curl_getinfo_pointer")
def getInfo(handle: Ptr[Curl], info: CInt, parameter: Ptr[_]): CInt = extern

@name("curl_easy_init")
def init: Ptr[Curl] = extern

@name("curl_easy_cleanup")
def cleanup(handle: Ptr[Curl]): Unit = extern

@name("curl_easy_setopt")
def setopt(handle: Ptr[Curl], option: CInt, parameter: Ptr[_]): CInt = extern

@name("curl_easy_setopt")
def setopt(handle: Ptr[Curl], option: CInt, parameter: CVarArgList): CInt = extern

@name("curl_easy_perform")
def perform(easy_handle: Ptr[Curl]): CInt = extern

@name("curl_easy_getinfo")
def getInfo(handle: Ptr[Curl], info: CInt, parameter: Ptr[_]): CInt = extern

@name("curl_mime_init")
def mimeInit(easy: Ptr[Curl]): Ptr[Mime] = extern

Expand Down
33 changes: 14 additions & 19 deletions core/src/main/scalanative/sttp/client4/curl/internal/CurlApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import sttp.client4.curl.internal.CurlCode.CurlCode
import sttp.client4.curl.internal.CurlInfo.CurlInfo
import sttp.client4.curl.internal.CurlOption.CurlOption

import scala.scalanative.runtime.Boxes
import scala.scalanative.unsafe.{Ptr, _}
import scala.scalanative.unsigned._
import java.nio.charset.StandardCharsets

private[client4] object CurlApi {
type CurlHandle = Ptr[Curl]
Expand All @@ -22,27 +22,28 @@ private[client4] object CurlApi {
implicit class CurlHandleOps(handle: CurlHandle) {
def mime: MimeHandle = CCurl.mimeInit(handle)

def perform: CurlCode = CurlCode(CCurl.perform(handle))
def perform: CurlCode =
CurlCode(CCurl.perform(handle))

def cleanup(): Unit = CCurl.cleanup(handle)

def option(option: CurlOption, parameter: String)(implicit z: Zone): CurlCode =
setopt(handle, option, toCString(parameter))
this.option(option, toCString(parameter, StandardCharsets.UTF_8))

def option(option: CurlOption, parameter: Long)(implicit z: Zone): CurlCode =
setopt(handle, option, parameter)
def option(option: CurlOption, parameter: Long): CurlCode =
CurlCode(CCurl.setoptLong(handle, option.id, parameter))

def option(option: CurlOption, parameter: Int)(implicit z: Zone): CurlCode =
setopt(handle, option, parameter)
def option(option: CurlOption, parameter: Int): CurlCode =
CurlCode(CCurl.setoptInt(handle, option.id, parameter))

def option(option: CurlOption, parameter: Boolean)(implicit z: Zone): CurlCode =
setopt(handle, option, if (parameter) 1L else 0L)
def option(option: CurlOption, parameter: Boolean): CurlCode =
this.option(option, if (parameter) 1 else 0)

def option(option: CurlOption, parameter: Ptr[_]): CurlCode =
setopt(handle, option, parameter)
CurlCode(CCurl.setoptPtr(handle, option.id, parameter))

def option[FuncPtr <: CFuncPtr](option: CurlOption, parameter: FuncPtr)(implicit z: Zone): CurlCode =
setopt(handle, option, Boxes.boxToPtr[Byte](Boxes.unboxToCFuncPtr0(parameter)))
def option(option: CurlOption, parameter: CFuncPtr): CurlCode =
this.option(option, CFuncPtr.toPtr(parameter))

def info(curlInfo: CurlInfo, parameter: Long)(implicit z: Zone): CurlCode = {
val lPtr = alloc[Long](sizeof[Long])
Expand All @@ -51,18 +52,12 @@ private[client4] object CurlApi {
}

def info(curlInfo: CurlInfo, parameter: String)(implicit z: Zone): CurlCode =
getInfo(handle, curlInfo, toCString(parameter))
getInfo(handle, curlInfo, toCString(parameter, StandardCharsets.UTF_8))

def info(curlInfo: CurlInfo, parameter: Ptr[_]): CurlCode =
getInfo(handle, curlInfo, parameter)
}

private def setopt(handle: CurlHandle, option: CurlOption, parameter: Ptr[_]): CurlCode =
CurlCode(CCurl.setopt(handle, option.id, parameter))

private def setopt(handle: CurlHandle, option: CurlOption, parameter: CVarArg)(implicit z: Zone): CurlCode =
CurlCode(CCurl.setopt(handle, option.id, toCVarArgList(Seq(parameter))))

private def getInfo(handle: CurlHandle, curlInfo: CurlInfo, parameter: Ptr[_]): CurlCode =
CurlCode(CCurl.getInfo(handle, curlInfo.id, parameter))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,8 @@ trait SyncHttpTest
.get(uri"$endpoint/timeout")
.readTimeout(200.milliseconds)
.response(asString)
req.send(backend)
val caught = intercept[RuntimeException] { req.send(backend) }
caught.getMessage should include("TIMEDOUT")
}

"not fail if read timeout is big enough" in {
Expand Down

0 comments on commit 1dab59a

Please sign in to comment.