├── Gemfile
├── Gemfile.lock
├── Passengerfile.json
├── README.md
├── config.ru
├── public
├── .gitkeep
├── images
│ ├── bg_hr.png
│ ├── blacktocat.png
│ ├── icon_download.png
│ └── sprite_download.png
├── index.html
└── stylesheets
│ ├── normalize.css
│ ├── pygment_trac.css
│ └── stylesheet.css
└── tmp
└── .gitkeep
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | gem 'websocket'
3 | gem 'rack'
4 | gem 'passenger', '>= 5.0.25', :require => false
5 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | passenger (5.0.25)
5 | rack
6 | rake (>= 0.8.1)
7 | rack (1.6.4)
8 | rake (10.4.2)
9 | websocket (1.1.1)
10 |
11 | PLATFORMS
12 | ruby
13 |
14 | DEPENDENCIES
15 | passenger (>= 5.0.25)
16 | rack
17 | websocket
18 |
19 | BUNDLED WITH
20 | 1.10.6
21 |
--------------------------------------------------------------------------------
/Passengerfile.json:
--------------------------------------------------------------------------------
1 | {
2 | "unlimited_concurrency_paths": ["/websocket"],
3 | "sticky_sessions": true
4 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebSockets on Phusion Passenger
2 |
3 | This application demonstrates WebSocket support in [Phusion Passenger](https://www.phusionpassenger.com/). Passenger supports all major modern web technologies, such as WebSockets, entirely out of the box. You don't have to do anything: WebSocket support just works.
4 |
5 | If you like this demo, please [tweet about it](https://twitter.com/share) or [follow us on Twitter](https://twitter.com/phusion_nl).
6 |
7 | More information about Passenger:
8 |
9 | * [Website](https://www.phusionpassenger.com/)
10 | * [Documentation](https://www.phusionpassenger.com/library/)
11 | * [Support](https://www.phusionpassenger.com/support)
12 | * [Source code](https://github.com/phusion/passenger)
13 | * [Community discussion forum](https://groups.google.com/d/forum/phusion-passenger)
14 | * [Issue tracker](https://github.com/phusion/passenger/issues)
15 |
16 | ## Getting started
17 |
18 | Clone this repository, install the gem bundle and start Passenger Standalone.
19 |
20 | git clone https://github.com/phusion/passenger-ruby-websocket-demo.git
21 | cd passenger-ruby-websocket-demo
22 | bundle install
23 | bundle exec passenger start
24 |
25 | Access the demo application at http://0.0.0.0:3000/ and see it in action.
26 |
27 | If you deploy this demo to production, be sure to enable [sticky sessions](https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_sticky_sessions) in Passenger.
28 |
29 | ## Compatibility
30 |
31 | * This app uses plain Rack, and thus is framework agnostic.
32 | * WebSockets work on Passenger for Nginx and Passenger Standalone. [Apache is currently not supported](https://github.com/phusion/passenger/issues/1202).
33 | * At least version 5.0.25 of Passenger is required.
34 | * Only the RFC 6455 version of the WebSocket protocol is supported.
35 |
36 | ## Tuning Passenger for WebSockets
37 |
38 | WebSockets work great on both the open source variant of Phusion Passenger, as well as on [Phusion Passenger Enterprise](https://www.phusionpassenger.com/enterprise). But you need to tune a few settings. Please refer to the following places in the Passenger Library for more information:
39 |
40 | * [Tuning for Server Sent Events and WebSockets: Passenger + Nginx](https://www.phusionpassenger.com/library/config/nginx/tuning_sse_and_websockets/)
41 | * [Tuning for Server Sent Events and WebSockets: Passenger + Apache](https://www.phusionpassenger.com/library/config/apache/tuning_sse_and_websockets/)
42 | * [Tuning for Server Sent Events and WebSockets: Passenger Standalone](https://www.phusionpassenger.com/library/config/standalone/tuning_sse_and_websockets/)
43 |
44 | This demo already contains tuning parameters for Passenger Standalone inside Passengerfile.json.
45 |
46 | ## Next steps
47 |
48 | * Using WebSockets on Phusion Passenger? [Tweet about us](https://twitter.com/share), [follow us on Twitter](https://twitter.com/phusion_nl) or [fork us on Github](https://github.com/phusion/passenger).
49 | * Having problems? Please post a message at [the community discussion forum](https://groups.google.com/d/forum/phusion-passenger).
50 |
51 | [ ](http://www.phusion.nl/)
52 |
53 | Please enjoy Phusion Passenger, a product by [Phusion](http://www.phusion.nl/). :-)
54 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | require 'websocket'
2 | require 'thread'
3 |
4 | def server_handshake(env)
5 | data = "#{env['REQUEST_METHOD']} #{env['REQUEST_URI']} #{env['SERVER_PROTOCOL']}\r\n"
6 | env.each_pair do |key, val|
7 | if key =~ /^HTTP_(.*)/
8 | name = rack_env_key_to_http_header_name($1)
9 | data << "#{name}: #{val}\r\n"
10 | end
11 | end
12 | data << "\r\n"
13 |
14 | server = WebSocket::Handshake::Server.new
15 | server << data
16 | puts "WebSocket request:"
17 | puts data
18 | puts
19 | puts "WebSocket response:"
20 | puts server
21 | server
22 | end
23 |
24 | def rack_env_key_to_http_header_name(key)
25 | name = key.downcase.gsub('_', '-')
26 | name[0] = name[0].upcase
27 | name.gsub!(/-(.)/) do |chr|
28 | chr.upcase
29 | end
30 | name
31 | end
32 |
33 | def process_input(io, server, input_parser)
34 | done = false
35 |
36 | # Slurp as much input as possible.
37 | begin
38 | while select([io], nil, nil, 0)
39 | input_parser << io.readpartial(1024)
40 | end
41 | rescue EOFError
42 | done = true
43 | end
44 |
45 | # Parse all input into WebSocket frames and process them.
46 | while (frame = input_parser.next)
47 | case frame.type
48 | when :data
49 | send_output(io, server, "Echo: #{frame}\n")
50 | when :close
51 | done = true
52 | end
53 | end
54 |
55 | done
56 | end
57 |
58 | def send_output(io, server, data)
59 | frame = WebSocket::Frame::Outgoing::Server.new(
60 | :version => server.version,
61 | :data => data,
62 | :type => :text)
63 | io.write(frame.to_s)
64 | end
65 |
66 | def serve_websocket(env)
67 | # On every request, hijack the socket using the Rack socket hijacking API
68 | # (http://blog.phusion.nl/2013/01/23/the-new-rack-socket-hijacking-api/),
69 | # then operate on the socket directly to send WebSocket messages.
70 | env['rack.hijack'].call
71 | io = env['rack.hijack_io']
72 | Thread.new do
73 | begin
74 | puts "WebSocket begun"
75 |
76 | # Parse client handshake message and send server handshake response.
77 | server = server_handshake(env)
78 | io.write(server.to_s)
79 |
80 | # Handle input and stream responses.
81 | input_parser = WebSocket::Frame::Incoming::Server.new(:version => server.version)
82 | done = false
83 | while !done
84 | done = process_input(io, server, input_parser)
85 | send_output(io, server, "#{Time.now}\n")
86 | sleep 1
87 | end
88 | rescue Exception => e
89 | STDERR.puts "*** ERROR: #{e} (#{e.class})\n#{e.backtrace.join("\n")}"
90 | ensure
91 | puts "WebSocket finished"
92 | io.close
93 | end
94 | end
95 | end
96 |
97 | def serve_static_file(env)
98 | if env["PATH_INFO"] == "/"
99 | env["PATH_INFO"] = "/index.html"
100 | end
101 | public_dir = File.expand_path(File.dirname(__FILE__)) + "/public"
102 | Rack::File.new(public_dir).call(env)
103 | end
104 |
105 | app = proc do |env|
106 | if env["PATH_INFO"] == "/websocket"
107 | serve_websocket(env)
108 | else
109 | serve_static_file(env)
110 | end
111 | end
112 |
113 | run app
114 |
--------------------------------------------------------------------------------
/public/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phusion/passenger-ruby-websocket-demo/3f321cf26ea5079aafbf99bb482ddc241591cbb4/public/.gitkeep
--------------------------------------------------------------------------------
/public/images/bg_hr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phusion/passenger-ruby-websocket-demo/3f321cf26ea5079aafbf99bb482ddc241591cbb4/public/images/bg_hr.png
--------------------------------------------------------------------------------
/public/images/blacktocat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phusion/passenger-ruby-websocket-demo/3f321cf26ea5079aafbf99bb482ddc241591cbb4/public/images/blacktocat.png
--------------------------------------------------------------------------------
/public/images/icon_download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phusion/passenger-ruby-websocket-demo/3f321cf26ea5079aafbf99bb482ddc241591cbb4/public/images/icon_download.png
--------------------------------------------------------------------------------
/public/images/sprite_download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phusion/passenger-ruby-websocket-demo/3f321cf26ea5079aafbf99bb482ddc241591cbb4/public/images/sprite_download.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Phusion Passenger: Ruby WebSocket demo
11 |
12 |
13 |
14 |
15 |
16 |
29 |
30 |
31 |
32 |
33 | Just a simple timestamp server
34 | In this demo, the server sends the current timestamp over a WebSocket once per second. The server generates raw WebSocket frames and sends them to the client using the Rack socket hijacking API. You can see the received timestamps below.
35 |
41 |
42 | Nothing received from the server yet...
43 |
44 |
45 |
46 |
47 |
48 |
49 |
55 |
56 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/public/stylesheets/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v2.1.3 | MIT License | git.io/normalize */
2 |
3 | /* ==========================================================================
4 | HTML5 display definitions
5 | ========================================================================== */
6 |
7 | /**
8 | * Correct `block` display not defined in IE 8/9.
9 | */
10 |
11 | article,
12 | aside,
13 | details,
14 | figcaption,
15 | figure,
16 | footer,
17 | header,
18 | hgroup,
19 | main,
20 | nav,
21 | section,
22 | summary {
23 | display: block;
24 | }
25 |
26 | /**
27 | * Correct `inline-block` display not defined in IE 8/9.
28 | */
29 |
30 | audio,
31 | canvas,
32 | video {
33 | display: inline-block;
34 | }
35 |
36 | /**
37 | * Prevent modern browsers from displaying `audio` without controls.
38 | * Remove excess height in iOS 5 devices.
39 | */
40 |
41 | audio:not([controls]) {
42 | display: none;
43 | height: 0;
44 | }
45 |
46 | /**
47 | * Address `[hidden]` styling not present in IE 8/9.
48 | * Hide the `template` element in IE, Safari, and Firefox < 22.
49 | */
50 |
51 | [hidden],
52 | template {
53 | display: none;
54 | }
55 |
56 | /* ==========================================================================
57 | Base
58 | ========================================================================== */
59 |
60 | /**
61 | * 1. Set default font family to sans-serif.
62 | * 2. Prevent iOS text size adjust after orientation change, without disabling
63 | * user zoom.
64 | */
65 |
66 | html {
67 | font-family: sans-serif; /* 1 */
68 | -ms-text-size-adjust: 100%; /* 2 */
69 | -webkit-text-size-adjust: 100%; /* 2 */
70 | }
71 |
72 | /**
73 | * Remove default margin.
74 | */
75 |
76 | body {
77 | margin: 0;
78 | }
79 |
80 | /* ==========================================================================
81 | Links
82 | ========================================================================== */
83 |
84 | /**
85 | * Remove the gray background color from active links in IE 10.
86 | */
87 |
88 | a {
89 | background: transparent;
90 | }
91 |
92 | /**
93 | * Address `outline` inconsistency between Chrome and other browsers.
94 | */
95 |
96 | a:focus {
97 | outline: thin dotted;
98 | }
99 |
100 | /**
101 | * Improve readability when focused and also mouse hovered in all browsers.
102 | */
103 |
104 | a:active,
105 | a:hover {
106 | outline: 0;
107 | }
108 |
109 | /* ==========================================================================
110 | Typography
111 | ========================================================================== */
112 |
113 | /**
114 | * Address variable `h1` font-size and margin within `section` and `article`
115 | * contexts in Firefox 4+, Safari 5, and Chrome.
116 | */
117 |
118 | h1 {
119 | font-size: 2em;
120 | margin: 0.67em 0;
121 | }
122 |
123 | /**
124 | * Address styling not present in IE 8/9, Safari 5, and Chrome.
125 | */
126 |
127 | abbr[title] {
128 | border-bottom: 1px dotted;
129 | }
130 |
131 | /**
132 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
133 | */
134 |
135 | b,
136 | strong {
137 | font-weight: bold;
138 | }
139 |
140 | /**
141 | * Address styling not present in Safari 5 and Chrome.
142 | */
143 |
144 | dfn {
145 | font-style: italic;
146 | }
147 |
148 | /**
149 | * Address differences between Firefox and other browsers.
150 | */
151 |
152 | hr {
153 | -moz-box-sizing: content-box;
154 | box-sizing: content-box;
155 | height: 0;
156 | }
157 |
158 | /**
159 | * Address styling not present in IE 8/9.
160 | */
161 |
162 | mark {
163 | background: #ff0;
164 | color: #000;
165 | }
166 |
167 | /**
168 | * Correct font family set oddly in Safari 5 and Chrome.
169 | */
170 |
171 | code,
172 | kbd,
173 | pre,
174 | samp {
175 | font-family: monospace, serif;
176 | font-size: 1em;
177 | }
178 |
179 | /**
180 | * Improve readability of pre-formatted text in all browsers.
181 | */
182 |
183 | pre {
184 | white-space: pre-wrap;
185 | }
186 |
187 | /**
188 | * Set consistent quote types.
189 | */
190 |
191 | q {
192 | quotes: "\201C" "\201D" "\2018" "\2019";
193 | }
194 |
195 | /**
196 | * Address inconsistent and variable font size in all browsers.
197 | */
198 |
199 | small {
200 | font-size: 80%;
201 | }
202 |
203 | /**
204 | * Prevent `sub` and `sup` affecting `line-height` in all browsers.
205 | */
206 |
207 | sub,
208 | sup {
209 | font-size: 75%;
210 | line-height: 0;
211 | position: relative;
212 | vertical-align: baseline;
213 | }
214 |
215 | sup {
216 | top: -0.5em;
217 | }
218 |
219 | sub {
220 | bottom: -0.25em;
221 | }
222 |
223 | /* ==========================================================================
224 | Embedded content
225 | ========================================================================== */
226 |
227 | /**
228 | * Remove border when inside `a` element in IE 8/9.
229 | */
230 |
231 | img {
232 | border: 0;
233 | }
234 |
235 | /**
236 | * Correct overflow displayed oddly in IE 9.
237 | */
238 |
239 | svg:not(:root) {
240 | overflow: hidden;
241 | }
242 |
243 | /* ==========================================================================
244 | Figures
245 | ========================================================================== */
246 |
247 | /**
248 | * Address margin not present in IE 8/9 and Safari 5.
249 | */
250 |
251 | figure {
252 | margin: 0;
253 | }
254 |
255 | /* ==========================================================================
256 | Forms
257 | ========================================================================== */
258 |
259 | /**
260 | * Define consistent border, margin, and padding.
261 | */
262 |
263 | fieldset {
264 | border: 1px solid #c0c0c0;
265 | margin: 0 2px;
266 | padding: 0.35em 0.625em 0.75em;
267 | }
268 |
269 | /**
270 | * 1. Correct `color` not being inherited in IE 8/9.
271 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
272 | */
273 |
274 | legend {
275 | border: 0; /* 1 */
276 | padding: 0; /* 2 */
277 | }
278 |
279 | /**
280 | * 1. Correct font family not being inherited in all browsers.
281 | * 2. Correct font size not being inherited in all browsers.
282 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
283 | */
284 |
285 | button,
286 | input,
287 | select,
288 | textarea {
289 | font-family: inherit; /* 1 */
290 | font-size: 100%; /* 2 */
291 | margin: 0; /* 3 */
292 | }
293 |
294 | /**
295 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in
296 | * the UA stylesheet.
297 | */
298 |
299 | button,
300 | input {
301 | line-height: normal;
302 | }
303 |
304 | /**
305 | * Address inconsistent `text-transform` inheritance for `button` and `select`.
306 | * All other form control elements do not inherit `text-transform` values.
307 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
308 | * Correct `select` style inheritance in Firefox 4+ and Opera.
309 | */
310 |
311 | button,
312 | select {
313 | text-transform: none;
314 | }
315 |
316 | /**
317 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
318 | * and `video` controls.
319 | * 2. Correct inability to style clickable `input` types in iOS.
320 | * 3. Improve usability and consistency of cursor style between image-type
321 | * `input` and others.
322 | */
323 |
324 | button,
325 | html input[type="button"], /* 1 */
326 | input[type="reset"],
327 | input[type="submit"] {
328 | -webkit-appearance: button; /* 2 */
329 | cursor: pointer; /* 3 */
330 | }
331 |
332 | /**
333 | * Re-set default cursor for disabled elements.
334 | */
335 |
336 | button[disabled],
337 | html input[disabled] {
338 | cursor: default;
339 | }
340 |
341 | /**
342 | * 1. Address box sizing set to `content-box` in IE 8/9/10.
343 | * 2. Remove excess padding in IE 8/9/10.
344 | */
345 |
346 | input[type="checkbox"],
347 | input[type="radio"] {
348 | box-sizing: border-box; /* 1 */
349 | padding: 0; /* 2 */
350 | }
351 |
352 | /**
353 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
354 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
355 | * (include `-moz` to future-proof).
356 | */
357 |
358 | input[type="search"] {
359 | -webkit-appearance: textfield; /* 1 */
360 | -moz-box-sizing: content-box;
361 | -webkit-box-sizing: content-box; /* 2 */
362 | box-sizing: content-box;
363 | }
364 |
365 | /**
366 | * Remove inner padding and search cancel button in Safari 5 and Chrome
367 | * on OS X.
368 | */
369 |
370 | input[type="search"]::-webkit-search-cancel-button,
371 | input[type="search"]::-webkit-search-decoration {
372 | -webkit-appearance: none;
373 | }
374 |
375 | /**
376 | * Remove inner padding and border in Firefox 4+.
377 | */
378 |
379 | button::-moz-focus-inner,
380 | input::-moz-focus-inner {
381 | border: 0;
382 | padding: 0;
383 | }
384 |
385 | /**
386 | * 1. Remove default vertical scrollbar in IE 8/9.
387 | * 2. Improve readability and alignment in all browsers.
388 | */
389 |
390 | textarea {
391 | overflow: auto; /* 1 */
392 | vertical-align: top; /* 2 */
393 | }
394 |
395 | /* ==========================================================================
396 | Tables
397 | ========================================================================== */
398 |
399 | /**
400 | * Remove most spacing between table cells.
401 | */
402 |
403 | table {
404 | border-collapse: collapse;
405 | border-spacing: 0;
406 | }
407 |
--------------------------------------------------------------------------------
/public/stylesheets/pygment_trac.css:
--------------------------------------------------------------------------------
1 | .highlight .hll { background-color: #ffffcc }
2 | .highlight { background: #f0f3f3; }
3 | .highlight .c { color: #0099FF; font-style: italic } /* Comment */
4 | .highlight .err { color: #AA0000; background-color: #FFAAAA } /* Error */
5 | .highlight .k { color: #006699; font-weight: bold } /* Keyword */
6 | .highlight .o { color: #555555 } /* Operator */
7 | .highlight .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */
8 | .highlight .cp { color: #009999 } /* Comment.Preproc */
9 | .highlight .c1 { color: #0099FF; font-style: italic } /* Comment.Single */
10 | .highlight .cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */
11 | .highlight .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */
12 | .highlight .ge { font-style: italic } /* Generic.Emph */
13 | .highlight .gr { color: #FF0000 } /* Generic.Error */
14 | .highlight .gh { color: #003300; font-weight: bold } /* Generic.Heading */
15 | .highlight .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */
16 | .highlight .go { color: #AAAAAA } /* Generic.Output */
17 | .highlight .gp { color: #000099; font-weight: bold } /* Generic.Prompt */
18 | .highlight .gs { font-weight: bold } /* Generic.Strong */
19 | .highlight .gu { color: #003300; font-weight: bold } /* Generic.Subheading */
20 | .highlight .gt { color: #99CC66 } /* Generic.Traceback */
21 | .highlight .kc { color: #006699; font-weight: bold } /* Keyword.Constant */
22 | .highlight .kd { color: #006699; font-weight: bold } /* Keyword.Declaration */
23 | .highlight .kn { color: #006699; font-weight: bold } /* Keyword.Namespace */
24 | .highlight .kp { color: #006699 } /* Keyword.Pseudo */
25 | .highlight .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */
26 | .highlight .kt { color: #007788; font-weight: bold } /* Keyword.Type */
27 | .highlight .m { color: #FF6600 } /* Literal.Number */
28 | .highlight .s { color: #CC3300 } /* Literal.String */
29 | .highlight .na { color: #330099 } /* Name.Attribute */
30 | .highlight .nb { color: #336666 } /* Name.Builtin */
31 | .highlight .nc { color: #00AA88; font-weight: bold } /* Name.Class */
32 | .highlight .no { color: #336600 } /* Name.Constant */
33 | .highlight .nd { color: #9999FF } /* Name.Decorator */
34 | .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
35 | .highlight .ne { color: #CC0000; font-weight: bold } /* Name.Exception */
36 | .highlight .nf { color: #CC00FF } /* Name.Function */
37 | .highlight .nl { color: #9999FF } /* Name.Label */
38 | .highlight .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */
39 | .highlight .nt { color: #330099; font-weight: bold } /* Name.Tag */
40 | .highlight .nv { color: #003333 } /* Name.Variable */
41 | .highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */
42 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */
43 | .highlight .mf { color: #FF6600 } /* Literal.Number.Float */
44 | .highlight .mh { color: #FF6600 } /* Literal.Number.Hex */
45 | .highlight .mi { color: #FF6600 } /* Literal.Number.Integer */
46 | .highlight .mo { color: #FF6600 } /* Literal.Number.Oct */
47 | .highlight .sb { color: #CC3300 } /* Literal.String.Backtick */
48 | .highlight .sc { color: #CC3300 } /* Literal.String.Char */
49 | .highlight .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */
50 | .highlight .s2 { color: #CC3300 } /* Literal.String.Double */
51 | .highlight .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */
52 | .highlight .sh { color: #CC3300 } /* Literal.String.Heredoc */
53 | .highlight .si { color: #AA0000 } /* Literal.String.Interpol */
54 | .highlight .sx { color: #CC3300 } /* Literal.String.Other */
55 | .highlight .sr { color: #33AAAA } /* Literal.String.Regex */
56 | .highlight .s1 { color: #CC3300 } /* Literal.String.Single */
57 | .highlight .ss { color: #FFCC33 } /* Literal.String.Symbol */
58 | .highlight .bp { color: #336666 } /* Name.Builtin.Pseudo */
59 | .highlight .vc { color: #003333 } /* Name.Variable.Class */
60 | .highlight .vg { color: #003333 } /* Name.Variable.Global */
61 | .highlight .vi { color: #003333 } /* Name.Variable.Instance */
62 | .highlight .il { color: #FF6600 } /* Literal.Number.Integer.Long */
63 |
64 | .type-csharp .highlight .k { color: #0000FF }
65 | .type-csharp .highlight .kt { color: #0000FF }
66 | .type-csharp .highlight .nf { color: #000000; font-weight: normal }
67 | .type-csharp .highlight .nc { color: #2B91AF }
68 | .type-csharp .highlight .nn { color: #000000 }
69 | .type-csharp .highlight .s { color: #A31515 }
70 | .type-csharp .highlight .sc { color: #A31515 }
71 |
--------------------------------------------------------------------------------
/public/stylesheets/stylesheet.css:
--------------------------------------------------------------------------------
1 | /*******************************************************************************
2 | Slate Theme for GitHub Pages
3 | by Jason Costello, @jsncostello
4 | *******************************************************************************/
5 |
6 | @import url(normalize.css);
7 | @import url(pygment_trac.css);
8 |
9 | /*******************************************************************************
10 | Theme Styles
11 | *******************************************************************************/
12 |
13 | *, *:before, *:after {
14 | -moz-box-sizing: border-box;
15 | -webkit-box-sizing: border-box;
16 | box-sizing: border-box;
17 | }
18 |
19 | body {
20 | color:#373737;
21 | background: #212121;
22 | font-size: 18px;
23 | font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif;
24 | line-height: 1.5;
25 | -webkit-font-smoothing: antialiased;
26 | }
27 |
28 | h1, h2, h3, h4, h5, h6 {
29 | margin: 10px 0;
30 | font-weight: 700;
31 | color:#222222;
32 | font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif;
33 | letter-spacing: -1px;
34 | }
35 |
36 | h1 {
37 | font-size: 36px;
38 | font-weight: 700;
39 | }
40 |
41 | h2 {
42 | padding-bottom: 10px;
43 | font-size: 32px;
44 | background: url('../images/bg_hr.png') repeat-x bottom;
45 | }
46 |
47 | h3 {
48 | font-size: 24px;
49 | }
50 |
51 | h4 {
52 | font-size: 21px;
53 | }
54 |
55 | h5 {
56 | font-size: 18px;
57 | }
58 |
59 | h6 {
60 | font-size: 16px;
61 | }
62 |
63 | p {
64 | font-size: 18px;
65 | margin: 10px 0 15px 0;
66 | }
67 |
68 | footer p {
69 | color: #f2f2f2;
70 | }
71 |
72 | a {
73 | text-decoration: none;
74 | color: #007edf;
75 | text-shadow: none;
76 | }
77 |
78 | a:hover, a:focus {text-decoration: underline;}
79 |
80 | footer a {
81 | color: #F2F2F2;
82 | text-decoration: underline;
83 | }
84 |
85 | em {
86 | font-style: italic;
87 | }
88 |
89 | strong {
90 | font-weight: bold;
91 | }
92 |
93 | img {
94 | position: relative;
95 | margin: 0 auto;
96 | max-width: 739px;
97 | padding: 5px;
98 | margin: 10px 0 10px 0;
99 | /*
100 | border: 1px solid #ebebeb;
101 |
102 | box-shadow: 0 0 5px #ebebeb;
103 | -webkit-box-shadow: 0 0 5px #ebebeb;
104 | -moz-box-shadow: 0 0 5px #ebebeb;
105 | -o-box-shadow: 0 0 5px #ebebeb;
106 | -ms-box-shadow: 0 0 5px #ebebeb;
107 | */
108 | }
109 |
110 | p img {
111 | display: inline;
112 | margin: 0;
113 | padding: 0;
114 | vertical-align: middle;
115 | text-align: center;
116 | border: none;
117 | }
118 |
119 | pre, code {
120 | width: 100%;
121 | color: #222;
122 | background-color: #fff;
123 |
124 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
125 | font-size: 14px;
126 |
127 | border-radius: 2px;
128 | -moz-border-radius: 2px;
129 | -webkit-border-radius: 2px;
130 |
131 | box-shadow: 0 0 10px rgba(0,0,0,.1);
132 | }
133 |
134 | pre {
135 | padding: 10px;
136 | overflow: auto;
137 | }
138 |
139 | code {
140 | padding: 3px;
141 | margin: 0 3px;
142 | }
143 |
144 | pre code {
145 | display: block;
146 | box-shadow: none;
147 | }
148 |
149 | blockquote {
150 | color: #666;
151 | margin: 0 0 20px 2px;
152 | padding-left: 20px;
153 | border-left: 3px solid #bbb;
154 | font-style: italic;
155 | }
156 |
157 | ul, ol, dl {
158 | margin: 0 0 15px 0;
159 | padding-left: 20px;
160 | }
161 |
162 | dl dt {
163 | font-weight: bold;
164 | }
165 |
166 | dl dd {
167 | margin-left: 0;
168 | padding-left: 0;
169 | font-style: italic;
170 | }
171 |
172 | dl p {
173 | padding-left: 20px;
174 | font-style: italic;
175 | }
176 |
177 | hr {
178 | height: 1px;
179 | margin-bottom: 5px;
180 | border: none;
181 | background: url('../images/bg_hr.png') repeat-x center;
182 | }
183 |
184 | table {
185 | border: 1px solid #373737;
186 | margin-bottom: 20px;
187 | text-align: left;
188 | }
189 |
190 | th {
191 | font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif;
192 | padding: 10px;
193 | background: #373737;
194 | color: #fff;
195 | }
196 |
197 | td {
198 | padding: 10px;
199 | border: 1px solid #373737;
200 | }
201 |
202 | form {
203 | background: #f2f2f2;
204 | padding: 20px;
205 | }
206 |
207 | /*******************************************************************************
208 | Full-Width Styles
209 | *******************************************************************************/
210 |
211 | .outer {
212 | width: 100%;
213 | }
214 |
215 | .inner {
216 | position: relative;
217 | max-width: 640px;
218 | padding: 20px 10px;
219 | margin: 0 auto;
220 | }
221 |
222 | #forkme_banner {
223 | display: block;
224 | position: absolute;
225 | top:0;
226 | right: 10px;
227 | z-index: 10;
228 | padding: 10px 50px 10px 10px;
229 | color: #fff;
230 | background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%;
231 | font-weight: 700;
232 | box-shadow: 0 0 10px rgba(0,0,0,.5);
233 | border-bottom-left-radius: 2px;
234 | border-bottom-right-radius: 2px;
235 | }
236 |
237 | #header_wrap {
238 | background: #212121;
239 | }
240 |
241 | #header_wrap .inner {
242 | padding: 50px 10px 30px 10px;
243 | }
244 |
245 | #project_title {
246 | margin: 0;
247 | color: #fff;
248 | font-size: 42px;
249 | font-weight: 700;
250 | text-shadow: #111 0px 0px 10px;
251 | }
252 |
253 | #project_tagline {
254 | color: #fff;
255 | font-size: 24px;
256 | font-weight: 300;
257 | background: none;
258 | text-shadow: #111 0px 0px 10px;
259 | }
260 |
261 | #downloads {
262 | position: absolute;
263 | width: 210px;
264 | z-index: 10;
265 | bottom: -40px;
266 | right: 0;
267 | height: 70px;
268 | background: url('../images/icon_download.png') no-repeat 0% 90%;
269 | }
270 |
271 | .zip_download_link {
272 | display: block;
273 | float: right;
274 | width: 90px;
275 | height:70px;
276 | text-indent: -5000px;
277 | overflow: hidden;
278 | background: url(../images/sprite_download.png) no-repeat bottom left;
279 | }
280 |
281 | .tar_download_link {
282 | display: block;
283 | float: right;
284 | width: 90px;
285 | height:70px;
286 | text-indent: -5000px;
287 | overflow: hidden;
288 | background: url(../images/sprite_download.png) no-repeat bottom right;
289 | margin-left: 10px;
290 | }
291 |
292 | .zip_download_link:hover {
293 | background: url(../images/sprite_download.png) no-repeat top left;
294 | }
295 |
296 | .tar_download_link:hover {
297 | background: url(../images/sprite_download.png) no-repeat top right;
298 | }
299 |
300 | #main_content_wrap {
301 | background: #f2f2f2;
302 | border-top: 1px solid #111;
303 | border-bottom: 1px solid #111;
304 | }
305 |
306 | #main_content {
307 | padding-top: 40px;
308 | }
309 |
310 | #footer_wrap {
311 | background: #212121;
312 | }
313 |
314 | /*******************************************************************************
315 | Small Device Styles
316 | *******************************************************************************/
317 |
318 | @media screen and (max-width: 480px) {
319 | body {
320 | font-size:14px;
321 | }
322 |
323 | #downloads {
324 | display: none;
325 | }
326 |
327 | .inner {
328 | min-width: 320px;
329 | max-width: 480px;
330 | }
331 |
332 | #project_title {
333 | font-size: 32px;
334 | }
335 |
336 | h1 {
337 | font-size: 28px;
338 | }
339 |
340 | h2 {
341 | font-size: 24px;
342 | }
343 |
344 | h3 {
345 | font-size: 21px;
346 | }
347 |
348 | h4 {
349 | font-size: 18px;
350 | }
351 |
352 | h5 {
353 | font-size: 14px;
354 | }
355 |
356 | h6 {
357 | font-size: 12px;
358 | }
359 |
360 | code, pre {
361 | min-width: 320px;
362 | max-width: 480px;
363 | font-size: 11px;
364 | }
365 | }
366 |
--------------------------------------------------------------------------------
/tmp/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/phusion/passenger-ruby-websocket-demo/3f321cf26ea5079aafbf99bb482ddc241591cbb4/tmp/.gitkeep
--------------------------------------------------------------------------------