Skip to content

aconCS/gemini-lite

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

151 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Gemini Lite Protocol Implementation

A complete Java implementation of the Gemini Lite protocol — a lightweight, TCP-based alternative to HTTP — featuring a command-line client, a multi-threaded file-serving server, and a transparent intermediating proxy.

Course: BCS2110, Maastricht University
Author: Andreas Constantinou


Architecture

The client and proxy are built around a State Machine (FSM) design pattern:

User Input → CONNECTING → SENDING_REQUEST → AWAITING_REPLY
                                                  ↓
                                         PROCESSING_REPLY
                                                  ↓
                    ┌──────────────────────────────┼──────────────────────────────┐
                    ↓                              ↓                              ↓
            Status 1x (10-19)              Status 2x (20-29)              Status 3x (30-39)
            AWAITING_INPUT                DISPLAYING_CONTENT              REDIRECTING
                    ↓                              ↓                              ↓
            SENDING_REQUEST                CLOSED → Exit                 CONNECTING (loop)
                    ↑                                                
            Status 44 (Slow Down)                                      
            CONNECTING (exponential backoff)                           
                                                                       
            Status 4x/5x (Errors) → ERROR → Exit                       

All shared state is held in a ClientContext object (URI, sockets, reply, redirect count, backoff timer, proxy flag), enabling the same state machine to power both the client and the proxy with minimal duplication.


Features

Client

  • Parses gemini-lite:// URIs with strict validation (no userinfo, no fragment, 1024-byte limit)
  • Connects via TCP (default port 1958, configurable via URI or GEMINI_LITE_PROXY env var)
  • Handles all Gemini Lite reply types:
    • Input (10-19): Prompts user for input with password masking support
    • Success (20-29): Streams response body to stdout
    • Redirect (30-39): Follows redirects (max 5, with loop detection)
    • Slow Down (44): Retries with exponential backoff
    • Errors (4x, 5x): Exits with appropriate status code

Server

  • Multi-threaded — one thread per client connection
  • Serves files from a configurable filesystem root directory
  • MIME type detection: .gmitext/gemini, others use Files.probeContentType() with application/octet-stream fallback
  • Directory listing in Gemtext format (=> filename links)
  • Path traversal protection — rejects requests attempting to escape the root directory

Proxy

  • Transparent relaying — handles redirections and slow-down responses just as the client does
  • Returns status code 43 (proxy error) for:
    • Unreachable upstream servers
    • Connection resets or drops
    • Malformed or invalid upstream responses
    • Timeouts
    • Internal I/O failures
  • Environment-variable-based proxy configuration (GEMINI_LITE_PROXY)

Load Testing

File Size p50 Latency p99 Latency Throughput
64 B 3 ms 113 ms 5,766 B/s
1 KB 3 ms 4 ms 371,071 B/s
128 KB 3 ms 4 ms 39,723,333 B/s
100 MB 155 ms 283 ms 624,896,395 B/s

Build & Install

Prerequisites: Java 23+, Apache Maven

git clone https://gitlab.maastrichtuniversity.nl/I6389371/bcs2110-project.git
cd bcs2110-project
mvn clean package

The compiled JAR will be at target/bcs2110-2025.jar.


Usage

Client

java -cp target/bcs2110-2025.jar gemini_lite.Client <URL> [<input>]
  • <URL> — A gemini-lite:// URI. Scheme must be gemini-lite://, userinfo and fragments are forbidden.
  • [<input>] — Optional input string (for status 10-19 replies that prompt the user).

Examples:

# Fetch a file
java -cp target/bcs2110-2025.jar gemini_lite.Client gemini-lite://localhost:1958/index.gmi

# Fetch with a query string
java -cp target/bcs2110-2025.jar gemini_lite.Client "gemini-lite://localhost/search?q=hello"

Server

java -cp target/bcs2110-2025.jar gemini_lite.Server <directory> [<port>]
  • <directory> — Path to the directory to serve (required).
  • [<port>] — Port to listen on (default: 1958).

Example:

java -cp target/bcs2110-2025.jar gemini_lite.Server src/main/resources/root 1958

Proxy

java -cp target/bcs2110-2025.jar gemini_lite.Proxy [<port>]
  • [<port>] — Port to listen on (default: 9999).

Configure clients to route through the proxy by setting the environment variable:

export GEMINI_LITE_PROXY=localhost:9999

Example:

java -cp target/bcs2110-2025.jar gemini_lite.Proxy 9999

Project Structure

src/main/java/gemini_lite/
├── Client.java                   # Client entry point
├── Server.java                   # Server entry point
├── Proxy.java                    # Proxy entry point
├── client_engine/
│   ├── ClientEngine.java         # FSM runner (loops through states)
│   ├── ClientContext.java        # Shared state object
│   └── states/
│       ├── ClientState.java          # State interface
│       ├── ConnectingState.java      # TCP socket connection
│       ├── SendingRequestState.java  # Format and send Request
│       ├── AwaitingReplyState.java   # Parse Reply from server
│       ├── ProcessingReplyState.java # Route to next state based on status
│       ├── AwaitingInputState.java   # Prompt user for input
│       ├── DisplayingContentState.java # Dump body to stdout
│       ├── RedirectingState.java     # Follow redirect
│       ├── ProxyRelayState.java      # Relay reply to downstream client
│       ├── ClosedState.java          # Close sockets, exit
│       └── ErrorState.java           # Handle errors, exit
├── io/
│   ├── Request.java              # Request model
│   ├── Reply.java                # Reply model (status + meta + body)
│   ├── replies/                  # Status-specific reply subtypes
│   └── exceptions/               # Custom exception hierarchy
└── util/
    ├── RequestUtils.java         # URI validation
    ├── ReplyUtils.java           # MIME validation, control char detection
    ├── RequestHandler.java       # Request handling interface
    └── FileSystemRequestHandler.java # Filesystem-backed handler

Testing

Run the unit test suite with:

mvn test

Tests cover:

  • Request parsing — valid URIs, long URIs, userinfo, fragments, path normalization
  • Reply parsing — encoded replies, input replies, emoji handling
  • URI behavior — Java URI edge cases
  • Client context — input encoding, state transitions
  • State machine transitions — correctness of all FSM paths

The repository also includes 9 custom integration test cases in test-cases.json (3 client, 4 server, 2 proxy) covering edge cases like unrecognized MIME parameters, query string redirects, path traversal attacks, and proxy error reporting.


Technologies

Category Tools & Libraries
Language Java 23
Build system Apache Maven
Testing JUnit Jupiter 5.11.3
Networking TCP sockets (java.net.Socket, java.net.ServerSocket)
Concurrency Java threads (one per client connection)
Design pattern State Machine (FSM)
Security Path traversal prevention, URI validation, input sanitization

License

This project is an academic assignment for BCS2110 at Maastricht University.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages