Skip to content

Instantly share code, notes, and snippets.

@ndavison
Created November 19, 2014 10:03
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save ndavison/6a5d97cb8a9091cffa7a to your computer and use it in GitHub Desktop.
Save ndavison/6a5d97cb8a9091cffa7a to your computer and use it in GitHub Desktop.
Python socket HTTPS client connection example
#!/bin/env python
"""
A simple example of using Python sockets for a client HTTPS connection.
"""
import ssl
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('github.com', 443))
s = ssl.wrap_socket(s, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23)
s.sendall("GET / HTTP/1.1\r\nHost: github.com\r\nConnection: close\r\n\r\n")
while True:
new = s.recv(4096)
if not new:
s.close()
break
print new
@thelonesailor
Copy link

"socket.error: [Errno 111] Connection refused"

@sahin52
Copy link

sahin52 commented Apr 30, 2021

I am not sure how to thank you!
Btw in python3, the 20th row must be print(new) and12nd row must be s.sendall(b"GET ........

@demetrius-mp
Copy link

I kept getting code 400 or 301, because of the httpS thing, but this solved the problem. Thanks a lot!

@liudonghua123
Copy link

I wrote some code like this before.

import socket
import ssl

target_host = "www.dbappsecurity.com.cn"
target_port = 443

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# connect the client
client.connect((target_host, target_port))

# ssl wrap the socket
context = ssl.create_default_context()
client = context.wrap_socket(client, server_hostname=target_host)

client.send(
    f"GET /xyz/../service/ HTTP/1.1\r\nHost:{target_host}\r\n\r\n".encode())

# receive some data
response = b''
while True:
    data = client.recv(4096)
    print(f'receiving {len(data)} bytes data...')
    response += data
    if not data:
        client.close()
        break

http_response = repr(response)
http_response_len = len(http_response)

# display the response
print(f"http_response_len={http_response_len}, http_response={http_response}")

@liudonghua123
Copy link

liudonghua123 commented Jun 24, 2022

I updated the code mentioned above.

#!/usr/bin/env python
# coding=utf-8

import fire
import logging
import re
import socket
import ssl
from urllib.parse import urlparse
import cchardet as chardet


def socket_http(hostname: str, port: int, path: str, http_version: str, method: str):
    """
    Use socket to connect to hostname:port, send GET request, receive response

    Args:
        hostname (_type_): _description_
        port (_type_): _description_
        path (_type_): _description_
    """
    logging.info(
        f'Connecting to {hostname}:{port}, send {method} {path} {http_version} request')
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # connect the client
    client.connect((hostname, port))

    # ssl wrap the socket
    if(port == 443):
        context = ssl.create_default_context()
        client = context.wrap_socket(client, server_hostname=hostname)

    # https://blog.insightdatascience.com/learning-about-the-http-connection-keep-alive-header-7ebe0efa209d
    # Keep-alive connections are enabled by default in HTTP/1.1 while not in HTTP/1.0. HTTP/1.0 was designed to close the connection after every request between client and server.
    client.send(
        f"{method} {path} HTTP/{http_version}\r\nHost:{hostname}\r\n\r\n".encode())

    # receive some data
    response = b''
    while True:
        data = client.recv(4096)
        logging.debug(f'receiving {len(data)} bytes data...')
        response += data
        if not data:
            client.close()
            break

    return response


def guess_encoding(data: bytes):
    """
    Detect the encoding of the data
    # https://stackoverflow.com/questions/2423872/how-to-determine-the-encoding-of-a-string-in-python
    # https://dev.to/bowmanjd/character-encodings-and-detection-with-python-chardet-and-cchardet-4hj7

    Args:
        data (bytes): _description_
    """
    detection = chardet.detect(data)
    return detection["encoding"]


def main(url: str = "www.jwc.ynu.edu.cn/info/1013/../../gzzd/../info/1009/3129.htm", method: str = "GET", http_version: str = f"1.0", debug: bool = True):
    """
    The main function

    Args:
        url (_type_, optional): _description_. Defaults to "www.jwc.ynu.edu.cn/info/1013/../../gzzd/../info/1009/3129.htm".
        method (str, optional): _description_. Defaults to "GET".
        http_version (str, optional): _description_. Defaults to "1.0".
    """
    # configure the logging
    logging.basicConfig(
        encoding='utf-8', level=logging.DEBUG if debug else logging.INFO)
    # normalize url if the url is not prefixed with a scheme
    if not re.match(r"^\w+:\/\/", url):
        logging.warning('The scheme of url is not set, default to http://')
        url = 'http://' + url
    # use urlparse to parse the url, return ParseResult
    parsedResult = urlparse(url)
    scheme, hostname, port, path = parsedResult.scheme, parsedResult.hostname, parsedResult.port, parsedResult.path
    # check if scheme is http or https
    if scheme != 'http' and scheme != 'https':
        logging.error(f'{url} is not a valid url, only support http or https')
        exit(-1)
    # nomalize port if port is not specified
    port = port if port else 80 if parsedResult.scheme == 'http' else 443
    path = path if path else '/'
    response = socket_http(hostname, port, path, http_version, method)
    # display the response
    encoding = guess_encoding(response)
    logging.debug(f'The guessed encoding of response is {encoding}')
    print(
        f"response_len={len(response)}, response={response.decode(encoding)}")


if __name__ == '__main__':
    # Make Python Fire not use a pager when it prints a help text
    fire.core.Display = lambda lines, out: print(*lines, file=out)
    fire.Fire(main)

@athoug
Copy link

athoug commented Dec 14, 2022

Thank you so much for this example 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment