@@ -371,7 +371,7 @@ public void service(HttpServletRequest request, HttpServletResponse response) th
371371 String query = _request .getParameter ("q" );
372372 if (query != null ) {
373373 Action .Mode mode = Action .Mode .fromName (request .getMethod ());
374- handleRequest (query , context , _response , mode );
374+ handleRequest (query , context , _request , _response , mode );
375375 } else {
376376 handleDefaultPage (context , _response );
377377 }
@@ -451,13 +451,48 @@ else if(call instanceof String) {
451451 * @param mode
452452 * @throws IOException if an I/O error occurs
453453 */
454- private void handleRequest (String query , org .tinystruct .application .Context context , Response <HttpServletResponse , ServletOutputStream > response , Action .Mode mode ) throws IOException , ApplicationException {
454+ private void handleRequest (String query , org .tinystruct .application .Context context , Request request , Response <HttpServletResponse , ServletOutputStream > response , Action .Mode mode ) throws IOException , ApplicationException {
455+ // Handle CORS preflight (OPTIONS) requests up-front: these have no body.
456+ if ("OPTIONS" .equalsIgnoreCase (request .method ().name ())) {
457+ String origin = request .headers ().get (Header .ORIGIN ).toString ();
458+ String acrMethod = request .headers ().get (Header .ACCESS_CONTROL_REQUEST_METHOD ).toString ();
459+ String acrHeaders = request .headers ().get (Header .ACCESS_CONTROL_REQUEST_HEADERS ).toString ();
460+
461+ // Allow origins: prefer explicit setting, otherwise echo Origin or wildcard
462+ String allowOrigin = settings .getOrDefault ("cors.allowed.origins" , origin != null ? origin : "*" );
463+ response .addHeader ("Access-Control-Allow-Origin" , allowOrigin );
464+ // Make responses vary by Origin when echoing it
465+ if (origin != null ) {
466+ response .addHeader ("Vary" , "Origin" );
467+ }
468+
469+ // Allow methods: prefer configured list, otherwise echo requested or use sensible defaults
470+ String allowMethods = settings .getOrDefault ("cors.allowed.methods" , acrMethod != null ? acrMethod : "GET,POST,PUT,DELETE,OPTIONS" );
471+ response .addHeader ("Access-Control-Allow-Methods" , allowMethods );
472+
473+ // Allow headers: prefer configured list, otherwise echo requested or common headers
474+ String allowHeaders = settings .getOrDefault ("cors.allowed.headers" , acrHeaders != null ? acrHeaders : "Content-Type,Authorization" );
475+ response .addHeader ("Access-Control-Allow-Headers" , allowHeaders );
476+
477+ // Allow credentials if explicitly enabled in settings
478+ if ("true" .equalsIgnoreCase (settings .get ("cors.allow.credentials" ))) {
479+ response .addHeader ("Access-Control-Allow-Credentials" , "true" );
480+ }
481+
482+ // Cache the preflight response for a configurable duration (seconds)
483+ String maxAge = settings .getOrDefault ("cors.preflight.maxage" , "3600" );
484+ response .addHeader ("Access-Control-Max-Age" , maxAge );
485+
486+ // No response body for preflight; return 204 No Content
487+ response .setStatus (ResponseStatus .NO_CONTENT );
488+ return ;
489+ }
490+
455491 // Handle request
456492 query = StringUtilities .htmlSpecialChars (query );
457493 Object message = ApplicationManager .call (query , context , mode );
458494 if (message != null ) {
459- if (message instanceof byte []) {
460- byte [] bytes = (byte []) message ;
495+ if (message instanceof byte [] bytes ) {
461496 response .addHeader (Header .CONTENT_LENGTH .name (), String .valueOf (bytes .length ));
462497 response .get ().write (bytes );
463498 } else {
@@ -518,7 +553,8 @@ private boolean isSSL() {
518553 }
519554
520555 private String getProtocol (HttpServletRequest request ) {
521- return isSSL () ? "https://" : "http://" ;
556+ // Use the request object to determine the protocol
557+ return request .isSecure () ? "https://" : "http://" ;
522558 }
523559
524560 private String getHost (HttpServletRequest request ) {
0 commit comments