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 24import sys, time 25 26from twisted.internet import protocol, reactor, defer 27from twisted.internet.protocol import ServerFactory, Protocol 28 29import singletonmixin, log 30 31class 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 59class Drop(BaseProtocol): 60 """Drops connection immediately after connect""" 61 PORT = 8001 62 def connectionMade(self): 63 BaseProtocol.connectionMade(self) 64 self.transport.loseConnection() 65 66class 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 73class 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 80class 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 90class 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 102class 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 111class 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 128class 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 145class 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 156class 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 172class 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 183class 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 196class 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 208class 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 238def 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 252if __name__ == '__main__': 253 main() 254