tsurezure 0.0.34 → 0.0.36
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +21 -21
- data/lib/tsurezure.rb +311 -320
- data/lib/utils/error_codes.rb +24 -24
- data/lib/utils/errors.rb +65 -65
- data/lib/utils/http_utils.rb +212 -202
- data/lib/utils/logbook.rb +56 -56
- data/lib/utils/object_utils.rb +63 -63
- data/lib/utils/response.rb +95 -96
- data/nodemon.json +5 -5
- data/readme.md +9 -1
- metadata +6 -34
data/lib/tsurezure.rb
CHANGED
@@ -1,320 +1,311 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require '
|
5
|
-
require '
|
6
|
-
|
7
|
-
|
8
|
-
require_relative 'utils/
|
9
|
-
require_relative 'utils/
|
10
|
-
require_relative 'utils/
|
11
|
-
|
12
|
-
|
13
|
-
$
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
$TRZR_PROCESS_MODE = '
|
22
|
-
$
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
include
|
37
|
-
include
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
#
|
46
|
-
#
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
#
|
52
|
-
#
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
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
|
-
return if @session.gets.
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
raise ErrorCodes.
|
184
|
-
|
185
|
-
|
186
|
-
@
|
187
|
-
@
|
188
|
-
@
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
#
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
# ----------------------------------------
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
true
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
return
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
end
|
311
|
-
|
312
|
-
at_exit do
|
313
|
-
if $TRZR_PROCESS_MODE == 'development' && $TRZR_LOG == true
|
314
|
-
time = Time.now.to_i - TRZR_STARTED_AT
|
315
|
-
puts
|
316
|
-
puts '[trzr_dev] shutting down. goodbye...'
|
317
|
-
puts "[trzr_dev] shut down after #{Time.at(time).utc.strftime('%H:%M:%S')}."
|
318
|
-
puts
|
319
|
-
end
|
320
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require 'json'
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
require_relative 'utils/http_utils' # mainly used to create http responses.
|
8
|
+
require_relative 'utils/object_utils' # various object validation utilities
|
9
|
+
require_relative 'utils/error_codes' # for generating errors.
|
10
|
+
require_relative 'utils/response' # handles request and generates responses.
|
11
|
+
|
12
|
+
$TRZR_PROCESS_MODE = nil
|
13
|
+
$TRZR_LOG = true
|
14
|
+
TRZR_STARTED_AT = Time.now.to_i
|
15
|
+
|
16
|
+
INVALID_RESPONSE_FORMAT = "if responding from a middleware, \
|
17
|
+
you must return a hash that includes a :message property."
|
18
|
+
|
19
|
+
ARGV.each do |arg|
|
20
|
+
$TRZR_PROCESS_MODE = 'development' if arg == '--development'
|
21
|
+
$TRZR_PROCESS_MODE = 'production' if arg == '--production'
|
22
|
+
$TRZR_LOG = false if arg == '--silent'
|
23
|
+
end
|
24
|
+
|
25
|
+
$TRZR_PROCESS_MODE = 'production' if $TRZR_PROCESS_MODE.nil?
|
26
|
+
|
27
|
+
# main class for tsurezure.
|
28
|
+
class Tsurezure
|
29
|
+
include OUtil
|
30
|
+
|
31
|
+
##
|
32
|
+
# this class is made to handle requests coming from
|
33
|
+
# a single client on a tcp server.
|
34
|
+
class RequestHandler
|
35
|
+
include HTTPUtils
|
36
|
+
include ErrorCodes
|
37
|
+
include TResponse
|
38
|
+
|
39
|
+
##
|
40
|
+
# initializes with a client socket returned from a +TCPSocket+ object.
|
41
|
+
|
42
|
+
def initialize(session)
|
43
|
+
@session = session
|
44
|
+
# endpoints are organized into arrays, sorted by method.
|
45
|
+
# ex: { get: [ ...endpoint objects ], post: [ ... ] }
|
46
|
+
# etc.
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# handles an incoming request from the open socket in +@session+.
|
51
|
+
# constructs a formatted request object from the original request object,
|
52
|
+
# and calls +send_final_response+ in order to send a response.
|
53
|
+
def handle_request(request)
|
54
|
+
url_main = request[:url].split('?')[0]
|
55
|
+
|
56
|
+
request_object = {
|
57
|
+
method: request[:method],
|
58
|
+
url: url_main,
|
59
|
+
params: HTTPUtils::URLUtils.extract_url_params(request[:url]),
|
60
|
+
protocol: request[:protocol],
|
61
|
+
headers: request[:headers],
|
62
|
+
data: request[:data]
|
63
|
+
}
|
64
|
+
|
65
|
+
generate_response request_object
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# generate a response from a supplied request object
|
70
|
+
# from +handle_request+.
|
71
|
+
def generate_response(request_object)
|
72
|
+
type = 'text/plain'
|
73
|
+
|
74
|
+
unless request_object[:options].nil? || request_object[:options].empty?
|
75
|
+
type = request_object[:options][:content_type]
|
76
|
+
end
|
77
|
+
|
78
|
+
res = TResponse.get_response request_object, @endpoints
|
79
|
+
|
80
|
+
# to initialize: session and length of response
|
81
|
+
responder = HTTPUtils::ServerResponse.new(
|
82
|
+
@session,
|
83
|
+
res[:message].nil? ? '' : res[:message].bytesize
|
84
|
+
)
|
85
|
+
|
86
|
+
go_through_middleware request_object, responder, res, type
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_correct_middleware(request_object)
|
90
|
+
@middleware.keys.select do |pat|
|
91
|
+
HTTPUtils::URLUtils.matches_url_regex?(pat, request_object[:url]) ||
|
92
|
+
pat == '*'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def fix_req(request, mid)
|
97
|
+
request[:vars] =
|
98
|
+
HTTPUtils::URLUtils.get_match_indices(
|
99
|
+
mid[:path_regex],
|
100
|
+
request[:url]
|
101
|
+
) || {}
|
102
|
+
|
103
|
+
request[:options] = mid[:options]
|
104
|
+
|
105
|
+
request
|
106
|
+
end
|
107
|
+
|
108
|
+
def respond_with_error(error)
|
109
|
+
Logbook::Dev.log(error)
|
110
|
+
|
111
|
+
message = { error: error }.to_json
|
112
|
+
|
113
|
+
responder = HTTPUtils::ServerResponse.new(
|
114
|
+
@session,
|
115
|
+
message.bytesize
|
116
|
+
)
|
117
|
+
|
118
|
+
responder.respond message, {}, 500, 'application/json'
|
119
|
+
end
|
120
|
+
|
121
|
+
def send_middleware_response(req, resp, type)
|
122
|
+
res = resp.merge req
|
123
|
+
|
124
|
+
return respond_with_error INVALID_RESPONSE_FORMAT if res[:message].nil?
|
125
|
+
|
126
|
+
responder = HTTPUtils::ServerResponse.new(
|
127
|
+
@session,
|
128
|
+
res[:message].bytesize
|
129
|
+
)
|
130
|
+
|
131
|
+
responder.respond res[:message], res[:options] || {}, res[:status], type
|
132
|
+
end
|
133
|
+
|
134
|
+
def call_each_middleware(request, middleware, type)
|
135
|
+
alt = nil
|
136
|
+
|
137
|
+
middleware.each do |path|
|
138
|
+
break if alt
|
139
|
+
|
140
|
+
@middleware[path]&.each do |mid|
|
141
|
+
alt = mid[:callback].call fix_req(request, mid)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
return true unless alt
|
146
|
+
|
147
|
+
send_middleware_response(request, alt, type)
|
148
|
+
end
|
149
|
+
|
150
|
+
def go_through_middleware(request_object, responder, res, type)
|
151
|
+
exp = get_correct_middleware request_object
|
152
|
+
|
153
|
+
done = call_each_middleware request_object, exp, type
|
154
|
+
|
155
|
+
return unless done
|
156
|
+
|
157
|
+
# to send: response, options, status, content_type
|
158
|
+
responder.respond res[:message], res[:options] || {}, res[:status], type
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# main process, allows server to handle requests
|
163
|
+
def process(client, endpoints, middleware)
|
164
|
+
@endpoints = endpoints
|
165
|
+
@middleware = middleware
|
166
|
+
@request = client.gets
|
167
|
+
# wait until server isn't recieving anything
|
168
|
+
return if @session.gets.nil?
|
169
|
+
return if @session.gets.chop.length.zero?
|
170
|
+
|
171
|
+
request_made = HTTPUtils.make_proper_request client, @request
|
172
|
+
|
173
|
+
request_to_handle = HTTPUtils.make_request_object request_made
|
174
|
+
|
175
|
+
handle_request request_to_handle
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# prepares the server to run on a specified port.
|
181
|
+
def initialize(port)
|
182
|
+
raise ErrorCodes.nan_error 'port' unless port.is_a? Numeric
|
183
|
+
raise ErrorCodes.range_error 0, 65_535 unless (0..65_535).include? port
|
184
|
+
|
185
|
+
@server = TCPServer.new port
|
186
|
+
@port = port
|
187
|
+
@endpoints = {}
|
188
|
+
@middleware = {}
|
189
|
+
end
|
190
|
+
|
191
|
+
attr_reader :endpoints # access endpoints object from outside scope
|
192
|
+
|
193
|
+
def add_middleware(path, callback, options)
|
194
|
+
unless path.is_a? String
|
195
|
+
raise ArgumentError, 'first argument to middleware\
|
196
|
+
must be string or function.'
|
197
|
+
end
|
198
|
+
|
199
|
+
middleware_object = {
|
200
|
+
options: options, callback: callback, path_regex: path
|
201
|
+
}
|
202
|
+
|
203
|
+
@middleware[path] << middleware_object if @middleware[path]
|
204
|
+
|
205
|
+
@middleware[path] = [middleware_object] unless @middleware[path]
|
206
|
+
end
|
207
|
+
|
208
|
+
def register(http_method, path, callback, options = nil)
|
209
|
+
http_method = http_method.upcase
|
210
|
+
insurance = ensure_registration http_method, path, callback, options
|
211
|
+
|
212
|
+
raise ArgumentError, insurance if insurance.instance_of? String
|
213
|
+
|
214
|
+
# register a new endpoint but do not register dupes
|
215
|
+
@endpoints[http_method] = {} unless @endpoints.key? http_method
|
216
|
+
|
217
|
+
new_endpoint = { path: path, responder: callback, options: options }
|
218
|
+
|
219
|
+
add_new_endpoint new_endpoint, http_method
|
220
|
+
end
|
221
|
+
|
222
|
+
##
|
223
|
+
# run when the server is prepared to accept requests.
|
224
|
+
def listen(callback = nil)
|
225
|
+
puts "[trzr_dev] running on port #{@port}!" if $TRZR_PROCESS_MODE == 'development'
|
226
|
+
|
227
|
+
# call the callback if there's one provided
|
228
|
+
callback.call server_opts if callback.is_a? Proc
|
229
|
+
|
230
|
+
# create a new thread for handle each incoming request
|
231
|
+
loop do
|
232
|
+
Thread.start(@server.accept) do |client|
|
233
|
+
RequestHandler.new(client).process client, @endpoints, @middleware
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def kill
|
239
|
+
abort
|
240
|
+
end
|
241
|
+
|
242
|
+
private
|
243
|
+
|
244
|
+
def server_opts
|
245
|
+
{
|
246
|
+
port: @port,
|
247
|
+
endpoints: @endpoints,
|
248
|
+
middleware: @middleware
|
249
|
+
}
|
250
|
+
end
|
251
|
+
|
252
|
+
# ----------------------------------------
|
253
|
+
# :section: registration of endpoints and
|
254
|
+
# all endpoint management methods follow.
|
255
|
+
# ----------------------------------------
|
256
|
+
|
257
|
+
def ensure_registration(*args)
|
258
|
+
verification = verify_registration(*args)
|
259
|
+
|
260
|
+
return verification unless verification
|
261
|
+
|
262
|
+
verification # to register
|
263
|
+
end
|
264
|
+
|
265
|
+
def validate_registration_params(method, path, responder)
|
266
|
+
return "#{method} is not a valid http method." unless TResponse::Utils.new.valid_methods.include? method
|
267
|
+
|
268
|
+
return 'invalid path type. must be a string.' unless path.instance_of?(String)
|
269
|
+
|
270
|
+
return 'invalid path. must begin with "/".' if path.empty? || path.chr != '/'
|
271
|
+
|
272
|
+
return 'invalid responder type. must a proc.' unless responder.instance_of?(Proc)
|
273
|
+
|
274
|
+
true
|
275
|
+
end
|
276
|
+
|
277
|
+
def verify_registration(http_method, path, responder, options)
|
278
|
+
valid = validate_registration_params http_method, path, responder
|
279
|
+
|
280
|
+
return valid unless valid == true
|
281
|
+
return true if options.nil? || options.empty?
|
282
|
+
return 'invalid options type.' unless options.instance_of?(Hash)
|
283
|
+
|
284
|
+
valid_opts = %w[content_type method location]
|
285
|
+
|
286
|
+
opts_valid = OUtil.check_against_array(options.keys, valid_opts, 'register')
|
287
|
+
|
288
|
+
return 'invalid options provided to register.' unless opts_valid
|
289
|
+
|
290
|
+
true # to ensure_registration
|
291
|
+
end
|
292
|
+
|
293
|
+
def add_new_endpoint(endpoint, method)
|
294
|
+
@endpoints[method].each do |_, value|
|
295
|
+
raise ArgumentError, 'cannot register duplicate path.' if value[:path] == endpoint[:path]
|
296
|
+
end
|
297
|
+
|
298
|
+
# add endpoint to list of registered endpoints
|
299
|
+
@endpoints[method][endpoint[:path]] = endpoint
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
at_exit do
|
304
|
+
if $TRZR_PROCESS_MODE == 'development' && $TRZR_LOG == true
|
305
|
+
time = Time.now.to_i - TRZR_STARTED_AT
|
306
|
+
puts
|
307
|
+
puts '[trzr_dev] shutting down. goodbye...'
|
308
|
+
puts "[trzr_dev] shut down after #{Time.at(time).utc.strftime('%H:%M:%S')}."
|
309
|
+
puts
|
310
|
+
end
|
311
|
+
end
|