Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

# Copyright (c) 2014, Facebook, Inc.  All rights reserved. 

# 

# This source code is licensed under the BSD-style license found in the 

# LICENSE file in the root directory of this source tree. An additional grant 

# of patent rights can be found in the PATENTS file in the same directory. 

# 

"""This module contains tornado-related helper tasks and classes.""" 

from __future__ import absolute_import 

 

from six import itervalues 

from sparts.counters import counter  #, samples, SampleType 

from sparts.sparts import option 

from sparts.vtask import VTask, SkipTask 

 

import tornado.ioloop 

import tornado.web 

import tornado.httpserver 

import tornado.netutil 

 

import grp 

import os 

 

 

class TornadoIOLoopTask(VTask): 

    """Configure and run the Tornado IO Loop in a sparts task""" 

    OPT_PREFIX = 'tornado' 

 

    def initTask(self): 

        super(TornadoIOLoopTask, self).initTask() 

        needed = getattr(self.service, 'REQUIRE_TORNADO', False) 

        for t in self.service.tasks: 

            if isinstance(t, TornadoTask): 

                needed = True 

 

36        if not needed: 

            raise SkipTask("No TornadoTasks found or enabled") 

 

        self.ioloop = tornado.ioloop.IOLoop.instance() 

 

    def _runloop(self): 

        self.ioloop.start() 

 

    def stop(self): 

        self.ioloop.stop() 

        super(TornadoIOLoopTask, self).stop() 

 

 

class TornadoTask(VTask): 

    """Base class for tasks that require the tornado IO Loop. 

     

    Implicitly configures the tornado IO Loop task as a dependency. 

     

    The ioloop can be accessed via `self.ioloop`""" 

    DEPS = [TornadoIOLoopTask] 

 

    def initTask(self): 

        super(TornadoTask, self).initTask() 

        self.ioloop_task = self.service.requireTask('TornadoIOLoopTask') 

 

    @property 

    def ioloop(self): 

        return self.ioloop_task.ioloop 

 

 

class TornadoHTTPTask(TornadoTask): 

    """A loopless task that implements an HTTP server using Tornado. 

     

    It is loopless because it depends on tornado's separate IOLoop task.  You 

    will need to subclass this to do something more useful.""" 

    LOOPLESS = True 

    OPT_PREFIX = 'http' 

    DEFAULT_PORT = 0 

    DEFAULT_HOST = '' 

    DEFAULT_SOCK = '' 

 

    requests = counter() 

    #latency = samples(windows=[60, 3600], 

    #                  types=[SampleType.AVG, SampleType.MIN, SampleType.MAX]) 

 

    host = option(metavar='HOST', default=lambda cls: cls.DEFAULT_HOST, 

                  help='Address to bind server to [%(default)s]') 

    port = option(metavar='PORT', default=lambda cls: cls.DEFAULT_PORT, 

                  help='Port to run server on [%(default)s]') 

    sock = option(metavar='PATH', default=lambda cls: cls.DEFAULT_SOCK, 

                  help='Default path to use for local file socket ' 

                       '[%(default)s]') 

    group = option(name='sock-group', metavar='GROUP', default='', 

                   help='Group to create unix files as [%(default)s]') 

 

    def getApplicationConfig(self): 

        """Override this to register custom handlers / routes.""" 

        return [ 

            ('/', HelloWorldHandler), 

        ] 

 

    def initTask(self): 

        super(TornadoHTTPTask, self).initTask() 

 

        self.app = tornado.web.Application( 

            self.getApplicationConfig(), 

            log_function=self.tornadoRequestLog) 

 

        self.server = tornado.httpserver.HTTPServer(self.app) 

 

106        if self.sock: 

            assert self.host == self.DEFAULT_HOST, \ 

                "Do not specify host *and* sock (%s, %s)" % \ 

                (self.host, self.sock) 

            assert int(self.port) == self.DEFAULT_PORT, \ 

                "Do not specify port *and* sock (%s, %s)" % \ 

                (self.port, self.DEFAULT_PORT) 

 

            gid, mode = -1, 0o600 

            if self.group != '': 

                e = grp.getgrnam(self.group) 

                gid, mode = e.gr_gid, 0o660 

 

            sock = tornado.netutil.bind_unix_socket(self.sock, mode=mode) 

            if gid != -1: 

                os.chown(self.sock, -1, gid) 

            self.server.add_sockets([sock]) 

        else: 

            self.server.listen(self.port, self.host) 

 

        self.bound_addrs = [] 

        for sock in itervalues(self.server._sockets): 

            sockaddr = sock.getsockname() 

            self.bound_addrs.append(sockaddr) 

            self.logger.info("%s Server Started on %s (port %s)", 

                             self.name, sockaddr[0], sockaddr[1]) 

 

    @property 

    def bound_v4_addrs(self): 

        return [a[0] for a in self.bound_addrs if len(a) == 2] 

 

    @property 

    def bound_v6_addrs(self): 

        return [a[0] for a in self.bound_addrs if len(a) == 4] 

 

    def tornadoRequestLog(self, handler): 

        self.requests.increment() 

 

    def stop(self): 

        super(TornadoHTTPTask, self).stop() 

        self.server.stop() 

 

 

class HelloWorldHandler(tornado.web.RequestHandler): 

    """A sample twisted web handler for use in the default http server task""" 

    def get(self): 

        self.write("Hello, world")