🐕

【Python】socket で TLS 対応の HTTP/1 サーバーをつくる

2024/07/04に公開

socket モジュールを使って C 言語のコードと似たような書き方をすると次のようなコードになる。Chrome でアクセスするとクラッシュしてしまうが、curl に対しては動く

import socket
import ssl

addr = '0.0.0.0'
port = 8000

server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((addr, port))
server.listen(5)

context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain('localhost.pem', 'localhost-key.pem')
context.set_alpn_protocols(['http/1.1'])

print('serving at port', port)

msg =(
  f"HTTP/1.1 200 OK\r\n"
  f"Content-Type: text/html; charset=UTF-8\r\n"
  f"Content-Length: 11\r\n"
  f"Connection: Close\r\n"
  f"\r\n"
  f"Hello World"
).encode()

while True:

    (client, addr2) = server.accept()
    stream = context.wrap_socket(sock=client, server_side=True)
    stream.sendall(msg)
    stream.shutdown(socket.SHUT_RDWR)
    stream.close()

server.close()

curl でリクエストを送信すると次のような結果が得られる

curl -v https://localhost:8000
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN: server accepted http/1.1
* Server certificate:
*  subject: O=mkcert development certificate; OU=masakielastic@penguin
*  start date: Jul  3 04:52:09 2024 GMT
*  expire date: Oct  3 04:52:09 2026 GMT
*  subjectAltName: host "localhost" matched cert's "localhost"
*  issuer: O=mkcert development CA; OU=masakielastic@penguin; CN=mkcert masakielastic@penguin
*  SSL certificate verify ok.
* using HTTP/1.1
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.88.1
> Accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/1.1 200 OK
< Content-Type: text/html; charset=UTF-8
< Content-Length: 11
< Connection: Close
< 
* Closing connection 0
* Send failure: Broken pipe
Hello World

Discussion