swee 0.0.1

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.
@@ -0,0 +1,79 @@
1
+ class Object
2
+ def try(*a, &b)
3
+ try!(*a, &b) if a.empty? || respond_to?(a.first)
4
+ end
5
+
6
+ def try!(*a, &b)
7
+ if a.empty? && block_given?
8
+ if b.arity.zero?
9
+ instance_eval(&b)
10
+ else
11
+ yield self
12
+ end
13
+ else
14
+ public_send(*a, &b)
15
+ end
16
+ end
17
+
18
+ def blank?
19
+ respond_to?(:empty?) ? !!empty? : !self
20
+ end
21
+ def present?
22
+ !blank?
23
+ end
24
+
25
+ def presence
26
+ self if present?
27
+ end
28
+ end
29
+
30
+ class NilClass
31
+ def try(*args)
32
+ nil
33
+ end
34
+
35
+ def try!(*args)
36
+ nil
37
+ end
38
+
39
+ def blank?
40
+ true
41
+ end
42
+ end
43
+
44
+ class FalseClass
45
+ def blank?
46
+ true
47
+ end
48
+ end
49
+
50
+ class TrueClass
51
+ def blank?
52
+ false
53
+ end
54
+ end
55
+
56
+ class Array
57
+ alias_method :blank?, :empty?
58
+ end
59
+
60
+ class Hash
61
+ alias_method :blank?, :empty?
62
+ end
63
+
64
+ class String
65
+ BLANK_RE = /\A[[:space:]]*\z/
66
+ def blank?
67
+ BLANK_RE === self
68
+ end
69
+
70
+ def html_safe
71
+ self.gsub /[&"'><]/, { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
72
+ end
73
+ end
74
+
75
+ class Numeric
76
+ def blank?
77
+ false
78
+ end
79
+ end
@@ -0,0 +1,59 @@
1
+ require "fiber"
2
+
3
+ class Swee::SweeLogger
4
+ def initialize
5
+ @logs = Array.new
6
+ @msg = Array.new
7
+ init_fiber
8
+ end
9
+
10
+ def init_fiber
11
+ @fb = Fiber.new { loop_log }
12
+ end
13
+
14
+ def loop_log
15
+ loop do
16
+ each_log
17
+ Fiber.yield
18
+ end
19
+ end
20
+
21
+ def each_log
22
+ while !@msg.empty?
23
+ _msg = @msg.shift
24
+ @logs.each { |_log| _log.debug _msg }
25
+ end
26
+ end
27
+
28
+ def get_binding
29
+ binding
30
+ end
31
+
32
+ def roll!
33
+ each_log
34
+ # Fiber 存在跨线程问题 暂时不用Fiber处理
35
+ # init_fiber if !@fb.alive?
36
+ # @fb.resume
37
+ end
38
+
39
+ def addlog log
40
+ @logs << log
41
+ end
42
+
43
+ def get_io
44
+ @logs.select { |log| log.io? }.first
45
+ end
46
+
47
+ def get_file
48
+ @logs.select { |log| log.file? }.first
49
+ end
50
+
51
+ def logs
52
+ @logs
53
+ end
54
+
55
+ def <<(msg)
56
+ @msg << msg
57
+ roll!
58
+ end
59
+ end
@@ -0,0 +1,40 @@
1
+ module Thin
2
+ # Store HTTP header name-value pairs direcly to a string
3
+ # and allow duplicated entries on some names.
4
+ class Headers
5
+ HEADER_FORMAT = "%s: %s\r\n".freeze
6
+ ALLOWED_DUPLICATES = %w(set-cookie set-cookie2 warning www-authenticate).freeze
7
+
8
+ def initialize
9
+ @sent = {}
10
+ @out = []
11
+ end
12
+
13
+ # Add <tt>key: value</tt> pair to the headers.
14
+ # Ignore if already sent and no duplicates are allowed
15
+ # for this +key+.
16
+ def []=(key, value)
17
+ downcase_key = key.downcase
18
+ if !@sent.has_key?(downcase_key) || ALLOWED_DUPLICATES.include?(downcase_key)
19
+ @sent[downcase_key] = true
20
+ value = case value
21
+ when Time
22
+ value.httpdate
23
+ when NilClass
24
+ return
25
+ else
26
+ value.to_s
27
+ end
28
+ @out << HEADER_FORMAT % [key, value]
29
+ end
30
+ end
31
+
32
+ def has_key?(key)
33
+ @sent[key.downcase]
34
+ end
35
+
36
+ def to_s
37
+ @out.join
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,162 @@
1
+ require 'tempfile'
2
+
3
+ module Thin
4
+ # Raised when an incoming request is not valid
5
+ # and the server can not process it.
6
+ class InvalidRequest < IOError; end
7
+
8
+ # A request sent by the client to the server.
9
+ class Request
10
+ # Maximum request body size before it is moved out of memory
11
+ # and into a tempfile for reading.
12
+ MAX_BODY = 1024 * (80 + 32)
13
+ BODY_TMPFILE = 'thin-body'.freeze
14
+ MAX_HEADER = 1024 * (80 + 32)
15
+
16
+ INITIAL_BODY = ''
17
+ # Force external_encoding of request's body to ASCII_8BIT
18
+ INITIAL_BODY.encode!(Encoding::ASCII_8BIT) if INITIAL_BODY.respond_to?(:encode!)
19
+
20
+ # Freeze some HTTP header names & values
21
+ SERVER_SOFTWARE = 'SERVER_SOFTWARE'.freeze
22
+ SERVER_NAME = 'SERVER_NAME'.freeze
23
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
24
+ LOCALHOST = 'localhost'.freeze
25
+ HTTP_VERSION = 'HTTP_VERSION'.freeze
26
+ HTTP_1_0 = 'HTTP/1.0'.freeze
27
+ REMOTE_ADDR = 'REMOTE_ADDR'.freeze
28
+ CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze
29
+ CONNECTION = 'HTTP_CONNECTION'.freeze
30
+ KEEP_ALIVE_REGEXP = /\bkeep-alive\b/i.freeze
31
+ CLOSE_REGEXP = /\bclose\b/i.freeze
32
+ HEAD = 'HEAD'.freeze
33
+
34
+ # Freeze some Rack header names
35
+ RACK_INPUT = 'rack.input'.freeze
36
+ RACK_VERSION = 'rack.version'.freeze
37
+ RACK_ERRORS = 'rack.errors'.freeze
38
+ RACK_MULTITHREAD = 'rack.multithread'.freeze
39
+ RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
40
+ RACK_RUN_ONCE = 'rack.run_once'.freeze
41
+ ASYNC_CALLBACK = 'async.callback'.freeze
42
+ ASYNC_CLOSE = 'async.close'.freeze
43
+
44
+ # CGI-like request environment variables
45
+ attr_reader :env
46
+
47
+ # Unparsed data of the request
48
+ attr_reader :data
49
+
50
+ # Request body
51
+ attr_reader :body
52
+
53
+ def initialize
54
+ @parser = Thin::HttpParser.new
55
+ @data = ''
56
+ @nparsed = 0
57
+ @body = StringIO.new(INITIAL_BODY.dup)
58
+ @env = {
59
+ # SERVER_SOFTWARE => SERVER,
60
+ SERVER_NAME => LOCALHOST,
61
+
62
+ # Rack stuff
63
+ RACK_INPUT => @body,
64
+
65
+ # RACK_VERSION => VERSION::RACK,
66
+ RACK_ERRORS => STDERR,
67
+
68
+ RACK_MULTITHREAD => false,
69
+ RACK_MULTIPROCESS => false,
70
+ RACK_RUN_ONCE => false
71
+ }
72
+ end
73
+
74
+ # Parse a chunk of data into the request environment
75
+ # Raises an +InvalidRequest+ if invalid.
76
+ # Returns +true+ if the parsing is complete.
77
+ def parse(data)
78
+ if @parser.finished? # Header finished, can only be some more body
79
+ @body << data
80
+ else # Parse more header using the super parser
81
+ @data << data
82
+ raise InvalidRequest, 'Header longer than allowed' if @data.size > MAX_HEADER
83
+
84
+ @nparsed = @parser.execute(@env, @data, @nparsed)
85
+
86
+ # Transfer to a tempfile if body is very big
87
+ move_body_to_tempfile if @parser.finished? && content_length > MAX_BODY
88
+ end
89
+
90
+
91
+ if finished? # Check if header and body are complete
92
+ @data = nil
93
+ @body.rewind
94
+ true # Request is fully parsed
95
+ else
96
+ false # Not finished, need more data
97
+ end
98
+ end
99
+
100
+ # +true+ if headers and body are finished parsing
101
+ def finished?
102
+ @parser.finished? && @body.size >= content_length
103
+ end
104
+
105
+ # Expected size of the body
106
+ def content_length
107
+ @env[CONTENT_LENGTH].to_i
108
+ end
109
+
110
+ # Returns +true+ if the client expects the connection to be persistent.
111
+ def persistent?
112
+ # Clients and servers SHOULD NOT assume that a persistent connection
113
+ # is maintained for HTTP versions less than 1.1 unless it is explicitly
114
+ # signaled. (http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html)
115
+ if @env[HTTP_VERSION] == HTTP_1_0
116
+ @env[CONNECTION] =~ KEEP_ALIVE_REGEXP
117
+
118
+ # HTTP/1.1 client intends to maintain a persistent connection unless
119
+ # a Connection header including the connection-token "close" was sent
120
+ # in the request
121
+ else
122
+ @env[CONNECTION].nil? || @env[CONNECTION] !~ CLOSE_REGEXP
123
+ end
124
+ end
125
+
126
+ def remote_address=(address)
127
+ @env[REMOTE_ADDR] = address
128
+ end
129
+
130
+ def threaded=(value)
131
+ @env[RACK_MULTITHREAD] = value
132
+ end
133
+
134
+ def async_callback=(callback)
135
+ @env[ASYNC_CALLBACK] = callback
136
+ @env[ASYNC_CLOSE] = EventMachine::DefaultDeferrable.new
137
+ end
138
+
139
+ def async_close
140
+ @async_close ||= @env[ASYNC_CLOSE]
141
+ end
142
+
143
+ def head?
144
+ @env[REQUEST_METHOD] == HEAD
145
+ end
146
+
147
+ # Close any resource used by the request
148
+ def close
149
+ @body.close! if @body.class == Tempfile
150
+ end
151
+
152
+ private
153
+ def move_body_to_tempfile
154
+ current_body = @body
155
+ current_body.rewind
156
+ @body = Tempfile.new(BODY_TMPFILE)
157
+ @body.binmode
158
+ @body << current_body.read
159
+ @env[RACK_INPUT] = @body
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,177 @@
1
+ module Thin
2
+
3
+ module VERSION #:nodoc:
4
+ MAJOR = 1
5
+ MINOR = 6
6
+ TINY = 3
7
+
8
+ STRING = [MAJOR, MINOR, TINY].join('.')
9
+
10
+ CODENAME = "Protein Powder".freeze
11
+
12
+ RACK = [1, 0].freeze # Rack protocol version
13
+ end
14
+
15
+ NAME = 'thin'.freeze
16
+ SERVER = "#{NAME} #{VERSION::STRING} codename #{VERSION::CODENAME}".freeze
17
+
18
+ HTTP_STATUS_CODES = {
19
+ 100 => 'Continue',
20
+ 101 => 'Switching Protocols',
21
+ 200 => 'OK',
22
+ 201 => 'Created',
23
+ 202 => 'Accepted',
24
+ 203 => 'Non-Authoritative Information',
25
+ 204 => 'No Content',
26
+ 205 => 'Reset Content',
27
+ 206 => 'Partial Content',
28
+ 300 => 'Multiple Choices',
29
+ 301 => 'Moved Permanently',
30
+ 302 => 'Moved Temporarily',
31
+ 303 => 'See Other',
32
+ 304 => 'Not Modified',
33
+ 305 => 'Use Proxy',
34
+ 400 => 'Bad Request',
35
+ 401 => 'Unauthorized',
36
+ 402 => 'Payment Required',
37
+ 403 => 'Forbidden',
38
+ 404 => 'Not Found',
39
+ 405 => 'Method Not Allowed',
40
+ 406 => 'Not Acceptable',
41
+ 407 => 'Proxy Authentication Required',
42
+ 408 => 'Request Time-out',
43
+ 409 => 'Conflict',
44
+ 410 => 'Gone',
45
+ 411 => 'Length Required',
46
+ 412 => 'Precondition Failed',
47
+ 413 => 'Request Entity Too Large',
48
+ 414 => 'Request-URI Too Large',
49
+ 415 => 'Unsupported Media Type',
50
+ 422 => 'Unprocessable Entity',
51
+ 500 => 'Internal Server Error',
52
+ 501 => 'Not Implemented',
53
+ 502 => 'Bad Gateway',
54
+ 503 => 'Service Unavailable',
55
+ 504 => 'Gateway Time-out',
56
+ 505 => 'HTTP Version not supported'
57
+ }
58
+
59
+ # A response sent to the client.
60
+ class Response
61
+ CONNECTION = 'Connection'.freeze
62
+ CLOSE = 'close'.freeze
63
+ KEEP_ALIVE = 'keep-alive'.freeze
64
+ SERVER = 'Server'.freeze
65
+ CONTENT_LENGTH = 'Content-Length'.freeze
66
+
67
+ PERSISTENT_STATUSES = [100, 101].freeze
68
+
69
+ #Error Responses
70
+ ERROR = [500, {'Content-Type' => 'text/plain'}, ['Internal server error']].freeze
71
+ PERSISTENT_ERROR = [500, {'Content-Type' => 'text/plain', 'Connection' => 'keep-alive', 'Content-Length' => "21"}, ['Internal server error']].freeze
72
+ BAD_REQUEST = [400, {'Content-Type' => 'text/plain'}, ['Bad Request']].freeze
73
+
74
+ # Status code
75
+ attr_accessor :status
76
+
77
+ # Response body, must respond to +each+.
78
+ attr_accessor :body
79
+
80
+ # Headers key-value hash
81
+ attr_reader :headers
82
+
83
+ def initialize
84
+ @headers = Headers.new
85
+ @status = 200
86
+ @persistent = false
87
+ @skip_body = false
88
+ end
89
+
90
+ # String representation of the headers
91
+ # to be sent in the response.
92
+ def headers_output
93
+ # Set default headers
94
+ @headers[CONNECTION] = persistent? ? KEEP_ALIVE : CLOSE unless @headers.has_key?(CONNECTION)
95
+ @headers[SERVER] = Thin::NAME unless @headers.has_key?(SERVER)
96
+
97
+ @headers.to_s
98
+ end
99
+
100
+ # Top header of the response,
101
+ # containing the status code and response headers.
102
+ def head
103
+ "HTTP/1.1 #{@status} #{HTTP_STATUS_CODES[@status.to_i]}\r\n#{headers_output}\r\n"
104
+ end
105
+
106
+ # if Thin.ruby_18?
107
+
108
+ # # Ruby 1.8 implementation.
109
+ # # Respects Rack specs.
110
+ # #
111
+ # # See http://rack.rubyforge.org/doc/files/SPEC.html
112
+ # def headers=(key_value_pairs)
113
+ # key_value_pairs.each do |k, vs|
114
+ # vs.each { |v| @headers[k] = v.chomp } if vs
115
+ # end if key_value_pairs
116
+ # end
117
+
118
+ # else
119
+
120
+ # Ruby 1.9 doesn't have a String#each anymore.
121
+ # Rack spec doesn't take care of that yet, for now we just use
122
+ # +each+ but fallback to +each_line+ on strings.
123
+ # I wish we could remove that condition.
124
+ # To be reviewed when a new Rack spec comes out.
125
+ def headers=(key_value_pairs)
126
+ key_value_pairs.each do |k, vs|
127
+ next unless vs
128
+ if vs.is_a?(Integer)
129
+ vs = vs.to_s
130
+ end
131
+ if vs.is_a?(String)
132
+ vs.each_line { |v| @headers[k] = v.chomp }
133
+ else
134
+ vs.each { |v| @headers[k] = v.chomp }
135
+ end
136
+ end if key_value_pairs
137
+ end
138
+
139
+ # end
140
+
141
+ # Close any resource used by the response
142
+ def close
143
+ @body.close if @body.respond_to?(:close)
144
+ end
145
+
146
+ # Yields each chunk of the response.
147
+ # To control the size of each chunk
148
+ # define your own +each+ method on +body+.
149
+ def each
150
+ yield head
151
+
152
+ unless @skip_body
153
+ if @body.is_a?(String)
154
+ yield @body
155
+ else
156
+ @body.each { |chunk| yield chunk }
157
+ end
158
+ end
159
+ end
160
+
161
+ # Tell the client the connection should stay open
162
+ def persistent!
163
+ @persistent = true
164
+ end
165
+
166
+ # Persistent connection must be requested as keep-alive
167
+ # from the server and have a Content-Length, or the response
168
+ # status must require that the connection remain open.
169
+ def persistent?
170
+ (@persistent && @headers.has_key?(CONTENT_LENGTH)) || PERSISTENT_STATUSES.include?(@status)
171
+ end
172
+
173
+ def skip_body!
174
+ @skip_body = true
175
+ end
176
+ end
177
+ end