Class: Anorexic::HTTPHost

Inherits:
Object
  • Object
show all
Defined in:
lib/anorexic/handlers/http_host.rb

Overview

this is a Handler stub class for an HTTP echo server.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params = {}) ⇒ HTTPHost

initializes an HTTP host with the parameters for the specific host.

parameters are the same (almost) as `add_service` and include `root` for file root, `assets` and other non service related options.


15
16
17
18
19
20
21
22
23
24
25
# File 'lib/anorexic/handlers/http_host.rb', line 15

def initialize params = {}
	@params = params
	@routes = []
	# params[:save_assets] = true unless params[:save_assets] == false
	params[:index_file] ||= 'index.html'
	params[:assets_public] ||= '/assets'
	params[:assets_public].chomp! '/'

	@sass_cache = Sass::CacheStores::Memory.new if defined?(::Sass)
	# @sass_cache_lock = Mutex.new
end

Instance Attribute Details

#paramsObject (readonly)

the parameters / settings for the Host.


7
8
9
# File 'lib/anorexic/handlers/http_host.rb', line 7

def params
  @params
end

#routesObject (readonly)

the routing array


9
10
11
# File 'lib/anorexic/handlers/http_host.rb', line 9

def routes
  @routes
end

Instance Method Details

#add_route(path, controller, &block) ⇒ Object

adds a route under the specific host


28
29
30
# File 'lib/anorexic/handlers/http_host.rb', line 28

def add_route path, controller, &block
	routes << Route.new(path, controller, params, &block)
end

#call(request) ⇒ Object

Dresses up as a Rack app (If you don't like WebSockets, it's a reasonable aaproach).


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
# File 'lib/anorexic/handlers/http_host.rb', line 62

def call request
	request = Rack::Request.new request if defined? Rack
	ret = nil
	begin
		# render any assets?
		ret = render_assets request
		return ret if ret

		# send static file, if exists and root is set.
		ret = send_static_file request
		return ret if ret

		# return if a route answered the request
		routes.each {|r| ret = r.call(request); return ret if ret }

		# send folder listing if root is set, directory listing is set and folder exists

		#to-do

		#return error code or 404 not found
		return send_by_code request, 404			
	rescue Exception => e
		# return 500 internal server error.
		Anorexic.error e
		return send_by_code request, 500
	end
	true
end

#on_request(request) ⇒ Object

handles requests sent to the host. returns true if the host delt with the request.

since hosts are required to handle the requests (send 404 errors if resources arrn't found), this method always returns true.


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/anorexic/handlers/http_host.rb', line 36

def on_request request
	begin
		# render any assets?
		return true if render_assets request

		# send static file, if exists and root is set.
		return true if send_static_file request

		# return if a route answered the request
		routes.each {|r| return true if r.on_request(request) }

		# send folder listing if root is set, directory listing is set and folder exists

		#to-do

		#return error code or 404 not found
		send_by_code request, 404			
	rescue Exception => e
		# return 500 internal server error.
		Anorexic.error e
		send_by_code request, 500
	end
	true
end

#refresh_sass?(sass) ⇒ Boolean

Returns:

  • (Boolean)

206
207
208
209
210
211
212
# File 'lib/anorexic/handlers/http_host.rb', line 206

def refresh_sass? sass
	return false unless File.exists?(sass)
	return true if Anorexic.cache_needs_update?(sass)
	mt = Anorexic.file_mtime(sass)
	Anorexic.get_cached(sass).each {|e| return true if File.exists?(e.options[:filename]) && (File.mtime(e.options[:filename]) > mt)} # fn = File.join( e.options[:load_paths][0].root, e.options[:filename]) 
	false
end

#render_assets(request) ⇒ Object

renders assets, if necessary, and places the rendered result in the cache and in the public folder.


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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/anorexic/handlers/http_host.rb', line 154

def render_assets request
	# contine only if assets are defined and called for
	return false unless @params[:assets] && request.path.match(/^#{params[:assets_public]}\/.+/)
	# review callback, if defined
	return true if params[:assets_callback] && params[:assets_callback].call(request)

	# get file requested
	source_file = File.join(params[:assets], *(request.path.match(/^#{params[:assets_public]}\/(.+)/)[1].split('/')))

	# stop if file name is reserved / has security issues
	return false if source_file.match(/(scss|sass|coffee|\.\.\/)$/)

	# set where to store the rendered asset
	target_file = false
	target_file = File.join( params[:root], params[:assets_public], *request.path.match(/^#{params[:assets_public]}\/(.*)/)[1].split('/') ) if params[:root]

	# send the file if it exists (no render needed)
	if File.exists?(source_file)
		data = Anorexic.cache_needs_update?(source_file) ? Anorexic.save_file(target_file, Anorexic.reload_file(source_file), params[:save_assets]) : Anorexic.load_file(source_file)
		return (data ? send_raw_data(request, data, MimeTypeHelper::MIME_DICTIONARY[::File.extname(source_file)]) : false)
	end

	# render supported assets
	case source_file
	when /\.css$/
		sass = source_file.gsub /css$/, 'sass'
		sass.gsub! /sass$/, 'scss' unless Anorexic.file_exists?(sass)
		return false unless Anorexic.file_exists?(sass)
		# review mtime and render sass if necessary
		if defined?(::Sass) && refresh_sass?(sass)
			eng = Sass::Engine.for_file(sass, cache_store: @sass_cache)
			Anorexic.cache_data sass, eng.dependencies
			css, map = eng.render_with_sourcemap(params[:assets_public])
			Anorexic.save_file target_file, css, params[:save_assets]
			Anorexic.save_file (target_file + ".map"), map, params[:save_assets]
		end
		# try to send the cached css file which started the request.
		return send_file request, target_file
	when /\.js$/
		coffee = source_file.gsub /js$/i, 'coffee'
		return false unless Anorexic.file_exists?(coffee)
		# review mtime and render coffee if necessary
		if defined?(::CoffeeScript) && Anorexic.cache_needs_update?(coffee)
			# render coffee to cache
			Anorexic.cache_data coffee, nil
			Anorexic.save_file target_file, CoffeeScript.compile(IO.read coffee), params[:save_assets]
		end
		# try to send the cached js file which started the request.
		return send_file request, target_file
	end
	false
end

#send_by_code(request, code, headers = {}) ⇒ Object

sends a response for an error code, rendering the relevent file (if exists).


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/anorexic/handlers/http_host.rb', line 96

def send_by_code request, code, headers = {}
	begin
		if params[:root]
			if defined?(::Slim) && Anorexic.file_exists?(File.join(params[:root], "#{code}.slim"))
				Anorexic.cache_data File.join(params[:root], "#{code}.slim"), Slim::Template.new( File.join( params[:root], "#{code}.slim" ) ) unless Anorexic.cached? File.join(params[:root], "#{code}.slim")
				return send_raw_data request, Anorexic.get_cached( File.join(params[:root], "#{code}.slim") ).render( self ), 'text/html', code, headers
			elsif defined?(::Haml) && Anorexic.file_exists?(File.join(params[:root], "#{code}.haml"))
				Anorexic.cache_data File.join(params[:root], "#{code}.haml"), Haml::Engine.new( IO.read( File.join( params[:root], "#{code}.haml" ) ) ) unless Anorexic.cached? File.join(params[:root], "#{code}.haml")
				return send_raw_data request, Anorexic.get_cached( File.join(params[:root], "#{code}.haml") ).render( self ), 'text/html', code, headers
			elsif defined?(::ERB) && Anorexic.file_exists?(File.join(params[:root], "#{code}.erb"))
				return send_raw_data request, ERB.new( Anorexic.load_file( File.join(params[:root], "#{code}.erb") ) ).result(binding), 'text/html', code, headers
			elsif Anorexic.file_exists?(File.join(params[:root], "#{code}.html"))
				return send_file(request, File.join(params[:root], "#{code}.html"), code, headers)
			end
		end
		return true if send_raw_data(request, HTTPResponse::STATUS_CODES[code], "text/plain", code, headers)
	rescue Exception => e
		Anorexic.error e
	end
	false
end

#send_file(request, filename, status_code = 200, headers = {}) ⇒ Object

sends a file/cacheed data if it exists. otherwise returns false.


134
135
136
137
138
139
# File 'lib/anorexic/handlers/http_host.rb', line 134

def send_file request, filename, status_code = 200, headers = {}
	if Anorexic.file_exists?(filename) && !::File.directory?(filename)
		return send_raw_data request, Anorexic.load_file(filename), MimeTypeHelper::MIME_DICTIONARY[::File.extname(filename)], status_code, headers
	end
	return false
end

#send_raw_data(request, data, mime, status_code = 200, headers = {}) ⇒ Object

sends raw data through the connection. always returns true (data send).


141
142
143
144
145
146
147
148
# File 'lib/anorexic/handlers/http_host.rb', line 141

def send_raw_data request, data, mime, status_code = 200, headers = {}
	response = HTTPResponse.new request, status_code, headers
	response['cache-control'] = 'public, max-age=86400'					
	response << data
	response['content-length'] = data.bytesize
	response.finish
	true
end

#send_static_file(request) ⇒ Object

attempts to send a static file by the request path (using `send_file` and `send_raw_data`).

returns true if data was sent.


121
122
123
124
125
126
127
128
129
130
131
# File 'lib/anorexic/handlers/http_host.rb', line 121

def send_static_file request
	return false unless params[:root]
	file_requested = request[:path].to_s.split('/')
	unless file_requested.include? '..'
		file_requested.shift
		file_requested = File.join(params[:root], *file_requested)
		return true if send_file request, file_requested
		return send_file request, File.join(file_requested, params[:index_file])
	end
	false
end