Home | History | Annotate | Download | only in axl
      1 #!/usr/bin/env python
      2 
      3 #
      4 # Copyright 2007, The Android Open Source Project
      5 #
      6 # Licensed under the Apache License, Version 2.0 (the "License");
      7 # you may not use this file except in compliance with the License.
      8 # You may obtain a copy of the License at
      9 #
     10 #     http://www.apache.org/licenses/LICENSE-2.0
     11 #
     12 # Unless required by applicable law or agreed to in writing, software
     13 # distributed under the License is distributed on an "AS IS" BASIS,
     14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 # See the License for the specific language governing permissions and
     16 # limitations under the License.
     17 #
     18 
     19 """
     20   axl.py: HTTP Client torture tester
     21 
     22 """
     23 
     24 import sys, time
     25 
     26 from twisted.internet import protocol, reactor, defer
     27 from twisted.internet.protocol import ServerFactory, Protocol
     28 
     29 import singletonmixin, log
     30 
     31 class BaseProtocol(Protocol):
     32     def __init__(self):
     33         self.log = log.Log.getInstance()
     34 
     35     def write(self, data):
     36         self.log("BaseProtocol.write()", len(data), data)
     37         return self.transport.write(data)
     38 
     39     def dataReceived(self, data):
     40         self.log("BaseProtocol.dataReceived()", len(data), data)
     41 
     42     def connectionMade(self):
     43         self.log("BaseProtocol.connectionMade()")
     44         self.transport.setTcpNoDelay(1)	# send immediately
     45 
     46     def connectionLost(self, reason):
     47         self.log("BaseProtocol.connectionLost():", reason)
     48 
     49     def sendResponse(self, response):
     50         self.write("HTTP/1.1 200 OK\r\n")
     51         self.write("Content-Length: %d\r\n\r\n" % len(response))
     52         if len(response) > 0:
     53             self.write(response)
     54 
     55 
     56 # Tests
     57 # 8000: test driven by resource request
     58 
     59 class Drop(BaseProtocol):
     60     """Drops connection immediately after connect"""
     61     PORT = 8001
     62     def connectionMade(self):
     63         BaseProtocol.connectionMade(self)
     64         self.transport.loseConnection()
     65 
     66 class ReadAndDrop(BaseProtocol):
     67     """Read 1st line of request, then drop connection"""
     68     PORT = 8002
     69     def dataReceived(self, data):
     70         BaseProtocol.dataReceived(self, data)
     71         self.transport.loseConnection()
     72 
     73 class GarbageStatus(BaseProtocol):
     74     """Send garbage statusline"""
     75     PORT = 8003
     76     def dataReceived(self, data):
     77         BaseProtocol.dataReceived(self, data)
     78         self.write("welcome to the jungle baby\r\n")
     79 
     80 class BadHeader(BaseProtocol):
     81     """Drop connection after a header is half-sent"""
     82     PORT = 8004
     83     def dataReceived(self, data):
     84         BaseProtocol.dataReceived(self, data)
     85         self.write("HTTP/1.1 200 OK\r\n")
     86         self.write("Cache-Contr")
     87         time.sleep(1)
     88         self.transport.loseConnection()
     89 
     90 class PauseHeader(BaseProtocol):
     91     """Pause for a second in middle of a header"""
     92     PORT = 8005
     93     def dataReceived(self, data):
     94         BaseProtocol.dataReceived(self, data)
     95         self.write("HTTP/1.1 200 OK\r\n")
     96         self.write("Cache-Contr")
     97         time.sleep(1)
     98         self.write("ol: private\r\n\r\nwe've got fun and games")
     99         time.sleep(1)
    100         self.transport.loseConnection()
    101 
    102 class Redirect(BaseProtocol):
    103     PORT = 8006
    104     def dataReceived(self, data):
    105         BaseProtocol.dataReceived(self, data)
    106         self.write("HTTP/1.1 302 Moved Temporarily\r\n")
    107         self.write("Content-Length: 0\r\n")
    108         self.write("Location: http://shopping.yahoo.com/p:Canon PowerShot SD630 Digital Camera:1993588104;_ylc=X3oDMTFhZXNmcjFjBF9TAzI3MTYxNDkEc2VjA2ZwLXB1bHNlBHNsawNyc3NfcHVsc2U0LmluYw--\r\n\r\n")
    109         self.transport.loseConnection()
    110 
    111 class DataDrop(BaseProtocol):
    112     """Drop connection in body"""
    113     PORT = 8007
    114     def dataReceived(self, data):
    115         if data.find("favico") >= 0:
    116             self.write("HTTP/1.1 404 Not Found\r\n\r\n")
    117             self.transport.loseConnection()
    118             return
    119 
    120         BaseProtocol.dataReceived(self, data)
    121         self.write("HTTP/1.1 200 OK\r\n")
    122 #        self.write("Content-Length: 100\r\n\r\n")
    123         self.write("\r\n")
    124 #        self.write("Data cuts off < 100 here!")
    125 #        time.sleep(4)
    126         self.transport.loseConnection()
    127 
    128 class DropOnce(BaseProtocol):
    129     """Drop every other connection"""
    130     PORT = 8008
    131     COUNT = 0
    132     def dataReceived(self, data):
    133         BaseProtocol.dataReceived(self, data)
    134         self.write("HTTP/1.1 200 OK\r\n")
    135         self.write("Content-Length: 5\r\n\r\n")
    136 
    137         if (not(DropOnce.COUNT & 1)):
    138             self.write("HE")
    139         else:
    140             self.write("HELLO")
    141         self.transport.loseConnection()
    142 
    143         DropOnce.COUNT += 1
    144 
    145 class NoCR(BaseProtocol):
    146     """Send headers without carriage returns"""
    147     PORT = 8009
    148     def dataReceived(self, data):
    149         BaseProtocol.dataReceived(self, data)
    150         self.write("HTTP/1.1 200 OK\n")
    151         self.write("Content-Length: 5\n\n")
    152 
    153         self.write("HELLO")
    154         self.transport.loseConnection()
    155 
    156 class PipeDrop(BaseProtocol):
    157     PORT = 8010
    158     COUNT = 0
    159     def dataReceived(self, data):
    160         BaseProtocol.dataReceived(self, data)
    161         if not PipeDrop.COUNT % 3:
    162             self.write("HTTP/1.1 200 OK\n")
    163             self.write("Content-Length: 943\n\n")
    164 
    165             self.write(open("./stfu.jpg").read())
    166             PipeDrop.COUNT += 1
    167 
    168         else:
    169             self.transport.loseConnection()
    170             PipeDrop.COUNT += 1
    171 
    172 class RedirectLoop(BaseProtocol):
    173     """Redirect back to same resource"""
    174     PORT = 8011
    175     def dataReceived(self, data):
    176         BaseProtocol.dataReceived(self, data)
    177         self.write("HTTP/1.1 302 Moved Temporarily\r\n")
    178         self.write("Content-Length: 0\r\n")
    179         self.write("Location: http://localhost:8011/\r\n")
    180         self.write("\r\n")
    181         self.transport.loseConnection()
    182 
    183 class ReadAll(BaseProtocol):
    184     """Read entire request"""
    185     PORT = 8012
    186 
    187     def connectionMade(self):
    188         self.count = 0
    189 
    190     def dataReceived(self, data):
    191         BaseProtocol.dataReceived(self, data)
    192         self.count += len(data)
    193         if self.count == 190890:
    194             self.transport.loseConnection()
    195 
    196 class Timeout(BaseProtocol):
    197     """Timout sending body"""
    198     PORT = 8013
    199 
    200     def connectionMade(self):
    201         self.count = 0
    202 
    203     def dataReceived(self, data):
    204         BaseProtocol.dataReceived(self, data)
    205         if self.count == 0: self.write("HTTP/1.1 200 OK\r\n\r\n")
    206         self.count += 1
    207 
    208 class SlowResponse(BaseProtocol):
    209     """Ensure client does not time out on slow writes"""
    210     PORT = 8014
    211 
    212     def connectionMade(self):
    213         self.count = 0
    214 
    215     def dataReceived(self, data):
    216         BaseProtocol.dataReceived(self, data)
    217         if self.count == 0: self.write("HTTP/1.1 200 OK\r\n\r\n")
    218         self.sendPack(0)
    219 
    220     def sendPack(self, count):
    221         if count > 10:
    222             self.transport.loseConnection()
    223 
    224         self.write("all work and no play makes jack a dull boy %s\n" % count)
    225         d = defer.Deferred()
    226         d.addCallback(self.sendPack)
    227         reactor.callLater(15, d.callback, count + 1)
    228 
    229 
    230 # HTTP/1.1 200 OK
    231 # Cache-Control: private
    232 # Content-Type: text/html
    233 # Set-Cookie: PREF=ID=10644de62c423aa5:TM=1155044293:LM=1155044293:S=0lHtymefQRs2j7nD; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
    234 # Server: GWS/2.1
    235 # Transfer-Encoding: chunked
    236 # Date: Tue, 08 Aug 2006 13:38:13 GMT
    237 
    238 def main():
    239     # Initialize log
    240     log.Log.getInstance(sys.stdout)
    241 
    242     for protocol in Drop, ReadAndDrop, GarbageStatus, BadHeader, PauseHeader, \
    243             Redirect, DataDrop, DropOnce, NoCR, PipeDrop, RedirectLoop, ReadAll, \
    244             Timeout, SlowResponse:
    245         factory = ServerFactory()
    246         factory.protocol = protocol
    247         reactor.listenTCP(protocol.PORT, factory)
    248 
    249 
    250     reactor.run()
    251 
    252 if __name__ == '__main__':
    253     main()
    254