package com.neverwinterdp.sparkngin.http; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.Cookie; import io.netty.handler.codec.http.CookieDecoder; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.ServerCookieEncoder; import static io.netty.handler.codec.http.HttpHeaders.Names.*; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; import com.neverwinterdp.message.Message; import com.neverwinterdp.netty.http.RouteHandlerGeneric; import com.neverwinterdp.sparkngin.Sparkngin; import com.neverwinterdp.util.JSONSerializer; import com.neverwinterdp.util.MapUtil; import com.neverwinterdp.util.text.StringUtil; /** * Route to handle returning a 1x1 100% transparent png * Responds to GET requests in HTTP * @author Richard Duarte */ public class TrackingPixelRouteHandler extends RouteHandlerGeneric { //This is a byte array for a 1x1 100% transparent .png image final static ByteBuf IMAGE = Unpooled.wrappedBuffer( new byte[] {(byte)0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x15, (byte)0xC4, (byte)0x89, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4B, 0x47, 0x44, 0x00, (byte)0xFF, 0x00, (byte)0xFF, 0x00, (byte)0xFF, (byte)0xA0, (byte)0xBD, (byte)0xA7, (byte)0x93, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, 0x00, 0x0B, 0x13, 0x01, 0x00, (byte)0x9A, (byte)0x9C, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4D, 0x45, 0x07, (byte)0xDE, 0x08, 0x14, 0x14, 0x24, 0x12, 0x12, (byte)0x95, (byte)0xC7, (byte)0xB4, 0x00, 0x00, 0x00, 0x0C, 0x69, 0x54, 0x58, 0x74, 0x43, 0x6F, 0x6D, 0x6D, 0x65, 0x6E, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xBC, (byte)0xAE, (byte)0xB2, (byte)0x99, 0x00, 0x00, 0x00, 0x10, 0x49, 0x44, 0x41, 0x54, 0x08, 0x1D, 0x01, 0x05, 0x00, (byte)0xFA, (byte)0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, (byte)0xBA, (byte)0x89, 0x10, (byte)0x8A, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, (byte)0xAE, 0x42, 0x60, (byte)0x82}); private Sparkngin sparkngin ; private AtomicLong idTracker = new AtomicLong(); private Pattern[] headerMatcher ; public TrackingPixelRouteHandler(Sparkngin sparkngin, Map<String, String> props) { this.sparkngin = sparkngin ; String extractHeaders = MapUtil.getString(props,"tracking.site.extract-headers", null) ; if(extractHeaders != null) { String[] extractHeader = StringUtil.toStringArray(extractHeaders) ; headerMatcher = new Pattern[extractHeader.length] ; for(int i = 0; i < extractHeader.length; i++) { headerMatcher[i] = Pattern.compile(extractHeader[i], Pattern.CASE_INSENSITIVE) ; } } } /** * Configure the handler and connection to sparkngin if sparkngin.connect is set */ public void configure(Map<String, String> props) { } /** * Serves IMAGE as mimetype "image/png" * If we're supposed to forward these messages to sparkngin, then do so in a thread */ protected void doGet(ChannelHandlerContext ctx, HttpRequest httpReq) { //Return Pixel to client FullHttpResponse response = this.createResponse(httpReq, IMAGE.retain(), "image/png") ; setCookie(httpReq, response) ; write(ctx, httpReq, response); sparknginLog(httpReq, response) ; } void sparknginLog(HttpRequest request, FullHttpResponse response) { //Send request log to sparkngin RequestLog log = new RequestLog(request, headerMatcher); Message message = new Message("id-" + idTracker.incrementAndGet(), log, false) ; message.getHeader().setTopic(log.getTrackerName()) ; sparkngin.push(message) ; } void setCookie(HttpRequest request, FullHttpResponse response) { // Encode the cookie. String cookieString = request.headers().get(COOKIE); if (cookieString != null) { Set<Cookie> cookies = CookieDecoder.decode(cookieString); if (!cookies.isEmpty()) { // Reset the cookies if necessary. for (Cookie cookie: cookies) { if("visit-count".equals(cookie.getName())) { int count = Integer.parseInt(cookie.getValue()) ; cookie.setValue(Integer.toString(count + 1)); //System.out.println("Visit: " + count); } response.headers().add(SET_COOKIE, ServerCookieEncoder.encode(cookie)); } } } else { // Browser sent no cookie. Add some. response.headers().add(SET_COOKIE, ServerCookieEncoder.encode("id", UUID.randomUUID().toString())); response.headers().add(SET_COOKIE, ServerCookieEncoder.encode("visit-count", "1")); response.headers().add(SET_COOKIE, ServerCookieEncoder.encode("first-visit-time", new Date().toString())); } } public static ByteBuf getImageBytes() { return IMAGE; } }