icaprb-server 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c29df2175284d6033e5651bfa7cd4c31b4b22a8b
4
+ data.tar.gz: eb49631c989580e438dbe18a8f8b64e2f2e5121c
5
+ SHA512:
6
+ metadata.gz: c9172a8ea5df5842fab74ed6d2b21ad1b72380882d36b8d1602ca0ec50655c576b89fad64ba6fe061dcfc7554a13c23c43e9d0f5a0fb769f18c63f0d563ce5f1
7
+ data.tar.gz: 6d8f06a8a04715fe4f3469ae060fe9975694119c5c5914b328b39bb7e3cdc731ab35238c2d0486ef9dee6e83a85ea78e1326aa8417f208c73c913772ce880f93
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /html/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in icaprb-server.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ Copyright (c) 2016, Fabian Franz
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
+
8
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9
+
10
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
11
+
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # ICAPrb::Server
2
+
3
+ Welcome to my projekt. I tried to implement the server part of the ICAP communication in [Ruby](https://www.ruby-lang.org/de/),
4
+ so the server configuration can load different things and the code may be changed at runtime (for example if you want a high uptime).
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'icaprb-server'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install icaprb-server
21
+
22
+ ## Usage
23
+
24
+ Getting your server running is really easy:
25
+
26
+ ```ruby
27
+ # require it
28
+ require "icaprb/server"
29
+
30
+ # create an instance of a server
31
+ s = ICAPrb::Server::ICAPServer.new
32
+
33
+ # add your services
34
+ s.services['echo'] = ICAPrb::Server::Services::EchoService.new
35
+
36
+ # and run it
37
+ s.run
38
+
39
+ ```
40
+
41
+ ## Development
42
+
43
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
44
+
45
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
46
+
47
+ ## Contributing
48
+
49
+ Bug reports and pull requests are welcome on GitHub at https://github.com/fabianfrz/ICAPrb-Server, however I may not have enough time to provide fixes soon.
50
+
data/README.rdoc ADDED
@@ -0,0 +1,34 @@
1
+ Welcome to my ICAP server
2
+ =========================
3
+
4
+ This server has been written as a semester project by Fabian Franz.
5
+ It is fully written in ruby, so it should run on any implementation of ruby in any version >= 2.0.0.
6
+
7
+ It is really easy to use it and create your own ICAP service and make it available for your proxy.
8
+
9
+ First of all you need an instance of ICAPrb::Server::ICAPServer, which does not need any parameters but can take a
10
+ hostname and a port, as well as some options which are requred for setting up logging or TLS.
11
+ The default is localhost on port 1344 which usually results in a binding of [::1]:1344 as IPv6
12
+ is preferred on modern systems.
13
+
14
+ When you created your server, you will have to add some +services+ by writing an instance of them in the services
15
+ property of the server, with the name as key.
16
+
17
+ When all your services are added to the server, you can simply start it by calling the "run" method.
18
+ Note, that run will +block+ the process and will run until the process is interrupted (for example by pressing ctrl + c).
19
+
20
+ ---
21
+
22
+ You can write your own services by extending the ServiceBase class which is in ICAPrb::Server::Services.
23
+ Please look at the documentation of this class or just copy the EchoService and use it as a base for your own service.
24
+
25
+ ---
26
+
27
+ Note: ICAP is a mighty tool and it can do good and evil things.
28
+ It could be illegal to use this kind of software in your country.
29
+ You are encuraged to check if this software and the services running on it are legal
30
+ and don't use this software in case it is not.
31
+
32
+ ---
33
+
34
+ If you want to know more about ICAP, you should read the RFC 3507.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require 'rdoc/task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ RDoc::Task.new do |rdoc|
8
+ rdoc.main = 'README.rdoc'
9
+ rdoc.rdoc_files.include('README.rdoc', 'lib/**/*.rb')
10
+ end
11
+
12
+ desc 'search for undocumented things'
13
+ task :not_documented do
14
+ sh 'rdoc --dry-run -V 2> /dev/null | grep "(undocumented)" | sed -E "s/^\\s+//g"'
15
+ end
16
+
17
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "icaprb/server"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+ include ICAPrb::Server
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,36 @@
1
+ #
2
+ require "bundler/setup"
3
+ require "icaprb/server"
4
+ require 'logger'
5
+ include ICAPrb::Server
6
+
7
+ trap('SIGINT') { exit! 0 }
8
+ ########################################################################
9
+ # DIFFERENT WAYS TO RUN THE SERVER #
10
+ ########################################################################
11
+
12
+ # normal socket
13
+ #s = ICAPServer.new
14
+ # puts 'Server is running on port 1344. Press CTRL+C to exit...'
15
+
16
+ # squid v4 variant
17
+ #options = {secure: true,
18
+ # certificate: '../cert.pem',
19
+ # key: '../key.pem',
20
+ # tls_socket: true}
21
+ #s = ICAPServer.new('localhost',11344,options)
22
+ # puts 'Server is running on port 11344. Press CTRL+C to exit...'
23
+
24
+ # rfc 3507 variant
25
+ options = {secure: true,
26
+ certificate: '../cert.pem',
27
+ key: '../key.pem',
28
+ tls_socket: false}
29
+ s = ICAPServer.new('localhost',1344,options)
30
+ puts 'Server is running on port 1344. Press CTRL+C to exit...'
31
+
32
+ ########################################################################
33
+ s.logger.level = Logger::INFO
34
+ s.services['echo'] = Services::EchoService.new
35
+
36
+ s.run
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'icaprb/server/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "icaprb-server"
8
+ spec.version = ICAPrb::Server::VERSION
9
+ spec.authors = ["Fabian Franz"]
10
+ spec.email = ["fabian.franz@students.fh-hagenberg.at"]
11
+
12
+ spec.summary = %q{This project includes an ICAP server fully implemented in Ruby but it does not include services.}
13
+ spec.homepage = "https://github.com/fabianfrz/ICAPrb-Server"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.11"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "rspec", "~> 3.0"
23
+ end
@@ -0,0 +1,24 @@
1
+ # = icaprb/server/icapuri.rb
2
+ # Author:: Fabian Franz
3
+ #
4
+ # See URI for general documentation
5
+ #
6
+
7
+ require 'uri/generic'
8
+
9
+ module URI #:nodoc:
10
+
11
+ # Class to represent ICAP URIs
12
+ class ICAP < Generic
13
+ # The ICAP default port is 1344
14
+ DEFAULT_PORT = 1344
15
+ end
16
+ # Class to represent ICAP URIs
17
+ class ICAPS < Generic
18
+ # The ICAP default port is 11344
19
+ DEFAULT_PORT = 11344
20
+ end
21
+
22
+ @@schemes['ICAP'] = ICAP
23
+ @@schemes['ICAPS'] = ICAPS
24
+ end
@@ -0,0 +1,83 @@
1
+ module ICAPrb
2
+ module Server
3
+ # Status Codes by IANA / RFC for HTTP
4
+ HTTP_STATUS_CODES =
5
+ {100=>'Continue',
6
+ 101=>'Switching Protocols',
7
+ 102=>'Processing',
8
+ 200=>'OK',
9
+ 201=>'Created',
10
+ 202=>'Accepted',
11
+ 203=>'Non-Authoritative Information',
12
+ 204=>'No Content',
13
+ 205=>'Reset Content',
14
+ 206=>'Partial Content',
15
+ 207=>'Multi-Status',
16
+ 208=>'Already Reported',
17
+ 226=>'IM Used',
18
+ 300=>'Multiple Choices',
19
+ 301=>'Moved Permanently',
20
+ 302=>'Found',
21
+ 303=>'See Other',
22
+ 304=>'Not Modified',
23
+ 305=>'Use Proxy',
24
+ 306=>'Unused',
25
+ 307=>'Temporary Redirect',
26
+ 308=>'Permanent Redirect',
27
+ 400=>'Bad Request',
28
+ 401=>'Unauthorized',
29
+ 402=>'Payment Required',
30
+ 403=>'Forbidden',
31
+ 404=>'Not Found',
32
+ 405=>'Method Not Allowed',
33
+ 406=>'Not Acceptable',
34
+ 407=>'Proxy Authentication Required',
35
+ 408=>'Request Timeout',
36
+ 409=>'Conflict',
37
+ 410=>'Gone',
38
+ 411=>'Length Required',
39
+ 412=>'Precondition Failed',
40
+ 413=>'Payload Too Large',
41
+ 414=>'URI Too Long',
42
+ 415=>'Unsupported Media Type',
43
+ 416=>'Range Not Satisfiable',
44
+ 417=>'Expectation Failed',
45
+ 421=>'Misdirected Request',
46
+ 422=>'Unprocessable Entity',
47
+ 423=>'Locked',
48
+ 424=>'Failed Dependency',
49
+ 425=>'Unassigned',
50
+ 426=>'Upgrade Required',
51
+ 427=>'Unassigned',
52
+ 428=>'Precondition Required',
53
+ 429=>'Too Many Requests',
54
+ 430=>'Unassigned',
55
+ 431=>'Request Header Fields Too Large',
56
+ 451=>'Unavailable For Legal Reasons',
57
+ 500=>'Internal Server Error',
58
+ 501=>'Not Implemented',
59
+ 502=>'Bad Gateway',
60
+ 503=>'Service Unavailable',
61
+ 504=>'Gateway Timeout',
62
+ 505=>'HTTP Version Not Supported',
63
+ 506=>'Variant Also Negotiates',
64
+ 507=>'Insufficient Storage',
65
+ 508=>'Loop Detected',
66
+ 509=>'Unassigned',
67
+ 510=>'Not Extended',
68
+ 511=>'Network Authentication Required'}
69
+ # differences defined in RFC 3507
70
+ ICAP_STATUS_CODES = HTTP_STATUS_CODES.dup.merge(
71
+ {
72
+ 100 => 'Continue after ICAP Preview',
73
+ 204 => 'No modifications needed',
74
+ 404 => 'ICAP Service not found',
75
+ 405 => 'Method not allowed for service',
76
+ 408 => 'Request timeout',
77
+ 500 => 'Server error',
78
+ 501 => 'Method not implemented',
79
+ 503 => 'Service overloaded',
80
+ 505 => 'ICAP version not supported by server'
81
+ })
82
+ end
83
+ end
@@ -0,0 +1,182 @@
1
+ # patch ruby strings
2
+ class String
3
+ # converts the string to a chunk (chunked encoding)
4
+ def to_chunk
5
+ length.to_s(16) + "\r\n" + self + "\r\n"
6
+ end
7
+ # extracts a ruby string from a chunk (chunked encoding)
8
+ def chunk_to_s
9
+ len_alpha = scan(/\A([\da-fA-F]+)\r\n/).first.first
10
+ len = len_alpha.to_i(16)
11
+ offset_data = len_alpha.length + 2 # 2 = CRLF
12
+ end_of_message = (offset_data+len)
13
+ str = self[offset_data...end_of_message]
14
+ raise 'Too many data in chunk' if end_of_message + 2 != length
15
+ str
16
+ end
17
+ end
18
+
19
+ module ICAPrb
20
+ module Server
21
+
22
+ # This class is the base for the HTTP header classes. It is a subclass of +Hash+.
23
+ class HTTP_Header < Hash
24
+
25
+ # Converts the array to a +String+ and which will be the complete HTTP Header,
26
+ # witch contains the request or status line.
27
+ #
28
+ # It will join the headers with \r\n and ends with \r\n\r\n
29
+ def to_s
30
+ data = []
31
+ each do |key, value|
32
+ data << "#{key}: #{value}"
33
+ end
34
+ request_line + data.join("\r\n") + "\r\n\r\n"
35
+ end
36
+ end
37
+
38
+ # The +RequestHeader+ represents the header information of an HTTP request. It contains the status line as well as
39
+ # the other headers. The headers are accessible by the array access operator
40
+ class RequestHeader < HTTP_Header
41
+ # the path requested by the user
42
+ attr_accessor :http_path
43
+ # The HTTP Method (also known as Verb) as a String. For Example GET, POST, PUT, DELETE or OPTIONS
44
+ attr_accessor :http_method
45
+ # The HTTP version which is used by the application
46
+ attr_accessor :http_version
47
+
48
+ # Initializes a new Object of +RequestHeader+
49
+ # Params:
50
+ # +http_method+:: The HTTP Method (also known as Verb) as a String. For Example GET, POST, PUT, DELETE or OPTIONS
51
+ # +http_path+:: The Resource which has been requested by the user
52
+ # +http_version+:: The HTTP version which is used by the application
53
+ def initialize(http_method, http_path, http_version = '1.1')
54
+ @http_method = http_method
55
+ @http_path = http_path
56
+ @http_version = http_version
57
+ end
58
+
59
+ # The request header is always the first header and it can be included only once in an ICAP response
60
+ def <=>(value)
61
+ return 0 if value.class == RequestHeader
62
+ -1
63
+ end
64
+
65
+ # creates the request line for the request
66
+ # it will include the method, the path and the used http version.
67
+ def request_line
68
+ "#{@http_method} #{@http_path} HTTP/#{@http_version}\r\n"
69
+ end
70
+ end
71
+
72
+ # This class represents the +Body+ of an HTTP request. It has to come after the headers in ICAP messages.
73
+ # It will be used if the request contains data (usually POST)
74
+ #
75
+ # Use the << operator to concatenate the strings as + would return a string and your ICAP answer would fail.
76
+ class RequestBody < String
77
+ # true if we are in the preview
78
+ attr_accessor :ieof
79
+
80
+ # initializes a new RequestBody which is a String but has an ieof tag additionally.
81
+ def initialize (string, ieof = false)
82
+ super string
83
+ @ieof = ieof
84
+ end
85
+ # A body comes after the headers
86
+ def <=>(other)
87
+ case other
88
+ when RequestHeader
89
+ 1
90
+ when ResponseHeader
91
+ 1
92
+ when RequestBody
93
+ 0
94
+ when ResponseBody
95
+ 0
96
+ when NullBody
97
+ -1
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ # This class represents the +Header+ of an HTTP response. It includes the headers sent by the web server as well as
104
+ # the status line
105
+ class ResponseHeader < HTTP_Header
106
+ # Params:
107
+ # +http_version+:: is the version of http to use - usually 1.1
108
+ # +status_code+:: is the status code of the http protocol. For example 200 for OK
109
+ def initialize(http_version, status_code)
110
+ @http_version = http_version
111
+ @status_code = status_code
112
+ end
113
+
114
+ # creates the response line for the request
115
+ # it will contain the http version, the status code and the status text from HTTP_STATUS_CODES
116
+ def request_line
117
+ "HTTP/#{@http_version} #{@status_code} #{HTTP_STATUS_CODES[@status_code]}\r\n"
118
+ end
119
+
120
+ # The request header has to come first
121
+ def <=>(other)
122
+ case other
123
+ when RequestHeader
124
+ 1
125
+ when ResponseHeader
126
+ 0
127
+ else
128
+ -1
129
+ end
130
+ end
131
+ end
132
+
133
+ # This class represents the response body which should be sent to the user.
134
+ # If it is missing, it could be replaced by a NullBody
135
+ class ResponseBody < String
136
+ # if it is in the preview, this will be true, otherwise nil or false
137
+ attr_accessor :ieof
138
+
139
+ # initializes a new RequestBody which is a String but has an ieof tag additionally.
140
+ def initialize(string, ieof = false)
141
+ super string
142
+ @ieof = ieof
143
+ end
144
+
145
+ # compare to sort within an array
146
+ # Response Bodies are usually at the end
147
+ def <=>(other)
148
+ case other
149
+ when RequestHeader
150
+ 1
151
+ when ResponseHeader
152
+ 1
153
+ when RequestBody
154
+ 0
155
+ when ResponseBody
156
+ 0
157
+ when NullBody
158
+ -1
159
+ end
160
+ end
161
+ end
162
+
163
+ # The +NullBody+ is a Class to have an Body without content. It exists to let the ICAP client know, that there
164
+ # will not be any body for this HTTP request. This usually happens when a 204 No Content ist sent.
165
+ class NullBody
166
+ # Returns an empty +String+
167
+ def to_s
168
+ ''
169
+ end
170
+
171
+ # It is always the last entry in ICAP responses.
172
+ def <=>(_)
173
+ 1
174
+ end
175
+
176
+ # Show an empty string on the IRB instead of the object id, so it looks like a +ResponseBody+
177
+ def inspect
178
+ '""'
179
+ end
180
+ end
181
+ end
182
+ end