Skip to main content

A straightforward Python package that functions as an HTTP(s) server

Project description

CryskuraHTTP

CryskuraHTTP Logo

CryskuraHTTP is a lightweight, customizable HTTP(s) server implemented in Python that supports basic HTTP(s) functionalities, including serving files and handling errors, with support for custom services, API calls, and authentication.

Features

CryskuraHTTP is an extension of Python's built-in http.server, with zero external dependencies. You can leverage it to implement Python HTTP services without needing to install large software or libraries. It can also be used as a file sharing tool, supporting file serving and uploading through the browser, and can be launched from the Windows right-click menu.

  • Customizable Services: Easily add custom services by extending the BaseService class.
  • Customizable API Calls: Define custom API calls with the APIService class.
  • Error Handling: Customizable error handling via the ErrorService class.
  • File Serving: Serve files from a specified directory via the FileService class.
  • File Uploading: Handle file uploads via POST requests with the FileService class.
  • WebPage Serving: Serve web pages without allowing directory listing via the PageService class.
  • Customizable Routes: Define custom routes for your services with the Route class.
  • Customizable Authentication: Implement custom authentication for your services.
  • Auto uPnP Port Forwarding: Automatically forward ports using uPnP.
  • Request Handling: Handle GET, POST, HEAD requests.
  • Resumable Downloads: Supports resumable downloads for large files when serving files.
  • Redirects: Supports 301 and 308 redirects.
  • SSL Support: Optionally enable SSL by providing a certificate file.
  • IPv6 & Dual-Stack: Full IPv6 support with dual-stack mode enabled by default.
  • Multi-IP/Port Listening: Bind to multiple interfaces and ports simultaneously.
  • Threaded Server: Supports multi-threaded request handling for better performance.
  • Command-Line Interface: Run the server from the command line with custom settings.
  • Right Click Support: Supports right-click context menu for launching the server on Windows.

This project is not designed to replace full-scale, production-grade HTTP servers. Instead, it is ideal for small-scale web UI development or for use alongside tools like pywebview and qtwebengine. So don't expect it to handle thousands of concurrent connections or to have advanced features like load balancing or caching.

Requirements

  • Python 3.7+ (no external dependencies)

Installation

  1. Install the package using pip:

    pip install cryskura
    
  2. You can also download whl file from GitHub Releases and install it using pip:

    pip install cryskura-1.0-py3-none-any.wh
    
  3. Clone the repository and install manually if you want to modify the source code:

    git clone https://github.com/HofNature/CryskuraHTTP.git
    cd CryskuraHTTP
    python setup.py install
    

Quick Start

Starting the Server

To start the server with default settings:

from cryskura import Server
server = Server()
server.start()

This starts on [::1]:8080 with dual-stack enabled, serving the current directory.

The CLI listens on :: (all interfaces, dual-stack) at port 8080 by default:

cryskura

You can also specify a custom interface, port, and directory:

cryskura --interface ::1 --port 8080 --path /path/to/serve

This will start the server on localhost at port 8080 and serve files from /path/to/serve.

Register to Right-Click Menu

You can add the server to the right-click context menu on Windows by running:

cryskura --addRightClick # You can also use -ar as a short form

Note: If arguments like --interface, --port, --browser, etc., are provided, the server will start with the specified settings when launched from the right-click menu.

If you want to remove it from the right-click menu, run:

cryskura --removeRightClick # You can also use -rr as a short form

Note: This feature is only available on Windows. For Windows 11 24h2 and above, if Sudo is enabled, it will be called automatically; otherwise, you need to run it manually with administrator privileges.

Stopping the Server

When using the Python API, you can stop the server by calling the stop() method:

server.stop()

Note: Only the threaded server can be stopped using this method. The non-threaded server will block the thread, so it cannot be stopped by calling the stop() method. You can stop the non-threaded server by pressing Ctrl+C in the terminal.

When using the command line, you can stop the server by pressing Ctrl+C in the terminal.

Command-Line Interface

You can get help on the command-line interface by running:

cryskura --help

This will show the available options:

  • -h, --help: Show help message and exit.
  • -u, --uPnP: Enable uPnP port forwarding.
  • -v, --version: Show program's version number and exit.
  • -b, --browser: Open the browser after starting the server.
  • -ba, --browserAddress: The address to open in the browser.
  • -w, --webMode: Enable web mode, which means only files can be accessed, not directories.
  • -f, --forcePort: Force to use the specified port even if it is already in use.
  • -t, --allowUpload: Allow file upload.
  • -r, --allowResume: Allow resume download.
  • -ar, --addRightClick: Add to right-click menu.
  • -rr, --removeRightClick: Remove from right-click menu.
  • -d PATH, --path PATH: The path to the directory to serve.
  • -n NAME, --name NAME: The name of the server.
  • -p PORT, --port PORT: The port to listen on. Can be specified multiple times for multi-port listening.
  • -c CERTFILE, --certfile CERTFILE: The path to the certificate file.
  • -i INTERFACE, --interface INTERFACE: The interface to listen on. Can be specified multiple times for multi-IP listening. Accepts IPv4, IPv6 addresses, or hostnames. Default: ::.
  • -j HTTP_TO_HTTPS, --http_to_https HTTP_TO_HTTPS: Port to redirect HTTP requests to HTTPS.
  • --single-stack: Disable dual-stack mode. IPv6 sockets will only accept IPv6 connections (sets IPV6_V6ONLY=1). Useful on platforms with broken dual-stack support.

Multi-IP/Port Listening & Dual-Stack

The server supports listening on multiple interfaces and ports simultaneously. All combinations of the specified interfaces and ports are used. Dual-stack is enabled by default — binding to an IPv6 address accepts both IPv4 and IPv6 connections.

Default Behavior

# Default: 127.0.0.1:8080, dual-stack enabled
server = Server()
server.start()
# CLI default: [::]:8080 (all interfaces, dual-stack)
cryskura

Listening on Multiple Ports

server = Server(interface="::", port=[8080, 8443])
server.start()

Or from the command line:

cryskura -i :: -p 8080 -p 8443

Listening on Multiple Interfaces

server = Server(interface=["192.168.1.5", "10.0.0.1"], port=8080)
server.start()

IPv6 and Dual-Stack

Full IPv6 is supported. Use :: for all IPv6 interfaces, ::1 for IPv6 localhost:

# IPv6 + IPv4 via dual-stack (default)
server = Server(interface="::", port=8080)

# IPv6-only (no IPv4)
server = Server(interface="::", port=8080, dual_stack=False)

# IPv4-only
server = Server(interface="0.0.0.0", port=8080)

From the command line:

# All interfaces, dual-stack (default)
cryskura

# IPv6 only, disable dual-stack
cryskura --single-stack

# IPv4 only
cryskura -i 0.0.0.0

Note: Dual-stack is implemented by setting IPV6_V6ONLY=0 on IPv6 sockets. On platforms where this is unreliable, use --single-stack and explicitly bind to -i :: -i 0.0.0.0 instead.

Mixed IPv4/IPv6 Multi-Bind

server = Server(interface=["127.0.0.1", "::1"], port=[8080])
server.start()

This creates two separate sockets — one for IPv4 on 127.0.0.1:8080 and one for IPv6 on [::1]:8080.

Using as a Python Module

Custom Configuration

You can customize the server by providing different parameters:

from cryskura import Server
from cryskura.Services import FileService,PageService,RedirectService,APIService

# Create services
fs=FileService(r"/path/to/file","/Files",allowResume=True,allowUpload=True)
rs=RedirectService("/Redirect","https://www.google.com")
ps1=PageService(r"/path/to/html/example.com","/",host="example.com")
ps2=PageService(r"/path/to/html/default","/")

# Define the API function
def APIFunc(request, path, args, headers, content, method):
    """
    A sample API function for handling API requests.

    Parameters:
    - request: The HTTP request object.
    - path: The sub-path of the request URL after the API endpoint.
    - args: The query parameters from the URL as a dictionary.
    - headers: The headers from the request as a dictionary.
    - content: The body content of the request as bytes.
    - method: The HTTP method used (e.g., "GET", "POST").

    Returns:
    - code: An integer HTTP status code (e.g., 200 for success).
    - response_headers: A dictionary of headers to include in the response.
    - response_content: The body content to send in the response as bytes.
    """
    print(f"API {method} {path} {args}")
    # Process the request and generate a response
    code = 200  # HTTP status code
    response_headers = {"Content-Type": "text/plain"}  # Response headers
    response_content = b"API Call"  # Response body content
    return code, response_headers, response_content

# Create API service
api=APIService("/API",func=APIFunc)

# Start the server
server=Server(services=[fs,rs,api,ps1,ps2],certfile="/path/to/cert.pem",uPnP=True)
server.start()

This will start the server with the following services:

  • FileService: Serves files from /path/to/file at the /Files endpoint, allowing resumable downloads and file uploads.
  • RedirectService: Redirects requests from /Redirect to https://www.google.com.
  • PageService 1: Hosts web pages located in /path/to/html/example.com and serves them at the root endpoint / only when user requests with domain example.com.
  • PageService 2: Hosts default web pages located in /path/to/html/default and serves them at the root endpoint / for all other requests.
  • APIService: Handles API calls at the /API endpoint, printing request details and responding with a plain text message.

And with the following settings:

  • SSL Support: Uses the certificate file located at /path/to/cert.pem for SSL encryption.
  • uPnP Port Forwarding: Automatically forwards ports using uPnP.

Route Priority

If multiple services have conflicting routes, the priority is determined by the order in which the services are listed in the services parameter. The service listed first will have the highest priority, and so on.

For example:

from cryskura import Server
from cryskura.Services import FileService, PageService

fs = FileService(r"/path/to/files", "/files")
ps = PageService(r"/path/to/pages", "/")

server = Server(services=[fs,ps])
server.start()

In this case, FileService will have priority over PageService for routes that conflict between the two services. So if a request is made to /files/index.html, it will be handled by FileService and not PageService.

Authentication

To implement custom authentication, you need to define an authentication function and pass it to the service that requires authentication. The authentication function should accept four parameters: cookies, path, args, and operation. It should return True if the authentication is successful, and False otherwise.

Here is an example of how to implement custom authentication:

from cryskura import Server
from cryskura.Services import FileService

# Define authentication function
def AUTHFunc(cookies, path, args, operation):
    print(f"AUTH {operation} {path} {args}")
    if args.get('passwd') == "passwd" and operation == "GET":
        return True
    elif args.get('passwd') == "admin" and operation == "POST":
        return True
    return False

# Create a file service with authentication
fs = FileService(r"/path/to/files", "/files", allowResume=True, auth_func=AUTHFunc)

# Start the server
server = Server(services=[fs])
server.start()

In this example, the AUTHFunc function checks the passwd parameter in the request arguments to authenticate GET and POST requests. If the passwd parameter is passwd for GET requests or admin for POST requests, the authentication is successful. Otherwise, the authentication fails.

You can customize the AUTHFunc function to implement your own authentication logic, such as checking cookies, headers, or other request parameters.

Built-in Login Page Authentication

CryskuraHTTP also provides a built-in cookie-based authentication service with a login page.

from cryskura import Server
from cryskura.Services import FileService, AuthService, AuthVerify

# 1) Define username/password pairs
verify = AuthVerify({
    "admin": "admin123",
    "guest": "guest123",
}, expire_time=7 * 86400)

# 2) Mount AuthService
#    /login        -> login page + auth API
#    /private/**   -> protected routes
auth = AuthService("/login", verify, "/private", methods=["GET", "HEAD", "POST"], route_type="prefix", title="My Protected Server")

# 3) Protected content under /private
files = FileService(r"/path/to/private-files", "/private", allowUpload=False)

server = Server(services=[auth, files])
server.start()

Behavior:

  • Unauthenticated GET /private/... requests are redirected to /login?next=<original_path>.
  • After successful login, the login page returns to next automatically.
  • For non-GET unauthenticated requests, the service returns 401 with a JSON body including auth_url.

Simple JSON API Router

If you want a lightweight JSON API layer, use SimpleAPIRouter. It handles JSON request parsing, JSON responses, and path parameters such as /users/{user_id}/posts/{post_id}.

from cryskura import Server
from cryskura.Services import SimpleAPIRouter

router = SimpleAPIRouter()

@router.get("/users/{user_id}/posts/{post_id}")
def get_post(params, body):
    return 200, {
        "user_id": params["user_id"],
        "post_id": params["post_id"],
        "body": body,
    }

@router.post("/users/{user_id}/posts")
def create_post(params, body):
    return 201, {"user_id": params["user_id"], "created": body}

server = Server(services=router.build("/api"))
server.start()

Notes:

  • The template must match exactly, so /api/users/1/posts/2 is valid, but /api/users/1/posts/2/extra is not.
  • HEAD requests are supported for @router.get(...) routes and return headers without a response body.

Custom Services

To create a custom service, extend the BaseService class and implement the required methods:

from cryskura.Services import BaseService, Route

class MyService(BaseService):
    def __init__(self):
        routes = [Route("/myservice", ["GET"], "exact")]
        super().__init__(routes)

    def handle_GET(self, request, path, args):
        request.send_response(200)
        request.send_header("Content-Type", "text/plain")
        request.end_headers()
        request.wfile.write(b"Hello from MyService!")

Using the uPnP Client

CryskuraHTTP includes a built-in uPnP client to facilitate automatic port forwarding. This can be particularly useful when running the server behind a router or firewall.

Enabling uPnP

To enable uPnP port forwarding, you can use the --uPnP flag when starting the server from the command line:

cryskura --interface 0.0.0.0 --port 8080 --path /path/to/serve --uPnP

Using uPnP in Python

You can also enable uPnP port forwarding when starting the server using the Python API:

from cryskura import Server

server = Server(interface="0.0.0.0", port=8080, uPnP=True)
server.start()

Custom uPnP Configuration

The built-in uPnP client can be used independently for custom port forwarding needs. Here’s how you can use the uPnPClient class directly in your Python code:

Initializing the uPnP Client

First, you need to initialize the uPnPClient with the desired network interface:

from cryskura import uPnP

# Initialize uPnP client for a specific interface
upnp_client = uPnP(interface="0.0.0.0")
# Use 0.0.0.0 for all IPv4 interfaces

if upnp_client.available:
    print("uPnP client initialized successfully.")
else:
    print("uPnP client is not available.")

Adding a Port Mapping

To add a port mapping, use the add_port_mapping method:

if upnp_client.available:
    success, mappings = upnp_client.add_port_mapping(
        remote_port=8080, 
        local_port=8080, 
        protocol="TCP", 
        description="CryskuraHTTP Server"
    )
    if success:
        print("Port mapping added successfully.")
        for mapping in mappings:
            print(f"Service is available at {mapping[0]}:{mapping[1]}")
    else:
        print("Failed to add port mapping.")

This will add a port mapping for port 8080 on the remote device to port 8080 on the local device using the TCP protocol. The description is just a label for the mapping, which can be used to identify it later.

Removing All Port Mappings

To remove port mappings, use the remove_port_mapping method:

if upnp_client.available:
    upnp_client.remove_port_mapping()
    print("Port mapping removed.")

This will remove all port mappings that were added by the client. It's a good practice to remove port mappings when they are no longer needed. You can place this code in a cleanup section of your script or in an exception handler to ensure that the mappings are removed even if an error occurs. For example:

try:
    Your code here...
except Exception as exception:
    upnp_client.remove_port_mapping()
    raise exception

This will ensure that the port mappings are removed even if an exception occurs during the execution of your code.

Troubleshooting uPnP

If you encounter issues with uPnP, ensure that:

  • Your router supports uPnP and has it enabled.

  • The upnpclient library is installed. You can install it using:

    pip install upnpclient
    

    or install the cryskura package with the upnp extra:

    pip install cryskura[upnp]
    
  • The network interface specified is correct and accessible.

For more detailed information on the uPnPClient class and its methods, refer to the source code in the uPnP.py file.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

Contact

For any questions or suggestions, please open an issue here on GitHub.


Enjoy using CryskuraHTTP!

CryskuraHTTP

CryskuraHTTP Logo

CryskuraHTTP 是一个用 Python 实现的轻量级、可定制的 HTTP(s) 服务器,支持基本的 HTTP(s) 功能,包括文件服务和错误处理,并支持自定义服务和鉴权。

特性

CryskuraHTTP 是 Python 内置 http.server 的扩展,无需任何外部依赖。您可以利用它实现 Python HTTP 服务,而无需安装大型软件或库。它还可以用作文件共享工具,支持通过浏览器进行文件下载和上传,并可通过 Windows 右键菜单启动。

  • 可定制服务:通过扩展 BaseService 类轻松添加自定义服务。
  • 可定制 API 调用:使用 APIService 类定义自定义 API 调用。
  • 错误处理:通过 ErrorService 类实现可定制的错误处理。
  • 文件服务:通过 FileService 类从指定目录提供文件服务。
  • 文件上传:通过 FileService 类处理通过 POST 请求的文件上传。
  • 网页服务:通过 PageService 类提供网页服务,而不允许用户查看目录列表。
  • 可定制路由:使用 Route 类为您的服务定义自定义路由。
  • 可定制身份验证:为您的服务实现自定义身份验证。
  • 自动 uPnP 端口转发:使用 uPnP 自动转发端口。
  • 请求处理:处理 GET、POST、HEAD 请求。
  • 可续传下载:在提供文件服务时支持大文件的可续传下载。
  • 重定向:支持 301 和 308 重定向。
  • SSL 支持:通过提供证书文件可选启用 SSL。
  • IPv6 与双栈:完整 IPv6 支持,支持自动双栈模式。
  • 多 IP/端口监听:可同时绑定多个网络接口和端口。
  • 多线程服务器:支持多线程请求处理以提高性能。
  • 命令行界面:通过命令行运行服务器并进行自定义设置。
  • 右键支持:支持在 Windows 上通过右键菜单启动服务器。

该项目并非设计用于替代全规模、生产级 HTTP 服务器。它更适合小规模的 Web UI 开发或与 pywebviewqtwebengine 等工具一起使用。因此,不要期望它能处理数千个并发连接或具有负载均衡或缓存等高级功能。

要求

  • Python 3.7+(无外部依赖)

安装

  1. 使用 pip 安装包:

    pip install cryskura
    
  2. 您也可以从 GitHub Releases 下载 whl 文件并使用 pip 安装:

    pip install cryskura-1.0-py3-none-any.whl
    
  3. 如果您想修改源代码,可以克隆仓库并手动安装:

    git clone https://github.com/HofNature/CryskuraHTTP.git
    cd CryskuraHTTP
    python setup.py install
    

快速开始

启动服务器

使用默认设置启动服务器:

from cryskura import Server
server = Server()
server.start()

这将在 [::1]:8080 启动服务器,从当前目录提供文件服务。

命令行默认监听 [::]:8080(所有接口,双栈):

cryskura

也可以手动指定接口、端口和目录:

cryskura --interface ::1 --port 8080 --path /path/to/serve

这将在 localhost 上的端口 8080 启动服务器,并从 /path/to/serve 提供文件服务。

注册到右键菜单

您可以通过运行以下命令将服务器添加到 Windows 的右键菜单:

cryskura --addRightClick # 您也可以使用 -ar 作为简写

注意:如果提供了 --interface--port--browser 等参数,当从右键菜单启动服务器时,将使用指定的设置。

如果您想从右键菜单中移除它,请运行:

cryskura --removeRightClick # 您也可以使用 -rr 作为简写

注意:此功能仅在 Windows 上可用。对于 Windows 11 24h2 及以上版本,如果启用了 Sudo,它将自动调用;否则,您需要手动以管理员权限运行。

停止服务器

使用 Python API 时,您可以通过调用 stop() 方法停止服务器:

server.stop()

注意:只有多线程服务器可以使用此方法停止。非多线程服务器将阻塞线程,因此无法通过调用 stop() 方法停止。您可以通过在终端中按 Ctrl+C 停止非多线程服务器。

使用命令行时,您可以通过在终端中按 Ctrl+C 停止服务器。

命令行界面

您可以通过运行以下命令获取命令行界面的帮助:

cryskura --help

这将显示可用选项:

  • -h, --help:显示帮助信息并退出。
  • -u, --uPnP:启用 uPnP 端口转发。
  • -v, --version:显示程序的版本号并退出。
  • -b, --browser:启动服务器后打开浏览器。
  • -ba, --browserAddress:浏览器打开的地址。
  • -w, --webMode:启用 Web 模式,这意味着只能访问文件,不能访问目录。
  • -f, --forcePort:强制使用指定端口,即使该端口已被占用。
  • -t, --allowUpload:允许文件上传。
  • -r, --allowResume:允许续传下载。
  • -ar, --addRightClick:添加到右键菜单。
  • -rr, --removeRightClick:从右键菜单中移除。
  • -d PATH, --path PATH:要提供服务的目录路径。
  • -n NAME, --name NAME:服务器的名称。
  • -p PORT, --port PORT:监听的端口。可多次指定以实现多端口监听。
  • -c CERTFILE, --certfile CERTFILE:证书文件的路径。
  • -i INTERFACE, --interface INTERFACE:监听的接口。可多次指定以实现多 IP 监听。支持 IPv4、IPv6 地址或主机名。默认值:::
  • -j HTTP_TO_HTTPS, --http_to_https HTTP_TO_HTTPS:将 HTTP 请求重定向到 HTTPS 的端口。
  • --single-stack:禁用双栈模式。IPv6 套接字将只接受 IPv6 连接(IPV6_V6ONLY=1)。适用于双栈支持有问题的平台。

多 IP/端口监听与双栈

服务器支持同时监听多个网络接口和端口,所有指定的接口和端口组合均会被使用。

默认行为

# 默认:127.0.0.1:8080,自动双栈
server = Server()
server.start()
# 命令行默认:[::]:8080,自动双栈
cryskura

监听多个端口

server = Server(interface="::", port=[8080, 8443])
server.start()

或通过命令行:

cryskura -i :: -p 8080 -p 8443

监听多个接口

server = Server(interface=["192.168.1.5", "10.0.0.1"], port=8080)
server.start()

IPv6 与双栈

完整支持 IPv6。使用 :: 表示所有 IPv6 接口,::1 表示 IPv6 本地回环:

# IPv6 + IPv4 双栈(默认)
server = Server(interface="::", port=8080)

# 仅 IPv6(关闭双栈)
server = Server(interface="::", port=8080, dual_stack=False)

# 仅 IPv4
server = Server(interface="0.0.0.0", port=8080)

通过命令行:

# 所有接口,双栈模式(默认)
cryskura -i ::

# 仅 IPv6,禁用双栈
cryskura -i :: --single-stack

# 仅 IPv4
cryskura -i 0.0.0.0

注意:双栈通过设置 IPV6_V6ONLY=0 实现。在部分平台上此方式可能不可靠,可使用 --single-stack 并显式绑定 -i :: -i 0.0.0.0 替代。

混合 IPv4/IPv6 多地址绑定

server = Server(interface=["127.0.0.1", "::1"], port=[8080])
server.start()

这将创建两个独立的套接字——一个用于 IPv4(127.0.0.1:8080),另一个用于 IPv6([::1]:8080)。

作为 Python 模块使用

自定义配置

您可以通过提供不同的参数来自定义服务器:

from cryskura import Server
from cryskura.Services import FileService, PageService, RedirectService, APIService

# 创建服务
fs = FileService(r"/path/to/file", "/Files", allowResume=True, allowUpload=True)
rs = RedirectService("/Redirect", "https://www.google.com")
ps1 = PageService(r"/path/to/html/example.com", "/", host="example.com")
ps2 = PageService(r"/path/to/html/default", "/")

# 定义 API 函数
def APIFunc(request, path, args, headers, content, method):
    """
    用于处理 API 请求的示例函数。

    参数:
    - request:HTTP 请求对象。
    - path:API 端点之后的请求 URL 子路径。
    - args:URL 中的查询参数,字典形式。
    - headers:请求头,字典形式。
    - content:请求的主体内容,字节类型。
    - method:使用的 HTTP 方法(例如 "GET"、"POST")。

    返回:
    - code:整数类型的 HTTP 状态码(例如 200 表示成功)。
    - response_headers:要包含在响应中的头信息,字典形式。
    - response_content:响应的主体内容,字节类型。
    """
    # 为演示目的,我们将简单返回一个 200 OK 状态和一个纯文本消息。
    code = 200
    response_headers = {"Content-Type": "text/plain"}
    response_content = b"API 调用"
    return code, response_headers, response_content

# 创建 API 服务
api = APIService("/API", func=APIFunc)

# 启动服务器
server = Server(services=[fs, rs, api, ps1, ps2], certfile="/path/to/cert.pem", uPnP=True)
server.start()

这将启动具有以下服务的服务器:

  • FileService:在 /Files 端点提供 /path/to/file 的文件服务,允许续传下载和文件上传。
  • RedirectService:将 /Redirect 的请求重定向到 https://www.google.com
  • PageService 1:在根端点 / 提供 /path/to/html/example.com 的网页服务,仅当用户请求的域名为 example.com 时生效。
  • PageService 2:在根端点 / 提供 /path/to/html/default 的默认网页服务,用于所有其他请求。
  • APIService:在 /API 端点处理 API 调用,打印请求详情并响应纯文本消息。

以及以下设置:

  • SSL 支持:使用位于 /path/to/cert.pem 的证书文件进行 SSL 加密。
  • uPnP 端口转发:使用 uPnP 自动转发端口。

路由优先级

如果多个服务有冲突的路由,优先级由 services 参数中服务的顺序决定。列在前面的服务优先级最高,依此类推。

例如:

from cryskura import Server
from cryskura.Services import FileService, PageService

fs = FileService(r"/path/to/files", "/files")
ps = PageService(r"/path/to/pages", "/")

server = Server(services=[fs, ps])
server.start()

在这种情况下,对于 FileServicePageService 之间冲突的路由,FileService 将优先处理。因此,如果请求 /files/index.html,将由 FileService 处理,而不是 PageService

身份验证

要实现自定义身份验证,您需要定义一个身份验证函数并将其传递给需要身份验证的服务。身份验证函数应接受四个参数:cookiespathargsoperation。如果身份验证成功,应返回 True,否则返回 False

以下是如何实现自定义身份验证的示例:

from cryskura import Server
from cryskura.Services import FileService

# 定义身份验证函数
def AUTHFunc(cookies, path, args, operation):
    print(f"AUTH {operation} {path} {args}")
    if args.get('passwd') == "passwd" and operation == "GET":
        return True
    elif args.get('passwd') == "admin" and operation == "POST":
        return True
    return False

# 创建带有身份验证的文件服务
fs = FileService(r"/path/to/files", "/files", allowResume=True, auth_func=AUTHFunc)

# 启动服务器
server = Server(services=[fs])
server.start()

在此示例中,AUTHFunc 函数检查请求参数中的 passwd 参数以验证 GET 和 POST 请求。如果 passwd 参数为 GET 请求的 passwd 或 POST 请求的 admin,则身份验证成功。否则,身份验证失败。

您可以自定义 AUTHFunc 函数以实现自己的身份验证逻辑,例如检查 cookies、头信息或其他请求参数。

内置登录页认证

CryskuraHTTP 还提供了内置的基于 Cookie 的认证服务,并自带登录页。

from cryskura import Server
from cryskura.Services import FileService, AuthService, AuthVerify

# 1) 定义用户名和密码
verify = AuthVerify({
    "admin": "admin123",
    "guest": "guest123",
}, expire_time=7 * 86400)

# 2) 挂载 AuthService
#    /login        -> 登录页面 + 认证接口
#    /private/**   -> 受保护路径
auth = AuthService("/login", verify, "/private", methods=["GET", "HEAD", "POST"], route_type="prefix", title="我的受保护服务")

# 3) 将受保护内容挂载到 /private
files = FileService(r"/path/to/private-files", "/private", allowUpload=False)

server = Server(services=[auth, files])
server.start()

行为说明:

  • 未认证访问 GET /private/... 时,会重定向到 /login?next=<原始路径>
  • 登录成功后,登录页会自动返回 next 指向的目标路径。
  • 对于非 GET 的未认证请求,服务返回 401,并在 JSON 中包含 auth_url 字段。

简化 JSON API 路由器

如果您需要一个轻量级的 JSON API 层,可以使用 SimpleAPIRouter。它会自动处理 JSON 请求体解析、JSON 响应序列化,并支持 /users/{user_id}/posts/{post_id} 这样的路径参数。

from cryskura import Server
from cryskura.Services import SimpleAPIRouter

router = SimpleAPIRouter()

@router.get("/users/{user_id}/posts/{post_id}")
def get_post(params, body):
    return 200, {
        "user_id": params["user_id"],
        "post_id": params["post_id"],
        "body": body,
    }

@router.post("/users/{user_id}/posts")
def create_post(params, body):
    return 201, {"user_id": params["user_id"], "created": body}

server = Server(services=router.build("/api"))
server.start()

注意:

  • 模板必须严格匹配,因此 /api/users/1/posts/2 是有效路径,但 /api/users/1/posts/2/extra 不是。
  • 对于 @router.get(...) 路由,HEAD 请求也受支持,并且只返回响应头,不返回响应体。

自定义服务

要创建自定义服务,请扩展 BaseService 类并实现所需的方法:

from cryskura.Services import BaseService, Route

class MyService(BaseService):
    def __init__(self):
        routes = [Route("/myservice", ["GET"], "exact")]
        super().__init__(routes)

    def handle_GET(self, request, path, args):
        request.send_response(200)
        request.send_header("Content-Type", "text/plain")
        request.end_headers()
        request.wfile.write(b"Hello from MyService!")

使用 uPnP 客户端

CryskuraHTTP 包含一个内置的 uPnP 客户端,以便自动端口转发。这在路由器或防火墙后运行服务器时特别有用。

启用 uPnP

要启用 uPnP 端口转发,您可以在从命令行启动服务器时使用 --uPnP 标志:

cryskura --interface 0.0.0.0 --port 8080 --path /path/to/serve --uPnP

在 Python 中使用 uPnP

您还可以在使用 Python API 启动服务器时启用 uPnP 端口转发:

from cryskura import Server

server = Server(interface="0.0.0.0", port=8080, uPnP=True)
server.start()

自定义 uPnP 配置

内置的 uPnP 客户端可以独立使用以满足自定义端口转发需求。以下是如何在 Python 代码中直接使用 uPnPClient 类:

初始化 uPnP 客户端

首先,您需要使用所需的网络接口初始化 uPnPClient

from cryskura import uPnP

# 为特定接口初始化 uPnP 客户端
upnp_client = uPnP(interface="0.0.0.0")
# 使用 0.0.0.0 表示所有 IPv4 接口

if upnp_client.available:
    print("uPnP 客户端初始化成功。")
else:
    print("uPnP 客户端不可用。")

添加端口映射

要添加端口映射,请使用 add_port_mapping 方法:

if upnp_client.available:
    success, mappings = upnp_client.add_port_mapping(
        remote_port=8080, 
        description="CryskuraHTTP Server"
    )
    if success:
        print("端口映射添加成功。")
    else:
        print("添加端口映射失败。")

这将在远程设备的端口 8080 上添加到本地设备端口 8080 的端口映射,使用 TCP 协议。描述只是映射的标签,可以用于以后识别它。

移除所有端口映射

要移除端口映射,请使用 remove_port_mapping 方法:

if upnp_client.available:
    upnp_client.remove_port_mapping()
    print("端口映射已移除。")

这将移除客户端添加的所有端口映射。最好在不再需要时移除端口映射。您可以将此代码放在脚本的清理部分或异常处理程序中,以确保即使发生错误也能移除映射。例如:

try:
    # 您的代码...
except Exception as exception:
    upnp_client.remove_port_mapping()
    raise exception

这将确保即使在代码执行期间发生异常,也能移除端口映射。

uPnP 故障排除

如果遇到 uPnP 问题,请确保:

  • 您的路由器 支持 uPnP 并且已 启用

  • 安装了 upnpclient 库。您可以使用以下命令安装它:

    pip install upnpclient
    
  • 指定的网络接口是正确且可访问的。

有关 uPnPClient 类及其方法的详细信息,请参阅 uPnP.py 文件中的源代码。

许可证

本项目采用 MIT 许可证。有关详细信息,请参阅 LICENSE 文件。

贡献

欢迎贡献!请提交问题或拉取请求。

联系方式

如有任何问题或建议,请在 GitHub 上提交问题。


享受使用 CryskuraHTTP 吧!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cryskura-1.0b18.tar.gz (99.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cryskura-1.0b18-py3-none-any.whl (214.2 kB view details)

Uploaded Python 3

File details

Details for the file cryskura-1.0b18.tar.gz.

File metadata

  • Download URL: cryskura-1.0b18.tar.gz
  • Upload date:
  • Size: 99.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for cryskura-1.0b18.tar.gz
Algorithm Hash digest
SHA256 3d9933022afbc7295e750b7f8a2f38b9fc57dc862ebb3461a050514a191cf0e6
MD5 b786a4177c5c3538ec6bb4e8b03b5257
BLAKE2b-256 097f827154c2df04d550f6d712593dd26fb94a12edfca10aaf34b85405c99fc5

See more details on using hashes here.

File details

Details for the file cryskura-1.0b18-py3-none-any.whl.

File metadata

  • Download URL: cryskura-1.0b18-py3-none-any.whl
  • Upload date:
  • Size: 214.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.11

File hashes

Hashes for cryskura-1.0b18-py3-none-any.whl
Algorithm Hash digest
SHA256 9c5be3323feee5f0a663de156aa211a40cd53b615b74f25d9224232ab022ebf5
MD5 ad5dfbd1286fa13a7b1c40c7c6a15fae
BLAKE2b-256 8b1dd3c4d165fa47a0b63d336cd76783cc3b2edce036f55ffa483ec190d7900c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page