Вопрос или проблема
Я ищу инструмент командной строки, который будет слушать на заданном порту, с радостью принимать каждую HTTP POST запрос и выводить его.
Я хочу использовать его в тестовых целях, т.е. для тестирования клиентов, которые отправляют HTTP POST запросы.
Это значит, что я ищу аналог curl -F
(который я могу использовать для отправки тестовых HTTP POST запросов на HTTP сервер).
Возможно, что-то вроде socat TCP4-LISTEN:80,fork,bind=127.0.0.1 ...
– но socat недостаточен, потому что он не поддерживает HTTP.
Я искал это сам и наткнулся на Node.js http-echo-server:
npm install http-echo-server -g
PORT=8081 http-echo-server
Он принимает все запросы и выводит полный запрос, включая заголовки, в командную строку.
Простые инструменты командной строки, такие как nc
, socat
, похоже, не способны обработать специфические HTTP особенности (чанки, кодировки передачи и т.д.). В результате это может привести к неожиданному поведению по сравнению с взаимодействием с настоящим веб-сервером. Поэтому моя первая мысль – поделиться кратчайшим способом настройки небольшого веб-сервера и заставить его делать то, что вам нужно: выводить весь вывод.
Самый короткий способ, который я мог придумать, используя Python Tornado:
#!/usr/bin/env python
import tornado.ioloop
import tornado.web
import pprint
class MyDumpHandler(tornado.web.RequestHandler):
def post(self):
pprint.pprint(self.request)
pprint.pprint(self.request.body)
if __name__ == "__main__":
tornado.web.Application([(r"/.*", MyDumpHandler),]).listen(8080)
tornado.ioloop.IOLoop.instance().start()
Замените строку pprint
, чтобы выводить только конкретные поля, которые вам нужны, например self.request.body
или self.request.headers
. В приведенном выше примере он слушает на порту 8080 на всех интерфейсах.
Альтернатив этому множество. web.py, Bottle и т.д.
(Я достаточно ориентирован на Python, извините)
Если вам не нравится его способ вывода, просто запустите его и попробуйте tcpdump
вот так:
tcpdump -i lo 'tcp[32:4] = 0x484f535420'
чтобы увидеть реальный необработанный дамп всех HTTP-POST запросов. В качестве альтернативы просто запустите Wireshark.
Используйте nc
(произносится как “netcat“).
Вы говорите ему, на каком порту слушать
nc -kl 8888
Затем в отдельном окне терминала отправьте ему запрос
curl localhost:8888 -d hello=world
и он выведет данные, которые вы ему отправили, в данном случае HTTP запрос:
POST / HTTP/1.1
Host: localhost:8888
User-Agent: curl/7.84.0
Accept: */*
Content-Length: 11
Content-Type: application/x-www-form-urlencoded
hello=world
Опция -k
означает, что он будет слушать запросы и выводить их бесконечно. Без этой опции (т.е. nc -l 8888
) он завершит работу после первого запроса.
https://hub.docker.com/r/jmalloc/echo-server/
Запустите
$ docker run -t --rm -p 8080:8080 jmalloc/echo-server
Не удалось найти изображение 'jmalloc/echo-server:latest' локально
latest: Загруска из jmalloc/echo-server
fbf67b0844fa: Полная загрузка
Digest: sha256:617a99b927c3b761621681eb4716582260391c0853b6da904e0f9f1d37785e7a
Статус: Загружено новое изображение для jmalloc/echo-server:latest
Echo сервер слушает на порту 8080
Post
$ curl -XPOST -H"ThisTook: 2 minutes to find" localhost:8080/asdf
Запрос обслуживается a2d8fa109b92
HTTP/1.1 POST /asdf
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
Thistook: 2 minutes to find
Стандартная библиотека Python поставляется с комплектом, т.е. она даже включает пакет HTTP сервера, который можно использовать для написания простого дампера HTTP запросов:
#!/usr/bin/env python3
import argparse
import http.server
import json
import sys
class Dumper(http.server.BaseHTTPRequestHandler):
def do_GET(self, method='GET'):
print(f'\n{method} {self.path}\n{self.headers}')
self.send_response(200)
self.end_headers()
def do_DELETE(self):
return self.do_GET('DELETE')
def do_POST(self, method='POST'):
n = int(self.headers.get('content-length', 0))
body = self.rfile.read(n)
print(f'\n{method} {self.path}\n{self.headers}{body}\n')
if self.headers.get('content-type') == 'application/json':
d = json.loads(body)
print(json.dumps(d, indent=4, sort_keys=True))
print()
self.send_response(200)
self.end_headers()
def do_PUT(self):
return self.do_POST('PUT')
def log_message(self, format, *args):
pass
Некоторые шаблоны:
def main():
p = argparse.ArgumentParser(description='Дампить HTTP запросы в stdout')
p.add_argument('address', help='адрес привязки')
p.add_argument('port', type=int, help='порт привязки')
xs = p.parse_args();
s = http.server.HTTPServer((xs.address, xs.port), Dumper)
s.serve_forever()
if __name__ == '__main__':
sys.exit(main())
Смотрите также: мой gist
У меня была такая же необходимость (получить поток POST сообщений) и я не смог заставить netcat работать. Я пробовал различные коды (200, 201, и 204) и заголовки (Location:, Content-type:, и Content-length:) комбинации, но ничего не работало.
Но решение @gertvdijk на Python сработало, так что я перехватил пакеты с помощью Wireshark во время его работы и обнаружил, что Tornado отправлял код 200 с заголовками “Connection: close” и “Content-length: 0”.
Так что я попробовал снова с netcat… и это сработало:
while true; do echo -e 'HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n' | nc -l 8000; done
Ответ или решение
Для тестирования HTTP-клиентов, которые отправляют POST-запросы, может быть полезен простой HTTP-сервер, который будет принимать каждый входящий POST-запрос и выводить его содержимое. В данной статье мы рассмотрим несколько различных подходов, как это можно реализовать в командной строке или с помощью кода.
1. Использование Python и библиотеки Tornado
Самый быстрый и популярный способ создать HTTP-сервер для этой цели — использовать фреймворк Tornado. Ниже представлен простой пример кода, который принимает POST-запросы и выводит их детали на консоль:
#!/usr/bin/env python
import tornado.ioloop
import tornado.web
import pprint
class MyDumpHandler(tornado.web.RequestHandler):
def post(self):
# Вывод информации о запросе
pprint.pprint(self.request.headers)
pprint.pprint(self.request.body)
if __name__ == "__main__":
tornado.web.Application([(r"/.*", MyDumpHandler),]).listen(8080)
tornado.ioloop.IOLoop.instance().start()
Этот сервер будет слушать на порту 8080 и выводить заголовки и тело каждого POST-запроса, полученного на любом пути.
2. Python HTTP-сервер стандартной библиотеки
Если вы хотите использовать стандартные библиотеки Python, можно воспользоваться встроенным HTTP-сервером. Вот пример кода, который также выводит содержимое запросов:
#!/usr/bin/env python3
import argparse
import http.server
import json
import sys
class Dumper(http.server.BaseHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers['Content-Length'])
body = self.rfile.read(content_length)
print(f'\nPOST {self.path}\n{self.headers}\n{body.decode()}\n')
self.send_response(200)
self.end_headers()
def main():
parser = argparse.ArgumentParser(description='Dump HTTP requests to stdout')
parser.add_argument('address', help='bind address')
parser.add_argument('port', type=int, help='bind port')
args = parser.parse_args()
server = http.server.HTTPServer((args.address, args.port), Dumper)
server.serve_forever()
if __name__ == '__main__':
sys.exit(main())
Запустив этот код, вы сможете принимать POST-запросы и выводить их детали на стандартный вывод.
3. Использование Docker и echo-сервера
Если вы предпочитаете использовать Docker, вы можете запустить готовый echo-сервер, который будет слушать на определенном порту. Просто выполните команду:
docker run -t --rm -p 8080:8080 jmalloc/echo-server
После этого вы сможете отправлять POST-запросы на localhost:8080
, и сервер будет выводить их содержимое.
4. Использование Netcat (nc)
Для простых случаев можно использовать утилиту nc
(netcat). Однако она не полностью поддерживает протокол HTTP. Пример команды:
nc -kl 8888
Затем, в другом терминале, вы можете отправить запрос:
curl -X POST -d "hello=world" localhost:8888
Этот подход работает, но помните, что netcat
имеет ограничения при работе с HTTP-запросами, особенно с различными типами кодирования и заголовками.
Заключение
Выбор подхода зависит от ваших специфических требований и предпочтений. Для простоты и расширяемости рекомендуется использовать Python с Tornado или встроенный HTTP-сервер. Docker-решение — отличный вариант для быстрой настройки окружения. Netcat может подойти для простого тестирования, но с ограничениями. Выберите метод, который наилучшим образом соответствует вашим нуждам при тестировании вашего HTTP-клиента.