/* * Copyright 2014, The Sporting Exchange Limited * Copyright 2015, Simon Matić Langford * * 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 com.betfair.cougar.transport.impl.protocol.http; import com.betfair.cougar.api.ExecutionContext; import com.betfair.cougar.api.ResponseCode; import com.betfair.cougar.api.geolocation.GeoLocationDetails; import com.betfair.cougar.api.security.IdentityTokenResolver; import com.betfair.cougar.core.api.RequestTimer; import com.betfair.cougar.logging.EventLogDefinition; import com.betfair.cougar.logging.EventLoggingRegistry; import com.betfair.cougar.transport.api.RequestLogger; import com.betfair.cougar.transport.api.protocol.http.HttpCommand; import com.betfair.cougar.util.HeaderUtils; import com.betfair.cougar.util.RequestUUIDImpl; import com.betfair.cougar.util.UUIDGeneratorImpl; import com.betfair.cougar.util.geolocation.RemoteAddressUtils; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.slf4j.ILoggerFactory; import org.slf4j.Logger; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.MediaType; import java.security.cert.X509Certificate; import java.text.SimpleDateFormat; import java.util.TimeZone; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; public class HttpRequestLoggerTest { protected HttpServletRequest request; protected HttpServletResponse response; private Logger eventLog; private ILoggerFactory loggerFactory; private EventLoggingRegistry registry; private ILoggerFactory oldLoggerFactory; @BeforeClass public static void setupStatic() { RequestUUIDImpl.setGenerator(new UUIDGeneratorImpl()); } @Before public void init() throws Exception { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); request = mock(HttpServletRequest.class); response = mock(HttpServletResponse.class); registry = new EventLoggingRegistry(); EventLogDefinition eld = new EventLogDefinition(); eld.setRegistry(registry); eld.setLogName("ACCESS-LOG"); eld.register(); loggerFactory = mock(ILoggerFactory.class); oldLoggerFactory = HttpRequestLogger.setLoggerFactory(loggerFactory); eventLog = mock(Logger.class); } @After public void after() { HttpRequestLogger.setLoggerFactory(oldLoggerFactory); } @Test public void testLogEventNoExtraFields() throws Exception { final HttpCommand command = createCommand(null); RequestLogger logger = new HttpRequestLogger(registry, true); ExecutionContext context = mock(ExecutionContext.class); RequestUUIDImpl uuid = new RequestUUIDImpl(); when(context.getRequestUUID()).thenReturn(uuid); when(response.getContentType()).thenReturn(MediaType.APPLICATION_XML); GeoLocationDetails geoDetails = mock(GeoLocationDetails.class); when(geoDetails.getCountry()).thenReturn("UK"); when(geoDetails.getRemoteAddr()).thenReturn("2.3.4.5"); when(geoDetails.getResolvedAddresses()).thenReturn(RemoteAddressUtils.parse("1.2.3.4", "1.2.3.4,5.6.7.8")); when(context.getLocation()).thenReturn(geoDetails); when(request.getHeader(HeaderUtils.USER_AGENT)).thenReturn("IE"); long bytesRead = 100; long bytesWritten = 1000; MediaType requestMediaType = MediaType.APPLICATION_JSON_TYPE; MediaType responseMediaType = MediaType.APPLICATION_XML_TYPE; ResponseCode responseCode = ResponseCode.Ok; command.getTimer().requestComplete(); when(loggerFactory.getLogger("ACCESS-LOG")).thenReturn(eventLog); logger.logAccess(command, context, bytesRead, bytesWritten, requestMediaType, responseMediaType, responseCode); ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class); verify(eventLog, times(1)).info(messageCaptor.capture()); String[] loggedValues = tokenise(messageCaptor.getValue()); int i=0; assertEquals(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(command.getTimer().getReceivedTime()), loggedValues[i++]); assertEquals(uuid.toString(), loggedValues[i++]); assertEquals("/test", loggedValues[i++]); assertEquals("none", loggedValues[i++]); assertEquals("2.3.4.5", loggedValues[i++]); assertEquals("1.2.3.4;5.6.7.8", loggedValues[i++]); assertEquals("UK", loggedValues[i++]); assertEquals("Ok", loggedValues[i++]); assertEquals(command.getTimer().getProcessTimeNanos(), Long.parseLong(loggedValues[i++])); assertEquals("100", loggedValues[i++]); assertEquals("1000", loggedValues[i++]); assertEquals("json", loggedValues[i++]); assertEquals("xml", loggedValues[i++]); assertEquals("[]", loggedValues[i++]); } @Test public void testLogEventWithExtraFields() throws Exception { final HttpCommand command = createCommand(null); HttpRequestLogger logger = new HttpRequestLogger(registry, true); logger.setHeadersToLog("User-Agent"); logger.addHeaderToLog("eeeep"); ExecutionContext context = mock(ExecutionContext.class); RequestUUIDImpl uuid = new RequestUUIDImpl(); when(context.getRequestUUID()).thenReturn(uuid); when(response.getContentType()).thenReturn(MediaType.APPLICATION_XML); GeoLocationDetails geoDetails = mock(GeoLocationDetails.class); when(geoDetails.getCountry()).thenReturn("UK"); when(geoDetails.getRemoteAddr()).thenReturn("2.3.4.5"); when(geoDetails.getResolvedAddresses()).thenReturn(RemoteAddressUtils.parse("1.2.3.4", "1.2.3.4")); when(context.getLocation()).thenReturn(geoDetails); when(request.getHeader(HeaderUtils.USER_AGENT)).thenReturn("IE"); when(request.getHeader("eeeep")).thenReturn("meeeep"); long bytesRead = 100; long bytesWritten = 1000; MediaType requestMediaType = MediaType.APPLICATION_JSON_TYPE; MediaType responseMediaType = MediaType.APPLICATION_XML_TYPE; ResponseCode responseCode = ResponseCode.Ok; when(loggerFactory.getLogger("ACCESS-LOG")).thenReturn(eventLog); command.getTimer().requestComplete(); logger.logAccess(command, context, bytesRead, bytesWritten, requestMediaType, responseMediaType, responseCode); ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class); verify(eventLog, times(1)).info(messageCaptor.capture()); String[] loggedValues = tokenise(messageCaptor.getValue()); int i=0; assertEquals(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(command.getTimer().getReceivedTime()), loggedValues[i++]); assertEquals(uuid.toString(), loggedValues[i++]); assertEquals("/test", loggedValues[i++]); assertEquals("none", loggedValues[i++]); assertEquals("2.3.4.5", loggedValues[i++]); assertEquals("1.2.3.4", loggedValues[i++]); assertEquals("UK", loggedValues[i++]); assertEquals("Ok", loggedValues[i++]); assertEquals(command.getTimer().getProcessTimeNanos(), Long.parseLong(loggedValues[i++])); assertEquals("100", loggedValues[i++]); assertEquals("1000", loggedValues[i++]); assertEquals("json", loggedValues[i++]); assertEquals("xml", loggedValues[i++]); assertEquals("[User-Agent=IE|eeeep=meeeep]", loggedValues[i++]); } private String[] tokenise(String message) { // Ruddy java regexes don't support infinite length look behinds return message.trim().split("(?<!\\[[^\\]]{1,500}),"); } protected HttpCommand createCommand(IdentityTokenResolver identityTokenResolver) { return new TestHttpCommand(identityTokenResolver); } protected class TestHttpCommand implements HttpCommand { private CommandStatus commandStatus = CommandStatus.InProgress; private RequestTimer timer = new RequestTimer(); private IdentityTokenResolver identityTokenResolver; public TestHttpCommand(IdentityTokenResolver identityTokenResolver) { this.identityTokenResolver = identityTokenResolver; } @Override public HttpServletRequest getRequest() { return request; } @Override public HttpServletResponse getResponse() { return response; } @Override public IdentityTokenResolver<?, ?, ?> getIdentityTokenResolver() { return this.identityTokenResolver; } @Override public CommandStatus getStatus() { return commandStatus; } @Override public void onComplete() { commandStatus = CommandStatus.Complete; } @Override public RequestTimer getTimer() { return timer; } @Override public String getFullPath() { return "/test"; } @Override public String getOperationPath() { return "/foo"+ getFullPath(); } }; }