All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.codestory.http.payload.Payload Maven / Gradle / Ivy

/**
 * Copyright (C) 2013 [email protected]
 *
 * 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.codestory.http.payload;

import static java.nio.charset.StandardCharsets.*;
import static java.time.format.DateTimeFormatter.*;
import static net.codestory.http.constants.Headers.*;
import static net.codestory.http.constants.Methods.*;

import java.io.*;
import java.net.*;
import java.nio.file.Path;
import java.time.*;
import java.util.*;

import net.codestory.http.compilers.*;
import net.codestory.http.convert.*;
import net.codestory.http.internal.*;
import net.codestory.http.io.*;
import net.codestory.http.misc.*;
import net.codestory.http.templating.*;
import net.codestory.http.types.*;

import org.simpleframework.http.*;

public class Payload {
  private final String contentType;
  private final Object content;
  private final Map headers;
  private final List cookies;
  private int code;

  public Payload(Object content) {
    this(null, content);
  }

  public Payload(String contentType, Object content) {
    this(contentType, content, 200);
  }

  public Payload(int code) {
    this(null, null, code);
  }

  public Payload(String contentType, Object content, int code) {
    if (content instanceof Payload) {
      Payload wrapped = (Payload) content;
      this.contentType = (null == contentType) ? wrapped.contentType : contentType;
      this.content = wrapped.content;
      this.headers = new LinkedHashMap<>(wrapped.headers);
      this.cookies = new ArrayList<>(wrapped.cookies);
      this.code = wrapped.code;
    } else {
      this.contentType = contentType;
      this.content = content;
      this.headers = new LinkedHashMap<>();
      this.cookies = new ArrayList<>();
      this.code = code;
    }
  }

  public Payload withHeader(String key, String value) {
    headers.put(key, value);
    return this;
  }

  public Payload withHeaders(Map headers) {
    this.headers.putAll(headers);
    return this;
  }

  public Payload withCookie(String name, int value) {
    return withCookie(name, Integer.toString(value));
  }

  public Payload withCookie(String name, long value) {
    return withCookie(name, Long.toString(value));
  }

  public Payload withCookie(String name, boolean value) {
    return withCookie(name, Boolean.toString(value));
  }

  public Payload withCookie(String name, String value) {
    return withCookie(new Cookie(name, value, "/", true));
  }

  public Payload withCookie(String name, Object value) {
    return withCookie(name, TypeConvert.toJson(value));
  }

  public Payload withCookie(Cookie cookie) {
    cookies.add(cookie);
    return this;
  }

  public Payload withCookies(List cookies) {
    cookies.addAll(cookies);
    return this;
  }

  public Payload withCode(int code) {
    this.code = code;
    return this;
  }

  public String rawContentType() {
    return contentType;
  }

  public Object rawContent() {
    return content;
  }

  public Map headers() {
    return headers;
  }

  public List cookies() {
    return cookies;
  }

  public int code() {
    return code;
  }

  public boolean isSuccess() {
    return (code >= 200) && (code <= 299);
  }

  public boolean isError() {
    return (code >= 400) && (code <= 599);
  }

  public static Payload ok() {
    return new Payload(200);
  }

  public static Payload created() {
    return new Payload(201);
  }

  public static Payload movedPermanently(String url) {
    return new Payload(301).withHeader(LOCATION, url);
  }

  public static Payload seeOther(String uri) {
    return new Payload(303).withHeader(LOCATION, uri);
  }

  public static Payload seeOther(URI uri) {
    return seeOther(uri.toString());
  }

  public static Payload temporaryRedirect(String uri) {
    return new Payload(307).withHeader(LOCATION, uri);
  }

  public static Payload temporaryRedirect(URI uri) {
    return temporaryRedirect(uri.toString());
  }

  public static Payload notModified() {
    return new Payload(304);
  }

  public static Payload unauthorized(String realm) {
    return new Payload(401).withHeader(WWW_AUTHENTICATE, "Basic realm=\"" + realm + "\"");
  }

  public static Payload forbidden() {
    return new Payload(403);
  }

  public static Payload notFound() {
    return new Payload(404);
  }

  public static Payload methodNotAllowed() {
    return new Payload(405);
  }

  public boolean isBetter(Payload other) {
    // WTF?
    if (200 == code) {
      return other.code() != 200;
    }
    if (405 == code) {
      return (other.code() != 200) && (other.code() != 405);
    }
    if (303 == code) {
      return (other.code() != 200) && (other.code() != 405) && (other.code() != 303);
    }
    return false;
  }

  public void writeTo(Context context) throws IOException {
    Response response = context.response();

    headers.entrySet().forEach(entry -> response.setValue(entry.getKey(), entry.getValue()));
    addHeadersForContent(response);
    cookies.forEach(cookie -> response.setCookie(cookie));

    String uri = context.uri();
    byte[] data = getData(uri, context);
    if (data == null) {
      response.setStatus(Status.getStatus(code));
      response.setContentLength(0);
      return;
    }

    String type = getContentType(uri);
    response.setValue(CONTENT_TYPE, type);

    String etag = etag(data);
    String previousEtag = context.getHeader(IF_NONE_MATCH);
    if (etag.equals(previousEtag)) {
      response.setStatus(Status.NOT_MODIFIED);
      return;
    }

    response.setStatus(Status.getStatus(code));
    response.setValue(ETAG, etag);

    if (!HEAD.equals(context.method())) {
      response.setContentLength(data.length);
      response.getOutputStream().write(data);
    }
  }

  private static String etag(byte[] data) {
    return Md5.of(data);
  }

  public String getContentType(String uri) {
    if (contentType != null) {
      return contentType;
    }
    if (content instanceof File) {
      File file = (File) content;
      return ContentTypes.get(file.toPath());
    }
    if (content instanceof Path) {
      Path path = (Path) content;
      return ContentTypes.get(path);
    }
    if (content instanceof byte[]) {
      return "application/octet-stream";
    }
    if (content instanceof String) {
      return "text/html;charset=UTF-8";
    }
    if (content instanceof InputStream) {
      return "application/octet-stream";
    }
    if (content instanceof ModelAndView) {
      Path path = Resources.findExistingPath(((ModelAndView) content).view());
      return ContentTypes.get(path);
    }
    if (content instanceof Model) {
      Path path = Resources.findExistingPath(uri);
      return ContentTypes.get(path);
    }
    return "application/json;charset=UTF-8";
  }

  public byte[] getData(String uri, Context context) throws IOException {
    if (content == null) {
      return null;
    }
    if (content instanceof File) {
      return forPath(((File) content).toPath(), context);
    }
    if (content instanceof Path) {
      return forPath((Path) content, context);
    }
    if (content instanceof byte[]) {
      return (byte[]) content;
    }
    if (content instanceof String) {
      return forString((String) content);
    }
    if (content instanceof InputStream) {
      return forInputStream((InputStream) content);
    }
    if (content instanceof ModelAndView) {
      return forModelAndView((ModelAndView) content, context);
    }
    if (content instanceof Model) {
      return forModelAndView(ModelAndView.of(uri, (Model) content), context);
    }

    return TypeConvert.toByteArray(content);
  }

  private void addHeadersForContent(Response response) {
    if (content instanceof Path) {
      addLastModifiedHeader(((Path) content).toFile(), response);
    } else if (content instanceof File) {
      addLastModifiedHeader((File) content, response);
    }
  }

  private void addLastModifiedHeader(File file, Response response) {
    response.setValue(LAST_MODIFIED, RFC_1123_DATE_TIME.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(file.lastModified()), ZoneOffset.systemDefault())));
  }

  private static byte[] forString(String value) {
    return value.getBytes(UTF_8);
  }

  private static byte[] forInputStream(InputStream stream) throws IOException {
    return InputStreams.readBytes(stream);
  }

  private static byte[] forModelAndView(ModelAndView modelAndView, Context context) {
    String view = modelAndView.view();

    Model model = modelAndView.model();
    model = model.merge(Model.of("cookies", cookieValues(context)));

    String html = new Template(view).render(model);

    return forString(html);
  }

  private static Map cookieValues(Context context) {
    Map keyValues = new HashMap<>();
    for (Cookie cookie : context.cookies()) {
      keyValues.put(cookie.getName(), cookie.getValue());
    }
    return keyValues;
  }

  private static byte[] forPath(Path path, Context context) throws IOException {
    if (ContentTypes.is_binary(path)) {
      return Resources.readBytes(path);
    }

    if (ContentTypes.support_templating(path)) {
      return forModelAndView(ModelAndView.of(path.toString()), context);
    }

    String content = Resources.read(path, UTF_8);
    String compiled = Compilers.INSTANCE.compile(path, content);
    return forString(compiled);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy