Simple HTTP Server for CircuitPython

Overview

Introduction

Documentation Status Discord Build Status Code Style: Black

Simple HTTP Server for CircuitPython

Dependencies

This driver depends on:

Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading the Adafruit library and driver bundle or individual libraries can be installed using circup.

Installing from PyPI

On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally from PyPI. To install for current user:

pip3 install adafruit-circuitpython-httpserver

To install system-wide (this may be required in some cases):

sudo pip3 install adafruit-circuitpython-httpserver

To install in a virtual environment in your current project:

mkdir project-name && cd project-name
python3 -m venv .env
source .env/bin/activate
pip3 install adafruit-circuitpython-httpserver

Installing to a Connected CircuitPython Device with Circup

Make sure that you have circup installed in your Python environment. Install it with the following command if necessary:

pip3 install circup

With circup installed and your CircuitPython device connected use the following command to install:

circup install httpserver

Or the following command to update an existing version:

circup update

Contributing

Contributions are welcome! Please read our Code of Conduct before contributing to help this project stay welcoming.

Documentation

API documentation for this library can be found on Read the Docs.

For information on building library documentation, please check out this guide.

Comments
  • Case insensitive HTTPHeaders, HTTPResponse context manager and some fixes

    Case insensitive HTTPHeaders, HTTPResponse context manager and some fixes

    Added separate class for HTTP headers, that allows accessing headers using case-insensitive names.

    Also, changed some docstrings the were unclear and one that was misleading as it was inconsistent with the code.

    Changed the order of Docs References, so that the most "important" ones are on the top, with enums and dataclass-like classes below.

    Updated library version to 1.1.0 in sphinx configuration file.

    Fixes #26 Fixes #28

    opened by michalpokusa 16
  • Refactor into separate files, additional features, addition of missing typing

    Refactor into separate files, additional features, addition of missing typing

    This PR, other that separating adafruit_httpserver into multiple files #8 adds following functionality:

    • Access to additional attributes of HTTPRequest:
      • http_version extracted from Start line, e.g. "HTTP/1.1"
      • query_params extracted from path, e.g. /path?foo=bar is parsed to {"foo": "bar"}
      • headers as a dict, e.g. {'user-agent': '...', 'host': 'esp32-s2-tft.local', 'connection': 'close'}
      • body as bytes, so both text content and images can be processed #16
    • Added new parameter to HTTPResponse which allows to set it's headers #24

    Other than that, this PR also includes:

    • Refactor or addition of "enums" like HTTPMethod and MIMEType
    • ~~Removed blocking socket~~ Implemented socket_timeout property, which fixes #21
    • Changed the way server receives data, which fixes #2
    • Smaller refactor here and there
    • More typing in functions/classes etc. #6

    Despite major refactor, the usage of library stays nearly unchanged, so it won't be neccessary to rewrite existing projects from scratch, only minor changes might be required. It surely is not perfect, but I belive it is a step in the right direction.

    Closes #2 Closes #6 Closes #8 Closes #16 Closes #17 Closes #21 Closes #24

    opened by michalpokusa 10
  • CircuitPython clients can't complete requests to HTTPServer

    CircuitPython clients can't complete requests to HTTPServer

    Trying to use adafruit_requests to connect either the 5100S-based WIZnet Pico EVB running Adafruit CircuitPython 7.2.5 on 2022-04-06; Raspberry Pi Pico with rp2040 or the Adafruit Feather RP2040 with Adafruit Ethernet FeatherWing running Adafruit CircuitPython 7.2.5 on 2022-04-06; Adafruit Feather RP2040 with rp2040 to an Adafruit Feather ESP32-S2 TFT running HTTPServer on an IPv4 (this repo simpletest example; no mDNS/hostname/FQDN involved) results in one of the following exception traces in all cases:

    Either (less often):

    Traceback (most recent call last):
      File "code.py", line 211, in <module>
      File "adafruit_requests.py", line 815, in get
      File "adafruit_requests.py", line 685, in request
    OutOfRetries: Repeated socket failures
    

    Or (more often):

    Traceback (most recent call last):
      File "code.py", line 211, in <module>
      File "adafruit_requests.py", line 815, in get
      File "adafruit_requests.py", line 661, in request
      File "adafruit_requests.py", line 529, in _get_socket
      File "adafruit_wiznet5k/adafruit_wiznet5k_socket.py", line 251, in connect
      File "adafruit_wiznet5k/adafruit_wiznet5k.py", line 574, in socket_connect
    RuntimeError: Failed to establish connection.
    

    I was initially going to file this issue in WIZnet, but a sanity check of trying to connect to the HTTPServer from ESP32-S2 (e.g., Adafruit Feather ESP32-S2 TFT) also gets an exception every time, after about a minute. That surprised me, I may be doing something wrong. Maybe it's a Requests issue.

    ESP32-S2 Client Code:

    import traceback
    import wifi
    import socketpool
    import ssl
    import adafruit_requests
    from adafruit_httpserver import HTTPServer, HTTPResponse
    from secrets import secrets
    
    wifi.radio.connect(secrets['ssid'], secrets['password'])
    pool = socketpool.SocketPool(wifi.radio)
    requests = adafruit_requests.Session(pool, ssl.create_default_context())
    
    URLS = [
        "http://wifitest.adafruit.com/testwifi/index.html",
        "http://192.168.5.32",   # LAN Apache server
        "http://192.168.6.164",  # LAN ESP32-S2 with adafruit_httpserver
    ]
    
    for url in URLS:
        try:
            print(url)
            with requests.get(url) as response:
                print(response.status_code, response.reason)
        except Exception as ex:
            traceback.print_exception(ex, ex, ex.__traceback__)
    

    Output:

    code.py output:
    http://wifitest.adafruit.com/testwifi/index.html
    200 bytearray(b'OK')
    http://192.168.5.32
    200 bytearray(b'OK')
    http://192.168.6.164
    Traceback (most recent call last):
      File "code.py", line 22, in <module>
      File "adafruit_requests.py", line 720, in get
      File "adafruit_requests.py", line 661, in request
      File "adafruit_requests.py", line 512, in _get_socket
    RuntimeError: Sending request failed
    

    Both Espressif client and Espressif server are running: Adafruit CircuitPython 7.2.5 on 2022-04-06; Adafruit Feather ESP32-S2 TFT with ESP32S2

    Connecting to the HTTPServer from a browser or curl works fine.

    Connecting to local Apache server at an IPv4 from any of these clients works fine.

    opened by anecdata 10
  • Added Features.

    Added Features.

    Yes, I've seen issue #17 and others. But I'm not the one to do major rewrites from scratch, and I liked how this server was small, and could both server files and cgi-ish content. The files being served are mainly css and js and some small graphics. These also benefit from having the ability to have cache control. The forms and table based content became to large to send as a single string body so I added the ability to send data with chunked transfer encoding.

    I looked at ampule, but TBH, I had already figured this out here, and I didn't need the extra dependencies either. This is a collection of a few added capabilities. All of the additions should not break current api implementations, just extends the capabilities. Feel free to accept, make comments or just throw away.

    This also solves the issue #21 that I was having.

    opened by paul-1 8
  • Allow user to specify request buffer size.

    Allow user to specify request buffer size.

    The next limit we ran into was and 1kb buffer for reading the request. Not knowing how much memory different devices have, it seemed best to expose the buffer size rather than just increase it.

    Co-authored-by: Shae Erisson [email protected]

    opened by cthulahoops 7
  • Explicitly set accepted socket to blocking

    Explicitly set accepted socket to blocking

    The Python documentation states that otherwise, the blocking status of the newly accepted socket is implementation-defined:

    if the listening socket is in non-blocking mode, whether the socket returned by accept() is in blocking or non-blocking mode is operating system-dependent. If you want to ensure cross-platform behaviour, it is recommended you manually override this setting. https://docs.python.org/3/library/socket.html#socket-timeouts

    When the connected socket is non-blocking (as it is on picow), the http library works erratically, depending whether the request has already arrived by the time recvfrom_into call occurs.

    Closes: adafruit/circuitpython#7086

    opened by jepler 6
  • Content-Length and multibyte characters in HTTPResponse

    Content-Length and multibyte characters in HTTPResponse

    I am building a tiny web server using adafruit_httpserver.server and circuitpython 8.0.0-beta.5 on ESP32-S2. When multibyte characters (Japanese) are included in response body, the content-length header value received by a web client looks shorter, and some tailing characters in the body are missing.

    page_html = """
    <html>
      <head><title>test</title></head>
      <body>
       some text in multibyte language here..
      </body>
    </html>
    """
    
    @server.route("/test")
    def base(request):
      return HTTPResponse(content_type='text/html;charset=UTF-8',body=page_html)
    
    $ curl -v http://192.168.xx.xx/test
     :
    < HTTP/1.1 200 OK
    < Content-Length: 117
    < Content-Type: text/html;charset=UTF-8
    < Connection: close
    <
    * Excess found in a read: excess = 10, size = 117, maxdownload = 117, bytecount = 0
    (html response are shown, but the last several characters in body are missing)
    

    Looking into adafruit_httpserver/response.py, content-length is calculated as len(body), as in, response_headers.setdefault("Content-Length", content_length or len(body)) and the whole response are sent after converted into bytes. If I replace it with len(body.encode("utf-8")), the above trouble disappears, but I'm not sure this modification is right.

    response.py#L78

    opened by jun2sak 4
  • Ensure the headers are not modified in HTTPResponse

    Ensure the headers are not modified in HTTPResponse

    Fix for issue #26 which was caused by the passed in headers dict getting modified for the response. Then the calling program reused the headers in subsequent calls. Thanks to @dhalbert and @anecdata for assistance on Discord.

    opened by spovlot 4
  • Documentation: `status` in response cannot be a tuple

    Documentation: `status` in response cannot be a tuple

    Hello,

    I started using this library today and followed the documentation describing how to set the status code when responding to a request. The triple-quoted docstring for HTTPResponse.__init__ describes status as:

        :param tuple status: The HTTP status code to return, as a tuple of (int, "message").
         Common statuses are available in `HTTPStatus`.
    

    while the type hint for the constructor argument defines it as status: tuple = HTTPStatus.OK. The name tuple is recognized by the documentation generator, which creates a link to the Python.org documentation for the tuple type.

    Passing a tuple does not actually work here, and causes the response to be badly formatted. The value given to status is saved in self.status, then passed to self._send_response, which in turn calls self._send_bytes with the value produced by self._HEADERS_FORMAT.format: https://github.com/adafruit/Adafruit_CircuitPython_HTTPServer/blob/67c3ac3b76dd419a90558460b727a07b2169351f/adafruit_httpserver.py#L232-L236

    This format method is str.format, since self._HEADERS_FORMAT is just a string: https://github.com/adafruit/Adafruit_CircuitPython_HTTPServer/blob/67c3ac3b76dd419a90558460b727a07b2169351f/adafruit_httpserver.py#L179-L185

    Passing a tuple as documented – e.g. (200, 'OK') causes it to be rendered at the position of the first {} in the format string, making the response look like this:

    HTTP/1.1 (200, 'OK')
    Content-Type: text/plain
    Content-Length: 5
    Connection: close
    
    hello
    

    This is not a valid HTTP response, so when curl receives it it fails with

    * Unsupported HTTP version in response
    * Closing connection 0
    

    The docs do suggest to use the common values pre-defined as static fields in HTTPStatus, which aren't tuples but HTTPStatus instances. They are only provided for status code 200, 404, and 500, so it's likely that users would want to provide other values and try to use tuples like (403, 'Forbidden') for example. HTTPStatus instances are rendered correctly in the format string because the class specifically defines its string representation with a __str__ method: https://github.com/adafruit/Adafruit_CircuitPython_HTTPServer/blob/67c3ac3b76dd419a90558460b727a07b2169351f/adafruit_httpserver.py#L49-L50

    I'm not sure whether this should be best handled as purely a documentation change – no longer suggesting tuple – or as a code change, actually supporting tuple and rendering it correctly, maybe even raising an exception if a tuple is provided that is not made of an int and a string. I'm far from an authority on the matter, but it seems more Pythonic to me to actually support tuples rather than require an HTTPStatus object or a protocol-formatted string to directly inject into the response.

    Thanks for this library!

    opened by nicolasff 4
  • Change request handling to use split instead of regular expressions.

    Change request handling to use split instead of regular expressions.

    The regular expression fails with a stack overflow for paths of more than 135 characters. The split also appears to be much faster.

    Failing parse:

    >>> import re
    >>> _REQUEST_RE = re.compile(r"(\S+)\s+(\S+)\s")
    >>> _REQUEST_RE.match("GET /" + ("a" * 135) + " whatev")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    RuntimeError: maximum recursion depth exceeded
    

    Co-authored-by: Shae Erisson [email protected]

    opened by cthulahoops 4
  • Adding cors headers messes up content-type and content-length

    Adding cors headers messes up content-type and content-length

    Given the following object:

    headers = {
        "Access-Control-Allow-Headers": "*",
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "GET,POST,DELETE"
    }
    

    And the following server function:

    @server.route("/api/v1/info", HTTPMethod.GET)
    def info(request):
        """Return info"""
        try:
            obj = {"version": Api.api_version, "name": Api.api_name}
            response = json.dumps(obj)
            return HTTPResponse(status=CommonHTTPStatus.OK_200, body=response, content_type=MIMEType.TYPE_JSON, headers=Api.headers)
        except:
            return HTTPResponse(status=CommonHTTPStatus.BAD_REQUEST_400, headers=Api.headers)
    

    The content-type gets turned to octet-stream and the content-length gets set to 1024.

    doing this in the response.py code in the _construct_response_bytes function works fine:

    headers.setdefault("Access-Control-Allow-Headers", "*")
    headers.setdefault("Access-Control-Allow-Origin", "*")
    headers.setdefault("Access-Control-Allow-Methods", "*")
    

    this is a temporary solution, but not very preferred. I have looked around in the code and I have no idea what causes this issue

    opened by mandar1jn 3
  • HTTP Requests from Chrome block the server

    HTTP Requests from Chrome block the server

    There seems to be a problem handling requests from Chrome. Chrome locks the http server for a minute, blocking any other requests from being handled - other than requests from the same Chrome tab. A minute after the last request from Chrome, the server will start handling other requests again.

    If you start a server, it will handle requests correctly from clients like node-red, firefox etc. (anything other than Chrome!). If you are looping with server.poll() the loop will run correctly, but will freeze for 60 seconds after the Chrome request. An example from an ESP32-S3 I am using:

    pool = socketpool.SocketPool(wifi.radio)
    server = HTTPServer(pool)
    server.request_buffer_size = 2048
    server.socket_timeout = 1
    server.start(str(wifi.radio.ipv4_address))
    
    @server.route("/")
    def base(request: HTTPRequest):
        with HTTPResponse(request, content_type=MIMEType.TYPE_TXT, chunked=False) as response:
            try:
                print("REQUEST: /")
                response.send("HELLO")
            except OSError:
                response.send("ERROR")
    
    while True:
        try:
            led.value = True
            time.sleep(0.05)
            led.value = False
            time.sleep(0.1)
            server.poll()
            
        except OSError as error:
            print(error)
            continue
    

    As soon as you hit the server with Chrome, the server responds but stops the loop and stops handling requests from other clients. If you make another request from chrome in the same tab, it will be handled, and seemingly at that time pending requests from other clients can sometimes be handled but the loop will freeze again right after.

    Making a request to an undefined path from Chrome freezes the loop also - you get the 404 in chrome and then server loop freezes - whatever code you put in your handler does not fix this problem.

    A minute after the last Chrome request, the loop restarts and the server functions normally.

    If you quit Chrome while the loop is locked, the loop restarts immediately.

    I've tried a bunch of things to fix this, including closing the connection in the handler, sending various keep-alive responses to drop the connection - none work.

    I think this needs an SDK level fix!

    opened by yousafs 10
Releases(2.0.0)
Owner
Adafruit Industries
Adafruit Industries
RollerScanner — Fast Port Scanner Written On Python

RollerScanner RollerScanner — Fast Port Scanner Written On Python Installation You should clone this repository using: git clone https://github.com/Ma

68 Nov 09, 2022
Utility for converting IP Fabric webhooks into a Teams format.

IP Fabric Webhook Integration for Microsoft Teams Setup IP Fabric Setup Go to Settings Webhooks Add webhook Provide a name URL will be: 'http://Y

Community Fabric 1 Jan 26, 2022
This is a simple python code to get the list of banned IP addresses from Fail2ban

Fail2ban Scripts Usage banned_list.py This script tries to get the banned list of IP addresses by Fail2ban for the service freeswitch. You can modify

Yehor Smoliakov 9 Dec 28, 2022
The sequel to SquidNet. It has many of the previous features that were in the original script, however a lot of the functions that do not serve much functionality have been removed.

SquidNet2 The sequel to SquidNet. It has many of the previous features that were in the original script, however a lot of the functions that do not se

DrSquidX 5 Mar 25, 2022
Python module to interface with Tuya WiFi smart devices

TinyTuya Python module to interface with Tuya WiFi smart devices Description This python module controls and monitors Tuya compatible WiFi Smart Devic

Jason Cox 365 Dec 26, 2022
league-connection is a python package to communicate to riot client and league client

league-connection is a python package to communicate to riot client and league client.

Sandbox 1 Sep 13, 2022
IP-Escaner - A Python Tool to obtain information from an IP address

IP-Escaner Herramienta para obtener informacion de una direccion IP Opciones de

4 Apr 09, 2022
Easily share folders between VMs.

This package aims to solve the problem of inter-VM file sharing (rather than manual copying) by allowing a VM to mount folders from any other VM's file system (or mounted network shares).

Rudd-O 12 Oct 17, 2022
A vpn that sits in your browser, accessible via a website

VPNInYourBrowser A vpn that sits in your browser, accessible via a website Example setup: https://VPNInBrowser.jaffa42.repl.co Setup Put the code onto

1 Jan 20, 2022
Python Scripts for Cisco Identity Services Engine (ISE)

A set of Python scripts to configure a freshly installed Cisco Identity Services Engine (ISE) for simple operation; in my case, a basic Cisco Software-Defined Access environment.

Roddie Hasan 9 Jul 19, 2022
TsuserverMoS - A Python-based server for Attorney Online,

tsuserverMoS A Python-based server for Attorney Online, forked from RealKaiser/tsuserverCC Requires Python 3.7+ and PyYAML. Changes/additions from tsu

1 Dec 30, 2021
HTTP proxy pool server primarily meant for evading IP whitelists

proxy-forwarder HTTP proxy pool server primarily meant for evading IP whitelists. Setup Create a file named proxies.txt and fill it with your HTTP pro

h0nda 2 Feb 19, 2022
tradingview socket api for fetching real time prices.

tradingView-API tradingview socket api for fetching real time prices. How to run git clone https://github.com/mohamadkhalaj/tradingView-API.git cd tra

MohammadKhalaj 35 Dec 31, 2022
BibleNotifyDesktop - Desktop version of Bible Notify

Bible Notify Desktop This is the repository for the Desktop version of the daily

Bible Notify 5 Nov 16, 2022
ThorFI: A Novel Approach for Network Fault Injection as a Service

ThorFI: a Novel Approach for Network Fault Injection as a Service This repo includes ThorFI, a novel fault injection solution for virtual networks in

DESSERT research lab (Federico II University of Naples, Italy) 6 Dec 14, 2022
A simple, configurable application and set of services to monitor multiple raspberry pi's on a network.

rpi-info-monitor A simple, configurable application and set of services to monitor multiple raspberry pi's on a network. It can be used in a terminal

Kevin Kirchhoff 11 May 22, 2022
ServerStatus with node management and monitor

ServerStatus with node management and monitor

lidalao 162 Jan 01, 2023
Cobalt Strike C2 Reverse proxy that fends off Blue Teams, AVs, EDRs, scanners through packet inspection and malleable profile correlation

Cobalt Strike C2 Reverse proxy that fends off Blue Teams, AVs, EDRs, scanners through packet inspection and malleable profile correlation

Mariusz B. 715 Dec 25, 2022
track IP Address

ipX Table of Contents ipX Welcome Features Uses Author 📝 License Welcome find the location of an IP address. Specifically, you can get the following

Ali Shahid 15 Sep 26, 2022
API Server for VoIP analysis (CDR + Audio CODECs)

Swagger generated server Overview This server was generated by the swagger-codegen project. By using the OpenAPI-Spec from a remote server, you can ea

Noor Muhammad Malik 1 Jan 11, 2022