본문 바로가기

Data Engineering/Server

[HTTP] Request와 Response 패킷 파헤치기

Flask에서 request를 받는 데에 단순 파싱만 해왔는데, 실제로 서버는 어떤 request를 받는지 궁금하여 조사했습니다.

1. Postman으로 request를 보냈을 때

2. 웹브라우저에서 접속했을 때

 

두 가지 경우로 나누어 봤습니다.

 

모든 자료는 MDN의 공식 문서를 활용하였습니다.

 

 

https://developer.mozilla.org/en-US/docs/Web/HTTP

 

HTTP | MDN

Hypertext Transfer Protocol (HTTP) is an application-layer protocol for transmitting hypermedia documents, such as HTML. It was designed for communication between web browsers and web servers, but it can also be used for other purposes. HTTP follows a clas

developer.mozilla.org

 

기준

 

HTTP 1.1, GET 기준입니다. 아래 코드를 실행시킨 결과입니다(크롬 개발자 도구의 Network 부분에서도 확인하실 수 있습니다).

@app.route('/api/category', defaults={'caid': None}, methods=['GET'])
@app.route('/api/category', methods=['GET'])
def select_all():        
    print("req:",request.__dict__)

 

 

코드 실행 결과:

{'method': 'GET',  : 부른 메소드가 나옵니다.

 

'scheme': 'http', : HTTP 프로토콜이라는 의미입니다.

 

'server':('127.0.0.1', 5000), : 자신의 IP주소와 포트번호가 나옵니다.

 

'root_path': '', : 애플리케이션을 실행시킨 경로 기준 절대 경로가 나옵니다(보통 공백일 듯 하네요).

 

Get root path of Flask application

I'm working on a Flask extension from which I want to create a directory in the project's root path on the file system. Suppose we have this directory structure /project /app /tests /

stackoverflow.com

'path': '/api/category', : 부른 경로가 나옵니다.

 

'query_string': b's_category=extra_battery', : 쿼리스트링인데, bytes 형태로 전달됩니다. 네트워크 리소스는 바이트 스트림으로 전달되기 때문입니다.

 

 1. Postman을 사용한 경우

'headers':

  EnvironHeaders(

[ ('Content-Type', 'application/json'): json 형태의 request가 왔다는 것입니다. audio, text, video 등 다양한 데이터 타입이 올 수 있습니다. 주의: 일반적으로 GET 메소드를 이용한 request는 query string이나 path parameter가 오지 json 형태는 오지 않습니다. 제가 임의로 Postman을 사용하여 넣은 것이니 이해해주세요.

 

 

('User-Agent', 'PostmanRuntime/7.41.0'), : Postman을 사용했기에 이렇게 나옵니다.

 

('Accept', '*/*'), : 클라이언트가 이해 가능한 타입(MIME)을 말하는데, 여기서는 전체라는 의미입니다.

 

('Cache-Control', 'no-cache'), : request쪽에서 캐시를 사용하지 않는다는 의미입니다.

 

('Postman-Token', '...'), : 토큰입니다.

 

('Host', 'localhost:5000'), Host하는 서버 URL입니다.

 

('Accept-Encoding', 'gzip, deflate, br'), : 더 빠른 전송을 위한 압축 알고리즘으로 encoding이 가능하다는 것을 의미합니다. 'gzip', 'deflate', 'br'은 텍스트 기반 포맷에 적용됩니다.

 

('Connection', 'keep-alive'), : 연결이 계속 유지될 지를 선택하는 것입니다. HTTP 1.1에서는 기본입니다. (HTTP 2.에서는 금지되거나 무시됩니다.)

 

('Content-Length', '23') : 메세지 본문의 바이트 길이를 나타냅니다.

]

),

 

'remote_addr': '127.0.0.1', : 클라이언트의 IP 주소입니다.

 

'environ': { : 아래는 wsgi 관련 설정과 중복된 부분이 많으므로 스킵하겠습니다.

'wsgi.version': (1, 0),

'wsgi.url_scheme': 'http', 'wsgi.input': <_io.BufferedReadername=780>,

'wsgi.errors': <_pydevd_bundle.pydevd_io.IORedirector object at 0x000001D62CA607F0>,

'wsgi.multithread': True, 'wsgi.multiprocess': False,

'wsgi.run_once': False, 'werkzeug.socket': <socket.socket fd=780, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0,

laddr=('127.0.0.1', 5000),

raddr=('127.0.0.1', 52980)>,

'SERVER_SOFTWARE': 'Werkzeug/3.0.1',

'REQUEST_METHOD': 

'GET',

 'SCRIPT_NAME': '', 

'PATH_INFO': '/api/category',

 'QUERY_STRING': 's_category=extra_battery', 'REQUEST_URI': '/api/category?s_category=extra_battery', 
'RAW_URI': '/api/category?s_category=extra_battery', 
'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': 52980, 'SERVER_NAME': '127.0.0.1', 'SERVER_PORT': '5000', 'SERVER_PROTOCOL': 'HTTP/1.1', 'CONTENT_TYPE': 'application/json', 'HTTP_USER_AGENT': 'PostmanRuntime/7.41.0', 'HTTP_ACCEPT': '*/*', 'HTTP_CACHE_CONTROL': 'no-cache', 'HTTP_POSTMAN_TOKEN': '...', 'HTTP_HOST': 'localhost:5000', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_CONNECTION': 'keep-alive', 'CONTENT_LENGTH': '23', 'werkzeug.request': <Request 'http://localhost:5000/api/category?s_category=extra_battery' [GET]>

}, 

 

이 외에도 다수가 있습니다.


2. 웹브라우저로 직접 접속한 경우.

 

'headers': EnvironHeaders(

        [

('Host', '127.0.0.1:5000'),

 

('Connection', 'keep-alive'),

 

('Cache-Control', 'max-age=0'), : 캐시를 저장하지만 요청마다 재검증한다는 의미입니다.

 

('Sec-Ch-Ua', '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"'), : Sec-Ch-Ua는 Security-ClientHint-UserAgent의 약자로, 이  계열은  브라우저 종류와 브랜드를 나타냅니다. 기본 값으로 보내지고, 임의로 변경하기 어렵습니다. HTTPS에서만 전송됩니다.

 

('Sec-Ch-Ua-Mobile', '?0'),: 모바일 환경인지 나타냅니다. 0이므로 false이니 데스크탑 환경입니다.

 

('Sec-Ch-Ua-Platform', '"Windows"'), : 윈도우 환경입니다.

 

('Dnt', '1'), : Do Not Track의 약자로, 사용자의 선호를 추적하지 말라는 의미입니다. 1이니 True입니다. 참고로 MDN에서는 비표준이고, Deprecated된다고 하네요.

 

('Upgrade-Insecure-Requests', '1'), : request를 보내기 전에 HTTP인 URL을 업그레이드하라고 브라우저에 지시합니다. 예를 들어, http://example.com 이라는 곳으로 리다이렉션이 되어 있으면 자동으로 https://example.com 으로 리다이렉션되게 합니다.

 

('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'), : 일반적인 User-Agent입니다. 클라이언트의 변경이 쉽습니다.

 

('Accept','text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7'), : 허용하는 데이터 타입 모음입니다. q는 quality를 의미하며 1이 최대입니다.

 

('Sec-Fetch-Site', 'none'), : 최근에 신설된 속성이며, 유저가 어디를 통하여 왔는지 알 수 있게끔 합니다. 값이 'none'이므로 유저가 스스로 접속했다는 의미입니다. cross-site(타 사이트에서 라우팅), same-origin(동일한 IP에서 라우팅), same-site(동일한 페이지에서 라우팅), none 값으로 이루어져 있습니다.

 

('Sec-Fetch-Mode', 'navigate'), : 이 값은 request가 어느 종류의 요청인지를 알려줍니다. html 페이지 사이인지, 아니면 이미지 및 기타 리소스에 대한 request인지를 알려줍니다. navigate이므로 html 사이를 이동하는 요청이라는 의미입니다. 

 

('Sec-Fetch-User', '?1'), :  유저가 스스로 접속하는 경우에만 발생하며, 값은 항상 ?1입니다.

 

('Sec-Fetch-Dest', 'document'), : 요청한 값이 document라는 의미입니다.

 

('Accept-Encoding', 'gzip, deflate, br, zstd'),

 

('Accept-Language', 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7') : request에 대한 언어 선호도를 나타냅니다. 컨텐츠 협상(content negotiation)을 사용하여 제안 중 하나를 선택하여 Content-Language response header와 함께 client에게 알려줍니다. 이를 이용하여 사용자별로 다른 언어 환경을 제공할 수 있습니다. 모든 Accept 계열 정보를 바탕으로 HTTP 1.1 기준으로 서버가 주도 콘텐츠 협상을 진행할 수 있습니다. 예를 들어 Accept: video이고, Accept-Language가 ko-KR이 가장 높은 q값이라면 한국어 자막이 달린 비디오를 보여줄 수 있을 것입니다.

 

]), 

...

 

아래는 Response에 대한 것입니다.

Header: 

  Server: Werkzeug/3.0.1 Python/3.10.0 : 웹서버 종류와 버전, 언어와 버전을 알려줍니다.

  Date: Wed, 14 Aug 2024 19:46:12 GMT : 시간을 알려줍니다.

  Content-Type: application/json

  Content-Length: 7689

  Access-Control-Allow-Orgin: "*": CORS 정책에 따라서 어느 도메인에서 온 요청이 접근이 가능한지를 나타냅니다. 전체(*)로 설정되어 있지만 보안상 권장되지 않습니다. 특정 도메인을 명시하는 게 좋습니다.

  Connection: close : 연결이 종료되었다는 것을 의미합니다.

 

정리:

Request의 header 부분을 파헤쳐보니 서버 로직에 이용할 수도 있고, 크롤링에도 이용할 수 있을 것 같습니다. 다양한 활용 분야가 있을 것 같네요.

 

신규로 계속 업데이트되는 Request 부분이 분명히 있으니 최신 MDN 문서를 참고하시는 것을 더욱 권장드립니다.

 

감사합니다.

'Data Engineering > Server' 카테고리의 다른 글

[HTTP/2] HTTP/2 개념과 실습코드  (2) 2024.12.30
[Server] REST API란 무엇일까?  (3) 2023.05.30