Skip to content

Conversation

marzipankaiser
Copy link
Contributor

@marzipankaiser marzipankaiser commented May 23, 2025

This is a simple Proof-of-concept HTTP requests library in Effekt.
It forwards to the respective web/node APIs.
Additionally, it contains a (very much not production-ready) implementation of HTTP/1.1 in Effekt that reads/writes from/to Effekt streams (for integration with potential TCP bindings).

@jiribenes
Copy link
Contributor

jiribenes commented May 23, 2025

Some nice common methods, like get(url: String): Option[String] or sth.

We could follow the other io/... modules with a custom effect like:

interface HttpClient {
  def get(url: String, body: Option[String], headers: List[(String, String)]): Response
  def post(url: String, body: Option[String], headers: List[(String, String)]): Response
}

(+ async-/streaming-related stuff of course)

and then provide get(url) := do get(url, None, []) wrappers?

@marzipankaiser
Copy link
Contributor Author

Apparently we could drop the node implementation and use fetch there, too; at least once we are sure all node versions we use are >=21: https://blog.logrocket.com/fetch-api-node-js/

@marzipankaiser marzipankaiser added the blocked Blocked on other issues / PRs label May 26, 2025
@marzipankaiser
Copy link
Contributor Author

For the dispatch, I'd wait for the resolution of #448.

@marzipankaiser

This comment was marked as resolved.

Comment on lines +109 to +140
// Native Byte Buffers
// -------------------
extern type NativeBytes
// jsNode "Buffer"
extern pure def length(n: NativeBytes): Int =
js "${n}.length"
extern pure def get(n: NativeBytes, x: Int): Byte =
js "${n}[${x}]"
def each(n: NativeBytes): Unit / emit[Byte] = {
each(0, n.length){ i =>
do emit(n.get(i))
}
}

// Event emitters
// --------------
extern type Undefined
extern type EventEmitter
record Event[T](name: String)
namespace ev {
def data(): Event[js::NativeBytes] = Event("data")
def end(): Event[Undefined] = Event("end")
}

extern io def unsafeOn[T](em: EventEmitter, ev: String, handler: T => Unit at {io, async, global}): Unit =
js "${em}.on(${ev}, (param) => $effekt.runToplevel((ks,k) => ${handler}(param, ks, k)))"
def on[T](em: EventEmitter, ev: Event[T], handler: T => Unit at {io, async, global}): Unit =
em.unsafeOn(ev.name, handler)
extern async def unsafeWait[T](em: EventEmitter, ev: String): T =
js "$effekt.capture(k => ${em}.on(${ev}, k))"
def wait[T](em: EventEmitter, ev: Event[T]): T =
em.unsafeWait(ev.name)
Copy link
Contributor Author

@marzipankaiser marzipankaiser May 27, 2025

Choose a reason for hiding this comment

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

Those (and the async iterator stuff) might be independently useful for other Node APIs (namely child_process) - do we have a convention where to put those kinds of "only for one backend, but used from common" things?

@marzipankaiser
Copy link
Contributor Author

CI fails due to #1075.

@marzipankaiser marzipankaiser removed the blocked Blocked on other issues / PRs label Jul 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants