The Android Open Source Project | 52d4c30 | 2009-03-03 19:29:09 -0800 | [diff] [blame] | 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() |