mayfly 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+