2013-05-11 39 views
2

我的應用程序連接到http服務器以下載多個文件(一次一個文件)。服務器間歇性地返回http 404作爲有效文件。這只是在生產中發生的,我無法訪問服務器。如何以404錯誤代碼中止HTTP連接

我把我的應用程序重試,現在我想編寫一個模擬情況的測試用例。

我讀了約tcpkill,但這些似乎並沒有達到目的。

我也讀了squid(設置代理),iptables,但他們似乎也沒有達到我的目的。

我在尋找的是,在向服務器發送1000個http成功請求之後,我應該關閉第1001個HTTP連接,並顯示錯誤代碼404。我傾向於使用一些代理,但不知道任何代理是否爲動態代理。

有人可以提出建議嗎?

回答

0

我不知道它是一個測試用例怎麼合適,但我不知道任何現有的解決方案(雖然我敢肯定有一個在那裏—我只是不知道在哪裏)這似乎是一個有趣的問題,所以我寫了一個小Python腳本來做到這一點。它只在Python 3上運行,所以確保你已經擁有了它。 (Python 2裏在寫的時候是比較常見的。)

#!/usr/bin/env python3 
# Latest version at http://stackoverflow.com/a/16494059/200291 
""" 
Accept TCP connections, usually proxying to another address, but occasionally 
responding with 404 Not Found. 
""" 

import socket 
import select 

DROP_DATA = b'HTTP/1.0 404 Not Found\r\n\r\nnot found\r\n' 
PROXY_BUFFER_SIZE = 4096 


class Reactor(object): 
    def __init__(self): 
     self.reactants = [] 

    def add_reactant(self, reactant): 
     self.reactants.append(reactant) 

    def remove_reactant(self, reactant): 
     self.reactants.remove(reactant) 

    def iterate(self): 
     r, w, x = [], [], [] 
     for reactant in self.reactants[:]: 
      reactant.modify(self, r, w, x) 
     r, w, x = select.select(r, w, x) 
     for reactant in self.reactants[:]: 
      reactant.notify(self, r, w, x) 

    def loop(self): 
     while self.reactants: 
      self.iterate() 


class Acceptor(object): 
    def __init__(self, socket, action): 
     self.socket = socket 
     self.action = action 

    def modify(self, reactor, r, w, x): 
     r.append(self.socket) 

    def notify(self, reactor, r, w, d): 
     if self.socket in r: 
      result = self.socket.accept() 
      self.action(result) 


class Dropper(object): 
    def __init__(self, socket): 
     self.socket = socket 
     self.queued = DROP_DATA 

    def modify(self, reactor, r, w, x): 
     w.append(self.socket) 

    def notify(self, reactor, r, w, x): 
     if self.socket in w: 
      if self.queued: 
       written = self.socket.send(self.queued) 
       self.queued = self.queued[written:] 
      else: 
       self.socket.shutdown(socket.SHUT_RDWR) 
       self.socket.close() 
       reactor.remove_reactant(self) 


class Connector(object): 
    def __init__(self, addr, done): 
     self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     self.socket.setblocking(False) 
     self.socket.connect_ex(addr) 
     self.done = done 

    def modify(self, reactor, r, w, x): 
     w.append(self.socket) 

    def notify(self, reactor, r, w, x): 
     if self.socket in w: 
      self.socket.setblocking(True) 
      reactor.remove_reactant(self) 
      self.done(self.socket) 


class SimplexProxy(object): 
    def __init__(self, source, dest, done): 
     self.source = source 
     self.dest = dest 
     self.buffer = b'' 
     self.want_shutdown = False 
     self.done = done 

    def modify(self, reactor, r, w, x): 
     if self.buffer or self.want_shutdown: 
      w.append(self.dest) 
     else: 
      r.append(self.source) 

    def notify(self, reactor, r, w, x): 
     if self.source in r: 
      read = self.source.recv(PROXY_BUFFER_SIZE) 
      if not read: 
       self.want_shutdown = True 
      else: 
       self.buffer += read 
     if self.dest in w: 
      if self.want_shutdown: 
       self.dest.shutdown(socket.SHUT_WR) 
       reactor.remove_reactant(self) 
      else: 
       written = self.dest.send(self.buffer) 
       self.buffer = self.buffer[written:] 


def join_sockets(reactor, socket_1, socket_2): 
    closed = 0 

    def done(): 
     nonlocal closed 
     closed += 1 
     if closed >= 2: 
      socket_1.close() 
      socket_2.close() 

    reactor.add_reactant(SimplexProxy(socket_1, socket_2, done)) 
    reactor.add_reactant(SimplexProxy(socket_2, socket_1, done)) 


def run_server(reactor, addr, action, backlog=5): 
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) 
    sock.bind(addr) 
    sock.listen(backlog) 
    reactor.add_reactant(Acceptor(sock, action)) 


def proxy_connect(reactor, our_socket, their_addr): 
    def connected(their_socket): 
     join_sockets(reactor, our_socket, their_socket) 

    reactor.add_reactant(Connector(their_addr, connected)) 


def run_dropper(reactor, our_addr, their_addr, drop_freq): 
    count = 0 

    def action(accepted): 
     nonlocal count 
     count += 1 
     sock, addr = accepted 
     if count % drop_freq == 0: 
      reactor.add_reactant(Dropper(sock)) 
     else: 
      proxy_connect(reactor, sock, their_addr) 

    run_server(reactor, our_addr, action) 


def main(): 
    def parse_port(s): 
     p = int(s) 
     if not (0 <= p < 65536): 
      raise ValueError("invalid port") 
     return p 

    def parse_natural(s): 
     n = int(s) 
     if n > 0: 
      return n 
     else: 
      raise ValueError("must be a natural number") 

    import argparse 

    parser = argparse.ArgumentParser(description=__doc__) 
    parser.add_argument('-H', '--local-host', metavar='HOST', 
         default='0.0.0.0', help="address to listen on") 
    parser.add_argument('-p', '--local-port', type=parse_port, metavar='PORT', 
         help="local port to listen on", required=True) 
    parser.add_argument('remote_host', help="host to connect to") 
    parser.add_argument('remote_port', type=parse_port, 
         help="port to connect to") 
    parser.add_argument('-D', '--drop-frequency', type=parse_natural, 
         metavar='FREQ', help="how many requests per drop", 
         required=True) 
    args = parser.parse_args() 

    def resolve(host, port): 
     info = socket.getaddrinfo(host, port, 
            socket.AF_INET, socket.SOCK_STREAM) 
     return info[0][-1] 

    local = resolve(args.local_host, args.local_port) 
    remote = resolve(args.remote_host, args.remote_port) 

    reactor = Reactor() 
    run_dropper(reactor, local, remote, args.drop_frequency) 

    try: 
     reactor.loop() 
    except KeyboardInterrupt: 
     pass 


if __name__ == '__main__': 
    main() 

用它來代理localhost:8000端口9000,下探每5連接,這樣運行:

% ./dropproxy.py localhost 8000 -p 9000 -D 5 
+0

感謝您的超反應快。這與我所尋找的非常接近。我要去測試它。 – MoneeK 2013-05-11 15:43:46