serverside 0.1.59 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +21 -1
- data/Rakefile +69 -40
- data/bin/serverside +7 -15
- data/doc/rdoc/classes/Daemon.html +18 -18
- data/doc/rdoc/classes/Daemon/Base.html +8 -6
- data/doc/rdoc/classes/Daemon/Cluster.html +36 -36
- data/doc/rdoc/classes/Daemon/Cluster/PidFile.html +20 -18
- data/doc/rdoc/classes/Daemon/PidFile.html +12 -12
- data/doc/rdoc/classes/Object.html +151 -0
- data/doc/rdoc/classes/Proc.html +151 -0
- data/doc/rdoc/classes/ServerSide.html +69 -0
- data/doc/rdoc/classes/ServerSide/Application.html +43 -11
- data/doc/rdoc/classes/ServerSide/Application/Base.html +30 -30
- data/doc/rdoc/classes/ServerSide/Application/Static.html +20 -18
- data/doc/rdoc/classes/ServerSide/Connection.html +3 -3
- data/doc/rdoc/classes/ServerSide/Connection/Base.html +142 -99
- data/doc/rdoc/classes/ServerSide/Connection/Const.html +11 -13
- data/doc/rdoc/classes/ServerSide/Connection/Router.html +464 -0
- data/doc/rdoc/classes/ServerSide/Server.html +8 -6
- data/doc/rdoc/classes/ServerSide/StaticFiles.html +75 -43
- data/doc/rdoc/classes/ServerSide/StaticFiles/Const.html +5 -0
- data/doc/rdoc/classes/String.html +20 -18
- data/doc/rdoc/classes/Symbol.html +2 -0
- data/doc/rdoc/created.rid +1 -1
- data/doc/rdoc/files/CHANGELOG.html +32 -2
- data/doc/rdoc/files/lib/serverside/application_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/connection_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/core_ext_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/daemon_rb.html +1 -1
- data/doc/rdoc/files/lib/serverside/routing_rb.html +101 -0
- data/doc/rdoc/files/lib/serverside/static_rb.html +1 -1
- data/doc/rdoc/fr_class_index.html +3 -1
- data/doc/rdoc/fr_file_index.html +1 -0
- data/doc/rdoc/fr_method_index.html +49 -33
- data/lib/serverside/application.rb +17 -4
- data/lib/serverside/connection.rb +23 -3
- data/lib/serverside/core_ext.rb +16 -1
- data/lib/serverside/routing.rb +119 -0
- data/lib/serverside/static.rb +9 -23
- data/test/functional/routing_server.rb +14 -0
- data/test/functional/routing_server_test.rb +41 -0
- data/test/functional/static_server_test.rb +8 -2
- data/test/unit/connection_test.rb +13 -0
- data/test/unit/core_ext_test.rb +14 -0
- data/test/unit/routing_test.rb +171 -0
- data/test/unit/static_test.rb +34 -1
- metadata +10 -3
- data/doc/rdoc/classes/ServerSide/Connection/Static.html +0 -172
@@ -25,6 +25,8 @@
|
|
25
25
|
<a href="classes/Daemon/Cluster.html">Daemon::Cluster</a><br />
|
26
26
|
<a href="classes/Daemon/Cluster/PidFile.html">Daemon::Cluster::PidFile</a><br />
|
27
27
|
<a href="classes/Daemon/PidFile.html">Daemon::PidFile</a><br />
|
28
|
+
<a href="classes/Object.html">Object</a><br />
|
29
|
+
<a href="classes/Proc.html">Proc</a><br />
|
28
30
|
<a href="classes/ServerSide.html">ServerSide</a><br />
|
29
31
|
<a href="classes/ServerSide/Application.html">ServerSide::Application</a><br />
|
30
32
|
<a href="classes/ServerSide/Application/Base.html">ServerSide::Application::Base</a><br />
|
@@ -32,7 +34,7 @@
|
|
32
34
|
<a href="classes/ServerSide/Connection.html">ServerSide::Connection</a><br />
|
33
35
|
<a href="classes/ServerSide/Connection/Base.html">ServerSide::Connection::Base</a><br />
|
34
36
|
<a href="classes/ServerSide/Connection/Const.html">ServerSide::Connection::Const</a><br />
|
35
|
-
<a href="classes/ServerSide/Connection/
|
37
|
+
<a href="classes/ServerSide/Connection/Router.html">ServerSide::Connection::Router</a><br />
|
36
38
|
<a href="classes/ServerSide/Server.html">ServerSide::Server</a><br />
|
37
39
|
<a href="classes/ServerSide/StaticFiles.html">ServerSide::StaticFiles</a><br />
|
38
40
|
<a href="classes/ServerSide/StaticFiles/Const.html">ServerSide::StaticFiles::Const</a><br />
|
data/doc/rdoc/fr_file_index.html
CHANGED
@@ -29,6 +29,7 @@
|
|
29
29
|
<a href="files/lib/serverside/connection_rb.html">lib/serverside/connection.rb</a><br />
|
30
30
|
<a href="files/lib/serverside/core_ext_rb.html">lib/serverside/core_ext.rb</a><br />
|
31
31
|
<a href="files/lib/serverside/daemon_rb.html">lib/serverside/daemon.rb</a><br />
|
32
|
+
<a href="files/lib/serverside/routing_rb.html">lib/serverside/routing.rb</a><br />
|
32
33
|
<a href="files/lib/serverside/server_rb.html">lib/serverside/server.rb</a><br />
|
33
34
|
<a href="files/lib/serverside/static_rb.html">lib/serverside/static.rb</a><br />
|
34
35
|
</div>
|
@@ -20,40 +20,56 @@
|
|
20
20
|
<div id="index">
|
21
21
|
<h1 class="section-bar">Methods</h1>
|
22
22
|
<div id="index-entries">
|
23
|
-
<a href="classes/String.html#
|
24
|
-
<a href="classes/ServerSide/
|
25
|
-
<a href="classes/ServerSide/
|
26
|
-
<a href="classes/
|
27
|
-
<a href="classes/
|
28
|
-
<a href="classes/ServerSide/Application.html#
|
29
|
-
<a href="classes/ServerSide/Application/
|
30
|
-
<a href="classes/
|
31
|
-
<a href="classes/Daemon
|
32
|
-
<a href="classes/
|
33
|
-
<a href="classes/ServerSide/Application/
|
34
|
-
<a href="classes/ServerSide/
|
35
|
-
<a href="classes/ServerSide/Connection/
|
36
|
-
<a href="classes/ServerSide/Connection/
|
37
|
-
<a href="classes/Daemon/
|
38
|
-
<a href="classes/
|
39
|
-
<a href="classes/
|
40
|
-
<a href="classes/
|
41
|
-
<a href="classes/ServerSide/
|
42
|
-
<a href="classes/ServerSide/Connection/Base.html#
|
43
|
-
<a href="classes/ServerSide/
|
44
|
-
<a href="classes/ServerSide/
|
45
|
-
<a href="classes/Daemon.html#
|
46
|
-
<a href="classes/
|
47
|
-
<a href="classes/
|
48
|
-
<a href="classes/Daemon.html#
|
49
|
-
<a href="classes/Daemon/Cluster.html#
|
50
|
-
<a href="classes/
|
51
|
-
<a href="classes/
|
52
|
-
<a href="classes/
|
53
|
-
<a href="classes/ServerSide
|
23
|
+
<a href="classes/String.html#M000006">/ (String)</a><br />
|
24
|
+
<a href="classes/ServerSide/Connection/Router.html#M000046">cache_constant (ServerSide::Connection::Router)</a><br />
|
25
|
+
<a href="classes/ServerSide/Connection/Router.html#M000042">compile_rules (ServerSide::Connection::Router)</a><br />
|
26
|
+
<a href="classes/ServerSide/Connection/Router.html#M000044">condition_part (ServerSide::Connection::Router)</a><br />
|
27
|
+
<a href="classes/ServerSide/Application.html#M000027">config= (ServerSide::Application)</a><br />
|
28
|
+
<a href="classes/ServerSide/Application/Base.html#M000030">configuration (ServerSide::Application::Base)</a><br />
|
29
|
+
<a href="classes/ServerSide/Application/Base.html#M000029">configure (ServerSide::Application::Base)</a><br />
|
30
|
+
<a href="classes/Object.html#M000003">const_tag (Object)</a><br />
|
31
|
+
<a href="classes/Daemon.html#M000007">control (Daemon)</a><br />
|
32
|
+
<a href="classes/Daemon/Cluster.html#M000016">daemon_loop (Daemon::Cluster)</a><br />
|
33
|
+
<a href="classes/ServerSide/Application/Static.html#M000032">daemonize (ServerSide::Application::Static)</a><br />
|
34
|
+
<a href="classes/ServerSide/Application.html#M000028">daemonize (ServerSide::Application)</a><br />
|
35
|
+
<a href="classes/ServerSide/Connection/Router.html#M000049">default_handler (ServerSide::Connection::Router)</a><br />
|
36
|
+
<a href="classes/ServerSide/Connection/Router.html#M000045">define_proc (ServerSide::Connection::Router)</a><br />
|
37
|
+
<a href="classes/Daemon/Cluster/PidFile.html#M000019">delete (Daemon::Cluster::PidFile)</a><br />
|
38
|
+
<a href="classes/Daemon/Cluster.html#M000013">fork_server (Daemon::Cluster)</a><br />
|
39
|
+
<a href="classes/ServerSide/Connection/Router.html#M000040">has_routes? (ServerSide::Connection::Router)</a><br />
|
40
|
+
<a href="classes/ServerSide/Server.html#M000050">new (ServerSide::Server)</a><br />
|
41
|
+
<a href="classes/ServerSide/Application/Base.html#M000031">new (ServerSide::Application::Base)</a><br />
|
42
|
+
<a href="classes/ServerSide/Connection/Base.html#M000033">new (ServerSide::Connection::Base)</a><br />
|
43
|
+
<a href="classes/ServerSide/Connection/Base.html#M000036">parse_parameters (ServerSide::Connection::Base)</a><br />
|
44
|
+
<a href="classes/ServerSide/Connection/Base.html#M000035">parse_request (ServerSide::Connection::Base)</a><br />
|
45
|
+
<a href="classes/Daemon/Base.html#M000012">pid_fn (Daemon::Base)</a><br />
|
46
|
+
<a href="classes/Proc.html#M000002">proc_tag (Proc)</a><br />
|
47
|
+
<a href="classes/ServerSide/Connection/Base.html#M000034">process (ServerSide::Connection::Base)</a><br />
|
48
|
+
<a href="classes/Daemon/PidFile.html#M000011">recall (Daemon::PidFile)</a><br />
|
49
|
+
<a href="classes/Daemon/Cluster/PidFile.html#M000021">recall_pids (Daemon::Cluster::PidFile)</a><br />
|
50
|
+
<a href="classes/ServerSide/Connection/Base.html#M000038">redirect (ServerSide::Connection::Base)</a><br />
|
51
|
+
<a href="classes/ServerSide/Connection/Router.html#M000041">route (ServerSide::Connection::Router)</a><br />
|
52
|
+
<a href="classes/ServerSide.html#M000022">route (ServerSide)</a><br />
|
53
|
+
<a href="classes/ServerSide.html#M000023">route_default (ServerSide)</a><br />
|
54
|
+
<a href="classes/ServerSide/Connection/Router.html#M000047">route_default (ServerSide::Connection::Router)</a><br />
|
55
|
+
<a href="classes/ServerSide/Connection/Router.html#M000043">rule_to_statement (ServerSide::Connection::Router)</a><br />
|
56
|
+
<a href="classes/ServerSide/Connection/Base.html#M000037">send_response (ServerSide::Connection::Base)</a><br />
|
57
|
+
<a href="classes/ServerSide/StaticFiles.html#M000025">serve_dir (ServerSide::StaticFiles)</a><br />
|
58
|
+
<a href="classes/ServerSide/StaticFiles.html#M000024">serve_file (ServerSide::StaticFiles)</a><br />
|
59
|
+
<a href="classes/ServerSide/StaticFiles.html#M000026">serve_static (ServerSide::StaticFiles)</a><br />
|
60
|
+
<a href="classes/Daemon/Cluster.html#M000017">start (Daemon::Cluster)</a><br />
|
61
|
+
<a href="classes/Daemon.html#M000008">start (Daemon)</a><br />
|
62
|
+
<a href="classes/Daemon/Cluster.html#M000014">start_servers (Daemon::Cluster)</a><br />
|
63
|
+
<a href="classes/Daemon/Cluster.html#M000018">stop (Daemon::Cluster)</a><br />
|
64
|
+
<a href="classes/Daemon.html#M000009">stop (Daemon)</a><br />
|
65
|
+
<a href="classes/Daemon/Cluster.html#M000015">stop_servers (Daemon::Cluster)</a><br />
|
66
|
+
<a href="classes/Daemon/PidFile.html#M000010">store (Daemon::PidFile)</a><br />
|
67
|
+
<a href="classes/Daemon/Cluster/PidFile.html#M000020">store_pid (Daemon::Cluster::PidFile)</a><br />
|
68
|
+
<a href="classes/ServerSide/Connection/Base.html#M000039">stream (ServerSide::Connection::Base)</a><br />
|
54
69
|
<a href="classes/Symbol.html#M000001">to_s (Symbol)</a><br />
|
55
|
-
<a href="classes/
|
56
|
-
<a href="classes/String.html#
|
70
|
+
<a href="classes/ServerSide/Connection/Router.html#M000048">unhandled (ServerSide::Connection::Router)</a><br />
|
71
|
+
<a href="classes/String.html#M000004">uri_escape (String)</a><br />
|
72
|
+
<a href="classes/String.html#M000005">uri_unescape (String)</a><br />
|
57
73
|
</div>
|
58
74
|
</div>
|
59
75
|
</body>
|
@@ -4,6 +4,23 @@ require File.join(File.dirname(__FILE__), 'connection')
|
|
4
4
|
|
5
5
|
module ServerSide
|
6
6
|
module Application
|
7
|
+
@@config = nil
|
8
|
+
|
9
|
+
def self.config=(c)
|
10
|
+
@@config = c
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.daemonize(config, cmd)
|
14
|
+
config = @@config.merge(config) if @@config
|
15
|
+
daemon_class = Class.new(Daemon::Cluster) do
|
16
|
+
meta_def(:pid_fn) {Daemon::WorkingDirectory/'serverside.pid'}
|
17
|
+
meta_def(:server_loop) do |port|
|
18
|
+
ServerSide::Server.new(config[:host], port, ServerSide::Connection::Router)
|
19
|
+
end
|
20
|
+
meta_def(:ports) {config[:ports]}
|
21
|
+
end
|
22
|
+
Daemon.control(daemon_class, cmd)
|
23
|
+
end
|
7
24
|
|
8
25
|
class Base < ServerSide::Connection::Base
|
9
26
|
def self.configure(options)
|
@@ -32,9 +49,5 @@ module ServerSide
|
|
32
49
|
Daemon.control(daemon_class, cmd)
|
33
50
|
end
|
34
51
|
end
|
35
|
-
|
36
|
-
def self.daemonize
|
37
|
-
|
38
|
-
end
|
39
52
|
end
|
40
53
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'static')
|
2
|
+
|
1
3
|
module ServerSide
|
2
4
|
# The Connection module takes care of HTTP connection. While most HTTP servers
|
3
5
|
# (at least the OO ones) will define separate classes for request and
|
@@ -10,7 +12,9 @@ module ServerSide
|
|
10
12
|
module Const
|
11
13
|
LineBreak = "\r\n".freeze
|
12
14
|
# Here's a nice one - parses the first line of a request.
|
13
|
-
|
15
|
+
# The expected format is as follows:
|
16
|
+
# <method> </path>[/][?<query>] HTTP/<version>
|
17
|
+
RequestRegexp = /([A-Za-z0-9]+)\s(\/[^\/\?]*(?:\/[^\/\?]+)*)\/?(?:\?(.*))?\sHTTP\/(.+)\r/.freeze
|
14
18
|
# Regexp for parsing headers.
|
15
19
|
HeaderRegexp = /([^:]+):\s?(.*)\r\n/.freeze
|
16
20
|
ContentLength = 'Content-Length'.freeze
|
@@ -24,9 +28,11 @@ module ServerSide
|
|
24
28
|
StatusClose = "HTTP/1.1 %d\r\nConnection: close\r\nContent-Type: %s\r\n%sContent-Length: %d\r\n\r\n".freeze
|
25
29
|
StatusStream = "HTTP/1.1 %d\r\nConnection: close\r\nContent-Type: %s\r\n%s\r\n".freeze
|
26
30
|
StatusPersist = "HTTP/1.1 %d\r\nContent-Type: %s\r\n%sContent-Length: %d\r\n\r\n".freeze
|
31
|
+
StatusRedirect = "HTTP/1.1 %d\r\nConnection: close\r\nLocation: %s\r\n\r\n".freeze
|
27
32
|
Header = "%s: %s\r\n".freeze
|
28
33
|
Empty = ''.freeze
|
29
34
|
Slash = '/'.freeze
|
35
|
+
Location = 'Location'
|
30
36
|
end
|
31
37
|
|
32
38
|
# This is the base request class. When a new request is created, it starts
|
@@ -35,6 +41,8 @@ module ServerSide
|
|
35
41
|
# Connection::Base is overriden by applications to create
|
36
42
|
# application-specific behavior.
|
37
43
|
class Base
|
44
|
+
include StaticFiles
|
45
|
+
|
38
46
|
# Initializes the request instance. A new thread is created for
|
39
47
|
# processing requests.
|
40
48
|
def initialize(conn)
|
@@ -51,7 +59,11 @@ module ServerSide
|
|
51
59
|
respond
|
52
60
|
break unless @persistent
|
53
61
|
end
|
54
|
-
rescue
|
62
|
+
rescue => e
|
63
|
+
puts '*******************'
|
64
|
+
puts e.message
|
65
|
+
puts e.backtrace.first
|
66
|
+
puts '*******************'
|
55
67
|
# We don't care. Just close the connection.
|
56
68
|
ensure
|
57
69
|
@conn.close
|
@@ -63,7 +75,7 @@ module ServerSide
|
|
63
75
|
# 'Connection' header).
|
64
76
|
def parse_request
|
65
77
|
return nil unless @conn.gets =~ Const::RequestRegexp
|
66
|
-
@method, @path, @query, @version = $1.downcase.to_sym, $2, $
|
78
|
+
@method, @path, @query, @version = $1.downcase.to_sym, $2, $3, $4
|
67
79
|
@parameters = @query ? parse_parameters(@query) : {}
|
68
80
|
@headers = {}
|
69
81
|
while (line = @conn.gets)
|
@@ -106,6 +118,14 @@ module ServerSide
|
|
106
118
|
@persistent = false
|
107
119
|
end
|
108
120
|
|
121
|
+
# Send a redirect response.
|
122
|
+
def redirect(location, permanent = false)
|
123
|
+
@conn << (Const::StatusRedirect % [permanent ? 301 : 302, location])
|
124
|
+
rescue
|
125
|
+
ensure
|
126
|
+
@persistent = false
|
127
|
+
end
|
128
|
+
|
109
129
|
# Streams additional data to the client.
|
110
130
|
def stream(body)
|
111
131
|
(@conn << body if body) rescue (@persistent = false)
|
data/lib/serverside/core_ext.rb
CHANGED
@@ -24,4 +24,19 @@ class Symbol
|
|
24
24
|
def to_s
|
25
25
|
@_to_s || (@_to_s = id2name)
|
26
26
|
end
|
27
|
-
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Proc
|
30
|
+
# Returns a unique proc tag. This method is used by the router.
|
31
|
+
def proc_tag
|
32
|
+
'proc_' + object_id.to_s(36).sub('-', '_')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Object
|
37
|
+
# Returns a unique tag for the object. This method is used by the router.
|
38
|
+
def const_tag
|
39
|
+
'C' + object_id.to_s(36).upcase.sub('-', '_')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module ServerSide
|
2
|
+
module Connection
|
3
|
+
# The Router class defines a kind of connection that can route requests
|
4
|
+
# to different handlers based on rules that can be specified either by
|
5
|
+
# lambdas or by hashes containing keys corresponding to patterns.
|
6
|
+
class Router < Base
|
7
|
+
# Returns true if routes were defined.
|
8
|
+
def self.has_routes?
|
9
|
+
@@rules && !@@rules.empty? rescue false
|
10
|
+
end
|
11
|
+
|
12
|
+
# Adds a routing rule. The normalized rule is a hash containing keys (acting
|
13
|
+
# as instance variable names) with patterns as values. If the rule is not a
|
14
|
+
# hash, it is normalized into a pattern checked against the request path.
|
15
|
+
# Pattern values can also be arrays, in any array member is checked as a pattern.
|
16
|
+
def self.route(rule, &block)
|
17
|
+
@@rules ||= []
|
18
|
+
rule = {:path => rule} unless (Hash === rule) || (Proc === rule)
|
19
|
+
@@rules.unshift [rule, block]
|
20
|
+
compile_rules
|
21
|
+
end
|
22
|
+
|
23
|
+
# Compiles all rules into a respond method that is invoked when a request
|
24
|
+
# is received.
|
25
|
+
def self.compile_rules
|
26
|
+
@@rules ||= []
|
27
|
+
code = @@rules.inject('lambda {') {|m, r| m << rule_to_statement(r[0], r[1])}
|
28
|
+
code << 'default_handler}'
|
29
|
+
define_method(:respond, &eval(code))
|
30
|
+
end
|
31
|
+
|
32
|
+
# Converts a rule into an if statement. All keys in the rule are matched
|
33
|
+
# against their respective values.
|
34
|
+
def self.rule_to_statement(rule, block)
|
35
|
+
proc_tag = define_proc(&block)
|
36
|
+
if Proc === rule
|
37
|
+
cond = define_proc(&rule).to_s
|
38
|
+
else
|
39
|
+
cond = rule.to_a.map {|kv|
|
40
|
+
if Array === kv[1]
|
41
|
+
'(' + kv[1].map {|v| condition_part(kv[0], v)}.join('||') + ')'
|
42
|
+
else
|
43
|
+
condition_part(kv[0], kv[1])
|
44
|
+
end
|
45
|
+
}.join('&&')
|
46
|
+
end
|
47
|
+
"return #{proc_tag} if #{cond}\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Pattern for finding parameters inside patterns. Parameters are parts of the
|
51
|
+
# pattern, which the routing pre-processor turns into sub-regexp that are
|
52
|
+
# used to extract parameter values from the pattern.
|
53
|
+
#
|
54
|
+
# For example, matching '/controller/show' against '/controller/:action' will
|
55
|
+
# give us @parameters[:action] #=> "show"
|
56
|
+
ParamRegexp = /(?::([a-z]+))/
|
57
|
+
|
58
|
+
# Returns the condition part for the key and value specified. The key is the
|
59
|
+
# name of an instance variable and the value is a pattern to match against.
|
60
|
+
# If the pattern contains parameters (for example, /controller/:action,) the
|
61
|
+
# method creates a lambda for extracting the parameter values.
|
62
|
+
def self.condition_part(key, value)
|
63
|
+
p_parse, p_count = '', 0
|
64
|
+
while (String === value) && (value =~ ParamRegexp)
|
65
|
+
value = value.dup
|
66
|
+
p_name = $1
|
67
|
+
p_count += 1
|
68
|
+
value.sub!(ParamRegexp, '(.+)')
|
69
|
+
p_parse << "@parameters[:#{p_name}] = $#{p_count}\n"
|
70
|
+
end
|
71
|
+
cond = "(@#{key} =~ #{cache_constant(Regexp.new(value))})"
|
72
|
+
if p_count == 0
|
73
|
+
cond
|
74
|
+
else
|
75
|
+
tag = define_proc(&eval(
|
76
|
+
"lambda {if #{cond}\n#{p_parse}true\nelse\nfalse\nend}"))
|
77
|
+
"(#{tag})"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Converts a proc into a method, returning the method's name (as a symbol)
|
82
|
+
def self.define_proc(&block)
|
83
|
+
tag = block.proc_tag
|
84
|
+
define_method(tag.to_sym, &block) unless instance_methods.include?(tag)
|
85
|
+
tag.to_sym
|
86
|
+
end
|
87
|
+
|
88
|
+
# Converts a value into a local constant and freezes it. Returns the
|
89
|
+
# constant's tag name
|
90
|
+
def self.cache_constant(value)
|
91
|
+
tag = value.const_tag
|
92
|
+
class_eval "#{tag} = #{value.inspect}.freeze" rescue nil
|
93
|
+
tag
|
94
|
+
end
|
95
|
+
|
96
|
+
# Sets the default handler for incoming requests.
|
97
|
+
def self.route_default(&block)
|
98
|
+
define_method(:default_handler, &block)
|
99
|
+
compile_rules
|
100
|
+
end
|
101
|
+
|
102
|
+
def unhandled
|
103
|
+
send_response(403, 'text', 'No handler found.')
|
104
|
+
end
|
105
|
+
|
106
|
+
alias_method :default_handler, :unhandled
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Adds a routing rule. This is a convenience method.
|
111
|
+
def self.route(rule, &block)
|
112
|
+
Connection::Router.route(rule, &block)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Sets the default request handler. This is a convenience method.
|
116
|
+
def self.route_default(&block)
|
117
|
+
Connection::Router.route_default(&block)
|
118
|
+
end
|
119
|
+
end
|
data/lib/serverside/static.rb
CHANGED
@@ -17,6 +17,7 @@ module ServerSide
|
|
17
17
|
DirListingStart = '<html><head><title>Directory Listing for %s</title></head><body><h2>Directory listing for %s:</h2>'.freeze
|
18
18
|
DirListing = '<a href="%s">%s</a><br/>'.freeze
|
19
19
|
DirListingStop = '</body></html>'.freeze
|
20
|
+
FileNotFound = 'File not found.'.freeze
|
20
21
|
end
|
21
22
|
|
22
23
|
@@mime_types = Hash.new {|h, k| ServerSide::StaticFiles::Const::TextPlain}
|
@@ -66,31 +67,16 @@ module ServerSide
|
|
66
67
|
} + Const::DirListingStop
|
67
68
|
send_response(200, 'text/html', html)
|
68
69
|
end
|
69
|
-
end
|
70
|
-
|
71
|
-
module Connection
|
72
|
-
module Const
|
73
|
-
WD = '.'.freeze
|
74
|
-
FileNotFound = "Couldn't open file %s.".freeze
|
75
|
-
end
|
76
70
|
|
77
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
if File.file?(fn)
|
86
|
-
serve_file(fn)
|
87
|
-
elsif File.directory?(fn)
|
88
|
-
serve_dir(fn)
|
89
|
-
else
|
90
|
-
send_response(404, 'text', Const::FileNotFound % @path)
|
91
|
-
end
|
71
|
+
# Serves static files and directory listings.
|
72
|
+
def serve_static(path)
|
73
|
+
if File.file?(path)
|
74
|
+
serve_file(path)
|
75
|
+
elsif File.directory?(path)
|
76
|
+
serve_dir(path)
|
77
|
+
else
|
78
|
+
send_response(404, 'text', Const::FileNotFound)
|
92
79
|
end
|
93
80
|
end
|
94
81
|
end
|
95
82
|
end
|
96
|
-
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
FileUtils.cd(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
trap('TERM') {exit}
|
7
|
+
|
8
|
+
ServerSide::route(:path => '^/static/:path') {serve_static('.'/@parameters[:path])}
|
9
|
+
ServerSide::route(:path => '/hello$') {send_response(200, 'text', 'Hello world!')}
|
10
|
+
ServerSide.route(:path => '/xml/:flavor/feed.xml') do
|
11
|
+
redirect('http://feeds.feedburner.com/RobbyOnRails')
|
12
|
+
end
|
13
|
+
|
14
|
+
ServerSide::Server.new('0.0.0.0', 8000, ServerSide::Connection::Router)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_helper'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
class StaticServerTest < Test::Unit::TestCase
|
5
|
+
def test_basic
|
6
|
+
ServerSide::route(:path => '^/static/:path') {serve_static('.'/@parameters[:path])}
|
7
|
+
ServerSide::route(:path => '/hello$') {send_response(200, 'text', 'Hello world!')}
|
8
|
+
ServerSide.route(:path => '/xml/:flavor/feed.xml') do
|
9
|
+
redirect('http://feeds.feedburner.com/RobbyOnRails')
|
10
|
+
end
|
11
|
+
|
12
|
+
t = Thread.new {ServerSide::Server.new('0.0.0.0', 17654, ServerSide::Connection::Router)}
|
13
|
+
sleep 0.1
|
14
|
+
|
15
|
+
h = Net::HTTP.new('localhost', 17654)
|
16
|
+
resp, data = h.get('/hello', nil)
|
17
|
+
assert_equal 200, resp.code.to_i
|
18
|
+
assert_equal "Hello world!", data
|
19
|
+
|
20
|
+
h = Net::HTTP.new('localhost', 17654)
|
21
|
+
resp, data = h.get('/static/qqq.zzz', nil)
|
22
|
+
assert_equal 404, resp.code.to_i
|
23
|
+
assert_equal "File not found.", data
|
24
|
+
|
25
|
+
h = Net::HTTP.new('localhost', 17654)
|
26
|
+
resp, data = h.get("/static/#{__FILE__}", nil)
|
27
|
+
assert_equal 200, resp.code.to_i
|
28
|
+
assert_equal IO.read(__FILE__), data
|
29
|
+
assert_equal 'text/plain', resp['Content-Type']
|
30
|
+
# Net::HTTP includes this header in the request, so our server returns
|
31
|
+
# likewise.
|
32
|
+
assert_equal 'close', resp['Connection']
|
33
|
+
|
34
|
+
h = Net::HTTP.new('localhost', 17654)
|
35
|
+
resp, data = h.get('/xml/rss/feed.xml', nil)
|
36
|
+
assert_equal 302, resp.code.to_i
|
37
|
+
assert_equal 'http://feeds.feedburner.com/RobbyOnRails', resp['Location']
|
38
|
+
|
39
|
+
t.exit
|
40
|
+
end
|
41
|
+
end
|