Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
10 changes: 6 additions & 4 deletions core/src/main/scalanative/sttp/client3/AbstractCurlBackend.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,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)
responseMonad.flatMap(lift(curl.perform)) { _ =>
curl.info(ResponseCode, spaces.httpCode)
val responseBody = fromCString((!spaces.bodyResp)._1)
Expand Down Expand Up @@ -152,7 +152,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)
responseMonad.flatMap(lift(curl.perform)) { _ =>
curl.info(ResponseCode, spaces.httpCode)
val httpCode = StatusCode((!spaces.httpCode).toInt)
Expand Down Expand Up @@ -192,7 +192,7 @@ abstract class AbstractCurlBackend[F[_]](monad: MonadError[F], verbose: Boolean)
lift(m)
}

private def setRequestBody[R >: PE](curl: CurlHandle, body: RequestBody[R])(implicit zone: Zone): F[CurlCode] =
private def setRequestBody[R >: PE](curl: CurlHandle, body: RequestBody[R], method: Method)(implicit zone: Zone): F[CurlCode] =
body match { // todo: assign to responseMonad object
case b: BasicRequestBody =>
val str = basicBodyToString(b)
Expand All @@ -218,7 +218,9 @@ abstract class AbstractCurlBackend[F[_]](monad: MonadError[F], verbose: Boolean)
case StreamBody(_) =>
responseMonad.error(new IllegalStateException("CurlBackend does not support stream request body"))
case NoBody =>
responseMonad.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 responseMonad.unit(CurlCode.Ok)
}

private def basicBodyToString(body: RequestBody[_]): String =
Expand Down
22 changes: 13 additions & 9 deletions core/src/main/scalanative/sttp/client3/curl/CCurl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,29 @@ private[curl] trait MimePart {}

@link("curl")
@extern
@define("STTP_CURL_FFI")
private[curl] object 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
42 changes: 14 additions & 28 deletions core/src/main/scalanative/sttp/client3/curl/CurlApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import sttp.client3.curl.CurlCode.CurlCode
import sttp.client3.curl.CurlInfo.CurlInfo
import sttp.client3.curl.CurlOption.CurlOption

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

private[client3] object CurlApi {
type CurlHandle = Ptr[Curl]
Expand All @@ -26,29 +26,23 @@ private[client3] object CurlApi {

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

def option(option: CurlOption, parameter: String)(implicit z: Zone): CurlCode = {
setopt(handle, option, toCString(parameter))
}
def option(option: CurlOption, parameter: String)(implicit z: Zone): CurlCode =
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)
}
def option(option: CurlOption, parameter: Ptr[_]): CurlCode =
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 @@ -57,22 +51,14 @@ private[client3] 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
Loading