module Wpxf::Net::HttpServer

Provides basic, single threaded HTTP server functionality.

Public Class Methods

new() click to toggle source

Initialize a new instance of {HttpServer}.

Calls superclass method
# File lib/wpxf/net/http_server.rb, line 11
def initialize
  super

  register_options([
    StringOption.new(
      name: 'http_server_bind_address',
      desc: 'Address to bind the HTTP server to',
      default: '0.0.0.0',
      required: true
    ),
    PortOption.new(
      name: 'http_server_bind_port',
      desc: 'Port for the HTTP server to listen on',
      default: 80,
      required: true
    )
  ])

  @http_server_kill_switch = false
end

Public Instance Methods

http_server_bind_address() click to toggle source

@return [String] the address the HTTP server is bound to.

# File lib/wpxf/net/http_server.rb, line 33
def http_server_bind_address
  normalized_option_value('http_server_bind_address')
end
http_server_bind_port() click to toggle source

@return [Integer] the port the HTTP server is listening on.

# File lib/wpxf/net/http_server.rb, line 38
def http_server_bind_port
  normalized_option_value('http_server_bind_port')
end
http_server_thread() click to toggle source

@return [Thread] thread that the server runs on when in non-blocking mode.

# File lib/wpxf/net/http_server.rb, line 98
def http_server_thread
  @http_server_thread
end
js_ajax_download() click to toggle source

@return [String] the AJAX download helper script.

# File lib/wpxf/net/http_server.rb, line 54
def js_ajax_download
  File.read(File.join(Wpxf.data_directory, 'js', 'ajax_download.js'))
end
js_ajax_post() click to toggle source

@return [String] the AJAX post helper script.

# File lib/wpxf/net/http_server.rb, line 59
def js_ajax_post
  File.read(File.join(Wpxf.data_directory, 'js', 'ajax_post.js'))
end
js_post() click to toggle source

@return [String] the JS post helper script.

# File lib/wpxf/net/http_server.rb, line 64
def js_post
  File.read(File.join(Wpxf.data_directory, 'js', 'post.js'))
end
on_http_request(path, params, headers) click to toggle source

Invoked when a HTTP request is made to the server. @param path [String] the path requested. @param params [Hash] the query string parameters. @param headers [Hash] the HTTP headers. @return [String, Hash] if a string is returned, it will be used as the response body

to send to the client. If a hash is returned, it should contain the keys:
* +:body+ - the body text of the response.
* +:type+ - the MIME type of the response.
* +:headers+ - an array of header strings.
# File lib/wpxf/net/http_server.rb, line 51
def on_http_request(path, params, headers) end
start_http_server(non_block = false) click to toggle source

Start the HTTP server. @param non_block [Boolean] run the server in non-blocking mode.

# File lib/wpxf/net/http_server.rb, line 70
def start_http_server(non_block = false)
  @tcp_server = TCPServer.new(http_server_bind_address, http_server_bind_port)
  emit_info "Started HTTP server on #{http_server_bind_address}:"\
            "#{http_server_bind_port}"

  if non_block
    @http_server_thread = Thread.new do
      _http_server_loop
    end
  else
    _http_server_loop
  end
end
stop_http_server() click to toggle source

Stop the HTTP server after it has finished processing the current request.

# File lib/wpxf/net/http_server.rb, line 85
def stop_http_server
  return false if @is_stopping
  @is_stopping = true

  emit_info 'Stopping HTTP server...', true
  @http_server_kill_switch = true
  @http_server_thread&.exit
  @tcp_server.close if !@tcp_server.nil? && !@tcp_server.closed?
  emit_info 'HTTP server stopped'
  @is_stopping = false
end

Private Instance Methods

_handle_incoming_http_request(socket) click to toggle source
# File lib/wpxf/net/http_server.rb, line 159
def _handle_incoming_http_request(socket)
  request = socket.gets
  return unless request

  emit_info "Incoming HTTP request: #{request}", true

  headers = ''
  while (line = socket.gets) != "\r\n"
    headers += line
    emit_info line, true
  end

  headers = Hash[headers.each_line.map { |l| l.chomp.split(': ', 2) }]
  path = request.gsub(/^[A-Za-z]+\s(.+?)\s.*$/, '\1').chomp
  params = {}

  if path.include?('?')
    params = CGI.parse(path.split('?')[-1])
    params.each do |k, v|
      params[k] = v.join(' ')
    end
    path = path.split('?')[0]
  end

  # Dispatch parsed data to the callback in the module.
  on_http_request(path, params, headers)
end
_http_server_loop() click to toggle source
# File lib/wpxf/net/http_server.rb, line 135
def _http_server_loop
  begin
    loop do
      socket = @tcp_server.accept

      begin
        response = _handle_incoming_http_request(socket)
        _send_response(response, socket) if response
        socket.close
      rescue Errno::EPIPE
        emit_warning 'A socket was closed by the requester', true
      end

      break if @http_server_kill_switch
    end
  rescue SignalException
    emit_warning 'Caught kill signal', true
  rescue StandardError => e
    emit_error "Socket error: #{e}"
  end

  stop_http_server
end
_send_headers(socket, body, content_type, custom_headers) click to toggle source
# File lib/wpxf/net/http_server.rb, line 104
def _send_headers(socket, body, content_type, custom_headers)
  headers = []
  headers.push 'HTTP/1.1 200 OK'
  headers.push "Content-Type: #{content_type}"
  headers.push "Content-Length: #{body.bytesize}"
  headers += custom_headers unless custom_headers.nil?
  headers.push 'Connection: close'

  headers.each do |header|
    socket.print "#{header}\r\n"
  end
end
_send_response(response, socket) click to toggle source
# File lib/wpxf/net/http_server.rb, line 117
def _send_response(response, socket)
  content_type = 'text/plain'
  body = ''
  custom_headers = nil

  if response.is_a? String
    body = response
  else
    body = response[:body]
    content_type = response[:type]
    custom_headers = response[:headers]
  end

  _send_headers(socket, body, content_type, custom_headers)
  socket.print "\r\n"
  socket.print body
end