Skip to content
Merged
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
44 changes: 11 additions & 33 deletions floki/body.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,33 @@ from std.collections.string._utf8 import _is_valid_utf8
import emberjson


struct Body(Copyable, Sized):
struct Body(Copyable, Sized, Writable, Equatable):
"""Represents the body of an HTTP request or response.

At the moment, this only supports JSON serialization and deserialization.
"""

var body: List[Byte]
"""The raw body content as a list of bytes."""
var _json_cache: Optional[emberjson.Value]
"""An optional cache for the parsed JSON value, to avoid redundant parsing on multiple accesses."""

def __init__(out self, var body: List[Byte]) raises:
def __init__(out self, var body: List[Byte]):
"""Constructs a Body instance from a list of bytes.

Args:
body: The body content as a list of bytes.

Raises:
* Error: if the body is not valid UTF-8.
"""
if not _is_valid_utf8(body):
raise Error("Body must be valid UTF-8")

self.body = body^
self._json_cache = None

def __init__[origin: ImmutOrigin, //](out self, body: Span[Byte, origin]) raises:
def __init__[origin: ImmutOrigin, //](out self, body: Span[Byte, origin]):
"""Alternate constructor that accepts a Span[Byte] for the body content.

Parameters:
origin: The origin of the data span.

Args:
body: The body content as a span of bytes.

Raises:
* Error: if the body is not valid UTF-8.
"""
if not _is_valid_utf8(body):
raise Error("Body must be valid UTF-8")
self.body = List[Byte](body)
self._json_cache = None

def __len__(self) -> Int:
"""Returns the length of the body in bytes.
Expand All @@ -62,15 +47,15 @@ struct Body(Copyable, Sized):
"""
return Span(self.body)

def as_string_slice(self) -> StringSlice[origin_of(self.body)]:
def as_text(self) raises -> StringSlice[origin_of(self.body)]:
"""Creates and returns a `StringSlice` view of the body content.

Returns:
The body content as a string slice.
"""
return StringSlice(unsafe_from_utf8=Span(self.body))

def as_json(mut self) raises -> ref [origin_of(self._json_cache._value)] emberjson.Value:
return StringSlice(from_utf8=Span(self.body))
def as_json[T: Movable & ImplicitlyDestructible & Defaultable](mut self, out result: T) raises:
"""Converts the response body to a JSON object.

Returns:
Expand All @@ -79,24 +64,17 @@ struct Body(Copyable, Sized):
Raises:
Error: if the body is empty or cannot be parsed as JSON.
"""
if not self.body:
raise Error("Body is empty; cannot parse as JSON.")

if self._json_cache:
return self._json_cache.value()

self._json_cache = emberjson.parse(StringSlice(from_utf8=self.body))
return self._json_cache.value()
return emberjson.deserialize[T](emberjson.Parser(self.as_text()))

def write_to(self, mut writer: Some[Writer]):
def write_to(self, mut writer: Some[Writer]) raises:
"""Writes the body to a writer.

Args:
writer: The writer to which the body will be written.
"""
writer.write(StringSlice(unsafe_from_utf8=self.body))
writer.write(self.as_text())

def consume(deinit self) -> List[Byte]:
def take_bytes(deinit self) -> List[Byte]:
"""Consumes the body and returns it as List[Byte].

Returns:
Expand Down
7 changes: 3 additions & 4 deletions floki/callbacks.mojo
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from std.memory import memcpy
from std.ffi import c_char, c_size_t, get_errno
from std.sys import stderr
from mojo_curl.c.types import ImmutExternalPointer, MutExternalPointer
from mojo_curl.c.types import ImmutExternalPointer, MutExternalPointer, CURL_READFUNC_ABORT


# To read HTTP response data into a list of bytes.
Expand Down Expand Up @@ -101,6 +101,5 @@ def fd_read_callback(
var fd = FileDescriptor(file[]._get_raw_fd())
return fd.read_bytes(Span(ptr=ptr.bitcast[UInt8](), length=Int(buffer_size)))
except e:
print("fd_read_callback: Error reading from file descriptor: ", e, " errno: ", get_errno(), file=stderr)
# TODO: Add READ_FUNC_ABORT constant to mojo-curl and return it here to signal an error.
return 0x10000000
print(t"fd_read_callback: Error reading from file descriptor: {e}. Errno: {get_errno()}", file=stderr)
return CURL_READFUNC_ABORT
Loading
Loading