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.



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.



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.



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.



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.



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.



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

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.



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.



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
183
# 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,
    server: client[:local],
    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 = { headers: {}, 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.



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

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.



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