-
Notifications
You must be signed in to change notification settings - Fork 0
/
one_time_server.rb
111 lines (98 loc) · 2.95 KB
/
one_time_server.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# Author:: Erik Hetzner (mailto:[email protected])
# Copyright:: Copyright (c) 2011, Regents of the University of California
require 'webrick'
# An HTTP server that will serve each file ONCE before shutting down.
module Mrt
module Ingest
class OneTimeServer
attr_reader :dir, :port
# Find an open port, starting with start and adding one until we get
# an open port
def get_open_port(start = 8081)
try_port = start
loop do
begin
s = TCPServer.open(try_port)
s.close
return try_port
rescue Errno::EADDRINUSE
try_port += 1
end
end
end
def initialize
@dir = Dir.mktmpdir
@mutex = Mutex.new
@known_paths = {}
@requested = {}
@port = get_open_port
@file_callback = ->(req, _res) { @requested[req.path] ||= true }
@server = WEBrick::HTTPServer.new(Port: @port)
@server.mount('/', WEBrick::HTTPServlet::FileHandler, @dir, FileCallback: @file_callback)
end
# Return true if each file has been served.
def finished?
Dir.entries(@dir).each do |entry|
next if %w[. ..].include?(entry)
return false if @requested["/#{entry}"].nil?
end
true
end
def temppath
tmpfile = Tempfile.new('tmp', @dir)
tmppath = tmpfile.path
tmpfile.close!
@mutex.synchronize do
unless @known_paths.key?(tmppath)
# no collision
@known_paths[tmppath] = true
return tmppath
end
end
# need to retry, there was a collision
temppath
end
# Add a file to this server. Returns the URL to use
# to fetch the file & the file path
def add_file(sourcefile = nil)
fullpath = temppath
path = File.basename(fullpath)
if sourcefile
@server.mount("/#{path}", WEBrick::HTTPServlet::FileHandler, sourcefile.path, FileCallback: @file_callback)
else
File.open(fullpath, 'w+') { |f| yield f }
end
["http://#{Socket.gethostname}:#{@port}/#{path}", fullpath]
end
def start_server
if @thread.nil?
@thread = Thread.new do
@server.start
end
end
sleep(0.1) while @server.status != :Running
@thread
end
# Stop server unconditionally.
def stop_server
@server.shutdown
@thread.join
end
# Wait for server to finish serving all files.
def join_server
# ensure that each file is requested once before shutting down
sleep(1) until finished?
@server.shutdown
@thread.join
end
# Run the server and wait until each file has been served once.
# Cleans up files before it returns.
def run
start_server
join_server
# FileUtils.rm_rf(@dir)
nil
end
end
end
end