Class: Waitress::HttpServer

Inherits:
Array
  • Object
show all
Defined in:
lib/waitress/server.rb

Overview

The Waitress HTTPServer. This class is responsible for handling traffic from clients and delegating it to the correct Virtual Host to further handle. New threads and Processes are spawned for each connection to the server

Defined Under Namespace

Classes: HttpParams

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*ports) ⇒ HttpServer

Create a new Server instance with the given ports. If no ports are given, port 80 will be used as a default



19
20
21
22
23
24
25
26
# File 'lib/waitress/server.rb', line 19

def initialize(*ports)
  ports << 80 if ports.length == 0
  @ports = ports
  @processes = 5
  @processes = ENV["WAITRESS_PROCESSES"].to_i if ENV.include? "WAITRESS_PROCESSES"
  @backtrace_500 = false
  @running_processes = []
end

Instance Attribute Details

#processesObject

Returns the value of attribute processes.



15
16
17
# File 'lib/waitress/server.rb', line 15

def processes
  @processes
end

Instance Method Details

#build_request(headers, client_socket) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/waitress/server.rb', line 139

def build_request headers, client_socket
  request_headers = {}
  headers.each do |k,v|
    if k.start_with? "HTTP_HEAD_"
      request_headers[k.sub(/HTTP_HEAD_/, "")] = v
    end
  end
  request = Waitress::Request.new(
    headers["REQUEST_METHOD"], headers["REQUEST_PATH"], headers["REQUEST_URI"],
    headers["QUERY_STRING"], headers["HTTP_VERSION"], headers.http_body, request_headers
  )
  handle_request request, client_socket
end

#connect_client(client) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/waitress/server.rb', line 97

def connect_client client
  begin
    gofork do               # Makes sure requires etc don't get triggered across requests
      handle_client client
    end.wait
    client.close rescue nil
  rescue => e
    puts "Server Error: #{e} (Fix This!)"
    puts e.backtrace
    client.close rescue nil
  end
end

#handle_client(client_socket) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/waitress/server.rb', line 110

def handle_client client_socket
  begin
    data = client_socket.readpartial(8192)
    nparsed = 0

    parser = Waitress::HttpParser.new
    params = HttpParams.new

    while nparsed < data.length
      nparsed = parser.execute(params, data, nparsed)
      if parser.finished?
        build_request params, client_socket
      else
        ch = client.readpartial(8192)
        break if !ch or ch.length == 0

        data << ch
      end
    end
  rescue EOFError, Errno::ECONNRESET, Errno::EPIPE, Errno::EINVAL, Errno::EBADF
    client_socket.close rescue nil
  rescue => e
    # puts "Client Error: #{e}"
    # puts e.backtrace
    Handler500.trigger client_socket, self, e, @backtrace_500
  end
  client_socket.close rescue nil
end

#handle_request(request, client) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/waitress/server.rb', line 153

def handle_request request, client
  match, pri = self[0], nil
  self.each do |vhost|
    if (request.headers['Host'].to_s =~ vhost.domain) != nil
      match = vhost if pri.nil? || vhost.priority > pri
    end
  end

  if match.nil?
    # Subdomain not found (or default)
  else
    match.handle_request request, client
  end
end

#internal_error(enabled) ⇒ Object

Set to true to enable clients to view the 500 error backtrace



74
75
76
# File 'lib/waitress/server.rb', line 74

def internal_error enabled
  @backtrace_500 = enabled
end

#joinObject

Join the server, blocking the current thread in order to keep the server alive.



63
64
65
# File 'lib/waitress/server.rb', line 63

def join
  @running_processes.each { |x| x.wait }
end

#killallObject

Killall running processes



58
59
60
# File 'lib/waitress/server.rb', line 58

def killall
  @running_processes.each { |x| x.kill rescue nil }
end

#launch_port(port) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/waitress/server.rb', line 79

def launch_port port
  serv = TCPServer.new port
  processes = []
  @processes.times do
    processes << gofork {
      while true
        client = serv.accept
        connect_client(client)
      end
    }
  end

  processes.each do |pr|
    Process.detach(pr.pid)
  end
  processes
end

#ports(*ports) ⇒ Object

Set or Get the ports for this server. If arguments are provided, the ports for this server will be replaced with the ones listed. If no arguments are provided, this method simply returns the ports



36
37
38
39
# File 'lib/waitress/server.rb', line 36

def ports *ports
  @ports = *ports unless ports.length == 0
  @ports
end

#read_io(io) ⇒ Object

Handle a client based on an IO stream, if you plan to serve on a non-socket connection



69
70
71
# File 'lib/waitress/server.rb', line 69

def read_io io
  handle_client io
end

#run(*ports) ⇒ Object

Start the server. If arguments are provided, it will run with the ports declared in the arguments, otherwise, it will use the ports it already has set (or 80 for the default)



44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/waitress/server.rb', line 44

def run *ports
  @ports = ports unless ports.length == 0
  self.each do |vhost|
    vhost.on_server_start self
  end

  @running_processes = @ports.map do |port|
    launch_port(port)
  end
  @running_processes.flatten!
  self
end

#set_processes(count) ⇒ Object

Set the amount of concurrent Waitress Processes to run on this Server, per Port



29
30
31
# File 'lib/waitress/server.rb', line 29

def set_processes count
  @processes = count
end