or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdconnection-management.mdcontext-resources.mdhandlers.mdindex.mdrequest-logging.mdrequest-response.mdsecurity-ssl.mdserver-core.mdsession-management.mdutility-handlers.md

request-response.mddocs/

0

# Request and Response APIs

1

2

The request and response APIs provide the core interfaces for handling HTTP requests and generating responses, including content processing, header management, and lifecycle callbacks.

3

4

## Request Interface

5

6

The `Request` interface represents an HTTP request with content, headers, and context.

7

8

```java { .api }

9

public interface Request extends Attributes, Content.Source {

10

// Request identification and metadata

11

String getId();

12

Components getComponents();

13

ConnectionMetaData getConnectionMetaData();

14

15

// HTTP method and URI

16

String getMethod();

17

HttpURI getHttpURI();

18

19

// Request context

20

Context getContext();

21

22

// Headers and cookies

23

HttpFields getHeaders();

24

List<HttpCookie> getCookies();

25

26

// Session management

27

Session getSession(boolean create);

28

29

// Content handling (from Content.Source)

30

Content.Chunk read();

31

void demand(Runnable demandCallback);

32

void fail(Throwable failure);

33

34

// Nested interfaces

35

interface Handler {

36

boolean handle(Request request, Response response, Callback callback) throws Exception;

37

}

38

39

interface Wrapper extends Request {

40

Request getWrapped();

41

}

42

}

43

```

44

45

## Response Interface

46

47

The `Response` interface represents an HTTP response with status, headers, and content writing capabilities.

48

49

```java { .api }

50

public interface Response extends Content.Sink {

51

// Request association

52

Request getRequest();

53

54

// Status management

55

int getStatus();

56

void setStatus(int code);

57

58

// Header management

59

HttpFields.Mutable getHeaders();

60

61

// Content writing (from Content.Sink)

62

void write(boolean last, ByteBuffer byteBuffer, Callback callback);

63

64

// Error handling

65

Runnable writeError(Request request, Response response, Callback callback,

66

int code, String message);

67

68

// Response state

69

boolean isCommitted();

70

boolean isCompleted();

71

long getBytesWritten();

72

73

// Nested classes

74

class Wrapper implements Response {

75

public Response getWrapped();

76

}

77

}

78

```

79

80

## Usage Examples

81

82

### Basic Request Handling

83

84

```java

85

public class SimpleHandler extends Handler.Abstract {

86

@Override

87

public boolean handle(Request request, Response response, Callback callback)

88

throws Exception {

89

// Get request information

90

String method = request.getMethod();

91

String path = request.getHttpURI().getPath();

92

String userAgent = request.getHeaders().get("User-Agent");

93

94

System.out.println("Request: " + method + " " + path);

95

System.out.println("User-Agent: " + userAgent);

96

97

// Set response

98

response.setStatus(200);

99

response.getHeaders().put("Content-Type", "text/html");

100

101

String html = "<html><body><h1>Hello from Jetty!</h1>" +

102

"<p>Method: " + method + "</p>" +

103

"<p>Path: " + path + "</p></body></html>";

104

105

response.write(true, ByteBuffer.wrap(html.getBytes()), callback);

106

return true;

107

}

108

}

109

```

110

111

### Reading Request Content

112

113

```java

114

public class ContentHandler extends Handler.Abstract {

115

@Override

116

public boolean handle(Request request, Response response, Callback callback)

117

throws Exception {

118

119

// Check if request has content

120

String contentType = request.getHeaders().get("Content-Type");

121

if (contentType != null && contentType.startsWith("application/json")) {

122

readJsonContent(request, response, callback);

123

} else {

124

handleNoContent(response, callback);

125

}

126

127

return true;

128

}

129

130

private void readJsonContent(Request request, Response response, Callback callback) {

131

StringBuilder content = new StringBuilder();

132

133

// Read content chunks asynchronously

134

request.demand(() -> {

135

Content.Chunk chunk;

136

while ((chunk = request.read()) != null) {

137

if (chunk.hasRemaining()) {

138

// Process content chunk

139

ByteBuffer buffer = chunk.getByteBuffer();

140

byte[] bytes = new byte[buffer.remaining()];

141

buffer.get(bytes);

142

content.append(new String(bytes, StandardCharsets.UTF_8));

143

}

144

145

chunk.release();

146

147

if (chunk.isLast()) {

148

// All content read, process and respond

149

processJsonAndRespond(content.toString(), response, callback);

150

return;

151

}

152

}

153

154

// Need more content, demand again

155

request.demand(() -> readJsonContent(request, response, callback));

156

});

157

}

158

159

private void processJsonAndRespond(String json, Response response, Callback callback) {

160

response.setStatus(200);

161

response.getHeaders().put("Content-Type", "application/json");

162

163

String responseJson = "{\"received\": " + json + ", \"status\": \"processed\"}";

164

response.write(true, ByteBuffer.wrap(responseJson.getBytes()), callback);

165

}

166

167

private void handleNoContent(Response response, Callback callback) {

168

response.setStatus(400);

169

response.getHeaders().put("Content-Type", "text/plain");

170

response.write(true, ByteBuffer.wrap("No content provided".getBytes()), callback);

171

}

172

}

173

```

174

175

### Cookie and Session Handling

176

177

```java

178

public class SessionHandler extends Handler.Abstract {

179

@Override

180

public boolean handle(Request request, Response response, Callback callback)

181

throws Exception {

182

183

// Get or create session

184

Session session = request.getSession(true);

185

String sessionId = session.getId();

186

187

// Read session data

188

Integer visitCount = (Integer) session.getAttribute("visitCount");

189

if (visitCount == null) {

190

visitCount = 0;

191

}

192

visitCount++;

193

session.setAttribute("visitCount", visitCount);

194

195

// Read cookies

196

List<HttpCookie> cookies = request.getCookies();

197

String preferredLang = null;

198

for (HttpCookie cookie : cookies) {

199

if ("lang".equals(cookie.getName())) {

200

preferredLang = cookie.getValue();

201

break;

202

}

203

}

204

205

// Set response cookie

206

if (preferredLang == null) {

207

preferredLang = "en";

208

HttpCookie langCookie = HttpCookie.build("lang", preferredLang)

209

.path("/")

210

.maxAge(Duration.ofDays(30))

211

.httpOnly(true)

212

.build();

213

response.getHeaders().addCookie(langCookie);

214

}

215

216

// Generate response

217

response.setStatus(200);

218

response.getHeaders().put("Content-Type", "text/html");

219

220

String html = String.format(

221

"<html><body>" +

222

"<h1>Session Demo</h1>" +

223

"<p>Session ID: %s</p>" +

224

"<p>Visit count: %d</p>" +

225

"<p>Language: %s</p>" +

226

"</body></html>",

227

sessionId, visitCount, preferredLang);

228

229

response.write(true, ByteBuffer.wrap(html.getBytes()), callback);

230

return true;

231

}

232

}

233

```

234

235

## Content Processing

236

237

### FormFields

238

239

Parse form data from request content.

240

241

```java { .api }

242

public class FormFields extends ContentSourceCompletableFuture<Fields> {

243

public static CompletableFuture<Fields> from(Request request);

244

public static CompletableFuture<Fields> from(Request request, Charset charset, int maxFields, int maxLength);

245

}

246

```

247

248

Usage:

249

250

```java

251

public class FormHandler extends Handler.Abstract {

252

@Override

253

public boolean handle(Request request, Response response, Callback callback)

254

throws Exception {

255

256

String contentType = request.getHeaders().get("Content-Type");

257

if ("application/x-www-form-urlencoded".equals(contentType)) {

258

259

FormFields.from(request).thenAccept(fields -> {

260

// Process form fields

261

String name = fields.getValue("name");

262

String email = fields.getValue("email");

263

264

// Generate response

265

response.setStatus(200);

266

response.getHeaders().put("Content-Type", "text/plain");

267

String responseText = "Received: name=" + name + ", email=" + email;

268

response.write(true, ByteBuffer.wrap(responseText.getBytes()), callback);

269

270

}).exceptionally(throwable -> {

271

// Handle parsing error

272

response.setStatus(400);

273

response.getHeaders().put("Content-Type", "text/plain");

274

response.write(true, ByteBuffer.wrap("Invalid form data".getBytes()), callback);

275

return null;

276

});

277

278

return true;

279

}

280

281

return false; // Not handled

282

}

283

}

284

```

285

286

## HTTP URI Processing

287

288

### HttpURI Interface

289

290

```java { .api }

291

public interface HttpURI {

292

String getScheme();

293

String getHost();

294

int getPort();

295

String getPath();

296

String getQuery();

297

String getFragment();

298

String getParam(String key);

299

Map<String, List<String>> getQueryParameters();

300

boolean isAbsolute();

301

}

302

```

303

304

Usage:

305

306

```java

307

public boolean handle(Request request, Response response, Callback callback) throws Exception {

308

HttpURI uri = request.getHttpURI();

309

310

String scheme = uri.getScheme(); // "http" or "https"

311

String host = uri.getHost(); // "localhost"

312

int port = uri.getPort(); // 8080

313

String path = uri.getPath(); // "/api/users"

314

String query = uri.getQuery(); // "page=1&size=10"

315

316

// Parse query parameters

317

Map<String, List<String>> queryParams = uri.getQueryParameters();

318

String page = queryParams.containsKey("page") ?

319

queryParams.get("page").get(0) : "1";

320

String size = queryParams.containsKey("size") ?

321

queryParams.get("size").get(0) : "20";

322

323

// Generate response based on parameters

324

response.setStatus(200);

325

response.getHeaders().put("Content-Type", "application/json");

326

327

String json = String.format(

328

"{\"path\": \"%s\", \"page\": %s, \"size\": %s}",

329

path, page, size);

330

331

response.write(true, ByteBuffer.wrap(json.getBytes()), callback);

332

return true;

333

}

334

```

335

336

## Header Management

337

338

### HttpFields Interface

339

340

```java { .api }

341

public interface HttpFields extends Iterable<HttpField> {

342

// Field access

343

HttpField getField(String name);

344

String get(String name);

345

List<String> getValuesList(String name);

346

int size();

347

boolean contains(String name);

348

boolean contains(String name, String value);

349

350

// Iteration

351

Iterator<HttpField> iterator();

352

Stream<HttpField> stream();

353

354

// Mutable version for responses

355

interface Mutable extends HttpFields {

356

HttpField put(String name, String value);

357

HttpField add(String name, String value);

358

boolean remove(String name);

359

void clear();

360

void addCookie(HttpCookie cookie);

361

}

362

}

363

```

364

365

## Connection Metadata

366

367

### ConnectionMetaData Interface

368

369

```java { .api }

370

public interface ConnectionMetaData extends Attributes {

371

String getId();

372

HttpConfiguration getHttpConfiguration();

373

String getProtocol();

374

SocketAddress getLocalSocketAddress();

375

SocketAddress getRemoteSocketAddress();

376

boolean isSecure();

377

X509Certificate[] getPeerCertificates();

378

}

379

```

380

381

Usage:

382

383

```java

384

public boolean handle(Request request, Response response, Callback callback) throws Exception {

385

ConnectionMetaData connMeta = request.getConnectionMetaData();

386

387

String protocol = connMeta.getProtocol(); // "HTTP/1.1", "HTTP/2", etc.

388

boolean isSecure = connMeta.isSecure(); // true for HTTPS

389

SocketAddress remoteAddr = connMeta.getRemoteSocketAddress();

390

391

// Client certificate info for mutual TLS

392

X509Certificate[] clientCerts = connMeta.getPeerCertificates();

393

String clientDN = null;

394

if (clientCerts != null && clientCerts.length > 0) {

395

clientDN = clientCerts[0].getSubjectX500Principal().getName();

396

}

397

398

// Generate response with connection info

399

response.setStatus(200);

400

response.getHeaders().put("Content-Type", "application/json");

401

402

String json = String.format(

403

"{\"protocol\": \"%s\", \"secure\": %b, \"remote\": \"%s\", \"clientDN\": \"%s\"}",

404

protocol, isSecure, remoteAddr, clientDN);

405

406

response.write(true, ByteBuffer.wrap(json.getBytes()), callback);

407

return true;

408

}

409

```

410

411

## Request Wrappers

412

413

### Request.Wrapper

414

415

Base class for wrapping and modifying requests.

416

417

```java { .api }

418

public static class Request.Wrapper implements Request {

419

public Request getWrapped();

420

421

// All Request methods delegate to wrapped instance

422

// Override specific methods to modify behavior

423

}

424

```

425

426

Usage:

427

428

```java

429

public class TimingRequestWrapper extends Request.Wrapper {

430

private final long startTime;

431

432

public TimingRequestWrapper(Request wrapped) {

433

super(wrapped);

434

this.startTime = System.nanoTime();

435

}

436

437

public long getProcessingTime() {

438

return System.nanoTime() - startTime;

439

}

440

}

441

442

// In handler

443

TimingRequestWrapper timedRequest = new TimingRequestWrapper(request);

444

// Process request...

445

long processingTime = timedRequest.getProcessingTime();

446

```

447

448

## Response Wrappers

449

450

### Response.Wrapper

451

452

Base class for wrapping and modifying responses.

453

454

```java { .api }

455

public static class Response.Wrapper implements Response {

456

public Response getWrapped();

457

458

// Override methods to intercept/modify response behavior

459

}

460

```

461

462

Usage:

463

464

```java

465

public class CompressingResponseWrapper extends Response.Wrapper {

466

private final GZIPOutputStream gzipOut;

467

468

public CompressingResponseWrapper(Response wrapped) throws IOException {

469

super(wrapped);

470

this.gzipOut = new GZIPOutputStream(new ResponseOutputStream(wrapped));

471

}

472

473

@Override

474

public void write(boolean last, ByteBuffer content, Callback callback) {

475

try {

476

// Compress content before writing

477

byte[] bytes = new byte[content.remaining()];

478

content.get(bytes);

479

gzipOut.write(bytes);

480

481

if (last) {

482

gzipOut.close();

483

}

484

485

callback.succeeded();

486

} catch (IOException e) {

487

callback.failed(e);

488

}

489

}

490

}

491

```