A few of the methods that raise EOFError
are:
IO#read_nonblock
IO#readline
IO#readpartial
IO#readchar
IO#readbyte
IO#sysread
There are other methods like IO#gets
which don't raise EOFError
even when the end of file has reached and returns nil
.
» Handling it while reading files
For reading small files prefer File#read
which returns the contents of the whole file instead of reading it line by line (using File#readline
) to avoid running into EOFError
. For larger files, use File#gets
instead of File#readline
.
# parse_hotels_1.rb
# read a file till the end, one line at a time
f = File.open("hotels.xml")
loop do
begin
parse_line(f.readline)
rescue EOFError
break
end
end
The same code using File#gets
, which returns a nil
at the end of the file instead of raising EOFError
:
# parse_hotels_2.rb
# read a file till the end, one line at a time
f = File.open("hotels.xml")
while line = f.gets do
parse_line(line)
end
» Handling it while reading from sockets
You have a higher chance of running into EOFError
while making HTTP requests than while reading files. An EOFError
while making HTTP requests can be raised even for perfect code.
A successful HTTP request has 4 main steps:
- Open a TCP connection to the endpoint.
- Send a request over the connection.
- Read the response written to the connection.
- Close the TCP connection.
In a few situations the TCP connection is closed by the server before Step 3 completes successfully:
» 1. Incorrect Request Format
This is a common error caused by an requests being sent in an incorrect format. In the following code we connect to the SSL port (443) and send a plain text HTTP GET request payload to an SSL endpoint. The server, on receiving unecrypted data (before Step 3), immediately closes the connection. Now, when our code tries to read the received response it will raise EOFError
because the connection has been closed by the server.
require 'net/http'
conn = Net::HTTP.new('google.com', 443)
conn.request_get('/')
This can be fixed by asking Ruby to use ssl while talking to this endpoint.
require 'net/http'
conn = Net::HTTP.new('google.com', 443)
conn.use_ssl = true
resp = conn.request_get('/')
puts resp.code
# => 301
puts resp['location']
# => https://www.google.com/
» 2. Transient Network Outages
Sometimes because of transient network outages a connection can be closed before data is read from it (Step 3), when this happens your code will raise EOFError
. In these situations you can retry this request if your endpoint is idempotent, and if it is not you'll have to find another way to handle it.