net-http-server 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,151 @@
1
+ require 'net/protocol'
2
+ require 'time'
3
+
4
+ module Net
5
+ class HTTP < Protocol
6
+ module Server
7
+ module Responses
8
+ # The supported HTTP Protocol.
9
+ HTTP_VERSION = '1.1'
10
+
11
+ # The known HTTP Status codes and messages
12
+ HTTP_STATUSES = {
13
+ # 1xx
14
+ 100 => 'Continue',
15
+ 101 => 'Switching Protocols',
16
+ 102 => 'Processing',
17
+ # 2xx
18
+ 200 => 'OK',
19
+ 201 => 'Created',
20
+ 202 => 'Accepted',
21
+ 203 => 'Non-Authoritative Information',
22
+ 204 => 'No Content',
23
+ 205 => 'Reset Content',
24
+ 206 => 'Partial Content',
25
+ # 3xx
26
+ 300 => 'Multiple Choices',
27
+ 301 => 'Moved Permanently',
28
+ 302 => 'Found',
29
+ 303 => 'See Other',
30
+ 304 => 'Not Modified',
31
+ 305 => 'Use Proxy',
32
+ 307 => 'Temporary Redirect',
33
+ # 4xx
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
+ 416 => 'Requested range not satisfiable',
51
+ 417 => 'Expectation Failed',
52
+ # 5xx
53
+ 500 => 'Internal Server Error',
54
+ 501 => 'Not Implemented',
55
+ 502 => 'Bad Gateway',
56
+ 503 => 'Service Unavailable',
57
+ 504 => 'Gateway Time-out',
58
+ 505 => 'HTTP Version not supported extension-code'
59
+ }
60
+
61
+ # Generic Bad Request response
62
+ BAD_REQUEST = [400, {}, ['Bad Request']]
63
+
64
+ protected
65
+
66
+ #
67
+ # Writes the status of an HTTP Response to a stream.
68
+ #
69
+ # @param [IO] stream
70
+ # The stream to write the headers back to.
71
+ #
72
+ # @param [Integer] status
73
+ # The status of the HTTP Response.
74
+ #
75
+ def write_status(stream,status)
76
+ status = status.to_i
77
+
78
+ reason = HTTP_STATUSES[status]
79
+ stream.write("HTTP/#{HTTP_VERSION} #{status} #{reason}\r\n")
80
+ end
81
+
82
+ #
83
+ # Write the headers of an HTTP Response to a stream.
84
+ #
85
+ # @param [IO] stream
86
+ # The stream to write the headers back to.
87
+ #
88
+ # @param [Hash{String => [String, Time, Array<String>}] headers
89
+ # The headers of the HTTP Response.
90
+ #
91
+ def write_headers(stream,headers)
92
+ headers.each do |name,values|
93
+ case values
94
+ when String
95
+ values.each_line("\n") do |value|
96
+ stream.write("#{name}: #{value.chomp}\r\n")
97
+ end
98
+ when Time
99
+ stream.write("#{name}: #{values.httpdate}\r\n")
100
+ when Array
101
+ values.each do |value|
102
+ stream.write("#{name}: #{value}\r\n")
103
+ end
104
+ end
105
+ end
106
+
107
+ stream.write("\r\n")
108
+ stream.flush
109
+ end
110
+
111
+ #
112
+ # Writes the body of a HTTP Response to a stream.
113
+ #
114
+ # @param [IO] stream
115
+ # The stream to write the headers back to.
116
+ #
117
+ # @param [#each] body
118
+ # The body of the HTTP Response.
119
+ #
120
+ def write_body(stream,body)
121
+ body.each do |chunk|
122
+ stream.write(chunk)
123
+ stream.flush
124
+ end
125
+ end
126
+
127
+ #
128
+ # Writes a HTTP Response to a stream.
129
+ #
130
+ # @param [IO] stream
131
+ # The stream to write the HTTP Response to.
132
+ #
133
+ # @param [Integer] status
134
+ # The status of the HTTP Response.
135
+ #
136
+ # @param [Hash{String => [String, Time, Array<String>}] headers
137
+ # The headers of the HTTP Response.
138
+ #
139
+ # @param [#each] body
140
+ # The body of the HTTP Response.
141
+ #
142
+ def write_response(stream,status,headers,body)
143
+ write_status stream, status
144
+ write_headers stream, headers
145
+ write_body stream, body
146
+ end
147
+
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,49 @@
1
+ require 'net/http/server/daemon'
2
+
3
+ require 'net/protocol'
4
+
5
+ module Net
6
+ class HTTP < Protocol
7
+ module Server
8
+ #
9
+ # Starts the HTTP Server.
10
+ #
11
+ # @param [Hash] options
12
+ # Options for the server.
13
+ #
14
+ # @option options [String] :host (DEFAULT_HOST)
15
+ # The host to run on.
16
+ #
17
+ # @option options [String] :port (DEFAULT_PORT)
18
+ # The port to listen on.
19
+ #
20
+ # @option options [Integer] :max_connections (MAX_CONNECTIONS)
21
+ # The maximum number of simultaneous connections.
22
+ #
23
+ # @option options [Boolean] :background (false)
24
+ # Specifies whether to run the server in the background or
25
+ # foreground.
26
+ #
27
+ # @option options [#call] :handler
28
+ # The HTTP Request Handler object.
29
+ #
30
+ # @yield [request, socket]
31
+ # If a block is given, it will be used to process HTTP Requests.
32
+ #
33
+ # @yieldparam [Hash{Symbol => String,Array,Hash}] request
34
+ # The HTTP Request.
35
+ #
36
+ # @yieldparam [TCPSocket] socket
37
+ # The TCP socket of the client.
38
+ #
39
+ def Server.run(options={},&block)
40
+ daemon = Daemon.new(options,&block)
41
+
42
+ daemon.start
43
+ daemon.join unless options[:background]
44
+ return daemon
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,10 @@
1
+ require 'net/protocol'
2
+
3
+ module Net
4
+ class HTTP < Protocol
5
+ module Server
6
+ # net-http-server version.
7
+ VERSION = '0.1.0'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,170 @@
1
+ require 'net/http/server/daemon'
2
+ require 'net/http/server/version'
3
+
4
+ require 'rack'
5
+ require 'set'
6
+
7
+ module Rack
8
+ module Handler
9
+ #
10
+ # A Rack handler for {Net::HTTP::Server}.
11
+ #
12
+ class HTTP
13
+
14
+ # The default environment settings.
15
+ DEFAULT_ENV = {
16
+ 'rack.version' => Rack::VERSION,
17
+ 'rack.errors' => STDERR,
18
+ 'rack.multithread' => true,
19
+ 'rack.multiprocess' => false,
20
+ 'rack.run_once' => false,
21
+ 'rack.url_scheme' => 'http',
22
+
23
+ 'SERVER_SOFTWARE' => "Net::HTTP::Server/#{Net::HTTP::Server::VERSION} (Ruby/#{RUBY_VERSION}/#{RUBY_RELEASE_DATE})",
24
+ 'SCRIPT_NAME' => ''
25
+ }
26
+
27
+ # Special HTTP Headers used by Rack::Request
28
+ SPECIAL_HEADERS = Set[
29
+ 'Content-Type',
30
+ 'Content-Length',
31
+ ]
32
+
33
+ #
34
+ # Initializes the handler.
35
+ #
36
+ # @param [#call] app
37
+ # The application the handler will be passing requests to.
38
+ #
39
+ # @param [Hash] options
40
+ # Additional options.
41
+ #
42
+ # @option options [String] :Host
43
+ # The host to bind to.
44
+ #
45
+ # @option options [Integer] :Port
46
+ # The port to listen on.
47
+ #
48
+ def initialize(app,options={})
49
+ @app = app
50
+ @options = options
51
+
52
+ @server = nil
53
+ end
54
+
55
+ #
56
+ # Creates a new handler and begins handling HTTP Requests.
57
+ #
58
+ # @see #initialize
59
+ #
60
+ def self.run(app,options={})
61
+ new(app,options).run
62
+ end
63
+
64
+ #
65
+ # Starts {Net::HTTP::Server} and begins handling HTTP Requests.
66
+ #
67
+ def run
68
+ @server = Net::HTTP::Server::Daemon.new(
69
+ :host => @options[:Host],
70
+ :port => @options[:Port],
71
+ :handler => self
72
+ )
73
+
74
+ @server.start
75
+ @server.join
76
+ end
77
+
78
+ #
79
+ # Handles an HTTP Request.
80
+ #
81
+ # @param [Hash] request
82
+ # An HTTP Request received from {Net::HTTP::Server}.
83
+ #
84
+ # @param [TCPSocket] socket
85
+ # The socket that the request was received from.
86
+ #
87
+ # @return [Array<Integer, Hash, Array>]
88
+ # The response status, headers and body.
89
+ #
90
+ def call(request,socket)
91
+ request_uri = request[:uri]
92
+ remote_address = socket.remote_address
93
+ local_address = socket.local_address
94
+
95
+ env = {}
96
+
97
+ # add the default values
98
+ env.merge!(DEFAULT_ENV)
99
+
100
+ # populate
101
+ env['rack.input'] = socket
102
+
103
+ if request_uri[:scheme]
104
+ env['rack.url_scheme'] = request_uri[:scheme]
105
+ end
106
+
107
+ env['SERVER_NAME'] = local_address.getnameinfo[0]
108
+ env['SERVER_PORT'] = local_address.ip_port.to_s
109
+ env['SERVER_PROTOCOL'] = "HTTP/#{request[:http_version]}"
110
+
111
+ env['REMOTE_ADDR'] = remote_address.ip_address
112
+ env['REMOTE_PORT'] = remote_address.ip_port.to_s
113
+
114
+ env['REQUEST_METHOD'] = request[:method]
115
+ env['PATH_INFO'] = request_uri.fetch(:path,'*')
116
+ env['QUERY_STRING'] = request_uri[:query_string].to_s
117
+
118
+ # add the headers
119
+ request[:headers].each do |name,value|
120
+ key = name.dup
121
+
122
+ key.upcase!
123
+ key.tr!('-','_')
124
+
125
+ # if the header is not special, prepend 'HTTP_'
126
+ unless SPECIAL_HEADERS.include?(name)
127
+ key.insert(0,'HTTP_')
128
+ end
129
+
130
+ env[key] = case value
131
+ when Array
132
+ value.join("\n")
133
+ else
134
+ value.to_s
135
+ end
136
+ end
137
+
138
+ @app.call(env)
139
+ end
140
+
141
+ #
142
+ # Determines if the handler is running.
143
+ #
144
+ # @return [Boolean]
145
+ # Specifies whether the handler is still running.
146
+ #
147
+ def running?
148
+ @server && !(@server.stopped?)
149
+ end
150
+
151
+ #
152
+ # Determines whether the handler was stopped.
153
+ #
154
+ # @return [Boolean]
155
+ # Specifies whether the handler was previously stopped.
156
+ #
157
+ def stopped?
158
+ @server.nil? || @server.stopped?
159
+ end
160
+
161
+ #
162
+ # Stops the handler.
163
+ #
164
+ def stop
165
+ @server.stop if @server
166
+ end
167
+
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,10 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ begin
4
+ Ore::Specification.new do |gemspec|
5
+ # custom logic here
6
+ end
7
+ rescue NameError
8
+ STDERR.puts "The 'net-http-server.gemspec' file requires Ore."
9
+ STDERR.puts "Run `gem install ore-core` to install Ore."
10
+ end
@@ -0,0 +1,69 @@
1
+ require 'net/http'
2
+ require 'yaml'
3
+
4
+ # Borrowed from Rack's own specs.
5
+ class TestRequest
6
+ def call(env)
7
+ status = env["QUERY_STRING"] =~ /secret/ ? 403 : 200
8
+ env["test.postdata"] = env["rack.input"].read
9
+ body = env.to_yaml
10
+ size = body.respond_to?(:bytesize) ? body.bytesize : body.size
11
+ [status, {"Content-Type" => "text/yaml", "Content-Length" => size.to_s}, [body]]
12
+ end
13
+
14
+ module Helpers
15
+ attr_reader :status, :response
16
+
17
+ ROOT = File.expand_path(File.dirname(__FILE__) + "/..")
18
+ ENV["RUBYOPT"] = "-I#{ROOT}/lib -rubygems"
19
+
20
+ def root
21
+ ROOT
22
+ end
23
+
24
+ def rackup
25
+ "#{ROOT}/bin/rackup"
26
+ end
27
+
28
+ def GET(path, header={})
29
+ Net::HTTP.start(@host, @port) { |http|
30
+ user = header.delete(:user)
31
+ passwd = header.delete(:passwd)
32
+
33
+ get = Net::HTTP::Get.new(path, header)
34
+ get.basic_auth user, passwd if user && passwd
35
+ http.request(get) { |response|
36
+ @status = response.code.to_i
37
+ if response.content_type == "text/yaml"
38
+ load_yaml(response)
39
+ else
40
+ @response = response
41
+ end
42
+ }
43
+ }
44
+ end
45
+
46
+ def POST(path, formdata={}, header={})
47
+ Net::HTTP.start(@host, @port) { |http|
48
+ user = header.delete(:user)
49
+ passwd = header.delete(:passwd)
50
+
51
+ post = Net::HTTP::Post.new(path, header)
52
+ post.form_data = formdata
53
+ post.basic_auth user, passwd if user && passwd
54
+ http.request(post) { |response|
55
+ @status = response.code.to_i
56
+ @response = YAML.load(response.body)
57
+ }
58
+ }
59
+ end
60
+
61
+ def load_yaml(response)
62
+ begin
63
+ @response = YAML.load(response.body)
64
+ rescue ArgumentError
65
+ @response = nil
66
+ end
67
+ end
68
+ end
69
+ end