Module: Raptor::Http

Defined in:
lib/raptor/http.rb,
sig/generated/raptor/http.rbs

Overview

Shared HTTP utilities used by both the HTTP/1.x and HTTP/2 handlers: Rack env keys that aren't provided by Rack itself, low-level socket writing, and Common Log Format access-log formatting.

Defined Under Namespace

Classes: WriteError

Constant Summary collapse

WRITE_TIMEOUT =

Returns:

  • (::Integer)
5
CONTENT_LENGTH =

Returns:

  • (::String)
"CONTENT_LENGTH"
CONTENT_TYPE =

Returns:

  • (::String)
"CONTENT_TYPE"
HTTP_VERSION =

Returns:

  • (::String)
"HTTP_VERSION"
REMOTE_ADDR =

Returns:

  • (::String)
"REMOTE_ADDR"
SERVER_SOFTWARE =

Returns:

  • (::String)
"SERVER_SOFTWARE"
SERVER_SOFTWARE_VALUE =

Returns:

  • (Object)
"Raptor/#{Raptor::VERSION}".freeze

Class Method Summary collapse

Class Method Details

.socket_write(socket, string, timeout: WRITE_TIMEOUT) ⇒ void

This method returns an undefined value.

Writes string in full, retrying on partial writes. Bounded by timeout so a slow client can't pin the writing thread.

Parameters:

  • socket (TCPSocket)

    the socket to write to

  • string (String)

    the data to write

  • timeout (Integer) (defaults to: WRITE_TIMEOUT)

    seconds to wait for the socket to become writable on each partial write

  • timeout: (Integer) (defaults to: WRITE_TIMEOUT)

Raises:

  • (WriteError)

    if the socket is not writable within the timeout or raises IOError



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/raptor/http.rb', line 39

def self.socket_write(socket, string, timeout: WRITE_TIMEOUT)
  bytes = 0
  byte_size = string.bytesize

  while bytes < byte_size
    begin
      bytes += socket.write_nonblock(bytes.zero? ? string : string.byteslice(bytes..-1))
    rescue IO::WaitWritable
      raise WriteError unless socket.wait_writable(timeout)
      retry
    rescue IOError
      raise WriteError
    end
  end
end

.socket_writev(socket, strings, timeout: WRITE_TIMEOUT) ⇒ void

This method returns an undefined value.

Writes strings in full via a single writev(2) syscall when possible, falling back to per-string writes on partial results. Bounded by timeout so a slow client can't pin the writing thread.

Parameters:

  • socket (TCPSocket)

    the socket to write to

  • strings (Array<String>)

    the buffers to write in order

  • timeout (Integer) (defaults to: WRITE_TIMEOUT)

    seconds to wait for the socket to become writable on each retry

  • timeout: (Integer) (defaults to: WRITE_TIMEOUT)

Raises:

  • (WriteError)

    if the socket is not writable within the timeout or raises IOError



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/raptor/http.rb', line 66

def self.socket_writev(socket, strings, timeout: WRITE_TIMEOUT)
  total = strings.sum(&:bytesize)
  return if total.zero?

  begin
    written = Raptor::VectorIO.writev_nonblock(socket, strings)
  rescue IO::WaitWritable
    raise WriteError unless socket.wait_writable(timeout)
    retry
  rescue IOError
    raise WriteError
  end

  return if written == total

  offset = 0
  strings.each do |string|
    size = string.bytesize
    if written >= offset + size
      offset += size
    else
      start = written - offset
      socket_write(socket, start.zero? ? string : string.byteslice(start..-1), timeout: timeout)
      offset += size
      written = offset
    end
  end
end

.write_access_log(io, env, status, size, remote_addr) ⇒ void

This method returns an undefined value.

Writes a Common Log Format entry to io. Write failures are silently ignored.

Parameters:

  • io (IO)

    the destination IO

  • env (Hash)

    the Rack environment

  • status (Integer)

    the response status code

  • size (String)

    the response body size in bytes, or - if unknown

  • remote_addr (String)

    the client IP address



106
107
108
109
110
111
112
113
114
# File 'lib/raptor/http.rb', line 106

def self.write_access_log(io, env, status, size, remote_addr)
  timestamp = Time.now.strftime("%d/%b/%Y:%H:%M:%S %z")
  method = env[Rack::REQUEST_METHOD]
  query = env[Rack::QUERY_STRING]
  path = query.empty? ? env[Rack::PATH_INFO] : "#{env[Rack::PATH_INFO]}?#{query}"
  protocol = env[Rack::SERVER_PROTOCOL]

  io.puts(%(#{remote_addr} - - [#{timestamp}] "#{method} #{path} #{protocol}" #{status} #{size})) rescue nil
end