| /* |
| * Copyright 2014 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package com.google.gwt.dev.codeserver; |
| |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.dev.codeserver.Pages.ErrorPage; |
| import com.google.gwt.dev.json.JsonObject; |
| import com.google.gwt.thirdparty.guava.common.base.Charsets; |
| import com.google.gwt.thirdparty.guava.common.base.Preconditions; |
| import com.google.gwt.thirdparty.guava.common.io.ByteStreams; |
| import com.google.gwt.thirdparty.guava.common.io.Files; |
| import com.google.gwt.thirdparty.guava.common.io.Resources; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.io.Writer; |
| import java.net.URL; |
| import java.util.regex.Pattern; |
| |
| import javax.servlet.ServletOutputStream; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| /** |
| * Common HTTP responses other than HTML pages, which are in {@link Pages}. |
| */ |
| public class Responses { |
| |
| private static final Pattern SAFE_CALLBACK = |
| Pattern.compile("([a-zA-Z_][a-zA-Z0-9_]*\\.)*[a-zA-Z_][a-zA-Z0-9_]*"); |
| |
| /** |
| * A HTTP response that sends a file. |
| */ |
| static Response newFileResponse(final String mimeType, final File file) { |
| if (!file.isFile()) { |
| return new ErrorPage("file not found: " + file.toString()); |
| } |
| |
| return new Response() { |
| @Override |
| public void send(HttpServletRequest request, HttpServletResponse response, TreeLogger logger) |
| throws IOException { |
| response.setStatus(HttpServletResponse.SC_OK); |
| response.setContentType(mimeType); |
| Files.copy(file, response.getOutputStream()); |
| } |
| }; |
| } |
| |
| /** |
| * Returns a JSON response. If the request contains a _callback parameter, it will |
| * automatically be sent as a JSONP response. Otherwise, it's an AJAX response. |
| */ |
| static Response newJsonResponse(final JsonObject json) { |
| |
| return new Response() { |
| @Override |
| public void send(HttpServletRequest request, HttpServletResponse response, TreeLogger logger) |
| throws IOException { |
| response.setStatus(HttpServletResponse.SC_OK); |
| response.setHeader("Cache-control", "no-cache"); |
| PrintWriter out = response.getWriter(); |
| |
| String callbackExpression = request.getParameter("_callback"); |
| if (callbackExpression == null) { |
| // AJAX |
| response.setContentType("application/json"); |
| json.write(out); |
| } else { |
| // JSONP |
| response.setContentType("application/javascript"); |
| if (SAFE_CALLBACK.matcher(callbackExpression).matches()) { |
| out.print("/* API response */ " + callbackExpression + "("); |
| json.write(out); |
| out.println(");"); |
| } else { |
| logger.log(TreeLogger.ERROR, "invalid callback: " + callbackExpression); |
| // Notice that we cannot execute the callback |
| out.print("alert('invalid callback parameter');\n"); |
| json.write(out); |
| } |
| } |
| } |
| }; |
| } |
| |
| /** |
| * Sends an entire Js script. |
| */ |
| static Response newJavascriptResponse(final String jsScript) { |
| return new Response() { |
| @Override |
| public void send(HttpServletRequest request, HttpServletResponse response, TreeLogger logger) |
| throws IOException { |
| response.setStatus(HttpServletResponse.SC_OK); |
| response.setContentType("application/javascript"); |
| |
| ServletOutputStream outBytes = response.getOutputStream(); |
| Writer out = new OutputStreamWriter(outBytes, "UTF-8"); |
| out.write(jsScript); |
| out.close(); |
| } |
| }; |
| } |
| |
| /** |
| * Sends a JavaScript file with some JSON data prepended to it. |
| * @param variableName the global variable where the JSON should be stored. |
| * @param json the data to include. |
| * @param resourceName the name of the JavaScript file. |
| */ |
| static Response newJavascriptResponse(final String variableName, final JsonObject json, |
| final String resourceName) { |
| |
| final URL resource = WebServer.class.getResource(resourceName); |
| if (resource == null) { |
| return new ErrorPage("resource not found: " + resourceName); |
| } |
| |
| return new Response() { |
| @Override |
| public void send(HttpServletRequest request, HttpServletResponse response, TreeLogger logger) |
| throws IOException { |
| response.setStatus(HttpServletResponse.SC_OK); |
| response.setContentType("application/javascript"); |
| |
| ServletOutputStream outBytes = response.getOutputStream(); |
| Writer out = new OutputStreamWriter(outBytes, "UTF-8"); |
| |
| out.append("window." + variableName + " = "); |
| json.write(out); |
| out.append(";\n"); |
| out.flush(); |
| |
| Resources.copy(resource, outBytes); |
| } |
| }; |
| } |
| |
| /** |
| * Sends a text file, substituting one variable. (Doesn't preserve line endings.) |
| * @param templateVariable the string to replace |
| * @param replacement the replacement |
| */ |
| static Response newTextTemplateResponse(final String mimeType, final File file, |
| final String templateVariable, final String replacement) { |
| if (!file.isFile()) { |
| return new ErrorPage("file not found: " + file.toString()); |
| } |
| |
| return new Response() { |
| @Override |
| public void send(HttpServletRequest request, HttpServletResponse response, TreeLogger logger) |
| throws IOException { |
| BufferedReader reader = Files.newReader(file, Charsets.UTF_8); |
| try { |
| response.setStatus(HttpServletResponse.SC_OK); |
| response.setContentType(mimeType); |
| PrintWriter out = response.getWriter(); |
| while (true) { |
| String line = reader.readLine(); |
| if (line == null) { |
| break; |
| } |
| line = line.replace(templateVariable, replacement); |
| out.println(line); |
| } |
| } finally { |
| reader.close(); |
| } |
| } |
| }; |
| } |
| |
| /** |
| * Creates a page that sends the given stream of bytes. |
| * The response will close the stream after sending it. |
| * (Beware that if the page is never sent, the file handle will leak.) |
| * TODO: fix the callers and get rid of this. |
| */ |
| static Response newBinaryStreamResponse(final String mimeType, final InputStream pageBytes) { |
| return new Response() { |
| boolean sent = false; |
| |
| @Override |
| public void send(HttpServletRequest request, HttpServletResponse response, TreeLogger logger) |
| throws IOException { |
| Preconditions.checkState(!sent); |
| |
| try { |
| response.setStatus(HttpServletResponse.SC_OK); |
| response.setContentType(mimeType); |
| ByteStreams.copy(pageBytes, response.getOutputStream()); |
| } finally { |
| pageBytes.close(); |
| } |
| sent = true; |
| } |
| }; |
| } |
| |
| /** |
| * Wraps another response in order to log how long it takes to send it. |
| */ |
| static Response newTimedResponse(final Response barePage, final String message) { |
| return new Response() { |
| @Override |
| public void send(HttpServletRequest request, HttpServletResponse response, TreeLogger logger) |
| throws IOException { |
| long startTime = System.currentTimeMillis(); |
| barePage.send(request, response, logger); |
| long elapsedTime = System.currentTimeMillis() - startTime; |
| logger.log(TreeLogger.INFO, message + " in " + elapsedTime + " ms"); |
| } |
| }; |
| } |
| } |