/* ESXX - The friendly ECMAscript/XML Application Server Copyright (C) 2007-2015 Martin Blom <martin@blom.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.esxx.request; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Enumeration; import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.servlet.http.*; import org.esxx.*; public class ServletRequest extends WebRequest { public ServletRequest(HttpServletRequest sreq, HttpServletResponse sres) throws IOException { super(sreq.getInputStream(), System.err); this.sreq = sreq; this.sres = sres; this.done = new CountDownLatch(1); } @SuppressWarnings("unchecked") public void initRequest(URI fs_root_uri, URI path_translated) { try { StringBuffer request_url = sreq.getRequestURL(); String query_string = sreq.getQueryString(); if (query_string != null) { request_url.append('?'); request_url.append(query_string); } URI full_request_uri = new URI(request_url.toString()); Properties p = createCGIEnvironment(sreq.getMethod(), sreq.getProtocol(), full_request_uri, sreq.getLocalAddr(), sreq.getLocalPort(), sreq.getRemoteAddr(), sreq.getRemotePort(), fs_root_uri); // Add request headers for (Enumeration<?> e = sreq.getHeaderNames(); e.hasMoreElements(); ) { String h = (String) e.nextElement(); p.setProperty(ESXX.httpToCGI(h), sreq.getHeader(h)); } super.initRequest(sreq.getMethod(), full_request_uri, path_translated, p, fs_root_uri, true); } catch (URISyntaxException ex) { throw new ESXXException(400, "Malformed request URI: " + ex.getMessage(), ex); } } public Integer handleResponse(Response response) throws Exception { try { int status = response.getStatus(); sres.setStatus(status); sres.setContentType(response.getContentType(true)); response.enumerateHeaders(new Response.HeaderEnumerator() { public void header(String name, String value) { sres.addHeader(name, value); } }); if ((status >= 100 && status <= 199) || status == 204 || status == 205 || status == 304) { // No body } else { if (response.isBuffered()) { // Output Content-Length header, if size is known setContentLength(sres, response.getContentLength()); } // Output body response.writeResult(sres.getOutputStream()); } return 0; } catch (IOException ex) { // If we fail to send response, it's probably just because // nobody is listening anyway. return 20; } finally { try { sres.flushBuffer(); } catch (Exception ignored) {} done.countDown(); } } public void waitUntilDone(int timeout) { try { done.await(timeout, TimeUnit.MILLISECONDS); } catch (InterruptedException ignored) { Thread.currentThread().interrupt(); } } private static void setContentLength(HttpServletResponse sres, long length) { if(length <= Integer.MAX_VALUE){ sres.setContentLength((int) length); }else{ sres.addHeader("Content-Length", Long.toString(length)); } } protected static URI getPathTranslated(URI fs_root_uri, HttpServletRequest sreq) throws URISyntaxException { // Prefer getRequestURI(), but use getServletPath() as a fall-back String path = sreq.getRequestURI(); String context = sreq.getContextPath() != null ? sreq.getContextPath() : "/"; if (! path.startsWith(context)) { path = org.esxx.util.StringUtil.encodeURI(sreq.getServletPath(), true); context = "/"; } return getPathTranslated(fs_root_uri, path, context); } public static void handleServletRequest(HttpServletRequest sreq, HttpServletResponse sres, URI fs_root_uri, String error_subtitle) throws IOException { ESXX esxx = ESXX.getInstance(); ServletRequest sr = new ServletRequest(sreq, sres); ESXX.Workload wl = null; try { sr.initRequest(fs_root_uri, getPathTranslated(fs_root_uri, sreq)); wl = esxx.addRequest(sr, sr, 0); sres = null; // Wait for request to complete. Yes, this blocks and locks up // this thread until the result is ready! In an JEE application // server, you may prefer to use AsyncServletRequest. Note that // in Google App Engine, this does not matter since there we use // a single-threaded "thread-pool" anyway. wl.getResult(); } catch (java.util.concurrent.CancellationException ex) { esxx.getLogger().logp(java.util.logging.Level.WARNING, null, null, "Workload " + wl + " cancelled"); sr.waitUntilDone(5000); } catch (Exception ex) { sr.reportInternalError(500, "ESXX Server Error", error_subtitle, ex.getMessage(), ex); sres = null; } finally { if (sres != null) { sres.flushBuffer(); } } } private HttpServletRequest sreq; protected HttpServletResponse sres; /* Allow AsyncServletRequest to access this */ private CountDownLatch done; }