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