Module: Nut::Handler

Extended by:
RxIO::IOFilters::BinDelim
Defined in:
lib/nut/handler.rb

Overview

Service Handler Module

Constant Summary collapse

SUPPORTED_VERSION =

Only handle HTTP/1.1 Requests

'http/1.1'

Class Method Summary collapse

Class Method Details

.assemble_process_req(creq, msg) ⇒ Object

Assemble & Process Request: Assembles and processes HTTP Requests from individual data lines.

Parameters:

  • creq (Hash)

    Client Request Context

  • msg (String)

    The message received from the Client (including newline)



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/nut/handler.rb', line 113

def self.assemble_process_req creq, msg

	# Register Request Line
	creq[:lines] << msg

	# Build Request Line
	Request.build_req_line creq, msg

	# Check Version
	raise "Unsupported HTTP Version [#{creq[:version]}]" unless creq[:version].downcase == SUPPORTED_VERSION

	# Build Headers
	Request.build_header creq, msg

	# Build Request Body
	Request.build_req_body creq, msg

	# Process Request when Complete
	process_req creq[:client]
end

.ensure_creq(client) ⇒ Object

Ensure Client Request Hash: Ensures existence of Client Request Hash inside Client Context.

Parameters:

  • client (Hash)

    An RxIO Client Hash



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/nut/handler.rb', line 83

def self.ensure_creq client

	# Ensure Client Request Structure is available
	client[:nut] ||= {}
	client[:nut][:req] ||= {

		# Request Lines (full request, including headers)
		lines: [],

		# Request Headers
		headz: {},

		# Full Request Line (including verb, URI and version)
		req_l: nil,

		# Full Request Body (just the body though, no headers)
		req_b: '',

		# Chunk Info (for Chunked Transfer-Encoding)
		chunk: {},

		# Client
		client: client
	}
end

.handle_msg(client, msg) ⇒ Object

Handle Message: RxIO Service Interface Callback - Triggered each time a message is received from a Client.

Parameters:

  • client (Hash)

    An RxIO Client Hash

  • msg (String)

    The message received from the Client



48
49
50
51
52
# File 'lib/nut/handler.rb', line 48

def self.handle_msg client, msg

	# Pass Full Message to Request Builder (including Message Delimiter)
	handle_req_dline client, msg + @msg_delim
end

.handle_req_dline(client, msg) ⇒ Object

Handle Request Data Line: Ensures availability of a Client Request Context before passing Assembling & Processing.

Parameters:

  • client (Hash)

    An RxIO Client Hash

  • msg (String)

    The message received from the Client (including newline)



71
72
73
74
75
76
77
78
# File 'lib/nut/handler.rb', line 71

def self.handle_req_dline client, msg

	# Ensure & Acquire Client Request Context
	creq = ensure_creq client

	# Assemble & Process
	assemble_process_req creq, msg unless creq[:body_complete]
end

.on_drop(client) ⇒ Object

On Drop: RxIO Service Interface Callback - Triggered each time a Client disconnects.

Parameters:

  • client (Hash)

    An RxIO Client Hash



40
41
42
# File 'lib/nut/handler.rb', line 40

def self.on_drop client
	# NoOp
end

.on_err(ex, request, response) ⇒ Object

Default Error Handler: Throws up a 500 Internal Server Error, with a description of the exception in the response body.

Parameters:

  • ex (Exception)

    An exception that was raised inside the handle method

  • request (Hash)

    Request Hash

  • response (Hash)

    Response Hash



198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/nut/handler.rb', line 198

def self.on_err ex, request, response

	# Throw up a 500 Internal Server Error
	response[:code] = 500

	# Spit out Exception in Response Body
	response[:body] << '<html><head><title>Error</title></head><body>'
	response[:body] << '<h1>Internal Server Error</h1>'
	response[:body] << "<h2>#{ex}</h2>"
	ex.backtrace.each { |b| response[:body] << " -> #{b}<br/>" }
	response[:body] << '</body></html>'
end

.on_join(client) ⇒ Object

On Join: RxIO Service Interface Callback - Triggered each time a Client connects.

Parameters:

  • client (Hash)

    An RxIO Client Hash



33
34
35
# File 'lib/nut/handler.rb', line 33

def self.on_join client
	# NoOp
end

.process_req(client) ⇒ Object

Process Request for Client: Constructs a complete Request Hash from a Client Request Context and passes it to the Request Handler Module’s handle_req method.

Parameters:

  • client (Hash)

    An RxIO Client Hash



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/nut/handler.rb', line 137

def self.process_req client

	# Check Request Body Complete
	return unless client[:nut][:req][:body_complete]

	# Pull Request Hash & Complete
	creq = client[:nut].delete :req
	uri = URI.parse creq[:uri]
	request = {
		peer: client[:peer].dclone,
		verb: creq[:verb].downcase.to_sym,
		uri: uri,
		body: creq[:req_b],
		ctype: creq[:ctype],
		params: Hash[*(CGI.parse(uri.query || '').inject([]) { |a, e| a + [e.first, (e.last.size == 1) ? e.last.first : e.last] })].sym_keys,
		headers: creq[:headz].dclone
	}

	# Pull all Request Details
	Request.extract_req_data request

	# Prepare Response
	response = { body: '' }

	# Acquire Request Handler
	rh = client[:serv].request_handler

	# Begin
	begin

		# Pass to Request Handler
		rh.handle request, response

	# Rescue (Catch Exceptions raised during request handling)
	rescue Exception => e

		# Handle Error
		(rh.respond_to?(:on_err) ? rh : self).on_err e, request, response
	end

	# Respond to Client
	serve_response client, response

	# Drop
	client[:drop] = true
end

.serve_response(client, response) ⇒ Object

Serve Response: Sends out an HTTP Response to the Client.

Parameters:

  • response (Hash)

    Response Hash



187
188
189
190
191
# File 'lib/nut/handler.rb', line 187

def self.serve_response client, response

	# Send out Response String
	write client, Response.build_response(response)
end

.subprocess_input(client) ⇒ Object

Sub-Process Input: RxIO Service Interface Callback - Triggered each time a chunk of data is received from a Client. Some Content-Length-based Requests may not end with a CRLF - this pulls in any left-over data directly from the RxIO Input Buffer.

Parameters:

  • client (Hash)

    Client Hash



58
59
60
61
62
63
64
65
# File 'lib/nut/handler.rb', line 58

def self.subprocess_input client

	# Acquire Client Request Context Hash
	creq = client[:nut].try(:[], :req) || {}

	# Pull any left-overs from Input Buffer for Content-Length Requests
	handle_req_dline client, creq[:client][:ibuf].slice!(0, creq[:client][:ibuf].bytesize) if Request.is_content_len? creq
end