Created
June 27, 2023 11:40
-
-
Save carlhoerberg/0d031f876918fc725f4cfb1b5d23d3b3 to your computer and use it in GitHub Desktop.
sendfile implementation for crystal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "socket" | |
lib LibC | |
{% if flag?(:linux) %} | |
fun sendfile(fd_out : Int, fd_in : Int, offset : OffT*, count : SizeT) : SSizeT | |
TCP_CORK = 3 | |
{% else %} | |
struct SendfileHeader | |
headers : IoVec* | |
hdr_cnt : Int | |
trailers : IoVec* | |
trl_cnt : Int | |
end | |
fun sendfile(fd : Int, s : Int, offset : OffT*, len : OffT*, hdtr : SendfileHeader*, flags : Int) : Int | |
{% end %} | |
end | |
class Socket | |
# Zero-copy implementation of sending *length* bytes of *file* | |
# to the socket | |
# Returns the number of bytes sent | |
def sendfile(file_fd : Int32, offset : Int64, length : Int64) : Int32 | |
flush | |
evented_sendfile(length, "sendfile") do |remaining| | |
offs = pointerof(offset) | |
{% if flag?(:linux) %} | |
LibC.sendfile(fd, file_fd, offs, remaining) | |
{% else %} | |
len = pointerof(remaining) | |
LibC.sendfile(file_fd, fd, offs, len, nil, 0) | |
len.value | |
{% end %} | |
end | |
end | |
def cork(&) | |
setsockopt_bool LibC::TCP_CORK, true, level: Protocol::TCP | |
yield | |
setsockopt_bool LibC::TCP_CORK, false, level: Protocol::TCP | |
end | |
def cork | |
setsockopt_bool LibC::TCP_CORK, true, level: Protocol::TCP | |
end | |
def uncork | |
setsockopt_bool LibC::TCP_CORK, false, level: Protocol::TCP | |
end | |
end | |
module IO::Evented | |
def evented_sendfile(limit : Int64, errno_msg : String, & : Int32 -> Int64) : Int32 | |
limit = limit.to_i32 | |
remaining = limit | |
loop do | |
bytes_written = (yield remaining).to_i32 | |
case bytes_written | |
when -1 | |
if Errno.value == Errno::EAGAIN | |
wait_writable | |
else | |
raise Socket::Error.from_errno(errno_msg) | |
end | |
when 0 | |
break | |
else | |
remaining -= bytes_written | |
break if remaining.zero? | |
end | |
end | |
limit - remaining | |
ensure | |
resume_pending_writers | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment