mayfly 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,68 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $:.push(File.dirname(__FILE__) + "/../lib")
4
+
5
+ require 'optparse'
6
+ require 'ostruct'
7
+ require 'mayfly'
8
+
9
+ include Mayfly
10
+
11
+ options = OpenStruct.new
12
+
13
+ OptionParser.new do |opts|
14
+
15
+ opts.banner = 'Usage: mayfly [options] file'
16
+ opts.on("-h", "--help", "Show this message") {|h| options.help = h}
17
+ opts.on("-v", "--verbose", "Run verbosely") {|v| options.verbose = v}
18
+ opts.on("-pnumber", "--port number", Integer, "Port number, default 7887") {|p| options.port = p}
19
+ opts.on("-lnumber", "--lives number", Integer, "How long mayfly should live for, how many requests it serves, default 1") {|l| options.lives = l}
20
+ opts.on("--[no-]secure", "Turn https on or off, off by default") {|s| options.secure = s}
21
+ opts.on("--[no-]auth", "Turn authentication on or off, off by default") {|a| options.auth = a}
22
+ opts.on("--passwd password", String,"If using auth then provide your password as an argument") {|p| options.passwd = p}
23
+ opts.on("--version", "Show version") do
24
+ puts "Mayfly " + Mayfly::VERSION.join('.')
25
+ exit
26
+ end
27
+
28
+ begin
29
+ opts.parse!
30
+ rescue OptionParser::MissingArgument,
31
+ OptionParser::InvalidOption,
32
+ OptionParser::InvalidArgument => e
33
+ puts e.message + ', use --help for more details'
34
+ exit 1
35
+ end
36
+
37
+ if (options.help)
38
+ puts opts
39
+ exit 1
40
+ end
41
+
42
+ if (ARGV.length != 1)
43
+ puts "File argument not specified, use --help for more details"
44
+ exit 1
45
+ end
46
+
47
+ end
48
+
49
+ file = ARGV[0]
50
+ port = options.port || 7887
51
+ lives = options.lives || 1
52
+ secure = options.secure
53
+ passwd = options.passwd ||= Mayfly::Utils::Cli.get_passwd if (options.auth)
54
+ verbose = options.verbose || false
55
+
56
+ args = [file, port.to_i, lives.to_i, secure, passwd, verbose]
57
+
58
+ if (ENV['MAYFLY_TESTING']) # Just test the stuff here happens ok
59
+ system("echo #{args.join(',')}")
60
+ else
61
+ begin
62
+ server = Server.new(*args)
63
+ server.start
64
+ rescue FileNotFoundException => e
65
+ puts "#{e.message}, shutting down"
66
+ exit 1
67
+ end
68
+ end
@@ -0,0 +1,96 @@
1
+ require 'webrick'
2
+ require 'webrick/https'
3
+ require 'logger'
4
+ require 'mayfly/file'
5
+ require 'mayfly/servlet'
6
+ require 'mayfly/utils'
7
+ include Mayfly::Utils
8
+
9
+ module Mayfly
10
+
11
+ VERSION = [0,0,1]
12
+
13
+ class FileNotFoundException < RuntimeError; end
14
+
15
+ class Log < Logger
16
+ def mayfly_log(msg)
17
+ (@logdev.nil?) ? puts(msg) : info(msg)
18
+ end
19
+ end
20
+
21
+ class Server
22
+
23
+ attr_accessor :config
24
+
25
+ def initialize(file, port, lives, secure = nil, passwd = nil, verbose = false)
26
+
27
+ local_variables.each {|v| eval("@#{v}=eval(v)")}
28
+
29
+ if (!File.exists?(@file))
30
+ raise FileNotFoundException, "#{@file} does not exist"
31
+ end
32
+
33
+ logger.mayfly_log("#{file} will be served #{lives} times from: " +
34
+ "http#{(secure) ? 's' : ''}://#{local_ip}:#{port}/mayfly/")
35
+
36
+ logger.mayfly_log("Using HTTP Authentication with the username 'mayfly'" +
37
+ " and the password you specified") if @passwd
38
+
39
+
40
+ logger.info("Starting mayfly with the following settings: ")
41
+ logger.info("file=#{file}")
42
+ logger.info("port=#{port}")
43
+ logger.info("lives=#{lives}")
44
+ logger.info("secure=#{secure}")
45
+ logger.info("passwd=#{passwd}")
46
+
47
+ @config = {
48
+ :Port => port,
49
+ :Logger => logger,
50
+ :AccessLog => [[$stdout, "%t Request from %h, response code = %s"]],
51
+ }
52
+
53
+ if (secure)
54
+
55
+ cert, key = get_cert
56
+
57
+ @config.merge!({
58
+ :SSLEnable => true,
59
+ :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
60
+ :SSLCertificate => cert,
61
+ :SSLPrivateKey => key
62
+ })
63
+
64
+ end
65
+
66
+ end
67
+
68
+ def start
69
+
70
+ server = WEBrick::HTTPServer.new(@config)
71
+ server.mount('/mayfly', Mayfly::Servlet, @file, @lives, @passwd)
72
+
73
+ ['INT', 'TERM'].each { |signal|
74
+ trap(signal){server.shutdown}
75
+ }
76
+
77
+ server.start
78
+
79
+ end
80
+
81
+ # Generate SSL Cert, and hack stderr to not output some ugly crap from webrick
82
+ def get_cert
83
+ old_stderr = $stderr
84
+ $stderr = StringIO.new
85
+ cert, key = WEBrick::Utils.create_self_signed_cert(1024, [["C","GB"], ["O","#{local_ip}"], ["CN", "Mayfly"]], "Generated by Ruby/OpenSSL")
86
+ $stderr = old_stderr
87
+ return cert, key
88
+ end
89
+
90
+ def logger
91
+ @logger ||= Log.new((@verbose) ? $stdout : nil, WEBrick::Log::INFO)
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,19 @@
1
+ module Mayfly
2
+
3
+ class File < File
4
+
5
+ @close_callback
6
+
7
+ def initialize(file, close_callback = nil)
8
+ super file
9
+ @close_callback = close_callback
10
+ end
11
+
12
+ def close
13
+ super
14
+ @close_callback.call if @close_callback != nil
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,69 @@
1
+ module Mayfly
2
+
3
+ require 'rubygems'
4
+ require 'thread'
5
+ require 'webrick/httputils'
6
+ require 'mayfly/utils'
7
+ include Mayfly::Utils
8
+
9
+ class Servlet < WEBrick::HTTPServlet::AbstractServlet
10
+
11
+ @@instance = nil
12
+ @@instance_creation_mutex = Mutex.new
13
+
14
+ def self.get_instance(config, *options)
15
+ @@instance_creation_mutex.synchronize {
16
+ @@instance = @@instance || self.new(config, *options)
17
+ }
18
+ end
19
+
20
+ def initialize(config, file, max, passwd = nil)
21
+ super
22
+ @file = file
23
+ @max = max
24
+ @count = 1
25
+ @count_mutex = Mutex.new
26
+ @passwd = passwd
27
+ end
28
+
29
+ def do_GET(req, resp)
30
+
31
+ if (@count > @max)
32
+ resp.status = 401
33
+ end
34
+
35
+ if (@passwd)
36
+ WEBrick::HTTPAuth.basic_auth(req, resp, 'mayfly') do |user, pass|
37
+ user == 'mayfly' && pass == @passwd
38
+ end
39
+ end
40
+
41
+ st = File::stat(@file)
42
+ resp['content-type'] = WEBrick::HTTPUtils::mime_type(@file, WEBrick::HTTPUtils::DefaultMimeTypes)
43
+ resp['content-length'] = st.size
44
+ resp['content-disposition'] = "attachment; filename=\"#{File.basename(@file)}\""
45
+ resp['last-modified'] = st.mtime.httpdate
46
+ resp['Cache-Control'] = 'no-cache, must-revalidate'
47
+
48
+ close_callback = nil
49
+
50
+ @count_mutex.synchronize {
51
+ @count += 1
52
+ close_callback = Proc.new do
53
+ growl("mayfly served #{@file} to #{req.peeraddr[2]}")
54
+ if (@count > @max)
55
+ @server.shutdown if (@count >= @max)
56
+ growl("mayfly has died, it served #{@file} #{@max} times")
57
+ end
58
+ end
59
+ }
60
+
61
+ if (@count <= @max + 1)
62
+ resp.body = File.new(@file, close_callback)
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,51 @@
1
+ module Mayfly
2
+ module Utils
3
+
4
+ require 'rubygems'
5
+ gem 'growl', '=1.0.3'
6
+ require 'growl'
7
+ include Growl
8
+
9
+ require 'socket'
10
+
11
+ def growl(msg)
12
+ if Growl.installed?
13
+ notify_info(msg, {:title => 'mayfly'})
14
+ end
15
+ end
16
+
17
+ # http://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/
18
+ def local_ip
19
+
20
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
21
+
22
+ UDPSocket.open do |s|
23
+ s.connect '64.233.187.99', 1
24
+ s.addr.last
25
+ end
26
+
27
+ ensure
28
+ Socket.do_not_reverse_lookup = orig
29
+ end
30
+ module_function :local_ip
31
+
32
+ # Some helpers for the cli
33
+ class Cli
34
+
35
+ gem 'highline', '=1.5.1'
36
+ require 'highline/import'
37
+
38
+ def self.get_passwd
39
+ passwd = ask('Password, leave blank for none: ') {|q| q.echo = false}
40
+ return nil if passwd == ''
41
+ if (passwd != ask('Confirm password: ') {|q| q.echo = false})
42
+ puts 'Passwords do not match, exiting'
43
+ exit 1
44
+ end
45
+ passwd
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mayfly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matt Haynes
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-18 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: growl
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - "="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: highline
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.5.1
34
+ version:
35
+ description: "mayfly is a tiny HTTP server with a very short lifespan, existing only to serve a single file for a predefined number of times, it then quietly shuffles off this mortal coil. "
36
+ email: matt@matthaynes.net
37
+ executables:
38
+ - mayfly
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - lib/mayfly.rb
45
+ - lib/mayfly/file.rb
46
+ - lib/mayfly/servlet.rb
47
+ - lib/mayfly/utils.rb
48
+ has_rdoc: true
49
+ homepage: http://github.com/matth/mayfly/tree/master
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options: []
54
+
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ version:
69
+ requirements: []
70
+
71
+ rubyforge_project:
72
+ rubygems_version: 1.3.5
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: A tiny HTTP server with a very short lifespan
76
+ test_files: []
77
+