serverside 0.2.6 → 0.2.7

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.
Files changed (43) hide show
  1. data/CHANGELOG +22 -16
  2. data/Rakefile +11 -4
  3. data/bin/serverside +1 -1
  4. data/doc/rdoc/classes/Daemon.html +4 -4
  5. data/doc/rdoc/classes/ServerSide.html +10 -10
  6. data/doc/rdoc/classes/ServerSide/HTTP.html +5 -6
  7. data/doc/rdoc/classes/ServerSide/HTTP/Connection.html +24 -22
  8. data/doc/rdoc/classes/ServerSide/HTTP/Request.html +367 -121
  9. data/doc/rdoc/classes/ServerSide/Router.html +58 -58
  10. data/doc/rdoc/classes/ServerSide/StaticFiles.html +60 -49
  11. data/doc/rdoc/classes/ServerSide/StaticFiles/Const.html +12 -2
  12. data/doc/rdoc/classes/ServerSide/Template.html +12 -12
  13. data/doc/rdoc/created.rid +1 -1
  14. data/doc/rdoc/files/CHANGELOG.html +36 -31
  15. data/doc/rdoc/files/COPYING.html +1 -1
  16. data/doc/rdoc/files/README.html +1 -1
  17. data/doc/rdoc/files/lib/serverside/application_rb.html +1 -1
  18. data/doc/rdoc/files/lib/serverside/cluster_rb.html +1 -1
  19. data/doc/rdoc/files/lib/serverside/connection_rb.html +1 -1
  20. data/doc/rdoc/files/lib/serverside/core_ext_rb.html +1 -1
  21. data/doc/rdoc/files/lib/serverside/daemon_rb.html +1 -1
  22. data/doc/rdoc/files/lib/serverside/request_rb.html +8 -1
  23. data/doc/rdoc/files/lib/serverside/routing_rb.html +1 -1
  24. data/doc/rdoc/files/lib/serverside/server_rb.html +1 -1
  25. data/doc/rdoc/files/lib/serverside/static_rb.html +8 -1
  26. data/doc/rdoc/files/lib/serverside/template_rb.html +1 -1
  27. data/doc/rdoc/files/lib/serverside_rb.html +1 -1
  28. data/doc/rdoc/fr_class_index.html +0 -1
  29. data/doc/rdoc/fr_method_index.html +21 -20
  30. data/lib/serverside/connection.rb +6 -4
  31. data/lib/serverside/request.rb +101 -61
  32. data/lib/serverside/static.rb +22 -7
  33. data/test/functional/request_body_test.rb +93 -0
  34. data/test/functional/routing_server.rb +5 -5
  35. data/test/functional/routing_server_test.rb +3 -3
  36. data/test/spec/connection_spec.rb +61 -0
  37. data/test/spec/core_ext_spec.rb +20 -1
  38. data/test/unit/connection_test.rb +9 -9
  39. data/test/unit/request_test.rb +55 -55
  40. data/test/unit/server_test.rb +1 -1
  41. data/test/unit/static_test.rb +24 -25
  42. metadata +60 -60
  43. data/doc/rdoc/classes/ServerSide/HTTP/Const.html +0 -257
data/doc/rdoc/created.rid CHANGED
@@ -1 +1 @@
1
- Sat Sep 30 23:13:26 IDT 2006
1
+ Wed Oct 11 10:16:55 IST 2006
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Sat Sep 30 23:07:59 IDT 2006</td>
59
+ <td>Wed Oct 11 10:16:23 IST 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -69,9 +69,38 @@
69
69
  <div id="contextContent">
70
70
 
71
71
  <div id="description">
72
- <p>
73
- <b>SVN</b>
74
- </p>
72
+ <h2>0.2.7</h2>
73
+ <ul>
74
+ <li>Wrote spec for HTTP::Connection.
75
+
76
+ </li>
77
+ <li>Added spec files to rake stats reporting.
78
+
79
+ </li>
80
+ <li>Changed @conn to @socket in both HTTP::Connection and HTTP::Request for
81
+ better readability.
82
+
83
+ </li>
84
+ <li>Wrote functional test for request body (but at least some of the testing
85
+ should be in a unit test.)
86
+
87
+ </li>
88
+ <li>Added request body parsing (both URL-encoded and multipart.)
89
+
90
+ </li>
91
+ <li>Moved all HTTP::Const constants into HTTP::Request.
92
+
93
+ </li>
94
+ <li>Added Date to response headers. This is needed for caching to work
95
+ correctly.
96
+
97
+ </li>
98
+ <li>Fixed bug in serverside script that caused an exception when specifying
99
+ port number.
100
+
101
+ </li>
102
+ </ul>
103
+ <h2>0.2.6</h2>
75
104
  <ul>
76
105
  <li>Refactored HTTP-related code into a new HTTP::Request class and a
77
106
  simplified HTTP::Connection.
@@ -88,9 +117,7 @@ the docs.
88
117
 
89
118
  </li>
90
119
  </ul>
91
- <p>
92
- *0.2.5*
93
- </p>
120
+ <h2>0.2.5</h2>
94
121
  <ul>
95
122
  <li>Added template serving to static file module.
96
123
 
@@ -124,9 +151,7 @@ test task to include spec and rcov tasks.
124
151
 
125
152
  </li>
126
153
  </ul>
127
- <p>
128
- *0.2.0*
129
- </p>
154
+ <h2>0.2.0</h2>
130
155
  <ul>
131
156
  <li>Updated RFuzz script to work, but it doesn&#8217;t still do anything
132
157
  interesting.
@@ -228,38 +253,21 @@ core_ext.
228
253
  </li>
229
254
  <li>Added HTTP parsing code with unit tests.
230
255
 
231
- </li>
232
- <li>Started adding request code.
233
-
234
256
  </li>
235
257
  <li>More unit tests for application code.
236
258
 
237
259
  </li>
238
260
  <li>Basic server code works with unit tests.
239
261
 
240
- </li>
241
- <li>Started work on application code.
242
-
243
262
  </li>
244
263
  <li>Added option parsing to serverside script.
245
264
 
246
265
  </li>
247
266
  <li>Added daemon code and unit tests.
248
267
 
249
- </li>
250
- <li>Created serverside script.
251
-
252
- </li>
253
- <li>Created Gem spec.
254
-
255
- </li>
256
- <li>Created directory structure.
257
-
258
268
  </li>
259
269
  </ul>
260
- <p>
261
- *Mongrel-based branch*
262
- </p>
270
+ <h2>0.1</h2>
263
271
  <ul>
264
272
  <li>Added host attribute to Controller::Request.
265
273
 
@@ -311,9 +319,6 @@ core_ext.
311
319
  </li>
312
320
  <li>Implemented daemon and server cluster.
313
321
 
314
- </li>
315
- <li>Basic configuration infrastructure is ready.
316
-
317
322
  </li>
318
323
  </ul>
319
324
 
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Thu Aug 17 18:37:26 IDT 2006</td>
59
+ <td>Wed Aug 16 11:52:20 IDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Sat Sep 30 23:10:14 IDT 2006</td>
59
+ <td>Sat Sep 30 23:36:42 IDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Sat Sep 30 23:04:45 IDT 2006</td>
59
+ <td>Sat Sep 30 23:36:41 IDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Thu Aug 24 18:47:24 IDT 2006</td>
59
+ <td>Mon Aug 21 10:29:28 IDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Sat Sep 30 23:06:05 IDT 2006</td>
59
+ <td>Sun Oct 08 08:43:13 IST 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Fri Aug 25 16:38:37 IDT 2006</td>
59
+ <td>Sun Aug 27 10:06:32 IDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Mon Aug 21 01:03:58 IDT 2006</td>
59
+ <td>Mon Aug 28 10:38:28 IDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Sat Sep 30 22:55:32 IDT 2006</td>
59
+ <td>Sun Oct 08 08:44:21 IST 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -69,6 +69,13 @@
69
69
  <div id="contextContent">
70
70
 
71
71
 
72
+ <div id="requires-list">
73
+ <h3 class="section-bar">Required files</h3>
74
+
75
+ <div class="name-list">
76
+ time&nbsp;&nbsp;
77
+ </div>
78
+ </div>
72
79
 
73
80
  </div>
74
81
 
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Sat Sep 30 22:47:12 IDT 2006</td>
59
+ <td>Sat Sep 30 23:36:41 IDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Sat Sep 30 23:04:12 IDT 2006</td>
59
+ <td>Sat Sep 30 23:36:41 IDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Sat Sep 30 22:55:21 IDT 2006</td>
59
+ <td>Sun Oct 08 08:55:40 IST 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -69,6 +69,13 @@
69
69
  <div id="contextContent">
70
70
 
71
71
 
72
+ <div id="requires-list">
73
+ <h3 class="section-bar">Required files</h3>
74
+
75
+ <div class="name-list">
76
+ time&nbsp;&nbsp;
77
+ </div>
78
+ </div>
72
79
 
73
80
  </div>
74
81
 
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Mon Sep 04 18:10:56 IDT 2006</td>
59
+ <td>Sun Sep 03 11:33:15 IDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Thu Aug 24 18:47:25 IDT 2006</td>
59
+ <td>Mon Aug 21 10:57:08 IDT 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -31,7 +31,6 @@
31
31
  <a href="classes/ServerSide/Application.html">ServerSide::Application</a><br />
32
32
  <a href="classes/ServerSide/HTTP.html">ServerSide::HTTP</a><br />
33
33
  <a href="classes/ServerSide/HTTP/Connection.html">ServerSide::HTTP::Connection</a><br />
34
- <a href="classes/ServerSide/HTTP/Const.html">ServerSide::HTTP::Const</a><br />
35
34
  <a href="classes/ServerSide/HTTP/Request.html">ServerSide::HTTP::Request</a><br />
36
35
  <a href="classes/ServerSide/HTTP/Server.html">ServerSide::HTTP::Server</a><br />
37
36
  <a href="classes/ServerSide/Router.html">ServerSide::Router</a><br />
@@ -21,55 +21,56 @@
21
21
  <h1 class="section-bar">Methods</h1>
22
22
  <div id="index-entries">
23
23
  <a href="classes/String.html#M000006">/ (String)</a><br />
24
- <a href="classes/ServerSide/Router.html#M000049">cache_constant (ServerSide::Router)</a><br />
25
- <a href="classes/ServerSide/Router.html#M000045">compile_rules (ServerSide::Router)</a><br />
26
- <a href="classes/ServerSide/Router.html#M000047">condition_part (ServerSide::Router)</a><br />
24
+ <a href="classes/ServerSide/Router.html#M000050">cache_constant (ServerSide::Router)</a><br />
25
+ <a href="classes/ServerSide/Router.html#M000046">compile_rules (ServerSide::Router)</a><br />
26
+ <a href="classes/ServerSide/Router.html#M000048">condition_part (ServerSide::Router)</a><br />
27
27
  <a href="classes/ServerSide/Application.html#M000026">config= (ServerSide::Application)</a><br />
28
28
  <a href="classes/Object.html#M000003">const_tag (Object)</a><br />
29
29
  <a href="classes/Daemon.html#M000007">control (Daemon)</a><br />
30
30
  <a href="classes/Daemon/Cluster.html#M000016">daemon_loop (Daemon::Cluster)</a><br />
31
31
  <a href="classes/ServerSide/Application.html#M000027">daemonize (ServerSide::Application)</a><br />
32
- <a href="classes/ServerSide/Router.html#M000052">default_handler (ServerSide::Router)</a><br />
33
- <a href="classes/ServerSide/Router.html#M000048">define_proc (ServerSide::Router)</a><br />
32
+ <a href="classes/ServerSide/Router.html#M000053">default_handler (ServerSide::Router)</a><br />
33
+ <a href="classes/ServerSide/Router.html#M000049">define_proc (ServerSide::Router)</a><br />
34
34
  <a href="classes/Daemon/Cluster/PidFile.html#M000019">delete (Daemon::Cluster::PidFile)</a><br />
35
- <a href="classes/ServerSide/HTTP/Request.html#M000038">delete_cookie (ServerSide::HTTP::Request)</a><br />
35
+ <a href="classes/ServerSide/HTTP/Request.html#M000039">delete_cookie (ServerSide::HTTP::Request)</a><br />
36
36
  <a href="classes/Daemon/Cluster.html#M000013">fork_server (Daemon::Cluster)</a><br />
37
- <a href="classes/ServerSide/Router.html#M000043">has_routes? (ServerSide::Router)</a><br />
37
+ <a href="classes/ServerSide/Router.html#M000044">has_routes? (ServerSide::Router)</a><br />
38
38
  <a href="classes/ServerSide/HTTP/Request.html#M000029">new (ServerSide::HTTP::Request)</a><br />
39
+ <a href="classes/ServerSide/HTTP/Connection.html#M000040">new (ServerSide::HTTP::Connection)</a><br />
39
40
  <a href="classes/ServerSide/HTTP/Server.html#M000028">new (ServerSide::HTTP::Server)</a><br />
40
- <a href="classes/ServerSide/HTTP/Connection.html#M000039">new (ServerSide::HTTP::Connection)</a><br />
41
41
  <a href="classes/ServerSide/HTTP/Request.html#M000031">parse (ServerSide::HTTP::Request)</a><br />
42
+ <a href="classes/ServerSide/HTTP/Request.html#M000034">parse_body (ServerSide::HTTP::Request)</a><br />
42
43
  <a href="classes/ServerSide/HTTP/Request.html#M000033">parse_cookies (ServerSide::HTTP::Request)</a><br />
43
44
  <a href="classes/ServerSide/HTTP/Request.html#M000032">parse_parameters (ServerSide::HTTP::Request)</a><br />
44
45
  <a href="classes/Daemon/Base.html#M000012">pid_fn (Daemon::Base)</a><br />
45
46
  <a href="classes/Proc.html#M000002">proc_tag (Proc)</a><br />
46
47
  <a href="classes/ServerSide/HTTP/Request.html#M000030">process (ServerSide::HTTP::Request)</a><br />
47
- <a href="classes/ServerSide/HTTP/Connection.html#M000040">process (ServerSide::HTTP::Connection)</a><br />
48
+ <a href="classes/ServerSide/HTTP/Connection.html#M000041">process (ServerSide::HTTP::Connection)</a><br />
48
49
  <a href="classes/Daemon/PidFile.html#M000011">recall (Daemon::PidFile)</a><br />
49
50
  <a href="classes/Daemon/Cluster/PidFile.html#M000021">recall_pids (Daemon::Cluster::PidFile)</a><br />
50
- <a href="classes/ServerSide/HTTP/Request.html#M000035">redirect (ServerSide::HTTP::Request)</a><br />
51
- <a href="classes/ServerSide/Template.html#M000042">render (ServerSide::Template)</a><br />
52
- <a href="classes/ServerSide/Router.html#M000044">route (ServerSide::Router)</a><br />
53
- <a href="classes/ServerSide/Router.html#M000050">route_default (ServerSide::Router)</a><br />
54
- <a href="classes/ServerSide/Router.html#M000046">rule_to_statement (ServerSide::Router)</a><br />
55
- <a href="classes/ServerSide/HTTP/Request.html#M000034">send_response (ServerSide::HTTP::Request)</a><br />
51
+ <a href="classes/ServerSide/HTTP/Request.html#M000036">redirect (ServerSide::HTTP::Request)</a><br />
52
+ <a href="classes/ServerSide/Template.html#M000043">render (ServerSide::Template)</a><br />
53
+ <a href="classes/ServerSide/Router.html#M000045">route (ServerSide::Router)</a><br />
54
+ <a href="classes/ServerSide/Router.html#M000051">route_default (ServerSide::Router)</a><br />
55
+ <a href="classes/ServerSide/Router.html#M000047">rule_to_statement (ServerSide::Router)</a><br />
56
+ <a href="classes/ServerSide/HTTP/Request.html#M000035">send_response (ServerSide::HTTP::Request)</a><br />
56
57
  <a href="classes/ServerSide/StaticFiles.html#M000023">serve_dir (ServerSide::StaticFiles)</a><br />
57
58
  <a href="classes/ServerSide/StaticFiles.html#M000022">serve_file (ServerSide::StaticFiles)</a><br />
58
59
  <a href="classes/ServerSide/StaticFiles.html#M000025">serve_static (ServerSide::StaticFiles)</a><br />
59
60
  <a href="classes/ServerSide/StaticFiles.html#M000024">serve_template (ServerSide::StaticFiles)</a><br />
60
- <a href="classes/ServerSide/Template.html#M000041">set (ServerSide::Template)</a><br />
61
- <a href="classes/ServerSide/HTTP/Request.html#M000037">set_cookie (ServerSide::HTTP::Request)</a><br />
61
+ <a href="classes/ServerSide/Template.html#M000042">set (ServerSide::Template)</a><br />
62
+ <a href="classes/ServerSide/HTTP/Request.html#M000038">set_cookie (ServerSide::HTTP::Request)</a><br />
62
63
  <a href="classes/Daemon.html#M000008">start (Daemon)</a><br />
63
64
  <a href="classes/Daemon/Cluster.html#M000017">start (Daemon::Cluster)</a><br />
64
65
  <a href="classes/Daemon/Cluster.html#M000014">start_servers (Daemon::Cluster)</a><br />
65
- <a href="classes/Daemon/Cluster.html#M000018">stop (Daemon::Cluster)</a><br />
66
66
  <a href="classes/Daemon.html#M000009">stop (Daemon)</a><br />
67
+ <a href="classes/Daemon/Cluster.html#M000018">stop (Daemon::Cluster)</a><br />
67
68
  <a href="classes/Daemon/Cluster.html#M000015">stop_servers (Daemon::Cluster)</a><br />
68
69
  <a href="classes/Daemon/PidFile.html#M000010">store (Daemon::PidFile)</a><br />
69
70
  <a href="classes/Daemon/Cluster/PidFile.html#M000020">store_pid (Daemon::Cluster::PidFile)</a><br />
70
- <a href="classes/ServerSide/HTTP/Request.html#M000036">stream (ServerSide::HTTP::Request)</a><br />
71
+ <a href="classes/ServerSide/HTTP/Request.html#M000037">stream (ServerSide::HTTP::Request)</a><br />
71
72
  <a href="classes/Symbol.html#M000001">to_s (Symbol)</a><br />
72
- <a href="classes/ServerSide/Router.html#M000051">unhandled (ServerSide::Router)</a><br />
73
+ <a href="classes/ServerSide/Router.html#M000052">unhandled (ServerSide::Router)</a><br />
73
74
  <a href="classes/String.html#M000004">uri_escape (String)</a><br />
74
75
  <a href="classes/String.html#M000005">uri_unescape (String)</a><br />
75
76
  </div>
@@ -10,8 +10,8 @@ module ServerSide
10
10
  class Connection
11
11
  # Initializes the request instance. A new thread is created for
12
12
  # processing requests.
13
- def initialize(conn, request_class)
14
- @conn, @request_class = conn, request_class
13
+ def initialize(socket, request_class)
14
+ @socket, @request_class = socket, request_class
15
15
  @thread = Thread.new {process}
16
16
  end
17
17
 
@@ -20,12 +20,14 @@ module ServerSide
20
20
  # is closed.
21
21
  def process
22
22
  while true
23
- break unless @request_class.new(@conn).process
23
+ # the process function is expected to return true or a non-nil value
24
+ # if the connection is to persist.
25
+ break unless @request_class.new(@socket).process
24
26
  end
25
27
  rescue => e
26
28
  # We don't care. Just close the connection.
27
29
  ensure
28
- @conn.close
30
+ @socket.close
29
31
  end
30
32
  end
31
33
  end
@@ -1,58 +1,59 @@
1
1
  require File.join(File.dirname(__FILE__), 'static')
2
+ require 'time'
2
3
 
3
4
  module ServerSide
4
5
  module HTTP
5
- # A bunch of frozen constants to make the parsing of requests and rendering
6
- # of responses faster than otherwise.
7
- module Const
8
- LineBreak = "\r\n".freeze
9
- # Here's a nice one - parses the first line of a request.
10
- # The expected format is as follows:
11
- # <method> </path>[/][?<query>] HTTP/<version>
12
- RequestRegexp = /([A-Za-z0-9]+)\s(\/[^\/\?]*(?:\/[^\/\?]+)*)\/?(?:\?(.*))?\sHTTP\/(.+)\r/.freeze
13
- # Regexp for parsing headers.
14
- HeaderRegexp = /([^:]+):\s?(.*)\r\n/.freeze
15
- ContentLength = 'Content-Length'.freeze
16
- Version_1_1 = '1.1'.freeze
17
- Connection = 'Connection'.freeze
18
- Close = 'close'.freeze
19
- Ampersand = '&'.freeze
20
- # Regexp for parsing URI parameters.
21
- ParameterRegexp = /(.+)=(.*)/.freeze
22
- EqualSign = '='.freeze
23
- StatusClose = "HTTP/1.1 %d\r\nConnection: close\r\nContent-Type: %s\r\n%s%sContent-Length: %d\r\n\r\n".freeze
24
- StatusStream = "HTTP/1.1 %d\r\nConnection: close\r\nContent-Type: %s\r\n%s%s\r\n".freeze
25
- StatusPersist = "HTTP/1.1 %d\r\nContent-Type: %s\r\n%s%sContent-Length: %d\r\n\r\n".freeze
26
- StatusRedirect = "HTTP/1.1 %d\r\nConnection: close\r\nLocation: %s\r\n\r\n".freeze
27
- Header = "%s: %s\r\n".freeze
28
- EmptyString = ''.freeze
29
- EmptyHash = {}.freeze
30
- Slash = '/'.freeze
31
- Location = 'Location'.freeze
32
- Cookie = 'Cookie'
33
- SetCookie = "Set-Cookie: %s=%s; path=/; expires=%s\r\n".freeze
34
- CookieSplit = /[;,] */n.freeze
35
- CookieRegexp = /\s*(.+)=(.*)\s*/.freeze
36
- CookieExpiredTime = Time.at(0).freeze
37
- end
38
-
39
- # The HTTPRequest class encapsulates HTTP requests. The request class
6
+ # The Request class encapsulates HTTP requests. The request class
40
7
  # contains methods for parsing the request and rendering a response.
41
8
  # HTTP requests are created by the connection. Descendants of HTTPRequest
42
9
  # can be created
43
10
  # When a connection is created, it creates new requests in a loop until
44
11
  # the connection is closed.
45
12
  class Request
13
+
14
+ LINE_BREAK = "\r\n".freeze
15
+ # Here's a nice one - parses the first line of a request.
16
+ # The expected format is as follows:
17
+ # <method> </path>[/][?<query>] HTTP/<version>
18
+ REQUEST_REGEXP = /([A-Za-z0-9]+)\s(\/[^\/\?]*(?:\/[^\/\?]+)*)\/?(?:\?(.*))?\sHTTP\/(.+)\r/.freeze
19
+ # Regexp for parsing headers.
20
+ HEADER_REGEXP = /([^:]+):\s?(.*)\r\n/.freeze
21
+ CONTENT_LENGTH = 'Content-Length'.freeze
22
+ VERSION_1_1 = '1.1'.freeze
23
+ CONNECTION = 'Connection'.freeze
24
+ CLOSE = 'close'.freeze
25
+ AMPERSAND = '&'.freeze
26
+ # Regexp for parsing URI parameters.
27
+ PARAMETER_REGEXP = /(.+)=(.*)/.freeze
28
+ EQUAL_SIGN = '='.freeze
29
+ STATUS_CLOSE = "HTTP/1.1 %d\r\nDate: %s\r\nConnection: close\r\nContent-Type: %s\r\n%s%sContent-Length: %d\r\n\r\n".freeze
30
+ STATUS_STREAM = "HTTP/1.1 %d\r\nDate: %s\r\nConnection: close\r\nContent-Type: %s\r\n%s%s\r\n".freeze
31
+ STATUS_PERSIST = "HTTP/1.1 %d\r\nDate: %s\r\nContent-Type: %s\r\n%s%sContent-Length: %d\r\n\r\n".freeze
32
+ STATUS_REDIRECT = "HTTP/1.1 %d\r\nDate: %s\r\nConnection: close\r\nLocation: %s\r\n\r\n".freeze
33
+ HEADER = "%s: %s\r\n".freeze
34
+ EMPTY_STRING = ''.freeze
35
+ EMPTY_HASH = {}.freeze
36
+ SLASH = '/'.freeze
37
+ LOCATION = 'Location'.freeze
38
+ COOKIE = 'Cookie'
39
+ SET_COOKIE = "Set-Cookie: %s=%s; path=/; expires=%s\r\n".freeze
40
+ COOKIE_SPLIT = /[;,] */n.freeze
41
+ COOKIE_REGEXP = /\s*(.+)=(.*)\s*/.freeze
42
+ COOKIE_EXPIRED_TIME = Time.at(0).freeze
43
+ CONTENT_TYPE = "Content-Type".freeze
44
+ CONTENT_TYPE_URL_ENCODED = 'application/x-www-form-urlencoded'.freeze
45
+
46
46
  include StaticFiles
47
47
 
48
- attr_reader :conn, :method, :path, :query, :version, :parameters,
49
- :headers, :persistent, :cookies, :response_cookies
48
+ attr_reader :socket, :method, :path, :query, :version, :parameters,
49
+ :headers, :persistent, :cookies, :response_cookies, :body,
50
+ :content_length, :content_type
50
51
 
51
52
  # Initializes the request instance. Any descendants of HTTP::Request
52
- # which override the initialize method must receive conn as the
53
- # single argument, and copy it to @conn.
54
- def initialize(conn)
55
- @conn = conn
53
+ # which override the initialize method must receive socket as the
54
+ # single argument, and copy it to @socket.
55
+ def initialize(socket)
56
+ @socket = socket
56
57
  end
57
58
 
58
59
  # Processes the request by parsing it and then responding.
@@ -65,29 +66,35 @@ module ServerSide
65
66
  # connection is persistent (by checking the HTTP version and the
66
67
  # 'Connection' header).
67
68
  def parse
68
- return nil unless @conn.gets =~ Const::RequestRegexp
69
+ return nil unless @socket.gets =~ REQUEST_REGEXP
69
70
  @method, @path, @query, @version = $1.downcase.to_sym, $2, $3, $4
70
71
  @parameters = @query ? parse_parameters(@query) : {}
71
72
  @headers = {}
72
- while (line = @conn.gets)
73
- break if line.nil? || (line == Const::LineBreak)
74
- if line =~ Const::HeaderRegexp
73
+ while (line = @socket.gets)
74
+ break if line.nil? || (line == LINE_BREAK)
75
+ if line =~ HEADER_REGEXP
75
76
  @headers[$1.freeze] = $2.freeze
76
77
  end
77
78
  end
78
- @persistent = (@version == Const::Version_1_1) &&
79
- (@headers[Const::Connection] != Const::Close)
80
- @cookies = @headers[Const::Cookie] ? parse_cookies : Const::EmptyHash
79
+ @persistent = (@version == VERSION_1_1) &&
80
+ (@headers[CONNECTION] != CLOSE)
81
+ @cookies = @headers[COOKIE] ? parse_cookies : EMPTY_HASH
81
82
  @response_cookies = nil
82
83
 
84
+ if @content_length = @headers[CONTENT_LENGTH].to_i
85
+ @content_type = @headers[CONTENT_TYPE] || CONTENT_TYPE_URL_ENCODED
86
+ @body = @socket.read(@content_length) rescue nil
87
+ parse_body
88
+ end
89
+
83
90
  @headers
84
91
  end
85
92
 
86
93
  # Parses query parameters by splitting the query string and unescaping
87
94
  # parameter values.
88
95
  def parse_parameters(query)
89
- query.split(Const::Ampersand).inject({}) do |m, i|
90
- if i =~ Const::ParameterRegexp
96
+ query.split(AMPERSAND).inject({}) do |m, i|
97
+ if i =~ PARAMETER_REGEXP
91
98
  m[$1.to_sym] = $2.uri_unescape
92
99
  end
93
100
  m
@@ -96,35 +103,68 @@ module ServerSide
96
103
 
97
104
  # Parses cookie values passed in the request
98
105
  def parse_cookies
99
- @headers[Const::Cookie].split(Const::CookieSplit).inject({}) do |m, i|
100
- if i =~ Const::CookieRegexp
106
+ @headers[COOKIE].split(COOKIE_SPLIT).inject({}) do |m, i|
107
+ if i =~ COOKIE_REGEXP
101
108
  m[$1.to_sym] = $2.uri_unescape
102
109
  end
103
110
  m
104
111
  end
105
112
  end
113
+
114
+ MULTIPART_REGEXP = /multipart\/form-data.*boundary=\"?([^\";,]+)/n.freeze
115
+ CONTENT_DISPOSITION_REGEXP = /^Content-Disposition: form-data;([^\r]*)/m.freeze
116
+ FIELD_ATTRIBUTE_REGEXP = /\s*(\w+)=\"([^\"]*)/.freeze
117
+ CONTENT_TYPE_REGEXP = /^Content-Type: ([^\r]*)/m.freeze
118
+
119
+ # parses the body, either by using
120
+ def parse_body
121
+ if @content_type == CONTENT_TYPE_URL_ENCODED
122
+ @parameters.merge! parse_parameters(@body)
123
+ elsif @content_type =~ MULTIPART_REGEXP
124
+ boundary = "--#$1"
125
+ @body.split(/(?:\r?\n|\A)#{Regexp::quote(boundary)}(?:--)?\r\n/m).each do |pt|
126
+ headers, payload = pt.split("\r\n\r\n", 2)
127
+ atts = {}
128
+ if headers =~ CONTENT_DISPOSITION_REGEXP
129
+ $1.split(';').map {|part|
130
+ if part =~ FIELD_ATTRIBUTE_REGEXP
131
+ atts[$1.to_sym] = $2
132
+ end
133
+ }
134
+ end
135
+ if headers =~ CONTENT_TYPE_REGEXP
136
+ atts[:type] = $1
137
+ end
138
+ if name = atts[:name]
139
+ atts[:content] = payload
140
+ @parameters[name.to_sym] = atts[:filename] ? atts : atts[:content]
141
+ end
142
+ end
143
+ end
144
+ end
106
145
 
107
146
  # Sends an HTTP response.
108
147
  def send_response(status, content_type, body = nil, content_length = nil,
109
148
  headers = nil)
110
149
  h = headers ?
111
- headers.inject('') {|m, kv| m << (Const::Header % kv)} : ''
150
+ headers.inject('') {|m, kv| m << (HEADER % kv)} : ''
112
151
 
113
152
  content_length = body.length if content_length.nil? && body
114
153
  @persistent = false if content_length.nil?
115
154
 
116
155
  # Select the right format to use according to circumstances.
117
- @conn << ((@persistent ? Const::StatusPersist :
118
- (body ? Const::StatusClose : Const::StatusStream)) %
119
- [status, content_type, h, @response_cookies, content_length])
120
- @conn << body if body
156
+ @socket << ((@persistent ? STATUS_PERSIST :
157
+ (body ? STATUS_CLOSE : STATUS_STREAM)) %
158
+ [status, Time.now.httpdate, content_type, h, @response_cookies, content_length])
159
+ @socket << body if body
121
160
  rescue
122
161
  @persistent = false
123
162
  end
124
163
 
125
164
  # Send a redirect response.
126
165
  def redirect(location, permanent = false)
127
- @conn << (Const::StatusRedirect % [permanent ? 301 : 302, location])
166
+ @socket << (STATUS_REDIRECT %
167
+ [permanent ? 301 : 302, Time.now.httpdate, location])
128
168
  rescue
129
169
  ensure
130
170
  @persistent = false
@@ -132,18 +172,18 @@ module ServerSide
132
172
 
133
173
  # Streams additional data to the client.
134
174
  def stream(body)
135
- (@conn << body if body) rescue (@persistent = false)
175
+ (@socket << body if body) rescue (@persistent = false)
136
176
  end
137
177
 
138
178
  # Sets a cookie to be included in the response.
139
179
  def set_cookie(name, value, expires)
140
180
  @response_cookies ||= ""
141
- @response_cookies << (Const::SetCookie % [name, value.to_s.uri_escape, expires.rfc2822])
181
+ @response_cookies << (SET_COOKIE % [name, value.to_s.uri_escape, expires.rfc2822])
142
182
  end
143
183
 
144
184
  # Marks a cookie as deleted. The cookie is given an expires stamp in the past.
145
185
  def delete_cookie(name)
146
- set_cookie(name, nil, Const::CookieExpiredTime)
186
+ set_cookie(name, nil, COOKIE_EXPIRED_TIME)
147
187
  end
148
188
  end
149
189
  end