Skip to content

🐹🌊 How web works inside client and server side. focusing on browser rendering and web application server.

Notifications You must be signed in to change notification settings

kimtth/jerry-web-render-was

Repository files navigation

Browser rendering & Web Application Server

How the web works inside the client and server-side. This project focuses on browser rendering and web server implementation. 🗂️ Built with in Java, Rust, and Python.

Contents


This project consists of three main components:

  1. A toy web browser rendering engine — Implemented in Java, Rust, and Python
  2. A simple web application server (servlet container) — Implemented in Java
  3. NIO HTTP Client/Server — Minimal Java NIO example

web_render

Image from limpet.net/mbrubeck

The toy web browser engine is influenced by limpet.net/mbrubeck's Rust works.

Rendered output of test data with perf-rainbow.html and perf-rainbow.css.

web_render

Project Structure

jerry-web-render-was/
├── org.web.labs.inside.jerry/
│   └── src/jerry/
│       ├── render/          # Java rendering engine
│       ├── rust/            # Rust rendering engine  
│       ├── python/          # Python rendering engine
│       ├── test/            # Test HTML/CSS files
│       ├── nio/             # NIO HTTP Client/Server
│       └── was/             # Web Application Server
├── render_test.sh           # Test script (Linux/Mac)
├── render_test.bat          # Test script (Windows)
└── README.md

Web Browser Rendering Engine

The rendering engine parses HTML and CSS, builds a style tree, calculates layout, and paints pixels to an image.

Rendering Pipeline

  1. HTML Parsing → DOM Tree
  2. CSS Parsing → Stylesheet
  3. Style Tree → DOM + CSS Rules applied
  4. Layout Tree → Box model calculations
  5. Painting → Pixel output

Implementations

Language Directory Description
Java render/ Original implementation
Rust rust/ Reference implementation
Python python/ New implementation

Running the Renderer

Using Test Scripts

Windows:

render_test.bat python          # Run Python renderer
render_test.bat rust            # Run Rust renderer
render_test.bat all             # Run both and compare

# With custom files
render_test.bat python --html custom.html --css custom.css

Linux/Mac:

./render_test.sh python         # Run Python renderer
./render_test.sh rust           # Run Rust renderer
./render_test.sh all            # Run both and compare

# With custom files
./render_test.sh python --html custom.html --css custom.css

Running Manually

Python:

cd org.web.labs.inside.jerry/src/jerry/python
pip install Pillow  # Required for image output
python main.py -H ../test/test.html -c ../test/test.css -o output.png -v

Rust:

cd org.web.labs.inside.jerry/src/jerry/rust
cargo run -- -h ../test/test.html -c ../test/test.css -o output.png

Java:

# Compile and run RenderMain.java
# Update file paths in RenderMain.java first

Simple Container (Web Application Server)

A lightweight servlet container implementation that combines an HTTP server with a class loader.

Architecture

graph TD
    subgraph SimpleContainer
        subgraph HTTP_Layer[HTTP Server & Handling]
            SL(Socket Listener)
            TP(Thread Pool<br/>cached executor)
            RH(Request Handler<br/>Health & Servlets)
            
            SL --> TP(Accepts & Delegates Connection)
            TP --> RH(Executes Request)
        end
        
        subgraph Servlet_Management[Servlet Management]
            CL(ClassLoader<br/>URLClassLoader)
            SC(Servlet Cache<br/>Singleton Inst.)
            CHM(Concurrent Map<br/>Thread-Safe Storage)
            
            CL --> SC(Loads Class into Cache)
            SC --> CHM(Stores Servlet Instances)
        end
        
        subgraph HTTP_Data_Model[HTTP Data Components]
            REQ(HttpRequest)
            RES(HttpResponse)
            HDR(HttpHeader & Status)
        end
    end
    
    subgraph Deployment_Structure[webapps/ Directory]
        WEBINF[WEB-INF/]
        CLASSES[classes/<br/>.class files]
        LIB[lib/<br/>.jar dependencies]
        
        WEBINF --> CLASSES
        WEBINF --> LIB
    end
    
    RH --> CL(Needs Servlet for Request)
    CL --> Deployment_Structure(Loads Class/Resource)
    
    style SL fill:#ccf,stroke:#333
    style RH fill:#bbf,stroke:#333
    style CL fill:#fcf,stroke:#333
Loading

Request Flow:

flowchart TD
    A[Client Request] --> B(Socket Listener<br/>Accept Connection);
    B --> C(Thread Pool<br/>Execute Handler);
    
    subgraph Request_Handler_Flow[Request Handler Logic]
        C --> D{Route Matching: Request Path};
        
        D -- /health or /servlets --> E1[Built-in JSON Response];
        
        D -- /servlet/name --> F1(Check Servlet Cache);
        
        F1 -- Found in Cache --> G1(IToy.doService<br/>Execute Servlet);
        F1 -- Not Found --> G2[ClassLoader<br/>Load Servlet Class];
        
        G2 --> H1[Store in Servlet Cache];
        H1 --> G1;
        
        D -- Other Paths --> I1[Static File Serving];
    end
    
    E1 --> J(Prepare HttpResponse);
    G1 --> J;
    I1 --> J;
    
    J --> K[Send Response to Client];
    
    style G1 fill:#9f9,stroke:#333
    style G2 fill:#f99,stroke:#333
Loading

Container Hierarchy (Tomcat)

  1. Engine - Represents the entire Catalina servlet engine (e.g., Catalina)
  2. Host - Represents a virtual host with multiple contexts (e.g., localhost)
  3. Context - Represents a web application with one or more wrappers
  4. Wrapper - Represents an individual servlet

Each container has Realm (Authentication), Valve (Request/Response processing), and Logger.

Features

  • Servlet Registration & Caching - Thread-safe servlet management
  • Configurable Context Path - Customize webapp location
  • Graceful Shutdown - Proper resource cleanup
  • Health Check Endpoint - /health returns server status
  • Servlet List Endpoint - /servlets returns registered servlets
  • Query String Parsing - Full URL parameter support
  • Thread Pool - Configurable concurrent request handling

Running the Server

# Default (port 8080)
java org.web.labs.inside.jerry.was.SimpleContainer

# Custom port and context
java org.web.labs.inside.jerry.was.SimpleContainer -p 9090 -c /path/to/webapps

# Show help
java org.web.labs.inside.jerry.was.SimpleContainer --help

Command Line Options

Option Description Default
-p, --port Server port 8080
-c, --context Context path ./webapps
-h, --help Show help -

API Endpoints

Endpoint Method Description
/servlet/<name> GET Execute a servlet
/health GET Health check (JSON)
/servlets GET List registered servlets (JSON)
/* GET Serve static files

Example

Request: http://localhost:8080/servlet/ToyServlet

toyserve

Servlet Implementation

Create a servlet by implementing the IToy interface:

package org.web.labs.inside.jerry.was.toyservlet;

public class MyServlet implements IToy {
    private String name = "MyServlet";
    
    public void setName(String name) { this.name = name; }
    public String getName() { return name; }
    
    @Override
    public String doService() {
        return "<html><h1>Hello from " + name + "</h1></html>";
    }
}

Directory Structure

webapps/
└── WEB-INF/
    ├── classes/        # Compiled servlet classes
    └── lib/            # JAR dependencies

NIO HTTP Client/Server

The project includes a non-blocking I/O (NIO) implementation for HTTP client and server operations.

Location

org.web.labs.inside.jerry/src/jerry/nio/
├── NIOHttpClient.java  # Non-blocking HTTP client
└── NIOHttpServer.java  # Non-blocking HTTP server

Features

  • Non-blocking I/O using Java NIO Selectors and Channels
  • State Machine for HTTP response parsing
  • Chunked Transfer Encoding support
  • Single-threaded Event Loop architecture

Running the NIO Server

# Default port 8888
java org.web.labs.inside.jerry.nio.NIOHttpServer

# Custom port
java org.web.labs.inside.jerry.nio.NIOHttpServer -p 9999

Running the NIO Client

# Connect to localhost:8888
java org.web.labs.inside.jerry.nio.NIOHttpClient

# Connect to custom host/port
java org.web.labs.inside.jerry.nio.NIOHttpClient -h example.com -p 80

Endpoints (NIO Server)

Endpoint Description
/ Welcome page with server info
/health Health check (JSON)
/echo Echoes the request back

Notes on Netty (Production NIO)

  • Netty is a production-grade NIO framework offering battle‑tested event loops, backpressure, TLS, HTTP/2, and rich pipeline handlers.
  • This repo’s nio/ server is intentionally minimal for learning: Selector loop, basic parsing, and simple handlers.
  • When building real services, prefer Netty for:
    • Robust connection handling and performance tuning across OSes
    • Mature HTTP codecs, chunked streaming, and WebSocket support
    • Extensible pipeline (handlers for logging, compression, TLS, metrics)
  • Migration path: keep your request routing and business logic, and swap the low‑level selector code with Netty ChannelInboundHandler implementations.

NIO vs IO vs Async — What’s the difference?

  • Blocking I/O (IO): Each connection handled by a dedicated thread; read() blocks until data arrives. Simple to reason about, but threads are expensive under high concurrency and can suffer head‑of‑line blocking.
  • Non‑blocking I/O (NIO): Single (or few) selector loops multiplex many connections; read() returns immediately if no data, and readiness is signaled by the selector. Great for thousands of sockets with lower thread counts; requires state machines for partial reads/writes.
  • Async I/O: OS or runtime completes operations and invokes callbacks/futures/promises. In Java, async often means NIO + completion handlers (e.g., Netty pipeline) or CompletableFuture; in Rust/Python it’s typically event loops (tokio/asyncio). Reduces busy‑waiting and improves latency with structured concurrency.
  • Trade‑offs:
    • IO → easiest API, highest thread usage, simpler debugging.
    • NIO → scalable with explicit readiness handling, more complex state management.
    • Async → scalable and expressive with backpressure, requires async‑aware libraries and careful design around cancellation/timeouts.

HTTP Protocol Notes

HTTP is based on two-way transfer (request and response). Both consist of:

  • Header - Metadata (method, path, content-type, etc.)
  • Body - Content payload

Each line is tokenized by CRLF (\r\n).

HTTP 1.1 supports chunked transfer encoding for streaming data:

<chunk-size-hex>\r\n
<chunk-data>\r\n
...
0\r\n
\r\n

Development

Prerequisites

  • Java - JDK 8 or higher
  • Rust - For Rust renderer (install via rustup)
  • Python 3 - For Python renderer
  • Pillow - Python image library (pip install Pillow)

Building

# Java (using javac or your IDE)
javac -d out src/jerry/render/*.java

# Rust
cd org.web.labs.inside.jerry/src/jerry/rust
cargo build --release

# Python (no build needed)
pip install Pillow

References

License

This project is for educational purposes.

About

🐹🌊 How web works inside client and server side. focusing on browser rendering and web application server.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published