Skip to content

Commit

Permalink
Refactor Socket::Addrinfo::Error based on os_error (#10761)
Browse files Browse the repository at this point in the history
  • Loading branch information
straight-shoota authored Jun 4, 2021
1 parent 93a65e3 commit 94daeb0
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 26 deletions.
66 changes: 42 additions & 24 deletions src/socket/addrinfo.cr
Original file line number Diff line number Diff line change
Expand Up @@ -81,24 +81,45 @@ class Socket
end

class Error < Socket::Error
getter error_code : Int32
@[Deprecated("Use `#os_error` instead")]
def error_code : Int32
os_error.not_nil!.value.to_i32!
end

@[Deprecated("Use `.from_os_error` instead")]
def self.new(error_code : Int32, message, domain)
from_os_error(message, Errno.new(error_code), domain: domain)
end

def self.new(error_code, domain)
new error_code, error_string(error_code), domain
@[Deprecated("Use `.from_os_error` instead")]
def self.new(error_code : Int32, domain)
new error_code, nil, domain: domain
end

def initialize(@error_code, message, domain)
super("Hostname lookup for #{domain} failed: #{message}")
protected def self.new_from_os_error(message : String, os_error, *, domain, type, service, protocol, **opts)
new(message, **opts)
end

def self.error_string(error_code)
{% if flag?(:win32) %}
# gai_strerror is defined as a macro in WS2tcpip.h, we can just use
# WinError for this
return WinError.new(error_code.to_u32).message
{% else %}
String.new(LibC.gai_strerror(error_code))
{% end %}
def self.build_message(message, *, domain, **opts)
"Hostname lookup for #{domain} failed"
end

def self.os_error_message(os_error : Errno, *, type, service, protocol, **opts)
case os_error.value
when LibC::EAI_NONAME
"No address found"
when LibC::EAI_SOCKTYPE
"The requested socket type #{type} protocol #{protocol} is not supported"
when LibC::EAI_SERVICE
"The requested service #{service} is not available for the requested socket type #{type}"
else
{% unless flag?(:win32) %}
# There's no need for a special win32 branch because the os_error on Windows
# is of type WinError, which wouldn't match this overload anyways.

String.new(LibC.gai_strerror(os_error.value))
{% end %}
end
end
end

Expand Down Expand Up @@ -128,17 +149,14 @@ class Socket
end
{% end %}

case ret = LibC.getaddrinfo(domain, service.to_s, pointerof(hints), out ptr)
when 0
# success
when LibC::EAI_NONAME
raise Error.new(ret, "No address found", domain)
when LibC::EAI_SOCKTYPE
raise Error.new(ret, "The requested socket type #{type} protocol #{protocol} is not supported", domain)
when LibC::EAI_SERVICE
raise Error.new(ret, "The requested service #{service} is not available for the requested socket type #{type}", domain)
else
raise Error.new(ret, domain)
ret = LibC.getaddrinfo(domain, service.to_s, pointerof(hints), out ptr)
unless ret.zero?
error = {% if flag?(:win32) %}
WinError.new(ret.to_u32!)
{% else %}
Errno.new(ret)
{% end %}
raise Error.from_os_error(nil, error, domain: domain, type: type, protocol: protocol, service: service)
end

begin
Expand Down
18 changes: 16 additions & 2 deletions src/system_error.cr
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
# This is a factory method and by default it creates an instance
# of the current class. It can be overridden to generate different
# classes based on the `os_error` value or keyword arguments.
# * `protected def os_error_message(os_error : Errno | WinError | Nil, **opts) : String?`
# Returns the respective error message for *os_error*.
# By default it returns the result of `Errno#message` or `WinError#message`.
# This method can be overridden for customization of the error message based
# on *or_error* and *opts*.
module SystemError
macro included
extend ::SystemError::ClassMethods
Expand All @@ -53,9 +58,9 @@ module SystemError
message = self.build_message(message, **opts)
message =
if message
"#{message}: #{os_error.message}"
"#{message}: #{os_error_message(os_error, **opts)}"
else
os_error.message
os_error_message(os_error, **opts)
end

self.new_from_os_error(message, os_error, **opts).tap do |e|
Expand Down Expand Up @@ -84,6 +89,15 @@ module SystemError
message
end

# Returns the respective error message for *os_error*.
#
# By default it returns the result of `Errno#message` or `WinError#message`.
# This method can be overridden for customization of the error message based
# on *or_error* and *\*\*opts*.
protected def os_error_message(os_error : Errno | WinError | Nil, **opts) : String?
os_error.try &.message
end

# Creates an instance of the exception that wraps a system error.
#
# This is a factory method and by default it creates an instance
Expand Down

0 comments on commit 94daeb0

Please sign in to comment.