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
|
import socket
import fcntl
import os, os.path
import time
import warnings
from datetime import datetime, timedelta
from tornado.escape import json_encode
from config import server_socket_path
class WebtilesSocketConnection(object):
def __init__(self, io_loop, socketpath, logger):
self.io_loop = io_loop
self.crawl_socketpath = socketpath
self.logger = logger
self.message_callback = None
self.socket = None
self.socketpath = None
self.open = False
self.close_callback = None
self.msg_buffer = None
def connect(self, primary = True):
if not os.path.exists(self.crawl_socketpath):
# Wait until the socket exists
self.io_loop.add_timeout(time.time() + 1, self.connect)
return
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
self.socket.settimeout(10)
# Set close-on-exec
flags = fcntl.fcntl(self.socket.fileno(), fcntl.F_GETFD)
fcntl.fcntl(self.socket.fileno(), flags | fcntl.FD_CLOEXEC)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind to a temp path
# Ignore the security warning about tempnam; in this case,
# there is no security risk (the most that can happen is that
# the bind call fails)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
self.socketpath = os.tempnam(server_socket_path, "crawl")
self.socket.bind(self.socketpath)
# Install handler
self.io_loop.add_handler(self.socket.fileno(),
self._handle_read,
self.io_loop.ERROR | self.io_loop.READ)
msg = json_encode({
"msg": "attach",
"primary": primary
})
self.open = True
self.send_message(msg)
def _handle_read(self, fd, events):
if events & self.io_loop.READ:
data = self.socket.recv(128 * 1024, socket.MSG_DONTWAIT)
self._handle_data(data)
if events & self.io_loop.ERROR:
pass
def _handle_data(self, data):
if self.msg_buffer is not None:
data = self.msg_buffer + data
if data[-1] != "\n":
# All messages from crawl end with \n.
# If this one doesn't, it's fragmented.
self.msg_buffer = data
else:
self.msg_buffer = None
if self.message_callback:
self.message_callback(data)
def send_message(self, data):
start = datetime.now()
try:
self.socket.sendto(data, self.crawl_socketpath)
except socket.timeout:
self.logger.warning("Game socket send timeout", exc_info=True)
self.close()
return
end = datetime.now()
if end - start >= timedelta(seconds=1):
self.logger.warning("Slow socket send: " + str(end - start))
def close(self):
if self.socket:
self.io_loop.remove_handler(self.socket.fileno())
self.socket.close()
os.remove(self.socketpath)
self.socket = None
if self.close_callback:
self.close_callback()
|