撸一个简易的http客户端

简述

使用python来发送http请求非常简单,我们可以撸一个简单的脚本,指定参数方式运行,用pyinstaller打包成exe,就可以分享给其他小伙伴了。

先来看效果:

python3 http_client.py -h
usage: http_client.py [-h] --ip IP [--proto PROTO] [--port PORT]
                      [--source_ip SOURCE_IP]
                      [--source_ip_count SOURCE_IP_COUNT] [--method METHOD]
                      [--data DATA] [--data_len DATA_LEN] [--count COUNT]
                      [--one_connect ONE_CONNECT] [--urls URLS [URLS ...]]
                      [--headers HEADERS [HEADERS ...]]

optional arguments:
  -h, --help            show this help message and exit
  --ip IP               target IP address, support IPv4 and IPv6
  --proto PROTO         default is http, also you can use https
  --port PORT           target port
  --source_ip SOURCE_IP
                        source address, default is auto, choose source ip
                        address auto, you can assign like 20.1.1.10, if use
                        source ip address, per ip use a thread.
  --source_ip_count SOURCE_IP_COUNT
                        source address count, default is 1
  --method METHOD       default is GET
  --data DATA           upload data
  --data_len DATA_LEN   upload data length
  --count COUNT         default is 1, if set to 0, then run continuous
  --one_connect ONE_CONNECT
                        if set 1, then send all req in one tcp connection
  --urls URLS [URLS ...]
                        url list, example: / /index.html
  --headers HEADERS [HEADERS ...]
                        custom headers, example: Host:aaa.com User-Agent:pythontest

这里对参数简要说明一下:

  • 比较特别的一点是这个脚本支持指定源IP(当然源IP也要先配置到网卡上),其中source_ip 是基础源IP,source_ip_count 是指定源IP的个数,每个源IP会自动启用一个线程去运行HTTP 请求。

  • –one_connect参数设置为1后,当urls参数后面指定了多个uri时,会在一个tcp连接中发送所有的http请求

  • count参数设置为0的时候,会不停请求,需要ctrl+c中断

使用举例:

python3 http_client.py --ip 1.0.0.20 --one_connect 1 --urls /index.html /abcd.php --headers Host:www.test.com   --method GET --count 10

python3 http_client.py --ip 3ffe:1111::abcd --one_connect 1 --urls /index.html /abcd.php --headers Host:www.test.com   --method GET --count 10

python3 http_client.py --ip 3ffe:1::b  --urls /index.html --source_ip 3ffe:1::a01 --source_ip_count=100 --headers Host:www.test.com  --data_len 2000 --method POST

源代码

import argparse
import ipaddress
from http import client
from threading import Thread


def http_req(ip, port, uri_list, proto='http', method='GET', saddr=None, timeout=5, data=None, headers=None,
             one_connection=0):
    fail_count = 0
    if one_connection:
        if proto == 'http':
            conn = client.HTTPConnection(host=ip, port=port, source_address=saddr, timeout=timeout)
        elif proto == 'https':
            conn = client.HTTPSConnection(host=ip, port=port, source_address=saddr, timeout=timeout)
        else:
            return fail_count
        try:
            for uri in uri_list:
                conn.request(method, uri, body=data, headers=headers)
                conn.getresponse().read()
        except KeyboardInterrupt:
            return fail_count
        except Exception as e:
            print('request failed: ', e)
            fail_count += 1
            return fail_count
    else:
        for uri in uri_list:
            if proto == 'http':
                conn = client.HTTPConnection(host=ip, port=port, source_address=saddr, timeout=timeout)
            elif proto == 'https':
                conn = client.HTTPSConnection(host=ip, port=port, source_address=saddr, timeout=timeout)
            else:
                return 0
            try:
                conn.request(method, uri, body=data, headers=headers)
                conn.getresponse().read()
            except KeyboardInterrupt:
                return fail_count
            except Exception as e:
                print('request failed: ', e)
                fail_count += 1
                continue
    return fail_count


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--ip', type=str, required=True, help="target IP address, support IPv4 and IPv6")
    parser.add_argument('--proto', type=str, required=False, default='http',
                        help="default is http, also you can use https")
    parser.add_argument('--port', type=int, required=False, default=80, help="target port")
    parser.add_argument('--source_ip', type=str, required=False, default='auto',
                        help="source address, default is auto, choose source ip address auto, you can assign like 20.1.1.10, if use source ip address, per ip use a thread.")
    parser.add_argument('--source_ip_count', type=int, required=False, default=1,
                        help="source address count, default is 1")
    parser.add_argument('--method', type=str, required=False, default='GET', help="default is GET")
    parser.add_argument('--data', type=str, required=False, default=None, help="upload data")
    parser.add_argument('--data_len', type=int, required=False, default=0, help="upload data length")
    parser.add_argument('--count', type=int, required=False, default=1,
                        help="default is 1, if set to 0, then run continuous")
    parser.add_argument('--one_connect', type=int, required=False, default=0,
                        help="if set 1, then send all req in one tcp connection")
    parser.add_argument('--urls', type=str, required=False, default='/', nargs='+',
                        help="url list, example: / /index.html")
    parser.add_argument('--headers', type=str, required=False, default=None, nargs='+',
                        help="custom headers, example: Host:aaa.com User-Agent:pythontest")
    args = parser.parse_args()

    urls = ['/']
    headers = {}
    if isinstance(args.urls, list):
        urls = args.urls
    if isinstance(args.headers, list):
        for i in args.headers:
            headers[i.split(':')[0]] = i.split(':')[1]

    if args.data_len != 0:
        data = '#' * args.data_len
    else:
        data = args.data

    def run_req(count, saddr):
        fail_count = 0
        if count == 0:
            while 1:
                fail_count += http_req(ip=args.ip,
                                       port=args.port,
                                       uri_list=urls,
                                       proto=args.proto,
                                       method=args.method,
                                       saddr=saddr,
                                       timeout=5,
                                       data=data,
                                       headers=headers,
                                       one_connection=args.one_connect)
        else:
            for _ in range(count):
                fail_count += http_req(ip=args.ip,
                                       port=args.port,
                                       uri_list=urls,
                                       proto=args.proto,
                                       method=args.method,
                                       saddr=saddr,
                                       timeout=5,
                                       data=data,
                                       headers=headers,
                                       one_connection=args.one_connect)
            print("run count: %s, failed count: %s " % (count, fail_count))

    if args.source_ip != 'auto':
        if args.source_ip.find(':') != -1:
            # ipv6 仅支持地址最后一段数字变化
            assert args.ip.find(':') != -1, "source ip and target ip must be ipv6!"
            for i in range(args.source_ip_count):
                Thread(target=run_req, args=(args.count, (str(ipaddress.IPv6Address(args.source_ip) + i), 0))).start()
        else:
            for i in range(args.source_ip_count):
                Thread(target=run_req, args=(args.count, (str(ipaddress.IPv4Address(args.source_ip) + i), 0))).start()
    else:
        run_req(args.count, saddr=None)

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值