123456789_123456789_123456789_123456789_123456789_

Module: TestPuma::PumaSocketInclude

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Included In:
Defined in: test/helpers/test_puma/puma_socket_include.rb

Overview

This module contains the methods included in PumaSSLSocket, PumaTCPSocket, and PumaUNIXSocket, which are subclasses of the native Ruby sockets. All methods add functionality related to their use with client HTTP connections.

Constant Summary

Instance Method Summary

Instance Method Details

#<<(req = nil)

Alias for #send_http.

[ GitHub ]

  
# File 'test/helpers/test_puma/puma_socket_include.rb', line 151

alias_method :<<, :send_http

#read_all(timeout: RESP_READ_TIMEOUT, len: RESP_READ_LEN) ⇒ String

Reads all that is available on the socket. Used for reading sockets that contain multiple responses.

Parameters:

  • timeout: (Float, nil) (defaults to: RESP_READ_TIMEOUT)

    total socket read timeout, defaults to RESP_READ_TIMEOUT

  • len: (Integer, nil) (defaults to: RESP_READ_LEN)

    the read_nonblock maxlen, defaults to RESP_READ_LEN

[ GitHub ]

  
# File 'test/helpers/test_puma/puma_socket_include.rb', line 29

def read_all(timeout: RESP_READ_TIMEOUT, len: RESP_READ_LEN)
  timeout ||= RESP_READ_TIMEOUT
  read_len = len || RESP_READ_LEN
  end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
  read = String.new # rubocop: disable Performance/UnfreezeString
  prev_size = 0
  loop do
    raise(Timeout::Error, 'Client Read Timeout') if Process.clock_gettime(Process::CLOCK_MONOTONIC) > end_time
    if wait_readable 1
      read << sysread(read_len)
    end
    ttl_read = read.bytesize
    return read if prev_size == ttl_read && !ttl_read.zero?
    prev_size = ttl_read
  end
rescue EOFError
  return read
rescue => e
  raise e
end

#read_body(timeout: nil, len: nil) ⇒ String

Reads the response body on the socket. Assumes one response, use #read_all to read multiple responses.

Parameters:

  • timeout: (Float, nil) (defaults to: nil)

    total socket read timeout, defaults to RESP_READ_TIMEOUT

  • len: (Integer, nil) (defaults to: nil)

    the read_nonblock maxlen, defaults to RESP_READ_LEN

Returns:

  • (String)

    the HTTP body

[ GitHub ]

  
# File 'test/helpers/test_puma/puma_socket_include.rb', line 55

def read_body(timeout: nil, len: nil)
  self.read_response(timeout: timeout, len: len).split(RESP_SPLIT, 2).last
end

#read_response(timeout: nil, len: nil) ⇒ Response

Reads the HTTP response on the socket. Assumes one response, use #read_all to read multiple responses.

Parameters:

  • timeout: (Float, nil) (defaults to: nil)

    total socket read timeout, defaults to RESP_READ_TIMEOUT

  • len: (Integer, nil) (defaults to: nil)

    the read_nonblock maxlen, defaults to RESP_READ_LEN

Returns:

[ GitHub ]

  
# File 'test/helpers/test_puma/puma_socket_include.rb', line 64

def read_response(timeout: nil, len: nil)
  content_length = nil
  chunked = nil
  status = nil
  no_body = nil
  response = Response.new
  read_len = len || RESP_READ_LEN

  timeout  ||= RESP_READ_TIMEOUT
  time_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  time_end   = time_start + timeout
  times = []
  time_read = nil

  loop do
    begin
      self.to_io.wait_readable timeout
      time_read ||= Process.clock_gettime(Process::CLOCK_MONOTONIC)
      part = self.read_nonblock(read_len, exception: false)
      case part
      when String
        times << (Process.clock_gettime(Process::CLOCK_MONOTONIC) - time_read).round(4)
        status ||= part[/\AHTTP\/1\.[01] (\d{3})/, 1]
        if status
          no_body ||= NO_ENTITY_BODY.key?(status.to_i) || status.to_i < 200
        end
        if no_body && part.end_with?(RESP_SPLIT)
          response.times = times
          return response << part
        end

        unless content_length || chunked
          chunked ||= part.downcase.include? "\r\ntransfer-encoding: chunked\r\n"
          content_length = (t = part[/^Content-Length: (\d+)/i , 1]) ? t.to_i : nil
        end
        response << part
        hdrs, body = response.split RESP_SPLIT, 2
        unless body.nil?
          # below could be simplified, but allows for debugging...
          finished =
            if content_length
              body.bytesize == content_length
            elsif chunked
              body.end_with? "0\r\n\r\n"
            elsif !hdrs.empty? && !body.empty?
              true
            else
              false
            end
          response.times = times
          return response if finished
        end
        sleep 0.000_1
      when :wait_readable
        # continue loop
      when :wait_writable # :wait_writable for ssl
        to = time_end - Process.clock_gettime(Process::CLOCK_MONOTONIC)
        self.to_io.wait_writable to
      when nil
        if response.empty?
          raise EOFError
        else
          response.times = times
          return response
        end
      end
      timeout = time_end - Process.clock_gettime(Process::CLOCK_MONOTONIC)
      if timeout <= 0
        raise Timeout::Error, 'Client Read Timeout'
      end
    end
  end
end

#req_write(req = nil)

Alias for #send_http.

[ GitHub ]

  
# File 'test/helpers/test_puma/puma_socket_include.rb', line 152

alias_method :req_write, :send_http

#send_http(req = nil) ⇒ self Also known as: #<<, #req_write

Writes the request/data to the socket. Returns self

Parameters:

  • req (String) (defaults to: nil)

    the request or data to write

Returns:

  • (self)

    the socket

[ GitHub ]

  
# File 'test/helpers/test_puma/puma_socket_include.rb', line 142

def send_http(req = nil)
  req ||= PumaSocket::GET_11
  if String === req
    sent = 0
    size = req.bytesize
    sent += syswrite(req.byteslice(sent, size - sent)) while sent < size
  end
  self
end

#send_http_read_body(req = nil, timeout: nil, len: nil) ⇒ String

Writes the request/data to the socket and returns the response body. Assumes one response, use #read_all to read multiple responses.

Parameters:

  • req (String) (defaults to: nil)

    the request or data to write

  • timeout: (Float, nil) (defaults to: nil)

    total socket read timeout, defaults to RESP_READ_TIMEOUT

  • len: (Integer, nil) (defaults to: nil)

    the read_nonblock maxlen, defaults to RESP_READ_LEN

Returns:

  • (String)

    The response body. Chunked bodies are not decoded.

[ GitHub ]

  
# File 'test/helpers/test_puma/puma_socket_include.rb', line 160

def send_http_read_body(req = nil, timeout: nil, len: nil)
  req ||= PumaSocket::GET_11
  send_http_read_response(req, timeout: timeout, len: len)
    .split(RESP_SPLIT, 2).last
end

#send_http_read_response(req = nil, timeout: nil, len: nil) ⇒ Response

Writes the request/data to the socket and returns the response. Assumes one response, use #read_all to read multiple responses.

Parameters:

  • req (String) (defaults to: nil)

    the request or data to write

  • timeout: (Float, nil) (defaults to: nil)

    total socket read timeout, defaults to RESP_READ_TIMEOUT

  • len: (Integer, nil) (defaults to: nil)

    the read_nonblock maxlen, defaults to RESP_READ_LEN

Returns:

[ GitHub ]

  
# File 'test/helpers/test_puma/puma_socket_include.rb', line 172

def send_http_read_response(req = nil, timeout: nil, len: nil)
  req ||= PumaSocket::GET_11
  send_http(req).read_response(timeout: timeout, len: len)
end

#wait_read(len, timeout: 5) ⇒ String

Uses a single sysread statement to read the socket. Reads len bytes from the socket. A wait_readable call using timeout: preceeds it.

Parameters:

  • len (Integer)

    the number of bytes to read

  • timeout: (Float, Integer) (defaults to: 5)

    wait_readable timeout

[ GitHub ]

  
# File 'test/helpers/test_puma/puma_socket_include.rb', line 183

def wait_read(len, timeout: 5)
  Thread.pass
  self.wait_readable timeout
  Thread.pass
  sysread len
end