/* * The MIT License * * Copyright 2013 Tim Boudreau. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.mastfrog.acteur.server; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Optional; import com.google.common.collect.ImmutableSortedMap; import com.google.common.net.MediaType; import com.google.inject.util.Providers; import com.mastfrog.acteur.ContentConverter; import com.mastfrog.acteur.HttpEvent; import com.mastfrog.acteur.headers.HeaderValueType; import com.mastfrog.acteur.headers.Headers; import com.mastfrog.acteur.headers.Method; import com.mastfrog.acteur.util.Connection; import com.mastfrog.parameters.validation.ParamChecker; import com.mastfrog.url.Path; import com.mastfrog.util.Codec; import com.mastfrog.util.Streams; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.util.CharsetUtil; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.charset.Charset; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.joda.time.DateTime; import org.joda.time.Duration; /** * * @author Tim Boudreau */ final class EventImpl implements HttpEvent { private final HttpRequest req; private final Path path; private final SocketAddress address; private boolean neverKeepAlive = false; private final Channel channel; private ContentConverter converter; public EventImpl(HttpRequest req, PathFactory paths) { this.req = req; this.path = paths.toPath(req.getUri()); address = new InetSocketAddress("timboudreau.com", 8985); //XXX for tests this.channel = null; Codec codec = new ServerModule.CodecImpl(Providers.of(new ObjectMapper())); this.converter = new ContentConverter(codec, Providers.of(Charset.defaultCharset()), null, null); } public EventImpl(HttpRequest req, SocketAddress addr, Channel channel, PathFactory paths, ContentConverter converter) { this.req = req; this.path = paths.toPath(req.getUri()); address = addr; this.channel = channel; this.converter = converter; } @Override public String toString() { return req.getMethod() + "\t" + req.getUri(); } public void setNeverKeepAlive(boolean val) { neverKeepAlive = val; } @Override public Channel getChannel() { return channel; } @Override public ByteBuf getContent() { return req instanceof ByteBufHolder ? ((ByteBufHolder) req).content() : Unpooled.EMPTY_BUFFER; } @Override public <T> T getContentAsJSON(Class<T> type) throws IOException { MediaType mimeType = getHeader(Headers.CONTENT_TYPE); if (mimeType == null) { mimeType = MediaType.ANY_TYPE; } return converter.toObject(getContent(), mimeType, type); } @Override public String getContentAsString() throws IOException { MediaType type = getHeader(Headers.CONTENT_TYPE); if (type == null) { type = MediaType.PLAIN_TEXT_UTF_8; } Charset encoding; if (type.charset().isPresent()) { encoding = type.charset().get(); } else { encoding = CharsetUtil.UTF_8; } return converter.toString(getContent(), encoding); } @Override public HttpRequest getRequest() { return req; } @Override public Method getMethod() { try { return Method.get(req); } catch (IllegalArgumentException e) { return Method.UNKNOWN; } } @Override public SocketAddress getRemoteAddress() { return address; } @Override public String getHeader(String nm) { return req.headers().get(nm); } @Override public String getParameter(String param) { return getParametersAsMap().get(param); } @Override public Path getPath() { return path; } @Override public <T> T getHeader(HeaderValueType<T> value) { String header = getHeader(value.name()); if (header != null) { return value.toValue(header); } return null; } private Map<String, String> paramsMap; @Override public synchronized Map<String, String> getParametersAsMap() { if (paramsMap == null) { QueryStringDecoder queryStringDecoder = new QueryStringDecoder(req.getUri()); Map<String, List<String>> params = queryStringDecoder.parameters(); Map<String, String> result = new HashMap<>(); for (Map.Entry<String, List<String>> e : params.entrySet()) { if (e.getValue().isEmpty()) { continue; } result.put(e.getKey(), e.getValue().get(0)); } paramsMap = ImmutableSortedMap.copyOf(result); } return paramsMap; } @Override @SuppressWarnings("unchecked") public <T> T getParametersAs(Class<T> type) { return converter.toObject(getParametersAsMap(), type); } @Override public boolean isKeepAlive() { if (neverKeepAlive) { return false; } Connection c = getHeader(Headers.CONNECTION); return c == null ? false : c == Connection.keep_alive; } @Override public Optional<Integer> getIntParameter(String name) { String val = getParameter(name); if (val != null) { int ival = Integer.parseInt(val); return Optional.of(ival); } return Optional.absent(); } @Override public Optional<Long> getLongParameter(String name) { String val = getParameter(name); if (val != null) { long lval = Long.parseLong(val); return Optional.of(lval); } return Optional.absent(); } }