Skip to content

Enable FileHandle transfers#8

Merged
thatstoasty merged 3 commits intomainfrom
request-data
Nov 24, 2025
Merged

Enable FileHandle transfers#8
thatstoasty merged 3 commits intomainfrom
request-data

Conversation

@thatstoasty
Copy link
Owner

No description provided.

Copilot AI review requested due to automatic review settings November 24, 2025 20:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enables FileHandle support for POST, PUT, and PATCH requests, allowing files to be sent directly without loading them entirely into memory. It also fixes the HEAD and OPTIONS HTTP methods, updates the Body API to work with emberjson.Object instead of Dict[String, String], implements JSON caching for performance, and adds a verbose mode to Session.

  • Adds FileHandle overloads for POST, PUT, and PATCH methods with file descriptor read callback
  • Fixes HEAD method to use nobody() and OPTIONS method implementation
  • Changes Body and Session APIs to use emberjson.Object for JSON data
  • Adds JSON caching to Body to avoid re-parsing

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
floki/session.mojo Adds fd_read_callback for FileHandle transfers, overloaded send() and HTTP methods for FileHandle, added verbose mode, changed API from Dict to emberjson.Object
floki/body.mojo Added JSON caching with _json_cache field, changed constructor to accept emberjson.Object
floki/functions.mojo Entire file commented out (likely needs update for new API)
test/test_session.mojo Added test_post_file, test_put_file, test_patch_file to test FileHandle functionality; updated existing tests for new API
test/data/*.json Added test data files for FileHandle testing
pixi.toml, pixi.lock Updated mojo-curl dependency from 25.7.0 to 25.7.1

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Args:
url: The URL to which the request is sent.
headers: A dictionary of HTTP headers to include in the request.
file: An optional FileHandle representing the request body.
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The docstring incorrectly describes file as "An optional FileHandle" but the parameter is not optional (no default value). Update to: "A FileHandle representing the request body."

Suggested change
file: An optional FileHandle representing the request body.
file: A FileHandle representing the request body.

Copilot uses AI. Check for mistakes.
var headers: Dict[String, String] = {},
):
verbose: Bool = False,
) raises:
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The __init__ method is missing a docstring that describes the new verbose parameter. Consider adding documentation explaining that verbose enables libcurl's verbose logging mode.

Suggested change
) raises:
) raises:
"""
Initialize a new Session.
Args:
allow_redirects (Bool): Whether to follow HTTP redirects automatically.
headers (Dict[String, String]): Default headers to include in requests.
verbose (Bool): If True, enables libcurl's verbose logging mode for debugging.
"""

Copilot uses AI. Check for mistakes.
if data:
var result = easy.post_fields(data)
if result != Result.OK:
raise Error("_handle_patch: Failed to set POST fields: ", easy.describe_error(result))
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The error message refers to "POST fields" in a PATCH request handler, which is misleading. While libcurl uses post_fields internally, the error message should be clearer, such as "Failed to set request body" or "Failed to set PATCH request fields".

Suggested change
raise Error("_handle_patch: Failed to set POST fields: ", easy.describe_error(result))
raise Error("_handle_patch: Failed to set PATCH request fields: ", easy.describe_error(result))

Copilot uses AI. Check for mistakes.
"""
constrained[
method in [RequestMethod.POST, RequestMethod.PUT, RequestMethod.PATCH],
String("send: Unsupported HTTP method for FileDescriptor data. Received: ", method)
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The error message attempts to use string interpolation with the method parameter, but method is a compile-time parameter and cannot be directly interpolated in this way. Consider using a more descriptive static error message like: "send: FileHandle data only supported for POST, PUT, and PATCH methods"

Suggested change
String("send: Unsupported HTTP method for FileDescriptor data. Received: ", method)
String("send: FileHandle data only supported for POST, PUT, and PATCH methods")

Copilot uses AI. Check for mistakes.
Comment on lines 20 to 57
@fieldwise_init
struct DataToRead:
var data: ExternalImmutPointer[Byte]
var bytes_remaining: UInt


fn read_callback(ptr: ExternalMutPointer[c_char], size: c_size_t, nmemb: c_size_t, userdata: ExternalMutOpaquePointer) -> c_size_t:
var data = userdata.bitcast[DataToRead]()
var buffer_size = size * nmemb # Max bytes we can write to ptr

# Nothing to write
if buffer_size < 1:
return 0

# We will copy as much data as possible into the 'ptr' buffer, but no more than 'size' * 'nmemb' bytes
# Determine how much data to copy: either remaining data or buffer capacity
var bytes_to_read = min(data[].bytes_remaining, buffer_size)
if bytes_to_read > 0:
# Copy the data into the buffer
memcpy(
dest=ptr,
src=data[].data.bitcast[Int8](),
count=Int(bytes_to_read),
)

# Update the userdata to reflect the consumed data
data[].data += bytes_to_read
data[].bytes_remaining -= UInt(bytes_to_read)

return bytes_to_read

return 0


Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The read_callback function and DataToRead struct appear to be unused in this implementation. All file reading operations use fd_read_callback instead, which reads directly from a FileDescriptor. Consider removing these unused definitions or documenting their intended future use.

Suggested change
@fieldwise_init
struct DataToRead:
var data: ExternalImmutPointer[Byte]
var bytes_remaining: UInt
fn read_callback(ptr: ExternalMutPointer[c_char], size: c_size_t, nmemb: c_size_t, userdata: ExternalMutOpaquePointer) -> c_size_t:
var data = userdata.bitcast[DataToRead]()
var buffer_size = size * nmemb # Max bytes we can write to ptr
# Nothing to write
if buffer_size < 1:
return 0
# We will copy as much data as possible into the 'ptr' buffer, but no more than 'size' * 'nmemb' bytes
# Determine how much data to copy: either remaining data or buffer capacity
var bytes_to_read = min(data[].bytes_remaining, buffer_size)
if bytes_to_read > 0:
# Copy the data into the buffer
memcpy(
dest=ptr,
src=data[].data.bitcast[Int8](),
count=Int(bytes_to_read),
)
# Update the userdata to reflect the consumed data
data[].data += bytes_to_read
data[].bytes_remaining -= UInt(bytes_to_read)
return bytes_to_read
return 0

Copilot uses AI. Check for mistakes.
# Set PUT with zero-length body
result = easy.post_fields(List[Byte]())
if result != Result.OK:
raise Error("_handle_put: Failed to set POST fields: ", easy.describe_error(result))
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The error message refers to "POST fields" in a PUT request handler, which is misleading. While libcurl uses post_fields internally, the error message should be clearer, such as "Failed to set request body" or "Failed to set PUT request fields".

Suggested change
raise Error("_handle_put: Failed to set POST fields: ", easy.describe_error(result))
raise Error("_handle_put: Failed to set PUT request body: ", easy.describe_error(result))

Copilot uses AI. Check for mistakes.
# self.body = List[Byte](emberjson.to_string(emberjson.Object(json^)).as_bytes())

fn __init__(out self, var data: emberjson.Object):
"""Initializes the body from a dictionary, converting it to a form-encoded string."""
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The docstring is misleading - it says "converting it to a form-encoded string" but the implementation actually converts the data to JSON format. Update to: "Initializes the body from a JSON object."

Suggested change
"""Initializes the body from a dictionary, converting it to a form-encoded string."""
"""Initializes the body from a JSON object."""

Copilot uses AI. Check for mistakes.

# Copy the data into the buffer
try:
var fd = FileDescriptor(file[]._get_raw_fd())
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

Creating a FileDescriptor from file[]._get_raw_fd() may cause ownership/lifetime issues. The FileDescriptor constructor takes ownership of the file descriptor, but the original FileHandle still owns it. This could lead to double-close or use-after-close bugs. Consider using FileDescriptor.steal(file[]._get_raw_fd()) if available, or document this potential issue.

Suggested change
var fd = FileDescriptor(file[]._get_raw_fd())
var fd = FileDescriptor.steal(file[]._get_raw_fd())

Copilot uses AI. Check for mistakes.
add free functions

start laying the groundwork for read callback usage

add pathway to sending files

cleanup

enable free functions

enable free functions
thatstoasty and others added 2 commits November 24, 2025 16:10
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@thatstoasty thatstoasty merged commit ea73f94 into main Nov 24, 2025
3 checks passed
@thatstoasty thatstoasty deleted the request-data branch November 24, 2025 22:17
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.

1 participant