HttpUtil.java
- /***************************************************************************
- Copyright 2009 Emily Estes
- 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 net.metanotion.web.concrete;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.nio.charset.StandardCharsets;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import net.metanotion.io.Serializer;
- import net.metanotion.util.Pair;
- import net.metanotion.util.Wrap;
- import net.metanotion.web.Cookie;
- import net.metanotion.web.HttpStatus;
- import net.metanotion.web.HttpValues;
- /** Utilty methods for generating common types of HTTP responses.
- This class is just a collection of utility methods for generating many common types of HTTP responses.
- */
- public final class HttpUtil {
- private static final String HTML_MIME = "text/html; charset=utf-8";
- private static final String JSON_MIME = "application/json; charset=utf-8";
- private static final String CONTENT_TYPE = "Content-Type";
- private static final String LOCATION = "Location";
- /** Many of the SimpleHttpValues instances use an empty list. This is just a short cut instead of making a new one
- every time. */
- private static final List<Map.Entry<String,Object>> EMPTY_LIST = (List<Map.Entry<String,Object>>) Collections.EMPTY_LIST;
- /** Many common responses just need to send a header list with a Content-Type set to UTF-8 encoded HTML. */
- private static final List<Map.Entry<String,Object>> BASIC_HEADER_LIST =
- Collections.unmodifiableList(Arrays.asList((Map.Entry<String,Object>) new Pair<String,Object>(CONTENT_TYPE, HTML_MIME)));
- /** All the basic JSON responses need the Content-Type set to JSON. */
- private static final List<Map.Entry<String,Object>> JSON_HEADER_LIST =
- Collections.unmodifiableList(Arrays.asList((Map.Entry<String,Object>) new Pair<String,Object>(CONTENT_TYPE, JSON_MIME)));
- private static final String EMPTY_STRING = "";
- /** Generate an HTTP response that simply returns an HTTP Status code with blank content.
- @param status The HTTP status code to return.
- @return An HttpValues instance encoding the response.
- */
- public static HttpValues newStatus(final int status) {
- return new SimpleHttpValues(HttpUtil.BASIC_HEADER_LIST, HttpUtil.EMPTY_LIST, HttpUtil.EMPTY_STRING, status);
- }
- /** Issue temporary redirect(303) to another url.
- @param url The URL to redirect the request to.
- @return An HttpValues instance encoding the response.
- */
- public static HttpValues newRedirect(final String url) {
- return new SimpleHttpValues(Arrays.asList((Map.Entry<String,Object>) new Pair<String,Object>(LOCATION, url)),
- HttpUtil.EMPTY_LIST,
- HttpUtil.EMPTY_STRING,
- HttpStatus.REDIRECT_SEE_OTHER.codeNumber());
- }
- /** Issue a redirect by code number(actually, this just sets the Location header and uses whatever code number you
- give it, but it should be one of the 3xx codes to actually work correctly.
- @param url The URL to redirect the request to.
- @param type The HTTP status code of the response.
- @return An HttpValues instance encoding the response.
- */
- public static HttpValues newRedirect(final String url, final HttpStatus type) {
- return new SimpleHttpValues(Arrays.asList((Map.Entry<String,Object>) new Pair<String,Object>(LOCATION, url)),
- HttpUtil.EMPTY_LIST,
- HttpUtil.EMPTY_STRING,
- type.codeNumber());
- }
- /** Generate a 404 Not found response.
- @param message The content(presumed to be HTML) to send with the response.
- @return An HttpValues instance encoding the response.
- */
- public static HttpValues new404(final String message) {
- return new SimpleHttpValues(HttpUtil.BASIC_HEADER_LIST, HttpUtil.EMPTY_LIST, message, HttpStatus.NOT_FOUND.codeNumber());
- }
- /** Generate a 403 Forbidden response.
- @param message The content(presumed to be HTML) to send with the response.
- @return An HttpValues instance encoding the response.
- */
- public static HttpValues new403(final String message) {
- return new SimpleHttpValues(HttpUtil.BASIC_HEADER_LIST, HttpUtil.EMPTY_LIST, message, HttpStatus.FORBIDDEN.codeNumber());
- }
- /** Generate a 500 internal server error response.
- @param message The content(presumed to be HTML) to send with the response.
- @return An HttpValues instance encoding the response.
- */
- public static HttpValues new500(final String message) {
- return new SimpleHttpValues(HttpUtil.BASIC_HEADER_LIST,
- HttpUtil.EMPTY_LIST,
- message,
- HttpStatus.SERVER_ERROR.codeNumber());
- }
- /** Send back an HTML response. This is a "normal" response with a status of 200 "OK".
- @param html The content, presumed to be HTML.
- @return An HttpValues instance encoding the response.
- */
- public static HttpValues newHtmlString(final String html) {
- return new SimpleHttpValues(HttpUtil.BASIC_HEADER_LIST, HttpUtil.EMPTY_LIST, html);
- }
- /** Send a basic response to the client with a custom MIME type and a status of 200 "OK".
- @param content A object encoding a representation of the content suitable for issuing as a reply(as a fallback,
- {@link java.lang.Object#toString} is used to generate a representation of the content to send to the
- client).
- @param contentType The MIME-type to set for the value of the Content-Type header.
- @return An HttpValues instance encoding the response.
- */
- public static HttpValues newTypedContent(final Object content, final String contentType) {
- return new SimpleHttpValues(
- Arrays.asList((Map.Entry<String,Object>) new Pair<String,Object>(CONTENT_TYPE, contentType)),
- HttpUtil.EMPTY_LIST,
- content);
- }
- /** Generate a JSON response, and send it with 200 OK status.
- @param content A JSON object or an encoding of a JSON object suitable for issuing as a reply(as a fallback,
- {@link java.lang.Object#toString} is used to generate a representation of the content to send to the
- client).
- @return An HttpValues instance encoding the response.
- */
- public static HttpValues newJsonResponse(final Object content) {
- return newJsonResponse(content, HttpStatus.OK.codeNumber());
- }
- /** Generate a JSON response with a custom status.
- @param content A JSON object or an encoding of a JSON object suitable for issuing as a reply(as a fallback,
- {@link java.lang.Object#toString} is used to generate a representation of the content to send to the
- client).
- @param type The HTTP status code of the response.
- @return An HttpValues instance encoding the response.
- */
- public static HttpValues newJsonResponse(final Object content, final int type) {
- return new SimpleHttpValues(HttpUtil.JSON_HEADER_LIST, HttpUtil.EMPTY_LIST, content, type);
- }
- /** Convert an HTTP response object into a printable string for debugging purposes. This uses the same algorithm as
- the framework does in {@link net.metanotion.web.servlets.RequestHandler}
- @param result The HTTP response to serialize.
- @return a string representing the HTTP response.
- @throws IOException if there is a problem serializing the response.
- */
- public static String debugString(final Object result) throws IOException {
- final StringBuilder sb = new StringBuilder("");
- if(result == null) { return "NULL"; }
- if(result instanceof HttpValues) {
- final HttpValues hv = (HttpValues) result;
- sb.append("HTTP " + Integer.toString(hv.getHttpStatus()) + "\n");
- for(final Iterator<Map.Entry<String,Object>> it = hv.listHeaders(); it.hasNext();) {
- final Map.Entry<String,Object> e = it.next();
- sb.append("HEADER: " + e.getKey() + " = " + e.getValue().toString() + "\n");
- }
- for(final Iterator<Map.Entry<String,Object>> it = hv.listCookies(); it.hasNext();) {
- final Map.Entry<String,Object> e = it.next();
- sb.append("COOKIE: " + e.getKey() + " = '");
- final Object val = e.getValue();
- sb.append(((val instanceof Cookie) ? ((Cookie) val).getValue() : val.toString()) + "'\n");
- }
- }
- final Object content = (result instanceof Wrap) ? ((Wrap) result).unwrap() : result;
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- Serializer.write(content, baos, StandardCharsets.UTF_8);
- return sb.append(new String(baos.toByteArray(), StandardCharsets.UTF_8)).toString();
- }
- }