1 | # frozen_string_literal: true |
||
2 | |||
3 | 1 | module NoSE |
|
4 | # Query processing proxies to transparently execute queries against a backend |
||
5 | 1 | module Proxy |
|
6 | # A proxy server to interpret our query language and implement query plans |
||
7 | 1 | class ProxyBase |
|
8 | 1 | attr_reader :logger |
|
0 ignored issues
–
show
introduced
by
![]() |
|||
9 | 1 | def initialize(config, result, backend) |
|
10 | @logger = Logging.logger['nose::proxy'] |
||
11 | |||
12 | @result = result |
||
13 | @backend = backend |
||
14 | @config = config |
||
15 | |||
16 | @continue = true |
||
17 | end |
||
18 | |||
19 | # Start the proxy server |
||
20 | # @return [void] |
||
21 | 1 | def start |
|
22 | @logger.info "Starting server on port #{@config[:port]}" |
||
23 | |||
24 | server_socket = TCPServer.new('127.0.0.1', @config[:port]) |
||
25 | server_socket.listen(100) |
||
26 | |||
27 | @read_sockets = [server_socket] |
||
28 | @write_sockets = [] |
||
29 | loop do |
||
30 | break unless @continue && select_connection(server_socket) |
||
31 | end |
||
32 | end |
||
33 | |||
34 | # @abstract Subclasses should process a new connection |
||
35 | # on the given socket |
||
36 | # :nocov: |
||
37 | # @return [void] |
||
38 | 1 | def handle_connection(_socket) |
|
39 | fail NotImplementedError |
||
40 | end |
||
41 | # :nocov: |
||
42 | |||
43 | # @abstract Subclasses should dispose of state associated with the socket |
||
44 | # :nocov: |
||
45 | # @return [void] |
||
46 | 1 | def remove_connection(_socket) |
|
47 | fail NotImplementedError |
||
48 | end |
||
49 | # :nocov: |
||
50 | |||
51 | # Stop accepting connections |
||
52 | # @return [void] |
||
53 | 1 | def stop |
|
54 | @continue = false |
||
55 | end |
||
56 | |||
57 | 1 | private |
|
58 | |||
59 | # Select sockets which are available to be processed |
||
60 | # @return [void] |
||
61 | 1 | def select_connection(server_socket) |
|
62 | read, write, error = IO.select @read_sockets, @write_sockets, |
||
63 | @read_sockets + @write_sockets, 5 |
||
64 | return true if read.nil? |
||
65 | |||
66 | # Check if we have a new incoming connection |
||
67 | if read.include? server_socket |
||
68 | accept_connection server_socket |
||
69 | read.delete server_socket |
||
70 | elsif error.include? server_socket |
||
71 | @logger.error 'Server socket died' |
||
72 | return false |
||
73 | end |
||
74 | |||
75 | # Remove all sockets which have errors |
||
76 | error.each { |socket| remove_connection socket } |
||
77 | @read_sockets -= error |
||
78 | @write_sockets -= error |
||
79 | |||
80 | # Handle connections on each available socket |
||
81 | process_connections read + write |
||
82 | end |
||
83 | |||
84 | # Accept the new connection |
||
85 | # @return [void] |
||
86 | 1 | def accept_connection(server_socket) |
|
87 | client_socket, = server_socket.accept |
||
88 | @read_sockets << client_socket |
||
89 | @write_sockets << client_socket |
||
90 | end |
||
91 | |||
92 | # Process all pending connections |
||
93 | # @return [void] |
||
94 | 1 | def process_connections(sockets) |
|
95 | sockets.each do |socket| |
||
96 | @write_sockets.delete socket |
||
97 | @read_sockets.delete socket unless handle_connection socket |
||
98 | end |
||
99 | end |
||
100 | end |
||
101 | end |
||
102 | end |
||
103 |