안녕하세요.
오늘은 오랜만에 python 관련 포스팅을 해보도록 하겠습니다.
HTTP REST API 를 python 의 requests 모듈을 이용해서 많이 사용하실 겁니다.
python request 모듈을 이용한 HTTP 내용을 포스팅했던 적이 있었습니다.
저도 일반적으로 requests API를 사용하긴 하지만,
keep-alive로 Session을 유지하면서 data 를 계속 send 하거나 recv 하는 경우에는
requests 모듈이 blocking 으로 동작하기 때문에 data 를 계속 send 하거나 recv 하는데 어려움이 있었습니다.
다른 모듈들을 찾아보다가
"그래서 직접 raw socket 으로 짜는 것이 맘 편하겠어"
라고 생각하여 직접 짜게 되었습니다.
import socket
evt_data = '--eventBoundary' + '\r\n\r\n' + 'Event:Birthday' + '\r\n' + '--eventBoundary' + '\r\n\r\n'
req_header = "POST {0} HTTP/1.1\r\nHost: 192.168.10.3:80\r\nUser-Agent: CUSTOM PYTHON\r\nContent-Type: multipart/mixed; boundary=eventBoundary\r\nContent-Length: {1}\r\n\r\n"
header = req_header.format(url, len(data))
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # Create socket
s.connect(("192.168.10.3",80)) # Connect
data = header+evt_data # Header + Body
s.send(data.encode('UTF-8'))
POST 방식으로 Body 에 Data(Content) 를 실어서 send 하는 방식입니다.
이렇게 하면 Blocking 없이 계속 Data 를 Send 할 수 있습니다.
특히 multipart 방식으로 Data 를 Send 하는 경우에 유용합니다.
위의 예제에서 '--eventBoundary' 를 기준으로(Transaction) 계속 data 를 send 할 수 있습니다.
근데 이렇게 짜면 한가지 걸리는 것이 있습니다.
그렇다면 인증을 어떻게 하나요....??
"기존 requests 에서는 인증을 다 해주는데 이건 Digest 인증을 직접 raw string 으로 만들어줘야하잖아요..."
라는 문제가 있습니다.
그래서 직접 모두 구현하기 힘드니깐 조합해서 써야지가 결론이였습니다.
import urllib3
from requests.utils import parse_dict_header, parse_list_header
import hashlib
from urllib.parse import urlparse
import os
401 로 recv 된 data 에서 필요한 realm, nonce, algorithm 을 파싱하여,
(requests.utils 에 존재합니다.)
Digest 인증해주는 String 영역을 만들어주는 부분을 Custom 하게 만들었습니다.
reuqests 모듈 내부에서도 urllib3 과 hashlib 을 이용하여 digest auth string 을 만들어주게 됩니다.
공개되어 있는 requests 모듈의 소스를 참고해서 구현하셔도 되고,
스펙을 직접 보면서 구현하셔도 됩니다.
import urllib3
from requests.utils import parse_dict_header, parse_list_header
import hashlib
from urllib.parse import urlparse
import os
req_header = "GET {0} HTTP/1.1\r\nHost: 192.168.10.3:80\r\nUser-Agent: CUSTOM PYTHON\r\n\r\n"
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # Create socket
s.connect(("192.168.10.3",80)) # Connect it
header = req_header.format(url)
data = header
s.send(data.encode('UTF-8'))
r = s.recv(1024)
response = r.decode('utf-8')
# Header Parsing
resp = response.split('\r\n')
# auth 정보 Parsing
for get_auth in resp:
if 'WWW-Authenticate' in get_auth :
auth_info = get_auth
# Dictionary 로 realm 으로 따로 구별하기 위해서(requests 모듈 함수를 이용하려다 보니...)
auth = auth_info.replace('WWW-Authenticate: Digest realm', 'realm')
result = parse_dict_header(auth)
# digest_auth_str 만들기
digest_auth_str = make_digest_header('GET', search_url, result, 'admin', '~~QQww11')
# 만들어진 인증정보로 Data 만들기
req_header = 'GET {0} HTTP/1.1\r\nHost: 192.168.10.3:8080\r\nUser-Agent: CUSTOM PYTHON\r\nConnection: keep-alive\r\nAuthorization: {1}\r\n\r\n'
data = req_header.format(search_url, digest_auth_str)
# Data Send
s.send(data.encode('UTF-8'))
while True:
r = s.recv(1024)
print(r.decode('utf-8'))
(make_digest_header 는 Custom 하게 변경하여 만든 함수입니다.)
Raw String 으로 Data 를 send 하고 recv 하실 때, HTTP Header 와 Body 가 '\r\n\r\n' 으로 구분되기 때문에,
이 부분 꼭 신경써주세요!
Header 안에서 Content-Length 및 Authenficiation 영역의 구분은 '\r\n' 으로 구분됩니다.
(물론 모두 아실 것이라 믿습니다. 모르시면 이제 공부하면 되죠!)
이만 포스팅을 마치도록 하겠습니다.
오늘도 행복한 하루 되시길 바랍니다.
python os.popen() 시스템 명령어 실행결과 가져오기, python os.system() (0) | 2020.07.10 |
---|---|
python datetime to utc timestamp (2) | 2020.04.16 |
python datetime to OLE Automation date (0) | 2020.04.16 |
python HTTP, GET, POST, REST API (0) | 2020.03.24 |
파이썬 딕셔너리 json, python dictionary json 만들기, python json, json string (0) | 2020.02.26 |