Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cc400df

Browse files
committed
Improve the HttpServer.java
1 parent 2054a31 commit cc400df

File tree

1 file changed

+53
-53
lines changed

1 file changed

+53
-53
lines changed

src/main/java/org/tinystruct/system/HttpServer.java

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*******************************************************************************/
1616
package org.tinystruct.system;
1717

18-
import com.sun.net.httpserver.HttpContext;
1918
import com.sun.net.httpserver.HttpExchange;
2019
import com.sun.net.httpserver.HttpHandler;
2120
import org.tinystruct.AbstractApplication;
@@ -38,8 +37,6 @@
3837
import static org.tinystruct.http.Constants.HTTP_REQUEST;
3938
import static org.tinystruct.http.Constants.HTTP_RESPONSE;
4039
import static org.tinystruct.http.Constants.HTTP_HOST;
41-
import static org.tinystruct.Application.LANGUAGE;
42-
import static org.tinystruct.Application.METHOD;
4340

4441
public class HttpServer extends AbstractApplication implements Bootstrap {
4542
private final Logger logger = Logger.getLogger(HttpServer.class.getName());
@@ -123,11 +120,11 @@ public void start() throws ApplicationException {
123120
if (serverThreads > 0) {
124121
server.setExecutor(Executors.newFixedThreadPool(serverThreads));
125122
} else {
126-
server.setExecutor(null); // Use default executor
123+
server.setExecutor(Executors.newCachedThreadPool()); // Use default executor
127124
}
128125

129126
// Create context and set handler
130-
HttpContext context = server.createContext("/", new DefaultHttpHandler(getContext(), this.settings));
127+
server.createContext("/", new DefaultHttpHandler(getContext(), this.settings));
131128

132129
// Configure context attributes similar to Tomcat setup
133130
initServerDefaults();
@@ -188,8 +185,8 @@ public void stop() {
188185

189186
@Action(value = "error", description = "Error page")
190187
public Object exceptionCaught() throws ApplicationException {
191-
Request request = (Request) getContext().getAttribute(HTTP_REQUEST);
192-
Response response = (Response) getContext().getAttribute(HTTP_RESPONSE);
188+
Request<?, ?> request = (Request<?, ?>) getContext().getAttribute(HTTP_REQUEST);
189+
Response<?, ?> response = (Response<?, ?>) getContext().getAttribute(HTTP_RESPONSE);
193190

194191
Reforward reforward = new Reforward(request, response);
195192
this.setVariable("from", reforward.getFromURL());
@@ -237,7 +234,6 @@ private DefaultHttpHandler(Context context, Settings settings) {
237234

238235
@Override
239236
public void handle(HttpExchange exchange) throws IOException {
240-
boolean sseActive = false;
241237
try {
242238
// Serve static files first (mirror Netty's HttpStaticFileHandler precedence)
243239
if ("GET".equalsIgnoreCase(exchange.getRequestMethod())) {
@@ -256,7 +252,6 @@ public void handle(HttpExchange exchange) throws IOException {
256252

257253
// Process SSE first to ensure correct headers and long-lived connection
258254
if (isSSE(exchange)) {
259-
sseActive = true;
260255
handleSSE(request, response, this.context);
261256
return;
262257
}
@@ -267,31 +262,10 @@ public void handle(HttpExchange exchange) throws IOException {
267262
logger.log(Level.SEVERE, "Error processing request", e);
268263
// Try to send error only if headers haven't been committed yet
269264
try {
270-
Response resp = (Response) this.context.getAttribute(HTTP_RESPONSE);
271-
if (resp instanceof ServerResponse) {
272-
ServerResponse serverResponse = (ServerResponse) resp;
273-
if (!serverResponse.isCommitted()) {
274-
sendErrorResponse(exchange, 500, "Internal Server Error: " + e.getMessage());
275-
}
276-
} else {
277-
sendErrorResponse(exchange, 500, "Internal Server Error: " + e.getMessage());
278-
}
265+
sendErrorResponse(exchange, 500, "Internal Server Error: " + e.getMessage());
279266
} catch (Exception ignored) {
280267
// If we can't send an error (headers/body already sent), just log.
281268
}
282-
} finally {
283-
try {
284-
if (!sseActive) {
285-
Response resp = (Response) this.context.getAttribute(HTTP_RESPONSE);
286-
if (resp instanceof ServerResponse) {
287-
((ServerResponse) resp).close();
288-
} else {
289-
exchange.close();
290-
}
291-
}
292-
} catch (Exception ex) {
293-
exchange.close();
294-
}
295269
}
296270
}
297271

@@ -309,6 +283,7 @@ private void handleSSE(ServerRequest request, ServerResponse response, Context c
309283
response.addHeader(Header.CONTENT_TYPE.name(), "text/event-stream");
310284
response.addHeader(Header.CACHE_CONTROL.name(), "no-cache");
311285
response.addHeader(Header.CONNECTION.name(), "keep-alive");
286+
response.addHeader(Header.TRANSFER_ENCODING.name(), "chunked");
312287
response.addHeader("X-Accel-Buffering", "no");
313288

314289
String query = request.getParameter("q");
@@ -322,22 +297,34 @@ private void handleSSE(ServerRequest request, ServerResponse response, Context c
322297
Object call = ApplicationManager.call(query, context);
323298
String sessionId = context.getId();
324299
SSEPushManager pushManager = getAppropriatePushManager(isMCP);
300+
response.setStatus(ResponseStatus.OK);
301+
// Ensure chunked streaming for SSE before any write
302+
response.sendHeaders(-1);
325303
SSEClient client = pushManager.register(sessionId, response);
326304

327305
if (call instanceof org.tinystruct.data.component.Builder) {
328-
if (client != null) client.send((org.tinystruct.data.component.Builder) call);
329-
else pushManager.push(sessionId, (org.tinystruct.data.component.Builder) call);
330-
} else if (call != null) {
331-
// Send as data line
332-
String data = "data: " + String.valueOf(call).replace("\n", "\ndata: ") + "\n\n";
333-
response.writeAndFlush(data.getBytes("UTF-8"));
334-
} else {
335-
// Initial comment to open stream and avoid proxy buffering
336-
response.writeAndFlush(": ok\n\n".getBytes("UTF-8"));
306+
pushManager.push(sessionId, (org.tinystruct.data.component.Builder) call);
307+
} else if (call instanceof String) {
308+
org.tinystruct.data.component.Builder builder = new org.tinystruct.data.component.Builder();
309+
builder.parse((String) call);
310+
pushManager.push(sessionId, builder);
311+
}
312+
313+
if (client != null) {
314+
try {
315+
while (client.isActive()) {
316+
Thread.sleep(1000);
317+
}
318+
} catch (InterruptedException e) {
319+
Thread.currentThread().interrupt();
320+
throw new ApplicationException("Stream interrupted: " + e.getMessage(), e);
321+
} catch (Exception e) {
322+
throw new ApplicationException("Error in stream: " + e.getMessage(), e);
323+
} finally {
324+
client.close();
325+
pushManager.remove(sessionId);
326+
}
337327
}
338-
} else {
339-
// No query, still open SSE stream
340-
response.writeAndFlush(": ok\n\n".getBytes("UTF-8"));
341328
}
342329
}
343330

@@ -525,12 +512,12 @@ private void processRequest(ServerRequest request, ServerResponse response) thro
525512
} else {
526513
handleDefaultPage(this.context, response);
527514
}
528-
529515
} catch (Exception e) {
530516
logger.log(Level.SEVERE, "Error in request processing", e);
531517
response.setContentType("text/plain; charset=UTF-8");
532518
response.setStatus(org.tinystruct.http.ResponseStatus.INTERNAL_SERVER_ERROR);
533519
response.writeAndFlush("500 - Internal Server Error".getBytes("UTF-8"));
520+
response.close();
534521
}
535522
}
536523

@@ -546,19 +533,22 @@ private void handleRequest(String query, Context context, ServerResponse respons
546533
// Handle request
547534
query = StringUtilities.htmlSpecialChars(query);
548535
Object message = ApplicationManager.call(query, context);
536+
byte[] bytes;
549537
if (message != null) {
550538
if (message instanceof byte[]) {
551-
byte[] bytes = (byte[]) message;
552-
response.addHeader("Content-Length", String.valueOf(bytes.length));
553-
response.writeAndFlush(bytes);
539+
bytes = (byte[]) message;
554540
} else {
555541
response.setContentType("text/html; charset=UTF-8");
556-
response.writeAndFlush(String.valueOf(message).getBytes("UTF-8"));
542+
bytes = String.valueOf(message).getBytes("UTF-8");
557543
}
558544
} else {
559-
response.setContentType("text/plain; charset=UTF-8");
560-
response.writeAndFlush("No response retrieved!".getBytes("UTF-8"));
545+
response.setContentType("text/html; charset=UTF-8");
546+
bytes = "No response retrieved!".getBytes("UTF-8");
561547
}
548+
549+
response.setStatus(ResponseStatus.OK);
550+
response.writeAndFlush(bytes);
551+
response.close();
562552
}
563553

564554
/**
@@ -568,10 +558,20 @@ private void handleRequest(String query, Context context, ServerResponse respons
568558
* @param response The HTTP response object
569559
* @throws IOException if an I/O error occurs
570560
*/
571-
private void handleDefaultPage(Context context, ServerResponse response) throws IOException, ApplicationException {
561+
private void handleDefaultPage(Context context, ServerResponse response) throws ApplicationException {
572562
response.setContentType("text/html; charset=UTF-8");
573563
Object result = ApplicationManager.call(settings.getOrDefault("default.home.page", "say/Praise the Lord."), context);
574-
response.writeAndFlush(String.valueOf(result).getBytes("UTF-8"));
564+
if (!response.isClosed()) {
565+
try {
566+
byte[] bytes = String.valueOf(result).getBytes("UTF-8");
567+
response.setStatus(ResponseStatus.OK);
568+
response.writeAndFlush(bytes);
569+
} catch (IOException e) {
570+
throw new ApplicationException(e);
571+
} finally {
572+
response.close();
573+
}
574+
}
575575
}
576576

577577
private void sendErrorResponse(HttpExchange exchange, int statusCode, String message) {
@@ -582,11 +582,11 @@ private void sendErrorResponse(HttpExchange exchange, int statusCode, String mes
582582
try (OutputStream os = exchange.getResponseBody()) {
583583
os.write(responseBytes);
584584
}
585+
exchange.close();
585586
} catch (IOException e) {
586587
logger.log(Level.SEVERE, "Failed to send error response", e);
587588
}
588589
}
589590

590-
591591
}
592592
}

0 commit comments

Comments
 (0)