racknga 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,94 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2010 Kouhei Sutou <kou@clear-code.com>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+
19
+ require 'racknga/log_database'
20
+
21
+ module Racknga
22
+ module Middleware
23
+ class Log
24
+ LOGGER_KEY = "racknga.logger"
25
+
26
+ def initialize(application, options={})
27
+ @application = application
28
+ @options = Utils.normalize_options(options || {})
29
+ database_path = @options[:database_path]
30
+ raise ArgumentError, ":database_path is missing" if database_path.nil?
31
+ @database = LogDatabase.new(database_path)
32
+ @logger = Logger.new(@database)
33
+ end
34
+
35
+ def call(environment)
36
+ environment[LOGGER_KEY] = @logger
37
+
38
+ start_time = Time.now
39
+ status, headers, body = @application.call(environment)
40
+ end_time = Time.now
41
+
42
+ request = Rack::Request.new(environment)
43
+ log(start_time, end_time, request, status, headers, body)
44
+
45
+ [status, headers, body]
46
+ end
47
+
48
+ def ensure_database
49
+ @database.ensure_database
50
+ end
51
+
52
+ def close_database
53
+ @database.close_database
54
+ end
55
+
56
+ private
57
+ def log(start_time, end_time, request, status, headers, body)
58
+ request_time = end_time - start_time
59
+ length = headers["Content-Length"] || "-"
60
+ length = "-" if length == "0"
61
+ format = "%s - %s [%s] \"%s %s %s\" %s %s \"%s\" \"%s\" %0.8f"
62
+ message = format % [request.ip || "-",
63
+ request.env["REMOTE_USER"] || "-",
64
+ end_time.dup.utc.strftime("%d/%b/%Y:%H:%M:%S %z"),
65
+ request.request_method,
66
+ request.fullpath,
67
+ request.env["SERVER_PROTOCOL"] || "-",
68
+ status.to_s[0..3],
69
+ length,
70
+ request.env["HTTP_REFERER"] || "-",
71
+ request.user_agent || "-",
72
+ request_time]
73
+ @logger.log("access",
74
+ request.fullpath,
75
+ :message => message,
76
+ :user_agent => request.user_agent,
77
+ :runtime => request_time)
78
+ end
79
+
80
+ class Logger
81
+ def initialize(database)
82
+ @database = database
83
+ @entries = @database.entries
84
+ end
85
+
86
+ def log(tag, path, options={})
87
+ @entries.add(options.merge(:time_stamp => Time.now,
88
+ :tag => tag,
89
+ :path => path))
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,159 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2010 Kouhei Sutou <kou@clear-code.com>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+
19
+ require 'time'
20
+
21
+ module Racknga
22
+ module Middleware
23
+ class Range
24
+ def initialize(application)
25
+ @application = application
26
+ end
27
+
28
+ def call(environment)
29
+ status, headers, body = @application.call(environment)
30
+ return [status, headers, body] if status != 200
31
+
32
+ headers = Rack::Utils::HeaderHash.new(headers)
33
+ headers["Accept-Ranges"] = "bytes"
34
+ request = Rack::Request.new(environment)
35
+ range = request.env["HTTP_RANGE"]
36
+ if range and /\Abytes=(\d*)-(\d*)\z/ =~ range
37
+ first_byte, last_byte = $1, $2
38
+ status, headers, body = apply_range(status, headers, body, request,
39
+ first_byte, last_byte)
40
+ end
41
+ [status, headers.to_hash, body]
42
+ end
43
+
44
+ private
45
+ def apply_range(status, headers, body, request, first_byte, last_byte)
46
+ unless use_range?(request, headers)
47
+ return [status, headers, body]
48
+ end
49
+ length = guess_length(headers, body)
50
+ return [status, headers.to_hash, body] if length.nil?
51
+
52
+ if first_byte.empty? and last_byte.empty?
53
+ headers["Content-Length"] = "0"
54
+ return [Rack::Utils.status_code(:requested_range_not_satisfiable),
55
+ headers,
56
+ []]
57
+ end
58
+
59
+ if last_byte.empty?
60
+ last_byte = length - 1
61
+ else
62
+ last_byte = last_byte.to_i
63
+ end
64
+ if first_byte.empty?
65
+ first_byte = length - last_byte
66
+ last_byte = length - 1
67
+ else
68
+ first_byte = first_byte.to_i
69
+ end
70
+
71
+ byte_range_spec = "#{first_byte}-#{last_byte}/#{length}"
72
+ range_length = last_byte - first_byte + 1
73
+ headers["Content-Range"] = "bytes #{byte_range_spec}"
74
+ headers["Content-Length"] = range_length.to_s
75
+ stream = RangeStream.new(body, first_byte, range_length)
76
+ if body.respond_to?(:to_path)
77
+ def stream.to_path
78
+ @body.to_path
79
+ end
80
+ end
81
+ [Rack::Utils.status_code(:partial_content),
82
+ headers,
83
+ stream]
84
+ end
85
+
86
+ def use_range?(request, headers)
87
+ if_range = request.env["HTTP_IF_RANGE"]
88
+ return true if if_range.nil?
89
+
90
+ if /\A(?:Mo|Tu|We|Th|Fr|Sa|Su)/ =~ if_range
91
+ last_modified = headers["Last-Modified"]
92
+ return false if last_modified.nil?
93
+ begin
94
+ if_range = Time.httpdate(if_range)
95
+ last_modified = Time.httpdate(last_modified)
96
+ rescue ArgumentError
97
+ return true
98
+ end
99
+ if_range == last_modified
100
+ else
101
+ if_range == headers["ETag"]
102
+ end
103
+ end
104
+
105
+ def guess_length(headers, body)
106
+ length = headers["Content-Length"]
107
+ return length.to_i unless length.nil?
108
+ return body.stat.size if body.respond_to?(:stat)
109
+ nil
110
+ end
111
+
112
+ class RangeStream
113
+ def initialize(body, first_byte, length)
114
+ @body = body
115
+ @first_byte = first_byte
116
+ @length = length
117
+ end
118
+
119
+ def each
120
+ if @body.respond_to?(:seek)
121
+ @body.seek(@first_byte)
122
+ start = 0
123
+ else
124
+ start = @first_byte
125
+ end
126
+ rest = @length
127
+
128
+ @body.each do |chunk|
129
+ if chunk.respond_to?(:encoding)
130
+ if chunk.encoding != Encoding::ASCII_8BIT
131
+ chunk = chunk.dup.force_encoding(Encoding::ASCII_8BIT)
132
+ end
133
+ end
134
+
135
+ chunk_size = chunk.size
136
+ if start > 0
137
+ if chunk_size < start
138
+ start -= chunk_size
139
+ next
140
+ else
141
+ chunk = chunk[start..-1]
142
+ chunk_size -= start
143
+ start = 0
144
+ end
145
+ end
146
+ if rest > chunk_size
147
+ yield(chunk)
148
+ rest -= chunk_size
149
+ else
150
+ yield(chunk[0, rest])
151
+ rest -= rest
152
+ end
153
+ break if rest <= 0
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
data/lib/racknga/utils.rb CHANGED
@@ -4,7 +4,8 @@
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
7
- # License version 2.1 as published by the Free Software Foundation.
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
8
9
  #
9
10
  # This library is distributed in the hope that it will be useful,
10
11
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -4,7 +4,8 @@
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
7
- # License version 2.1 as published by the Free Software Foundation.
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
8
9
  #
9
10
  # This library is distributed in the hope that it will be useful,
10
11
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,5 +17,5 @@
16
17
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18
 
18
19
  module Racknga
19
- VERSION = '0.9.0'
20
+ VERSION = '0.9.1'
20
21
  end
@@ -0,0 +1,47 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2010 SHIMODA Hiroshi <shimoda@clear-code.com>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+
19
+ require "groonga"
20
+
21
+ module Groonga
22
+ module WillPaginateAPI
23
+ def total_pages
24
+ n_pages
25
+ end
26
+
27
+ def per_page
28
+ n_records_in_page
29
+ end
30
+
31
+ def total_entries
32
+ n_records
33
+ end
34
+
35
+ def out_of_bounds?
36
+ current_page > total_pages
37
+ end
38
+
39
+ def offset
40
+ (start_offset || 0) - 1
41
+ end
42
+ end
43
+
44
+ module Pagination
45
+ include WillPaginateAPI
46
+ end
47
+ end
data/lib/racknga.rb CHANGED
@@ -4,7 +4,8 @@
4
4
  #
5
5
  # This library is free software; you can redistribute it and/or
6
6
  # modify it under the terms of the GNU Lesser General Public
7
- # License version 2.1 as published by the Free Software Foundation.
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
8
9
  #
9
10
  # This library is distributed in the hope that it will be useful,
10
11
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -15,11 +16,11 @@
15
16
  # License along with this library; if not, write to the Free Software
16
17
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18
 
18
- require 'groonga'
19
19
  require 'rack'
20
20
 
21
21
  require 'racknga/version'
22
22
  require 'racknga/utils'
23
- require 'racknga/middleware/cache'
24
23
  require 'racknga/middleware/deflater'
25
24
  require 'racknga/middleware/exception_notifier'
25
+ require 'racknga/middleware/jsonp'
26
+ require 'racknga/middleware/range'
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (C) 2010 Kouhei Sutou <kou@clear-code.com>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #
18
+ #%# family=auto
19
+ #%# capabilities=autoconf suggest
20
+
21
+ require 'English'
22
+ require 'rubygems'
23
+
24
+ mode = ARGV[0]
25
+
26
+ def passenger_status_path(gem_path)
27
+ File.join(gem_path, "bin", "passenger-status")
28
+ end
29
+
30
+ @label = ENV["label"]
31
+ @pid_file = ENV["pid_file"]
32
+ @ruby = ENV["ruby"] || Gem.ruby
33
+ @gem_path = ((ENV["GEM_HOME"] || '').split(/:/) + Gem.path).find do |path|
34
+ File.exist?(passenger_status_path(path))
35
+ end
36
+
37
+ if @label
38
+ parameter_prefix = /\Apassenger_(?:#{@label}_)?domain_/
39
+ else
40
+ parameter_prefix = /\Apassenger_domain_/
41
+ end
42
+ parameter = File.basename($0).gsub(parameter_prefix, '')
43
+ if /_(sessions|processed|uptime)\z/ =~ parameter
44
+ @domain = $PREMATCH
45
+ @type = $1
46
+ @domain = nil if @domain and @domain.empty?
47
+ else
48
+ @domain = @type = nil
49
+ end
50
+
51
+ def passenger_status
52
+ if @pid_file
53
+ unless File.readable?(@pid_file)
54
+ return [false, "PID file isn't readable: #{@pid_file}"]
55
+ end
56
+ pid = File.read(@pid_file).strip
57
+ else
58
+ pid = nil
59
+ end
60
+ result = `#{@ruby} #{passenger_status_path(@gem_path)} #{pid}`
61
+ [$?.success?, result]
62
+ end
63
+
64
+ def parse_uptime(uptime)
65
+ uptime_in_minutes = 0
66
+ if /\A(?:(?:(\d+)h\s+)?(\d+)m\s+)?(\d+)s\z/ =~ uptime
67
+ hours = $1.to_i
68
+ minutes = $2.to_i
69
+ seconds = $3.to_i
70
+ uptime_in_minutes = minutes + hours * 60
71
+ end
72
+ uptime_in_minutes
73
+ end
74
+
75
+ def extract_domain(path)
76
+ components = path.split(/\//)
77
+ ignore_components = ["current"]
78
+ while ignore_components.include?(components.last)
79
+ components.pop
80
+ end
81
+ components.pop
82
+ end
83
+
84
+ def parse_result(result)
85
+ sections = {}
86
+ section = nil
87
+ domain = nil
88
+ result.each_line do |line|
89
+ case section
90
+ when "Domains"
91
+ case line.chomp
92
+ when /\A(.+):\s*\z/
93
+ path = $1
94
+ domain = extract_domain(path)
95
+ sections[section] << [domain, []]
96
+ when /\A\s+PID:\s+(\d+)\s+
97
+ Sessions:\s+(\d+)\s+
98
+ Processed:\s+(\d+)\s+
99
+ Uptime:\s+(.+)\z/x
100
+ pid = $1.to_i
101
+ sessions = $2.to_i
102
+ processed = $3.to_i
103
+ uptime = parse_uptime($4)
104
+ _domain, processes = sections[section].last
105
+ processes << {
106
+ :pid => pid,
107
+ :sessions => sessions,
108
+ :processed => processed,
109
+ :uptime => uptime
110
+ }
111
+ end
112
+ else
113
+ if /-+\s+(.+)\s+-+/ =~ line
114
+ section = $1
115
+ sections[section] = []
116
+ end
117
+ end
118
+ end
119
+ sections
120
+ end
121
+
122
+ def vlabel
123
+ case @type
124
+ when "sessions"
125
+ "number of processing sessions"
126
+ when "processed"
127
+ "number of processed sessions"
128
+ when "uptime"
129
+ "uptime by minutes"
130
+ else
131
+ "unknown"
132
+ end
133
+ end
134
+
135
+ def config
136
+ success, result = passenger_status
137
+ unless success
138
+ puts result
139
+ exit(false)
140
+ end
141
+
142
+ if @label
143
+ title = "Passenger: #{@label}: #{@type}: #{@domain}"
144
+ else
145
+ title = "Passenger: #{@type}: #{@domain}"
146
+ end
147
+ sections = parse_result(result)
148
+ puts(<<-EOC)
149
+ graph_title #{title}
150
+ graph_category passenger
151
+ graph_info Passenger #{@domain} #{@type}
152
+ graph_vlabel #{vlabel}
153
+
154
+ EOC
155
+ sections["Domains"].each do |domain, processes|
156
+ next if domain != @domain
157
+ processes.sort_by do |attributes|
158
+ attributes[:pid]
159
+ end.each_with_index do |attributes, i|
160
+ puts("#{@type}#{i}.label #{i} (PID #{attributes[:pid]})")
161
+ end
162
+ end
163
+ end
164
+
165
+ def report
166
+ success, result = passenger_status
167
+ unless success
168
+ puts result
169
+ exit(false)
170
+ end
171
+
172
+ sections = parse_result(result)
173
+ sections["Domains"].each do |domain, processes|
174
+ next if domain != @domain
175
+ processes.sort_by do |attributes|
176
+ attributes[:pid]
177
+ end.each_with_index do |attributes, i|
178
+ puts("#{@type}#{i}.value #{attributes[@type.to_sym]}")
179
+ end
180
+ end
181
+ end
182
+
183
+ case mode
184
+ when "auto", "autoconf", "detect"
185
+ success, result = passenger_status
186
+ if success
187
+ puts "yes"
188
+ exit(true)
189
+ else
190
+ puts "no (#{result})"
191
+ exit(false)
192
+ end
193
+ when "suggest"
194
+ success, result = passenger_status
195
+ if success
196
+ sections = parse_result(result)
197
+ domains = sections["Domains"]
198
+ if domains
199
+ domains.each do |domain, processes|
200
+ puts "#{domain}_sessions"
201
+ puts "#{domain}_processed"
202
+ puts "#{domain}_uptime"
203
+ end
204
+ exit(true)
205
+ else
206
+ puts "no domain: #{result.inspect}"
207
+ exit(false)
208
+ end
209
+ else
210
+ puts result
211
+ exit(false)
212
+ end
213
+ else
214
+ if @domain.nil?
215
+ puts "no domain"
216
+ exit(false)
217
+ end
218
+ if @type.nil?
219
+ puts "no type: #{@domain}"
220
+ exit(false)
221
+ end
222
+ case mode
223
+ when "config"
224
+ config
225
+ else
226
+ report
227
+ end
228
+ end