Skip to content

Commit bad05d9

Browse files
committed
added docs to describe the changes to terminal-http
1 parent aeae031 commit bad05d9

1 file changed

Lines changed: 196 additions & 95 deletions

File tree

content/docs/readline/connectivity.md

Lines changed: 196 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -381,142 +381,247 @@ public class TelnetShell {
381381

382382
## HTTP/WebSocket Connectivity
383383

384-
WebSocket terminals enable browser-based terminal access, perfect for web applications.
384+
WebSocket terminals enable browser-based terminal access, perfect for web applications. The terminal-http module uses Netty for high-performance WebSocket handling and includes a default web page using [xterm.js](https://github.com/xtermjs/xterm.js).
385385

386386
### Maven Dependency
387387

388388
```xml
389389
<dependency>
390390
<groupId>org.aesh</groupId>
391-
<artifactId>aesh-terminal-http</artifactId>
392-
<version>2.6</version>
391+
<artifactId>terminal-http</artifactId>
392+
<version>3.0</version>
393393
</dependency>
394394
```
395395

396396
### Basic WebSocket Server
397397

398398
```java
399-
import org.aesh.terminal.http.HttpTerminal;
399+
import org.aesh.terminal.http.netty.NettyWebsocketTtyBootstrap;
400400
import org.aesh.readline.Readline;
401401
import org.aesh.readline.ReadlineBuilder;
402402
import org.aesh.terminal.Connection;
403403

404+
import java.util.concurrent.TimeUnit;
405+
404406
public class WebTerminal {
405-
406-
public static void main(String[] args) throws Exception {
407-
HttpTerminal http = HttpTerminal.builder()
408-
.host("0.0.0.0")
409-
.port(8080)
410-
.webSocketPath("/terminal")
411-
.connectionHandler(WebTerminal::handleConnection)
412-
.build();
413-
414-
System.out.println("WebSocket terminal started");
415-
System.out.println("Open browser to: http://localhost:8080/terminal");
416-
417-
http.start();
418-
Thread.currentThread().join();
407+
408+
public static synchronized void main(String[] args) throws Exception {
409+
NettyWebsocketTtyBootstrap bootstrap = new NettyWebsocketTtyBootstrap()
410+
.setHost("localhost")
411+
.setPort(8080);
412+
413+
bootstrap.start(WebTerminal::handleConnection).get(10, TimeUnit.SECONDS);
414+
415+
System.out.println("WebSocket terminal started on http://localhost:8080");
416+
WebTerminal.class.wait();
419417
}
420-
418+
421419
private static void handleConnection(Connection connection) {
422420
Readline readline = ReadlineBuilder.builder().build();
423-
421+
424422
connection.write("Welcome to Web Terminal!\r\n");
425423
read(connection, readline);
426424
}
427-
425+
428426
private static void read(Connection connection, Readline readline) {
429427
readline.readline(connection, "[web]$ ", input -> {
430428
if (input == null || input.equals("exit")) {
431429
connection.write("Goodbye!\r\n");
432430
connection.close();
433431
return;
434432
}
435-
433+
436434
connection.write("You entered: " + input + "\r\n");
437435
read(connection, readline);
438436
});
439437
}
440438
}
441439
```
442440

443-
### WebSocket Builder Options
441+
### WebSocket Bootstrap Options
444442

445443
| Method | Type | Description |
446444
|--------|------|-------------|
447-
| `host(String)` | `String` | Bind address (default: "0.0.0.0") |
448-
| `port(int)` | `int` | HTTP port (default: 8080) |
449-
| `webSocketPath(String)` | `String` | WebSocket endpoint path |
450-
| `connectionHandler(Consumer<Connection>)` | `Consumer` | Handler for new connections |
451-
| `staticFilesPath(String)` | `String` | Path to serve static files |
452-
| `idleTimeout(long)` | `long` | Connection idle timeout (ms) |
445+
| `setHost(String)` | `String` | Bind address (default: "localhost") |
446+
| `setPort(int)` | `int` | HTTP port (default: 8080) |
447+
| `setResourcePath(String)` | `String` | Classpath path for static files (default: "/org/aesh/terminal/http") |
448+
| `setServeStaticFiles(boolean)` | `boolean` | Enable/disable static file serving (default: true) |
449+
450+
The WebSocket endpoint is always available at `/ws`.
451+
452+
### Custom Web Page
453+
454+
By default, terminal-http serves a built-in HTML page with xterm.js. To use your own web page:
455+
456+
```java
457+
// Use custom resources from your classpath
458+
NettyWebsocketTtyBootstrap bootstrap = new NettyWebsocketTtyBootstrap()
459+
.setHost("localhost")
460+
.setPort(8080)
461+
.setResourcePath("/com/myapp/web"); // Your classpath location
462+
```
463+
464+
Place your `index.html` at `src/main/resources/com/myapp/web/index.html`.
465+
466+
### WebSocket-Only Mode
467+
468+
For applications that serve HTML from a separate web server:
469+
470+
```java
471+
// Disable static file serving - only handle WebSocket at /ws
472+
NettyWebsocketTtyBootstrap bootstrap = new NettyWebsocketTtyBootstrap()
473+
.setHost("localhost")
474+
.setPort(8080)
475+
.setServeStaticFiles(false);
476+
```
453477

454478
### Browser Client with xterm.js
455479

456-
Create an HTML client using xterm.js:
480+
Create an HTML client using xterm.js from CDN:
457481

458482
```html
459-
<!DOCTYPE html>
460-
<html>
483+
<!doctype html>
484+
<html lang="en">
461485
<head>
486+
<meta charset="UTF-8">
487+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
462488
<title>Web Terminal</title>
463-
<link rel="stylesheet" href="https://unpkg.com/xterm/css/xterm.css" />
464-
<script src="https://unpkg.com/xterm/lib/xterm.js"></script>
465-
<script src="https://unpkg.com/xterm-addon-attach/lib/xterm-addon-attach.js"></script>
466-
<script src="https://unpkg.com/xterm-addon-fit/lib/xterm-addon-fit.js"></script>
489+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/css/xterm.css" />
467490
<style>
468-
body { margin: 0; padding: 20px; background: #1e1e1e; }
469-
#terminal { width: 100%; height: calc(100vh - 40px); }
491+
html, body {
492+
margin: 0;
493+
padding: 0;
494+
background: #1e1e1e;
495+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
496+
min-height: 100vh;
497+
}
498+
.wrapper {
499+
display: flex;
500+
flex-direction: column;
501+
align-items: center;
502+
padding: 20px;
503+
min-height: 100vh;
504+
box-sizing: border-box;
505+
}
506+
h1 {
507+
margin: 0 0 20px 0;
508+
font-size: 24px;
509+
color: #f0f0f0;
510+
}
511+
#terminal-container {
512+
border: 2px solid #444;
513+
border-radius: 8px;
514+
overflow: hidden;
515+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
516+
padding: 10px;
517+
background: #000;
518+
}
519+
.connection-status {
520+
margin-bottom: 15px;
521+
padding: 8px 16px;
522+
border-radius: 4px;
523+
font-size: 14px;
524+
}
525+
.status-connected { background: #1a472a; color: #4ade80; }
526+
.status-disconnected { background: #4a1a1a; color: #f87171; }
527+
.status-connecting { background: #4a3a1a; color: #fbbf24; }
470528
</style>
471529
</head>
472530
<body>
473-
<div id="terminal"></div>
531+
<div class="wrapper">
532+
<h1>Web Terminal</h1>
533+
<div id="status" class="connection-status status-connecting">Connecting...</div>
534+
<div id="terminal-container"></div>
535+
</div>
536+
537+
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.js"></script>
474538
<script>
475-
const term = new Terminal({
476-
cursorBlink: true,
477-
fontSize: 14,
478-
fontFamily: 'Menlo, Monaco, "Courier New", monospace'
479-
});
480-
481-
const fitAddon = new FitAddon.FitAddon();
482-
term.loadAddon(fitAddon);
483-
484-
term.open(document.getElementById('terminal'));
485-
fitAddon.fit();
486-
487-
// Connect to WebSocket
488-
const socket = new WebSocket('ws://localhost:8080/terminal');
489-
const attachAddon = new AttachAddon.AttachAddon(socket);
490-
term.loadAddon(attachAddon);
491-
492-
// Handle window resize
493-
window.addEventListener('resize', () => fitAddon.fit());
494-
495-
// Handle disconnection
496-
socket.onclose = () => {
497-
term.write('\r\n\x1b[31mConnection closed\x1b[0m\r\n');
498-
};
499-
500-
socket.onerror = (error) => {
501-
term.write('\r\n\x1b[31mConnection error\x1b[0m\r\n');
502-
};
539+
(function() {
540+
'use strict';
541+
542+
var statusEl = document.getElementById('status');
543+
544+
function setStatus(status, message) {
545+
statusEl.className = 'connection-status status-' + status;
546+
statusEl.textContent = message;
547+
}
548+
549+
function connect() {
550+
var wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
551+
var wsHost = window.location.host || 'localhost:8080';
552+
var wsUrl = wsProtocol + '//' + wsHost + '/ws';
553+
554+
setStatus('connecting', 'Connecting to ' + wsUrl + '...');
555+
556+
var socket = new WebSocket(wsUrl);
557+
558+
socket.onopen = function() {
559+
setStatus('connected', 'Connected');
560+
561+
var term = new Terminal({
562+
cols: 120,
563+
rows: 40,
564+
cursorBlink: true,
565+
cursorStyle: 'block',
566+
fontFamily: '"DejaVu Sans Mono", "Liberation Mono", "Courier New", monospace',
567+
fontSize: 14,
568+
theme: {
569+
background: '#000000',
570+
foreground: '#f0f0f0',
571+
cursor: '#f0f0f0'
572+
}
573+
});
574+
575+
term.open(document.getElementById('terminal-container'));
576+
577+
socket.onmessage = function(event) {
578+
if (event.type === 'message') {
579+
term.write(event.data);
580+
}
581+
};
582+
583+
term.onData(function(data) {
584+
socket.send(JSON.stringify({action: 'read', data: data}));
585+
});
586+
587+
socket.onclose = function(event) {
588+
setStatus('disconnected', 'Disconnected (code: ' + event.code + ')');
589+
term.write('\r\n\x1b[31mConnection closed.\x1b[0m\r\n');
590+
};
591+
592+
socket.onerror = function(error) {
593+
setStatus('disconnected', 'Connection error');
594+
console.error('WebSocket error:', error);
595+
};
596+
597+
term.focus();
598+
};
599+
600+
socket.onerror = function(error) {
601+
setStatus('disconnected', 'Failed to connect');
602+
console.error('WebSocket connection error:', error);
603+
};
604+
}
605+
606+
window.addEventListener('load', connect);
607+
})();
503608
</script>
504609
</body>
505610
</html>
506611
```
507612

508-
### Serving Static Files
613+
### Message Protocol
509614

510-
```java
511-
HttpTerminal http = HttpTerminal.builder()
512-
.host("0.0.0.0")
513-
.port(8080)
514-
.webSocketPath("/terminal")
515-
.staticFilesPath("src/main/resources/static") // Serve HTML/JS/CSS
516-
.connectionHandler(this::handleConnection)
517-
.build();
615+
The WebSocket uses JSON messages for communication:
616+
617+
**Client to Server (input):**
618+
```json
619+
{"action": "read", "data": "user input here"}
518620
```
519621

622+
**Server to Client (output):**
623+
Plain text with ANSI escape sequences for terminal rendering.
624+
520625
## Connection Management
521626

522627
### Connection Interface
@@ -611,21 +716,18 @@ public class MultiProtocolServer {
611716
.build();
612717

613718
// WebSocket on port 8080
614-
HttpTerminal http = HttpTerminal.builder()
615-
.port(8080)
616-
.webSocketPath("/terminal")
617-
.connectionHandler(MultiProtocolServer::handleConnection)
618-
.build();
619-
719+
NettyWebsocketTtyBootstrap http = new NettyWebsocketTtyBootstrap()
720+
.setPort(8080);
721+
620722
// Start all servers
621723
ssh.start();
622724
telnet.start();
623-
http.start();
624-
725+
http.start(MultiProtocolServer::handleConnection);
726+
625727
System.out.println("Servers started:");
626728
System.out.println(" SSH: ssh -p 2222 user@localhost");
627729
System.out.println(" Telnet: telnet localhost 2323");
628-
System.out.println(" WebSocket: http://localhost:8080/terminal");
730+
System.out.println(" WebSocket: http://localhost:8080");
629731

630732
Thread.currentThread().join();
631733
}
@@ -656,15 +758,10 @@ public class MultiProtocolServer {
656758
### Starting and Stopping
657759

658760
```java
659-
// Start server
761+
// Start servers
660762
ssh.start();
661763
telnet.start();
662-
http.start();
663-
664-
// Check if running
665-
if (ssh.isRunning()) {
666-
System.out.println("SSH server is running");
667-
}
764+
http.start(connectionHandler); // WebSocket requires handler
668765

669766
// Stop server (closes all connections)
670767
ssh.stop();
@@ -677,11 +774,15 @@ http.stop();
677774
```java
678775
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
679776
System.out.println("Shutting down servers...");
680-
777+
681778
ssh.stop();
682779
telnet.stop();
683-
http.stop();
684-
780+
try {
781+
http.stop().get(5, TimeUnit.SECONDS);
782+
} catch (Exception e) {
783+
// Handle timeout
784+
}
785+
685786
System.out.println("Servers stopped");
686787
}));
687788
```

0 commit comments

Comments
 (0)