package io.craft.atom.protocol.http.model; import static io.craft.atom.protocol.http.HttpConstants.S_CR; import static io.craft.atom.protocol.http.HttpConstants.S_LF; import java.io.Serializable; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import lombok.Getter; import lombok.Setter; import lombok.ToString; /** * HTTP messages consist of requests from client to server and responses * from server to client. * <pre> * HTTP-message = Request | Response ; HTTP/1.1 messages * </pre> * <p> * HTTP messages use the generic message format of RFC 822 for * transferring entities (the payload of the message). Both types * of message consist of a start-line, zero or more header fields * (also known as "headers"), an empty line (i.e., a line with nothing * preceding the CRLF) indicating the end of the header fields, * and possibly a message-body. * </p> * <pre> * generic-message = start-line * *(message-header CRLF) * CRLF * [ message-body ] * start-line = Request-Line | Status-Line * </pre> * * @author mindwind * @version 1.0, Feb 1, 2013 */ @ToString(of = { "headers", "entity", "cookies" }) public abstract class HttpMessage implements Serializable { private static final long serialVersionUID = -8373186983205172162L; @Getter @Setter protected List<HttpHeader> headers = new ArrayList<HttpHeader>(); @Getter @Setter protected HttpEntity entity ; @Setter protected List<HttpCookie> cookies ; // ~ ------------------------------------------------------------------------------------------------------------ public HttpMessage() { super(); } public HttpMessage(List<HttpHeader> headers) { this.headers = headers; } public HttpMessage(List<HttpHeader> headers, HttpEntity entity) { this.headers = headers; this.entity = entity; } // ~ ------------------------------------------------------------------------------------------------------------ /** * Add a new header to http message, if the header exists replace it. * * @param header */ public void addHeader(HttpHeader header) { if (header == null || header.getName() == null) { return; } headers.add(header); } /** * Remove the first header with given name. * * @param name the name of the header */ public void removeFirstHeader(String name) { removeHeaders0(name, true); } /** * Remove all the headers with the given name. * * @param name the name of the header */ public void removeHeaders(String name) { removeHeaders0(name, false); } private void removeHeaders0(String name, boolean interrupt) { if (name == null) { return; } for (int i = 0; i < headers.size(); i++) { HttpHeader header = headers.get(i); if (header.getName().equalsIgnoreCase(name)) { headers.remove(i); if (interrupt) { break; } } } } /** * Get the first header with the given name. * * <p>Header name comparison is case insensitive. * * @param name the name of the header * @return the first header or <code>null</code> */ public HttpHeader getFirstHeader(String name) { if (name == null) { return null; } for (int i = 0; i < headers.size(); i++) { HttpHeader header = headers.get(i); if (header.getName().equalsIgnoreCase(name)) { return header; } } return null; } /** * Gets all of the headers with the given name. The returned list * maintains the relative order in which the headers were added. * * <p>Header name comparison is case insensitive. * * @param name the name of the header(s) to get * @return header list */ public List<HttpHeader> getHeaders(String name) { List<HttpHeader> headersFound = new ArrayList<HttpHeader>(); for (int i = 0; i < headers.size(); i++) { HttpHeader header = headers.get(i); if (header.getName().equalsIgnoreCase(name)) { headersFound.add(header); } } return headersFound; } /** * Returns an list containing all of the <code>Cookie</code> objects. * * @return all cookies */ public List<HttpCookie> getCookies() { if (this.cookies != null) { return Collections.unmodifiableList(this.cookies); } synchronized (this) { if (this.cookies != null) { return Collections.unmodifiableList(this.cookies); } return Collections.unmodifiableList(parseCookies()); } } /** * Returns an list containing the <code>Cookie</code> objects with specified name. * * @param name * @return cookies with the name */ public List<HttpCookie> getCookies(String name) { List<HttpCookie> cookies = new ArrayList<HttpCookie>(); if (name == null) { return cookies; } List<HttpCookie> allCookies = getCookies(); for (HttpCookie cookie : allCookies) { if (name.equalsIgnoreCase(cookie.getName())) { cookies.add(cookie); } } return cookies; } /** * Returns the cookie with specified name or <tt>null</tt> if does not exist. * <p> * You should only use this method when you are sure the cookie is unique. * If the cookie might have more than one, use {@link #getCookies}. * * @param name * @return a cookie with the name */ public HttpCookie getCookie(String name) { List<HttpCookie> cookies = getCookies(name); if (cookies.isEmpty()) { return null; } return cookies.get(0); } /** * Adds the specified cookie to http message. This method can be called * multiple times to set more than one cookie. * * @param cookie * the Cookie to return to the client */ abstract public void addCookie(HttpCookie cookie); abstract protected List<HttpCookie> parseCookies(); /** * Returns the content type of the http message, or <code>null</code> if message has no entity. * * @return content type object. */ public HttpContentType getContentType() { HttpEntity entity = getEntity(); if (entity == null) { return null; } return entity.getContentType(); } public Iterator<HttpHeader> headerIterator() { return headers.iterator(); } // ~ ------------------------------------------------------------------------------------------------------------ public String toHttpString(Charset charset) { StringBuilder sb = new StringBuilder(); // headers Iterator<HttpHeader> it = headerIterator(); boolean hasHeader = false; while (it.hasNext()) { HttpHeader header = (HttpHeader) it.next(); sb.append(header.toHttpString()); if (!hasHeader) { hasHeader = true; } } // empty lines if (hasHeader) { sb.append(S_CR).append(S_LF); } // entity HttpEntity entity = getEntity(); if (entity != null) { if (entity instanceof HttpChunkEntity) { HttpChunkEntity chunkEntity = (HttpChunkEntity) entity; sb.append(chunkEntity.toHttpString()); } else { sb.append(entity.toHttpString()); } } return sb.toString(); } }