├── .gitignore ├── .travis.yml ├── src ├── main │ └── java │ │ └── de │ │ └── sstoehr │ │ └── harreader │ │ ├── model │ │ ├── HttpMethod.java │ │ ├── Har.java │ │ ├── HttpStatus.java │ │ ├── HarQueryParam.java │ │ ├── HarHeader.java │ │ ├── HarCreatorBrowser.java │ │ ├── HarPageTiming.java │ │ ├── HarPostData.java │ │ ├── HarPostDataParam.java │ │ ├── HarContent.java │ │ ├── HarPage.java │ │ ├── HarLog.java │ │ ├── HarCookie.java │ │ ├── HarTiming.java │ │ ├── HarCache.java │ │ ├── HarEntry.java │ │ ├── HarResponse.java │ │ └── HarRequest.java │ │ ├── HarReaderException.java │ │ ├── jackson │ │ ├── MapperFactory.java │ │ ├── DefaultMapperFactory.java │ │ ├── ExceptionIgnoringIntegerDeserializer.java │ │ └── ExceptionIgnoringDateDeserializer.java │ │ ├── HarReaderMode.java │ │ └── HarReader.java └── test │ ├── java │ └── de │ │ └── sstoehr │ │ └── harreader │ │ ├── model │ │ ├── HarQueryParamTest.java │ │ ├── HarTest.java │ │ ├── HttpStatusTest.java │ │ ├── AbstractMapperTest.java │ │ ├── HarHeaderTest.java │ │ ├── HarCreatorBrowserTest.java │ │ ├── HarPostDataParamTest.java │ │ ├── HarContentTest.java │ │ ├── HarPostDataTest.java │ │ ├── HarCookieTest.java │ │ ├── HarPageTest.java │ │ ├── HarCacheTest.java │ │ ├── HarPageTimingTest.java │ │ ├── HarTimingTest.java │ │ ├── HarEntryTest.java │ │ ├── HarResponseTest.java │ │ ├── HarLogTest.java │ │ └── HarRequestTest.java │ │ └── HarReaderTest.java │ └── resources │ ├── sstoehr.har │ ├── sstoehr.invalid-date.har │ └── sstoehr.invalid-integer.har ├── LICENSE.md ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | target/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk7 5 | - oraclejdk8 6 | script: "mvn verify -B" 7 | after_success: 8 | - mvn cobertura:cobertura coveralls:report 9 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HttpMethod.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | public enum HttpMethod { 4 | GET, POST, PUT, HEAD, PROPFIND, OPTIONS, REPORT, DELETE, CONNECT, TRACE, CCM_POST; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/HarReaderException.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader; 2 | 3 | public class HarReaderException extends Exception { 4 | 5 | public HarReaderException(Throwable cause) { 6 | super(cause); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/jackson/MapperFactory.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.jackson; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import de.sstoehr.harreader.HarReaderMode; 5 | 6 | public interface MapperFactory { 7 | 8 | ObjectMapper instance(HarReaderMode mode); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/HarReaderMode.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader; 2 | 3 | public enum HarReaderMode { 4 | 5 | /** 6 | * Using strict mode enforces some rules. 7 | * When trying to open an invalid HAR file an exception will be thrown. 8 | */ 9 | STRICT, 10 | 11 | /** 12 | * Using lax mode you are able to read even invalid HAR files. 13 | * Currently lax mode allows: 14 | * 17 | */ 18 | LAX; 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarQueryParamTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | 5 | public class HarQueryParamTest extends AbstractMapperTest { 6 | 7 | @Override 8 | public void testMapping() { 9 | HarQueryParam queryParam = map("{\"name\": \"aName\", \"value\":\"aValue\", \"comment\": \"My comment\"}", HarQueryParam.class); 10 | 11 | Assert.assertEquals("aName", queryParam.getName()); 12 | Assert.assertEquals("aValue", queryParam.getValue()); 13 | Assert.assertEquals("My comment", queryParam.getComment()); 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class HarTest extends AbstractMapperTest{ 7 | 8 | @Test 9 | public void testLogNull() { 10 | Har har = new Har(); 11 | har.setLog(null); 12 | Assert.assertNotNull(har.getLog()); 13 | } 14 | 15 | @Override 16 | public void testMapping() { 17 | Har har = map("{\"log\": {}}", Har.class); 18 | Assert.assertNotNull(har.getLog()); 19 | 20 | har = map(UNKNOWN_PROPERTY, Har.class); 21 | Assert.assertNotNull(har); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HttpStatusTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class HttpStatusTest { 7 | 8 | @Test 9 | public void test302() { 10 | Assert.assertEquals(HttpStatus.FOUND, HttpStatus.byCode(302)); 11 | } 12 | 13 | @Test 14 | public void testInvalidCode() { 15 | Assert.assertEquals(HttpStatus.UNKNOWN_HTTP_STATUS, HttpStatus.byCode(0)); 16 | Assert.assertEquals(HttpStatus.UNKNOWN_HTTP_STATUS, HttpStatus.byCode(1000)); 17 | Assert.assertEquals(HttpStatus.UNKNOWN_HTTP_STATUS, HttpStatus.byCode(-999)); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/AbstractMapperTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | public abstract class AbstractMapperTest { 8 | 9 | protected final static String UNKNOWN_PROPERTY = "{\"unknownProperty\":\"value\"}"; 10 | 11 | @Test 12 | public abstract void testMapping(); 13 | 14 | public T map(String input, Class tClass) { 15 | ObjectMapper mapper = new ObjectMapper(); 16 | try { 17 | return mapper.readValue(input, tClass); 18 | } catch (Exception e) { 19 | Assert.fail(e.getMessage()); 20 | } 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarHeaderTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | 5 | public class HarHeaderTest extends AbstractMapperTest { 6 | 7 | @Override 8 | public void testMapping() { 9 | HarHeader header = map("{\"name\":\"aName\",\"value\":\"aValue\",\"comment\":\"my comment\"}", HarHeader.class); 10 | 11 | Assert.assertNotNull(header); 12 | Assert.assertEquals("aName", header.getName()); 13 | Assert.assertEquals("aValue", header.getValue()); 14 | Assert.assertEquals("my comment", header.getComment()); 15 | 16 | header = map(UNKNOWN_PROPERTY, HarHeader.class); 17 | Assert.assertNotNull(header); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarCreatorBrowserTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | 5 | public class HarCreatorBrowserTest extends AbstractMapperTest { 6 | 7 | @Override 8 | public void testMapping() { 9 | HarCreatorBrowser creatorBrowser = map("{\"name\":\"aName\",\"version\":\"aVersion\",\"comment\":\"my comment\"}", HarCreatorBrowser.class); 10 | 11 | Assert.assertNotNull(creatorBrowser); 12 | Assert.assertEquals("aName", creatorBrowser.getName()); 13 | Assert.assertEquals("aVersion", creatorBrowser.getVersion()); 14 | Assert.assertEquals("my comment", creatorBrowser.getComment()); 15 | 16 | creatorBrowser = map(UNKNOWN_PROPERTY, HarCreatorBrowser.class); 17 | Assert.assertNotNull(creatorBrowser); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/jackson/DefaultMapperFactory.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.jackson; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fasterxml.jackson.databind.module.SimpleModule; 5 | import de.sstoehr.harreader.HarReaderMode; 6 | 7 | import java.util.Date; 8 | 9 | public class DefaultMapperFactory implements MapperFactory { 10 | 11 | public ObjectMapper instance(HarReaderMode mode) { 12 | ObjectMapper mapper = new ObjectMapper(); 13 | SimpleModule module = new SimpleModule(); 14 | if (mode == HarReaderMode.LAX) { 15 | module.addDeserializer(Date.class, new ExceptionIgnoringDateDeserializer()); 16 | module.addDeserializer(Integer.class, new ExceptionIgnoringIntegerDeserializer()); 17 | } 18 | mapper.registerModule(module); 19 | return mapper; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/jackson/ExceptionIgnoringIntegerDeserializer.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.jackson; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | import com.fasterxml.jackson.databind.deser.std.NumberDeserializers; 7 | 8 | import java.io.IOException; 9 | 10 | public class ExceptionIgnoringIntegerDeserializer extends JsonDeserializer { 11 | @Override 12 | public Integer deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { 13 | try { 14 | NumberDeserializers.IntegerDeserializer integerDeserializer = new NumberDeserializers.IntegerDeserializer(Integer.class, null); 15 | return integerDeserializer.deserialize(jp, ctxt); 16 | } catch (IOException e) { 17 | //ignore 18 | } 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/jackson/ExceptionIgnoringDateDeserializer.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.jackson; 2 | 3 | import java.io.IOException; 4 | import java.util.Date; 5 | 6 | import com.fasterxml.jackson.core.JsonParser; 7 | import com.fasterxml.jackson.databind.DeserializationContext; 8 | import com.fasterxml.jackson.databind.JsonDeserializer; 9 | import com.fasterxml.jackson.databind.deser.std.DateDeserializers; 10 | 11 | public class ExceptionIgnoringDateDeserializer extends JsonDeserializer { 12 | 13 | @Override 14 | public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws java.io.IOException { 15 | try { 16 | DateDeserializers.DateDeserializer dateDeserializer = new DateDeserializers.DateDeserializer(); 17 | return dateDeserializer.deserialize(jp, ctxt); 18 | } catch (IOException e) { 19 | //ignore 20 | } 21 | return new Date(0L); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarPostDataParamTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | 5 | public class HarPostDataParamTest extends AbstractMapperTest { 6 | 7 | @Override 8 | public void testMapping() { 9 | HarPostDataParam postDataParam = map("{\"name\": \"aName\", \"value\": \"aValue\", \"fileName\": \"aFilename\", \"contentType\": \"aContentType\", \"comment\": \"My comment\"}", HarPostDataParam.class); 10 | 11 | Assert.assertEquals("aName", postDataParam.getName()); 12 | Assert.assertEquals("aValue", postDataParam.getValue()); 13 | Assert.assertEquals("aFilename", postDataParam.getFileName()); 14 | Assert.assertEquals("aContentType", postDataParam.getContentType()); 15 | Assert.assertEquals("My comment", postDataParam.getComment()); 16 | 17 | 18 | postDataParam = map(UNKNOWN_PROPERTY, HarPostDataParam.class); 19 | Assert.assertNotNull(postDataParam); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarContentTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | 5 | public class HarContentTest extends AbstractMapperTest { 6 | 7 | @Override 8 | public void testMapping() { 9 | HarContent content = map("{\"size\":123,\"compression\":45,\"mimeType\":\"mime/type\"," + 10 | "\"text\":\"my content\",\"encoding\":\"base64\",\"comment\":\"my comment\"}", HarContent.class); 11 | 12 | Assert.assertEquals(123L, (long) content.getSize()); 13 | Assert.assertEquals(45L, (long) content.getCompression()); 14 | Assert.assertEquals("mime/type", content.getMimeType()); 15 | Assert.assertEquals("my content", content.getText()); 16 | Assert.assertEquals("base64", content.getEncoding()); 17 | Assert.assertEquals("my comment", content.getComment()); 18 | 19 | content = map(UNKNOWN_PROPERTY, HarContent.class); 20 | Assert.assertNotNull(content); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Sebastian Stöhr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/Har.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * Main HTTP Archive Class. 10 | * @see speicification 11 | */ 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class Har { 15 | 16 | private HarLog log; 17 | 18 | /** 19 | * @return HAR log. 20 | */ 21 | public HarLog getLog() { 22 | if (log == null) { 23 | log = new HarLog(); 24 | } 25 | return log; 26 | } 27 | 28 | public void setLog(HarLog log) { 29 | this.log = log; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (o == null || getClass() != o.getClass()) return false; 36 | Har har = (Har) o; 37 | return Objects.equals(log, har.log); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hash(log); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarPostDataTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class HarPostDataTest extends AbstractMapperTest { 10 | 11 | private static final List EXPECTED_LIST = new ArrayList<>(); 12 | 13 | @Override 14 | public void testMapping() { 15 | HarPostData postData = map("{\"mimeType\": \"aMimeType\", \"params\": [], \"text\":\"aText\", \"comment\": \"My comment\"}", HarPostData.class); 16 | 17 | Assert.assertEquals("aMimeType", postData.getMimeType()); 18 | Assert.assertEquals(EXPECTED_LIST, postData.getParams()); 19 | Assert.assertEquals("aText", postData.getText()); 20 | Assert.assertEquals("My comment", postData.getComment()); 21 | 22 | postData = map(UNKNOWN_PROPERTY, HarPostData.class); 23 | Assert.assertNotNull(postData); 24 | } 25 | 26 | @Test 27 | public void testParams() { 28 | HarPostData postData = new HarPostData(); 29 | postData.setParams(null); 30 | Assert.assertNotNull(postData.getParams()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarCookieTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | 5 | import java.util.Date; 6 | 7 | public class HarCookieTest extends AbstractMapperTest { 8 | 9 | private final static Date EXPECTED_EXPIRES = new Date() {{ 10 | setTime(1388577600000L); 11 | }}; 12 | 13 | @Override 14 | public void testMapping() { 15 | HarCookie cookie = map("{\"name\":\"aName\",\"value\":\"aValue\",\"path\":\"/\",\"domain\":\"sstoehr.de\"," + 16 | "\"expires\":\"2014-01-01T12:00:00\",\"httpOnly\":\"true\",\"secure\":\"false\",\"comment\":\"my comment\"}", HarCookie.class); 17 | 18 | Assert.assertNotNull(cookie); 19 | Assert.assertEquals("aName", cookie.getName()); 20 | Assert.assertEquals("aValue", cookie.getValue()); 21 | Assert.assertEquals("/", cookie.getPath()); 22 | Assert.assertEquals("sstoehr.de", cookie.getDomain()); 23 | Assert.assertEquals(EXPECTED_EXPIRES, cookie.getExpires()); 24 | Assert.assertEquals(true, cookie.getHttpOnly()); 25 | Assert.assertEquals(false, cookie.getSecure()); 26 | Assert.assertEquals("my comment", cookie.getComment()); 27 | 28 | cookie = map(UNKNOWN_PROPERTY, HarCookie.class); 29 | Assert.assertNotNull(cookie); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarPageTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.util.Date; 7 | 8 | public class HarPageTest extends AbstractMapperTest { 9 | 10 | private final static Date EXPECTED_STARTED = new Date() {{ 11 | setTime(1388577600000L); 12 | }}; 13 | 14 | @Override 15 | public void testMapping() { 16 | HarPage page = map("{\"startedDateTime\":\"2014-01-01T12:00:00\",\"id\":\"anId\"," 17 | + "\"title\":\"aTitle\",\"pageTimings\":{},\"comment\":\"my comment\", \"_add\": \"additional info\"}", HarPage.class); 18 | 19 | Assert.assertNotNull(page); 20 | Assert.assertEquals(EXPECTED_STARTED, page.getStartedDateTime()); 21 | Assert.assertEquals("anId", page.getId()); 22 | Assert.assertEquals("aTitle", page.getTitle()); 23 | Assert.assertNotNull(page.getPageTimings()); 24 | Assert.assertEquals("my comment", page.getComment()); 25 | Assert.assertEquals("additional info", page.getAdditional().get("_add")); 26 | 27 | page = map(UNKNOWN_PROPERTY, HarPage.class); 28 | Assert.assertNotNull(page); 29 | } 30 | 31 | @Test 32 | public void testPageTimingsNull() { 33 | HarPage page = new HarPage(); 34 | page.setPageTimings(null); 35 | Assert.assertNotNull(page.getPageTimings()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarCacheTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | 5 | import java.util.Date; 6 | 7 | public class HarCacheTest extends AbstractMapperTest { 8 | 9 | private final static Date EXPECTED_EXPIRES = new Date() {{ 10 | setTime(1388577600000L); 11 | }}; 12 | private final static Date EXPECTED_LAST_ACCESS = new Date() {{ 13 | setTime(1370088000000L); 14 | }}; 15 | 16 | @Override 17 | public void testMapping() { 18 | HarCache cache = map("{\"beforeRequest\":{\"expires\":\"2014-01-01T12:00:00\",\"lastAccess\":\"2013-06-01T12:00:00\",\"eTag\":\"abc123\"," + 19 | "\"hitCount\":3,\"comment\":\"my comment\"},\"afterRequest\":{},\"comment\":\"my comment 2\"}", HarCache.class); 20 | 21 | Assert.assertNotNull(cache.getBeforeRequest()); 22 | Assert.assertEquals(EXPECTED_EXPIRES, cache.getBeforeRequest().getExpires()); 23 | Assert.assertEquals(EXPECTED_LAST_ACCESS, cache.getBeforeRequest().getLastAccess()); 24 | Assert.assertEquals("abc123", cache.getBeforeRequest().geteTag()); 25 | Assert.assertEquals(3, (long) cache.getBeforeRequest().getHitCount()); 26 | Assert.assertEquals("my comment", cache.getBeforeRequest().getComment()); 27 | 28 | Assert.assertNotNull(cache.getAfterRequest()); 29 | 30 | Assert.assertEquals("my comment 2", cache.getComment()); 31 | 32 | cache = map(UNKNOWN_PROPERTY, HarCache.class); 33 | Assert.assertNotNull(cache); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/HarReader.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import de.sstoehr.harreader.jackson.DefaultMapperFactory; 5 | import de.sstoehr.harreader.jackson.MapperFactory; 6 | import de.sstoehr.harreader.model.Har; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | 11 | public class HarReader { 12 | 13 | private final MapperFactory mapperFactory; 14 | 15 | public HarReader(MapperFactory mapperFactory) { 16 | if (mapperFactory == null) { 17 | throw new IllegalArgumentException("mapperFactory must not be null!"); 18 | } 19 | this.mapperFactory = mapperFactory; 20 | } 21 | 22 | public HarReader() { 23 | this(new DefaultMapperFactory()); 24 | } 25 | 26 | public Har readFromFile(File har) throws HarReaderException { 27 | return this.readFromFile(har, HarReaderMode.STRICT); 28 | } 29 | 30 | public Har readFromFile(File har, HarReaderMode mode) throws HarReaderException { 31 | ObjectMapper mapper = mapperFactory.instance(mode); 32 | try { 33 | return mapper.readValue(har, Har.class); 34 | } catch (IOException e) { 35 | throw new HarReaderException(e); 36 | } 37 | } 38 | 39 | public Har readFromString(String har) throws HarReaderException { 40 | return this.readFromString(har, HarReaderMode.STRICT); 41 | } 42 | 43 | public Har readFromString(String har, HarReaderMode mode) throws HarReaderException { 44 | ObjectMapper mapper = mapperFactory.instance(mode); 45 | try { 46 | return mapper.readValue(har, Har.class); 47 | } catch (IOException e) { 48 | throw new HarReaderException(e); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarPageTimingTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class HarPageTimingTest extends AbstractMapperTest { 7 | 8 | private static final Integer EXPECTED_DEFAULT_DURATION = -1; 9 | 10 | @Test 11 | public void testOnContentLoad() { 12 | HarPageTiming pageTiming = new HarPageTiming(); 13 | Assert.assertEquals(EXPECTED_DEFAULT_DURATION, pageTiming.getOnContentLoad()); 14 | 15 | pageTiming.setOnContentLoad(1234); 16 | Assert.assertEquals(1234, (int) pageTiming.getOnContentLoad()); 17 | 18 | pageTiming.setOnContentLoad(null); 19 | Assert.assertEquals(EXPECTED_DEFAULT_DURATION, pageTiming.getOnContentLoad()); 20 | } 21 | 22 | @Test 23 | public void testOnLoad() { 24 | HarPageTiming pageTiming = new HarPageTiming(); 25 | Assert.assertEquals(EXPECTED_DEFAULT_DURATION, pageTiming.getOnLoad()); 26 | 27 | pageTiming.setOnLoad(1234); 28 | Assert.assertEquals(1234, (int) pageTiming.getOnLoad()); 29 | 30 | pageTiming.setOnLoad(null); 31 | Assert.assertEquals(EXPECTED_DEFAULT_DURATION, pageTiming.getOnLoad()); 32 | } 33 | 34 | @Override 35 | public void testMapping() { 36 | HarPageTiming pageTiming = map("{\"onContentLoad\": 1234, \"onLoad\": 5678, \"comment\": \"My comment\"}", HarPageTiming.class); 37 | 38 | Assert.assertEquals(1234, (int) pageTiming.getOnContentLoad()); 39 | Assert.assertEquals(5678, (int) pageTiming.getOnLoad()); 40 | Assert.assertEquals("My comment", pageTiming.getComment()); 41 | 42 | pageTiming = map(UNKNOWN_PROPERTY, HarPageTiming.class); 43 | Assert.assertNotNull(pageTiming); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HttpStatus.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public enum HttpStatus { 7 | 8 | UNKNOWN_HTTP_STATUS(0), 9 | 10 | OK(200), CREATED(201), ACCEPTED(202), NO_CONTENT(204), RESET_CONTENT(205), 11 | PARTIAL_CONTENT(206), 12 | 13 | MOVED_PERMANENTLY(301), FOUND(302), SEE_OTHER(303), NOT_MODIFIED(304), 14 | USE_PROXY(305), TEMPORARY_REDIRECT(307), 15 | 16 | BAD_REQUEST(400), UNAUTHORIZED(401), PAYMENT_REQUIRED(402), FORBIDDEN(403), 17 | NOT_FOUND(404), METHOD_NOT_ALLOWED(405), NOT_ACCEPTABLE(406), 18 | PROXY_AUTHENTICATION_REQUIRED(407), REQUEST_TIMEOUT(408), 19 | CONFLICT(409), GONE(410), LENGTH_REQUIRED(411), PRECONDITION_FAILED(412), 20 | REQUEST_ENTITY_TOO_LARGE(413), REQUEST_URI_TOO_LONG(414), 21 | UNSUPPORTED_MEDIA_TYPE(415), REQUESTED_RANGE_NOT_SATISFIABLE(416), 22 | EXPECTATION_FAILED(417), 23 | 24 | INTERNAL_SERVER_ERROR(500), NOT_IMPLEMENTED(501), 25 | BAD_GATEWAY(502), SERVICE_UNAVAILABLE(503), GATEWAY_TIMEOUT(504), 26 | HTTP_VERSION_NOT_SUPPORTED(505); 27 | 28 | private static final Map CODE_MAP = new HashMap<>(); 29 | 30 | static { 31 | for (HttpStatus status : HttpStatus.values()) { 32 | CODE_MAP.put(status.getCode(), status); 33 | } 34 | } 35 | 36 | private int code; 37 | 38 | private HttpStatus(int code) { 39 | this.code = code; 40 | } 41 | 42 | public int getCode() { 43 | return code; 44 | } 45 | 46 | public static HttpStatus byCode(int code) { 47 | HttpStatus status = CODE_MAP.get(code); 48 | if (status == null) { 49 | return UNKNOWN_HTTP_STATUS; 50 | } 51 | return status; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarTimingTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class HarTimingTest extends AbstractMapperTest { 7 | 8 | @Override 9 | public void testMapping() { 10 | HarTiming timing = map("{\"blocked\": 3804,\"dns\": 23,\"connect\": 5,\"send\": 9,\"wait\": 5209," 11 | + "\"receive\": 79, \"ssl\": 123, \"comment\": \"my comment\"}", HarTiming.class); 12 | 13 | Assert.assertNotNull(timing); 14 | Assert.assertEquals(3804, (int) timing.getBlocked()); 15 | Assert.assertEquals(23, (int) timing.getDns()); 16 | Assert.assertEquals(5, (int) timing.getConnect()); 17 | Assert.assertEquals(9, (int) timing.getSend()); 18 | Assert.assertEquals(5209, (int) timing.getWait()); 19 | Assert.assertEquals(79, (int) timing.getReceive()); 20 | Assert.assertEquals(123, (int) timing.getSsl()); 21 | Assert.assertEquals("my comment", timing.getComment()); 22 | } 23 | 24 | @Test 25 | public void testBlocked() { 26 | HarTiming timing = new HarTiming(); 27 | timing.setBlocked(null); 28 | Assert.assertEquals(-1, (int) timing.getBlocked()); 29 | } 30 | 31 | @Test 32 | public void testDns() { 33 | HarTiming timing = new HarTiming(); 34 | timing.setDns(null); 35 | Assert.assertEquals(-1, (int) timing.getDns()); 36 | } 37 | 38 | @Test 39 | public void testConnect() { 40 | HarTiming timing = new HarTiming(); 41 | timing.setConnect(null); 42 | Assert.assertEquals(-1, (int) timing.getConnect()); 43 | } 44 | 45 | @Test 46 | public void testSsl() { 47 | HarTiming timing = new HarTiming(); 48 | timing.setSsl(null); 49 | Assert.assertEquals(-1, (int) timing.getSsl()); 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarQueryParam.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * Information about query params. 10 | * @see specification 11 | */ 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class HarQueryParam { 15 | 16 | private String name; 17 | private String value; 18 | private String comment; 19 | 20 | /** 21 | * @return Name of param, null if not present. 22 | */ 23 | public String getName() { 24 | return name; 25 | } 26 | 27 | public void setName(String name) { 28 | this.name = name; 29 | } 30 | 31 | /** 32 | * @return Value of param, null if not present. 33 | */ 34 | public String getValue() { 35 | return value; 36 | } 37 | 38 | public void setValue(String value) { 39 | this.value = value; 40 | } 41 | 42 | /** 43 | * @return Comment provided by the user or application, null if not present. 44 | */ 45 | public String getComment() { 46 | return comment; 47 | } 48 | 49 | public void setComment(String comment) { 50 | this.comment = comment; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object o) { 55 | if (this == o) return true; 56 | if (o == null || getClass() != o.getClass()) return false; 57 | HarQueryParam that = (HarQueryParam) o; 58 | return Objects.equals(name, that.name) && 59 | Objects.equals(value, that.value) && 60 | Objects.equals(comment, that.comment); 61 | } 62 | 63 | @Override 64 | public int hashCode() { 65 | return Objects.hash(name, value, comment); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarHeader.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * Information about a header used in request and/or response. 10 | * @see specification 11 | */ 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class HarHeader { 15 | 16 | private String name; 17 | private String value; 18 | private String comment; 19 | 20 | /** 21 | * @return Header name, null if not present. 22 | */ 23 | public String getName() { 24 | return name; 25 | } 26 | 27 | public void setName(String name) { 28 | this.name = name; 29 | } 30 | 31 | /** 32 | * @return Header value, null if not present. 33 | */ 34 | public String getValue() { 35 | return value; 36 | } 37 | 38 | public void setValue(String value) { 39 | this.value = value; 40 | } 41 | 42 | /** 43 | * @return Comment provided by the user or application, null if not present. 44 | */ 45 | public String getComment() { 46 | return comment; 47 | } 48 | 49 | public void setComment(String comment) { 50 | this.comment = comment; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object o) { 55 | if (this == o) return true; 56 | if (o == null || getClass() != o.getClass()) return false; 57 | HarHeader harHeader = (HarHeader) o; 58 | return Objects.equals(name, harHeader.name) && 59 | Objects.equals(value, harHeader.value) && 60 | Objects.equals(comment, harHeader.comment); 61 | } 62 | 63 | @Override 64 | public int hashCode() { 65 | return Objects.hash(name, value, comment); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarCreatorBrowser.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * Information about the application/browser used for creating HAR. 10 | * @see specification 11 | */ 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class HarCreatorBrowser { 15 | 16 | private String name; 17 | private String version; 18 | private String comment; 19 | 20 | /** 21 | * @return Name of the application/browser used for creating HAR, null if not present. 22 | */ 23 | public String getName() { 24 | return name; 25 | } 26 | 27 | public void setName(String name) { 28 | this.name = name; 29 | } 30 | 31 | /** 32 | * @return Version of the application/browser used for creating HAR, null if not present. 33 | */ 34 | public String getVersion() { 35 | return version; 36 | } 37 | 38 | public void setVersion(String version) { 39 | this.version = version; 40 | } 41 | 42 | /** 43 | * @return Comment provided by the user or application, null if not present. 44 | */ 45 | public String getComment() { 46 | return comment; 47 | } 48 | 49 | public void setComment(String comment) { 50 | this.comment = comment; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object o) { 55 | if (this == o) return true; 56 | if (o == null || getClass() != o.getClass()) return false; 57 | HarCreatorBrowser that = (HarCreatorBrowser) o; 58 | return Objects.equals(name, that.name) && 59 | Objects.equals(version, that.version) && 60 | Objects.equals(comment, that.comment); 61 | } 62 | 63 | @Override 64 | public int hashCode() { 65 | return Objects.hash(name, version, comment); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarEntryTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.util.Date; 7 | 8 | public class HarEntryTest extends AbstractMapperTest { 9 | 10 | private final static Date EXPECTED_STARTED = new Date() {{ 11 | setTime(1388577600000L); 12 | }}; 13 | 14 | @Override 15 | public void testMapping() { 16 | HarEntry entry = map("{\"pageref\":\"aPageref\",\"startedDateTime\":\"2014-01-01T12:00:00\",\"time\":12345," 17 | + "\"request\":{},\"response\":{},\"cache\":{},\"timings\":{},\"serverIPAddress\":\"1.2.3.4\",\"connection\":\"aConnection\"," 18 | + "\"comment\":\"my comment\", \"_add\": \"additional info\"}", HarEntry.class); 19 | 20 | Assert.assertNotNull(entry); 21 | Assert.assertEquals("aPageref", entry.getPageref()); 22 | Assert.assertEquals(EXPECTED_STARTED, entry.getStartedDateTime()); 23 | Assert.assertEquals(12345, (int) entry.getTime()); 24 | Assert.assertNotNull(entry.getRequest()); 25 | Assert.assertNotNull(entry.getResponse()); 26 | Assert.assertNotNull(entry.getCache()); 27 | Assert.assertNotNull(entry.getTimings()); 28 | Assert.assertEquals("1.2.3.4", entry.getServerIPAddress()); 29 | Assert.assertEquals("aConnection", entry.getConnection()); 30 | Assert.assertEquals("my comment", entry.getComment()); 31 | Assert.assertEquals("additional info", entry.getAdditional().get("_add")); 32 | 33 | entry = map(UNKNOWN_PROPERTY, HarEntry.class); 34 | Assert.assertNotNull(entry); 35 | } 36 | 37 | @Test 38 | public void testRequestNull() { 39 | HarEntry entry = new HarEntry(); 40 | entry.setRequest(null); 41 | Assert.assertNotNull(entry.getRequest()); 42 | } 43 | 44 | @Test 45 | public void testResponseNull() { 46 | HarEntry entry = new HarEntry(); 47 | entry.setResponse(null); 48 | Assert.assertNotNull(entry.getResponse()); 49 | } 50 | 51 | @Test 52 | public void testCacheNull() { 53 | HarEntry entry = new HarEntry(); 54 | entry.setCache(null); 55 | Assert.assertNotNull(entry.getCache()); 56 | } 57 | 58 | @Test 59 | public void testTimingsNull() { 60 | HarEntry entry = new HarEntry(); 61 | entry.setTimings(null); 62 | Assert.assertNotNull(entry.getTimings()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarPageTiming.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * Information about events occurring during page load. 10 | * @see specification 11 | */ 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class HarPageTiming { 15 | 16 | protected static final Integer DEFAULT_TIME = -1; 17 | 18 | private Integer onContentLoad = DEFAULT_TIME; 19 | private Integer onLoad = DEFAULT_TIME; 20 | private String comment; 21 | 22 | /** 23 | * @return Duration in ms until content is loaded. 24 | * {@link #DEFAULT_TIME} when no information available. 25 | */ 26 | public Integer getOnContentLoad() { 27 | if (onContentLoad == null) { 28 | return DEFAULT_TIME; 29 | } 30 | return onContentLoad; 31 | } 32 | 33 | public void setOnContentLoad(Integer onContentLoad) { 34 | this.onContentLoad = onContentLoad; 35 | } 36 | 37 | /** 38 | * @return Duration in ms until onLoad event is fired. 39 | * {@link #DEFAULT_TIME} when no information available. 40 | */ 41 | public Integer getOnLoad() { 42 | if (onLoad == null) { 43 | return DEFAULT_TIME; 44 | } 45 | return onLoad; 46 | } 47 | 48 | public void setOnLoad(Integer onLoad) { 49 | this.onLoad = onLoad; 50 | } 51 | 52 | /** 53 | * @return Comment provided by the user or application, null if not present. 54 | */ 55 | public String getComment() { 56 | return comment; 57 | } 58 | 59 | public void setComment(String comment) { 60 | this.comment = comment; 61 | } 62 | 63 | @Override 64 | public boolean equals(Object o) { 65 | if (this == o) return true; 66 | if (o == null || getClass() != o.getClass()) return false; 67 | HarPageTiming that = (HarPageTiming) o; 68 | return Objects.equals(onContentLoad, that.onContentLoad) && 69 | Objects.equals(onLoad, that.onLoad) && 70 | Objects.equals(comment, that.comment); 71 | } 72 | 73 | @Override 74 | public int hashCode() { 75 | return Objects.hash(onContentLoad, onLoad, comment); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarPostData.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Objects; 9 | 10 | /** 11 | * Information about POST data. 12 | * @see specification 13 | */ 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonIgnoreProperties(ignoreUnknown = true) 16 | public class HarPostData { 17 | 18 | private String mimeType; 19 | private List params = new ArrayList<>(); 20 | private String text; 21 | private String comment; 22 | 23 | /** 24 | * @return MIME type of posted data, null if not present. 25 | */ 26 | public String getMimeType() { 27 | return mimeType; 28 | } 29 | 30 | public void setMimeType(String mimeType) { 31 | this.mimeType = mimeType; 32 | } 33 | 34 | /** 35 | * @return List of posted params. 36 | */ 37 | public List getParams() { 38 | if (params == null) { 39 | params = new ArrayList<>(); 40 | } 41 | return params; 42 | } 43 | 44 | public void setParams(List params) { 45 | this.params = params; 46 | } 47 | 48 | /** 49 | * @return Plain text posted data, null if not present. 50 | */ 51 | public String getText() { 52 | return text; 53 | } 54 | 55 | public void setText(String text) { 56 | this.text = text; 57 | } 58 | 59 | /** 60 | * @return Comment provided by the user or application, null if not present. 61 | */ 62 | public String getComment() { 63 | return comment; 64 | } 65 | 66 | public void setComment(String comment) { 67 | this.comment = comment; 68 | } 69 | 70 | @Override 71 | public boolean equals(Object o) { 72 | if (this == o) return true; 73 | if (o == null || getClass() != o.getClass()) return false; 74 | HarPostData that = (HarPostData) o; 75 | return Objects.equals(mimeType, that.mimeType) && 76 | Objects.equals(params, that.params) && 77 | Objects.equals(text, that.text) && 78 | Objects.equals(comment, that.comment); 79 | } 80 | 81 | @Override 82 | public int hashCode() { 83 | return Objects.hash(mimeType, params, text, comment); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/HarReaderTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader; 2 | 3 | import de.sstoehr.harreader.model.Har; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.io.File; 8 | 9 | public class HarReaderTest { 10 | 11 | private HarReader harReader = new HarReader(); 12 | 13 | @Test 14 | public void test() throws HarReaderException { 15 | File harFile = new File("src/test/resources/sstoehr.har"); 16 | Har har = harReader.readFromFile(harFile); 17 | Assert.assertNotNull(har); 18 | } 19 | 20 | @Test 21 | public void missingLog() throws HarReaderException { 22 | Har har = harReader.readFromString("{\"unknown\":\"!\"}"); 23 | Assert.assertNotNull(har); 24 | } 25 | 26 | @Test(expected = HarReaderException.class) 27 | public void invalidDateStrict() throws HarReaderException { 28 | File harFile = new File("src/test/resources/sstoehr.invalid-date.har"); 29 | harReader.readFromFile(harFile); 30 | } 31 | 32 | @Test 33 | public void invalidDateLax() throws HarReaderException { 34 | File harFile = new File("src/test/resources/sstoehr.invalid-date.har"); 35 | Har har = harReader.readFromFile(harFile, HarReaderMode.LAX); 36 | Assert.assertNotNull(har); 37 | } 38 | 39 | @Test(expected = HarReaderException.class) 40 | public void invalidIntegerStrict() throws HarReaderException { 41 | File harFile = new File("src/test/resources/sstoehr.invalid-integer.har"); 42 | harReader.readFromFile(harFile); 43 | } 44 | 45 | @Test 46 | public void invalidIntegerLax() throws HarReaderException { 47 | File harFile = new File("src/test/resources/sstoehr.invalid-integer.har"); 48 | Har har = harReader.readFromFile(harFile, HarReaderMode.LAX); 49 | Assert.assertNotNull(har); 50 | } 51 | 52 | @Test(expected = IllegalArgumentException.class) 53 | public void mapperFactoryNotNull() { 54 | new HarReader(null); 55 | } 56 | 57 | @Test 58 | public void testEquals() throws HarReaderException { 59 | File harFile = new File("src/test/resources/sstoehr.har"); 60 | Har har1 = harReader.readFromFile(harFile); 61 | Har har2 = harReader.readFromFile(harFile); 62 | Assert.assertTrue(har1.equals(har2)); 63 | } 64 | 65 | @Test 66 | public void testHashCode() throws HarReaderException { 67 | File harFile = new File("src/test/resources/sstoehr.har"); 68 | Har har1 = harReader.readFromFile(harFile); 69 | Har har2 = harReader.readFromFile(harFile); 70 | Assert.assertEquals(har1.hashCode(), har2.hashCode()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarResponseTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class HarResponseTest extends AbstractMapperTest { 7 | 8 | @Override 9 | public void testMapping() { 10 | HarResponse response = map("{\"status\": 200,\"statusText\": \"OK\",\"httpVersion\": \"HTTP/1.1\"," 11 | + "\"cookies\": [],\"headers\": [],\"content\": {},\"redirectURL\": \"redirectUrl\",\"headersSize\": 318," 12 | + "\"bodySize\": 16997,\"comment\": \"My comment\", \"_add\": \"additional info\"}", HarResponse.class); 13 | 14 | Assert.assertNotNull(response); 15 | Assert.assertEquals(200, response.getStatus()); 16 | Assert.assertEquals("OK", response.getStatusText()); 17 | Assert.assertEquals("HTTP/1.1", response.getHttpVersion()); 18 | Assert.assertNotNull(response.getCookies()); 19 | Assert.assertNotNull(response.getHeaders()); 20 | Assert.assertNotNull(response.getContent()); 21 | Assert.assertEquals("redirectUrl", response.getRedirectURL()); 22 | Assert.assertEquals(318L, (long) response.getHeadersSize()); 23 | Assert.assertEquals(16997L, (long) response.getBodySize()); 24 | Assert.assertEquals("My comment", response.getComment()); 25 | Assert.assertEquals("additional info", response.getAdditional().get("_add")); 26 | } 27 | 28 | @Test 29 | public void testStatus() { 30 | HarResponse response = new HarResponse(); 31 | Assert.assertEquals(0, response.getStatus()); 32 | } 33 | 34 | @Test 35 | public void testCookies() { 36 | HarResponse response = new HarResponse(); 37 | response.setCookies(null); 38 | Assert.assertNotNull(response.getCookies()); 39 | } 40 | 41 | @Test 42 | public void testHeaders() { 43 | HarResponse response = new HarResponse(); 44 | response.setHeaders(null); 45 | Assert.assertNotNull(response.getHeaders()); 46 | } 47 | 48 | @Test 49 | public void testContent() { 50 | HarResponse response = new HarResponse(); 51 | response.setContent(null); 52 | Assert.assertNotNull(response.getContent()); 53 | } 54 | 55 | @Test 56 | public void testHeadersSize() { 57 | HarResponse response = new HarResponse(); 58 | response.setHeadersSize(null); 59 | Assert.assertEquals(-1L, (long) response.getHeadersSize()); 60 | } 61 | 62 | @Test 63 | public void testBodySize() { 64 | HarResponse response = new HarResponse(); 65 | response.setBodySize(null); 66 | Assert.assertEquals(-1L, (long) response.getBodySize()); 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarPostDataParam.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * Information about POST params. 10 | * @see specification 11 | */ 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class HarPostDataParam { 15 | 16 | private String name; 17 | private String value; 18 | private String fileName; 19 | private String contentType; 20 | private String comment; 21 | 22 | /** 23 | * @return Name of param, null if not present. 24 | */ 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | public void setName(String name) { 30 | this.name = name; 31 | } 32 | 33 | /** 34 | * @return Value of a param or content of posted file, null if not present. 35 | */ 36 | public String getValue() { 37 | return value; 38 | } 39 | 40 | public void setValue(String value) { 41 | this.value = value; 42 | } 43 | 44 | /** 45 | * @return Name of posted file, null if not present. 46 | */ 47 | public String getFileName() { 48 | return fileName; 49 | } 50 | 51 | public void setFileName(String fileName) { 52 | this.fileName = fileName; 53 | } 54 | 55 | /** 56 | * @return Content type of posted file, null if not present. 57 | */ 58 | public String getContentType() { 59 | return contentType; 60 | } 61 | 62 | public void setContentType(String contentType) { 63 | this.contentType = contentType; 64 | } 65 | 66 | /** 67 | * @return Comment provided by the user or application, null if not present. 68 | */ 69 | public String getComment() { 70 | return comment; 71 | } 72 | 73 | public void setComment(String comment) { 74 | this.comment = comment; 75 | } 76 | 77 | @Override 78 | public boolean equals(Object o) { 79 | if (this == o) return true; 80 | if (o == null || getClass() != o.getClass()) return false; 81 | HarPostDataParam that = (HarPostDataParam) o; 82 | return Objects.equals(name, that.name) && 83 | Objects.equals(value, that.value) && 84 | Objects.equals(fileName, that.fileName) && 85 | Objects.equals(contentType, that.contentType) && 86 | Objects.equals(comment, that.comment); 87 | } 88 | 89 | @Override 90 | public int hashCode() { 91 | return Objects.hash(name, value, fileName, contentType, comment); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarLogTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class HarLogTest extends AbstractMapperTest { 10 | 11 | private static final String EXPECTED_DEFAULT_VERSION = "1.1"; 12 | private static final List EXPECTED_PAGES_LIST = new ArrayList<>(); 13 | private static final List EXPECTED_ENTRIES_LIST = new ArrayList<>(); 14 | 15 | @Test 16 | public void testVersion() { 17 | HarLog log = new HarLog(); 18 | Assert.assertEquals(EXPECTED_DEFAULT_VERSION, log.getVersion()); 19 | 20 | log.setVersion("1.2"); 21 | Assert.assertEquals("1.2", log.getVersion()); 22 | 23 | log.setVersion(null); 24 | Assert.assertEquals(EXPECTED_DEFAULT_VERSION, log.getVersion()); 25 | 26 | log.setVersion(""); 27 | Assert.assertEquals(EXPECTED_DEFAULT_VERSION, log.getVersion()); 28 | 29 | log.setVersion(" "); 30 | Assert.assertEquals(EXPECTED_DEFAULT_VERSION, log.getVersion()); 31 | } 32 | 33 | @Test 34 | public void testPages() { 35 | HarLog log = new HarLog(); 36 | Assert.assertEquals(EXPECTED_PAGES_LIST, log.getPages()); 37 | 38 | log.setPages(null); 39 | Assert.assertEquals(EXPECTED_PAGES_LIST, log.getPages()); 40 | } 41 | 42 | @Test 43 | public void testEntries() { 44 | HarLog log = new HarLog(); 45 | Assert.assertEquals(EXPECTED_ENTRIES_LIST, log.getEntries()); 46 | 47 | log.setEntries(null); 48 | Assert.assertEquals(EXPECTED_ENTRIES_LIST, log.getEntries()); 49 | } 50 | 51 | @Test 52 | public void testCreatorNull() { 53 | HarLog log = new HarLog(); 54 | log.setCreator(null); 55 | Assert.assertNotNull(log.getCreator()); 56 | } 57 | 58 | @Test 59 | public void testBrowserNull() { 60 | HarLog log = new HarLog(); 61 | log.setBrowser(null); 62 | Assert.assertNotNull(log.getBrowser()); 63 | } 64 | 65 | @Override 66 | public void testMapping() { 67 | HarLog log = map("{\"creator\": {}, \"browser\": {}, \"comment\": \"My comment\"}", HarLog.class); 68 | 69 | Assert.assertEquals(EXPECTED_DEFAULT_VERSION, log.getVersion()); 70 | Assert.assertNotNull(log.getCreator()); 71 | Assert.assertNotNull(log.getBrowser()); 72 | Assert.assertEquals(EXPECTED_PAGES_LIST, log.getPages()); 73 | Assert.assertEquals(EXPECTED_ENTRIES_LIST, log.getEntries()); 74 | Assert.assertEquals("My comment", log.getComment()); 75 | 76 | log = map(UNKNOWN_PROPERTY, HarLog.class); 77 | Assert.assertNotNull(log); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/de/sstoehr/harreader/model/HarRequestTest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class HarRequestTest extends AbstractMapperTest { 7 | 8 | @Override 9 | public void testMapping() { 10 | HarRequest request = map("{\"method\": \"GET\",\"url\": " 11 | + "\"http://www.sebastianstoehr.de/\",\"httpVersion\": " 12 | + "\"HTTP/1.1\",\"cookies\": [],\"headers\": [],\"queryString\": []," 13 | + "\"headersSize\": 676,\"bodySize\": -1, \"postData\": {}, \"comment\":\"my comment\"," 14 | + "\"_add\": \"additional info\"}", HarRequest.class); 15 | 16 | Assert.assertNotNull(request); 17 | Assert.assertEquals(HttpMethod.GET, request.getMethod()); 18 | Assert.assertEquals("http://www.sebastianstoehr.de/", request.getUrl()); 19 | Assert.assertEquals("HTTP/1.1", request.getHttpVersion()); 20 | Assert.assertNotNull(request.getCookies()); 21 | Assert.assertNotNull(request.getHeaders()); 22 | Assert.assertNotNull(request.getQueryString()); 23 | Assert.assertNotNull(request.getPostData()); 24 | Assert.assertEquals(676L, (long) request.getHeadersSize()); 25 | Assert.assertEquals(-1L, (long) request.getBodySize()); 26 | Assert.assertEquals("my comment", request.getComment()); 27 | Assert.assertEquals("additional info", request.getAdditional().get("_add")); 28 | } 29 | 30 | @Test 31 | public void testCookies() { 32 | HarRequest request = new HarRequest(); 33 | request.setCookies(null); 34 | Assert.assertNotNull(request.getCookies()); 35 | } 36 | 37 | @Test 38 | public void testHeaders() { 39 | HarRequest request = new HarRequest(); 40 | request.setHeaders(null); 41 | Assert.assertNotNull(request.getHeaders()); 42 | } 43 | 44 | @Test 45 | public void testQueryString() { 46 | HarRequest request = new HarRequest(); 47 | request.setQueryString(null); 48 | Assert.assertNotNull(request.getQueryString()); 49 | } 50 | 51 | @Test 52 | public void testPostData() { 53 | HarRequest request = new HarRequest(); 54 | request.setPostData(null); 55 | Assert.assertNotNull(request.getPostData()); 56 | } 57 | 58 | @Test 59 | public void testHeadersSize() { 60 | HarRequest request = new HarRequest(); 61 | request.setHeadersSize(null); 62 | Assert.assertEquals(-1L, (long) request.getHeadersSize()); 63 | } 64 | 65 | @Test 66 | public void testBodySize() { 67 | HarRequest request = new HarRequest(); 68 | request.setBodySize(null); 69 | Assert.assertEquals(-1L, (long) request.getBodySize()); 70 | } 71 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HAR reader 2 | ========== 3 | 4 | Read [HTTP Archives](http://www.softwareishard.com/blog/har-12-spec/) with Java. 5 | 6 | ``` 7 | 8 | de.sstoehr 9 | har-reader 10 | 2.1.0 11 | 12 | ``` 13 | 14 | [![Build Status](https://travis-ci.org/sdstoehr/har-reader.png?branch=master)](https://travis-ci.org/sdstoehr/har-reader) 15 | [![Coverage Status](https://coveralls.io/repos/sdstoehr/har-reader/badge.png?branch=master)](https://coveralls.io/r/sdstoehr/har-reader?branch=master) 16 | [![Maven Central](https://img.shields.io/maven-central/v/de.sstoehr/har-reader.svg)](http://mvnrepository.com/artifact/de.sstoehr/har-reader) 17 | 18 | ## Usage 19 | 20 | Reading HAR from File: 21 | 22 | ```java 23 | HarReader harReader = new HarReader(); 24 | Har har = harReader.readFromFile(new File("myhar.har")); 25 | System.out.println(har.getLog().getCreator().getName()); 26 | ``` 27 | 28 | Reading HAR from String: 29 | 30 | ```java 31 | HarReader harReader = new HarReader(); 32 | Har har = harReader.readFromString("{ ... HAR-JSON-Data ... }"); 33 | ``` 34 | 35 | ### Customizing HAR reader 36 | 37 | As of version 2.0.0 you can create your own `MapperFactory` [(DefaultMapperFactory)](src/main/java/de/sstoehr/harreader/jackson/DefaultMapperFactory.java) 38 | 39 | 40 | ```java 41 | public class MyMapperFactory implements MapperFactory { 42 | public ObjectMapper instance(HarReaderMode mode) { 43 | ObjectMapper mapper = new ObjectMapper(); 44 | SimpleModule module = new SimpleModule(); 45 | 46 | // configure Jackson object mapper as needed 47 | 48 | mapper.registerModule(module); 49 | return mapper; 50 | } 51 | } 52 | ``` 53 | 54 | You can now use your configuration by instantiating the `HarReader` with your `MapperFactory`: 55 | 56 | ```java 57 | HarReader harReader = new HarReader(new MyMapperFactory()); 58 | ``` 59 | 60 | ## Latest Releases 61 | 62 | ### 2.1.0 - 2018-03-11 63 | 64 | * You can now access additional fields, which are not part of the HAR spec: 65 | 66 | ```java 67 | response.getAdditional().get("_transferSize"); 68 | ``` 69 | 70 | [Details](https://github.com/sdstoehr/har-reader/releases/tag/har-reader-2.1.0) 71 | 72 | ### 2.0.3 - 2017-04-14 73 | 74 | * Added equals and hashCode methods 75 | 76 | ### 2.0.2 - 2016-11-21 77 | 78 | * Added CCM_POST HttpMethod to enum 79 | 80 | ### 2.0.1 - 2016-04-16 81 | 82 | * Ignore invalid integers in lax mode 83 | 84 | [Details](https://github.com/sdstoehr/har-reader/releases/tag/har-reader-2.0.1) 85 | 86 | ### 2.0.0 - 2015-08-30 87 | 88 | * HAR reader is now easier customizable. Use your own `MapperFactory` to adjust HAR reader for your project! 89 | * HAR reader threw exceptions, when required fields were empty. This behaviour was changed, so that you can now read non-standard-compliant HAR files 90 | 91 | [Details](https://github.com/sdstoehr/har-reader/releases/tag/har-reader-2.0.0) 92 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarContent.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * Information about the response's content. 10 | * @see specification 11 | */ 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class HarContent { 15 | 16 | private Long size; 17 | private Long compression; 18 | private String mimeType; 19 | private String text; 20 | private String encoding; 21 | private String comment; 22 | 23 | /** 24 | * @return Length of returned content in bytes, null if not present. 25 | */ 26 | public Long getSize() { 27 | return size; 28 | } 29 | 30 | public void setSize(Long size) { 31 | this.size = size; 32 | } 33 | 34 | /** 35 | * @return Number of bytes saved by compression, null if not present. 36 | */ 37 | public Long getCompression() { 38 | return compression; 39 | } 40 | 41 | public void setCompression(Long compression) { 42 | this.compression = compression; 43 | } 44 | 45 | /** 46 | * @return MIME-Type of response, null if not present. May include the charset. 47 | */ 48 | public String getMimeType() { 49 | return mimeType; 50 | } 51 | 52 | public void setMimeType(String mimeType) { 53 | this.mimeType = mimeType; 54 | } 55 | 56 | /** 57 | * @return Response body loaded from server or cache, null if not present. 58 | * Binary content may be encoded using encoding specified by {@link #getEncoding()}. 59 | */ 60 | public String getText() { 61 | return text; 62 | } 63 | 64 | public void setText(String text) { 65 | this.text = text; 66 | } 67 | 68 | /** 69 | * @return Encoding used for encoding response body, null if not present. 70 | * @see #getText() 71 | */ 72 | public String getEncoding() { 73 | return encoding; 74 | } 75 | 76 | public void setEncoding(String encoding) { 77 | this.encoding = encoding; 78 | } 79 | 80 | /** 81 | * @return Comment provided by the user or application, null if not present. 82 | */ 83 | public String getComment() { 84 | return comment; 85 | } 86 | 87 | public void setComment(String comment) { 88 | this.comment = comment; 89 | } 90 | 91 | @Override 92 | public boolean equals(Object o) { 93 | if (this == o) return true; 94 | if (o == null || getClass() != o.getClass()) return false; 95 | HarContent that = (HarContent) o; 96 | return Objects.equals(size, that.size) && 97 | Objects.equals(compression, that.compression) && 98 | Objects.equals(mimeType, that.mimeType) && 99 | Objects.equals(text, that.text) && 100 | Objects.equals(encoding, that.encoding) && 101 | Objects.equals(comment, that.comment); 102 | } 103 | 104 | @Override 105 | public int hashCode() { 106 | return Objects.hash(size, compression, mimeType, text, encoding, comment); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarPage.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAnyGetter; 4 | import com.fasterxml.jackson.annotation.JsonAnySetter; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | 9 | import java.util.Date; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.Objects; 13 | 14 | /** 15 | * Information about an exported page. 16 | * @see specification 17 | */ 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | public class HarPage { 21 | 22 | private Date startedDateTime; 23 | private String id; 24 | private String title; 25 | private HarPageTiming pageTimings; 26 | private String comment; 27 | private Map additional = new HashMap<>(); 28 | 29 | /** 30 | * @return Start time of page load, null if not present. 31 | */ 32 | @JsonFormat(shape = JsonFormat.Shape.STRING) 33 | public Date getStartedDateTime() { 34 | return startedDateTime; 35 | } 36 | 37 | public void setStartedDateTime(Date startedDateTime) { 38 | this.startedDateTime = startedDateTime; 39 | } 40 | 41 | /** 42 | * @return Unique identifier, null if not present. 43 | */ 44 | public String getId() { 45 | return id; 46 | } 47 | 48 | public void setId(String id) { 49 | this.id = id; 50 | } 51 | 52 | /** 53 | * @return Page title, null if not present. 54 | */ 55 | public String getTitle() { 56 | return title; 57 | } 58 | 59 | public void setTitle(String title) { 60 | this.title = title; 61 | } 62 | 63 | /** 64 | * @return Detailed information about page loading timings. 65 | */ 66 | public HarPageTiming getPageTimings() { 67 | if (pageTimings == null) { 68 | pageTimings = new HarPageTiming(); 69 | } 70 | return pageTimings; 71 | } 72 | 73 | public void setPageTimings(HarPageTiming pageTimings) { 74 | this.pageTimings = pageTimings; 75 | } 76 | 77 | /** 78 | * @return Comment provided by the user or application, null if not present. 79 | */ 80 | public String getComment() { 81 | return comment; 82 | } 83 | 84 | public void setComment(String comment) { 85 | this.comment = comment; 86 | } 87 | 88 | @JsonAnyGetter 89 | public Map getAdditional() { 90 | return additional; 91 | } 92 | 93 | @JsonAnySetter 94 | public void setAdditionalField(String name, Object value) { 95 | this.additional.put(name, value); 96 | } 97 | 98 | @Override 99 | public boolean equals(Object o) { 100 | if (this == o) return true; 101 | if (o == null || getClass() != o.getClass()) return false; 102 | HarPage harPage = (HarPage) o; 103 | return Objects.equals(startedDateTime, harPage.startedDateTime) && 104 | Objects.equals(id, harPage.id) && 105 | Objects.equals(title, harPage.title) && 106 | Objects.equals(pageTimings, harPage.pageTimings) && 107 | Objects.equals(comment, harPage.comment) && 108 | Objects.equals(additional, harPage.additional); 109 | } 110 | 111 | @Override 112 | public int hashCode() { 113 | return Objects.hash(startedDateTime, id, title, pageTimings, comment, additional); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarLog.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Objects; 9 | 10 | /** 11 | * Root object of exported data. 12 | * @see specification 13 | */ 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonIgnoreProperties(ignoreUnknown = true) 16 | public class HarLog { 17 | 18 | protected static final String DEFAULT_VERSION = "1.1"; 19 | 20 | private String version = DEFAULT_VERSION; 21 | private HarCreatorBrowser creator; 22 | private HarCreatorBrowser browser; 23 | private List pages = new ArrayList<>(); 24 | private List entries = new ArrayList<>(); 25 | private String comment; 26 | 27 | /** 28 | * @return Version number of the format. 29 | * Defaults to {@link #DEFAULT_VERSION} 30 | */ 31 | public String getVersion() { 32 | return version; 33 | } 34 | 35 | public void setVersion(String version) { 36 | if (version == null || version.trim().equals("")) { 37 | version = DEFAULT_VERSION; 38 | } 39 | this.version = version; 40 | } 41 | 42 | /** 43 | * @return Information about the application used to generate HAR. 44 | */ 45 | public HarCreatorBrowser getCreator() { 46 | if (creator == null) { 47 | creator = new HarCreatorBrowser(); 48 | } 49 | return creator; 50 | } 51 | 52 | public void setCreator(HarCreatorBrowser creator) { 53 | this.creator = creator; 54 | } 55 | 56 | /** 57 | * @return Information about the browser used. 58 | */ 59 | public HarCreatorBrowser getBrowser() { 60 | if (browser == null) { 61 | browser = new HarCreatorBrowser(); 62 | } 63 | return browser; 64 | } 65 | 66 | public void setBrowser(HarCreatorBrowser browser) { 67 | this.browser = browser; 68 | } 69 | 70 | /** 71 | * @return List of all exported pages, may be empty. 72 | */ 73 | public List getPages() { 74 | if (pages == null) { 75 | pages = new ArrayList<>(); 76 | } 77 | return pages; 78 | } 79 | 80 | public void setPages(List pages) { 81 | this.pages = pages; 82 | } 83 | 84 | /** 85 | * @return List of all exported requests, may be empty. 86 | */ 87 | public List getEntries() { 88 | if (entries == null) { 89 | entries = new ArrayList<>(); 90 | } 91 | return entries; 92 | } 93 | 94 | public void setEntries(List entries) { 95 | this.entries = entries; 96 | } 97 | 98 | /** 99 | * @return Comment provided by the user or application, null if not present. 100 | */ 101 | public String getComment() { 102 | return comment; 103 | } 104 | 105 | public void setComment(String comment) { 106 | this.comment = comment; 107 | } 108 | 109 | @Override 110 | public boolean equals(Object o) { 111 | if (this == o) return true; 112 | if (o == null || getClass() != o.getClass()) return false; 113 | HarLog harLog = (HarLog) o; 114 | return Objects.equals(version, harLog.version) && 115 | Objects.equals(creator, harLog.creator) && 116 | Objects.equals(browser, harLog.browser) && 117 | Objects.equals(pages, harLog.pages) && 118 | Objects.equals(entries, harLog.entries) && 119 | Objects.equals(comment, harLog.comment); 120 | } 121 | 122 | @Override 123 | public int hashCode() { 124 | return Objects.hash(version, creator, browser, pages, entries, comment); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.sonatype.oss 7 | oss-parent 8 | 7 9 | 10 | 11 | de.sstoehr 12 | har-reader 13 | 2.1.1-SNAPSHOT 14 | jar 15 | 16 | har-reader 17 | A library to access HTTP archive format with Java 18 | https://github.com/sdstoehr/har-reader 19 | 20 | 21 | UTF-8 22 | 2.9.4 23 | 24 | 25 | 26 | 27 | com.fasterxml.jackson.core 28 | jackson-core 29 | ${jackson.version} 30 | 31 | 32 | 33 | com.fasterxml.jackson.core 34 | jackson-databind 35 | ${jackson.version} 36 | 37 | 38 | 39 | junit 40 | junit 41 | 4.12 42 | test 43 | 44 | 45 | 46 | 47 | 48 | 49 | maven-compiler-plugin 50 | 3.1 51 | 52 | 1.7 53 | 1.7 54 | 55 | 56 | 57 | maven-failsafe-plugin 58 | 2.6 59 | 60 | 61 | 62 | integration-test 63 | verify 64 | 65 | 66 | 67 | 68 | 69 | org.eluder.coveralls 70 | coveralls-maven-plugin 71 | 4.3.0 72 | 73 | 74 | org.codehaus.mojo 75 | cobertura-maven-plugin 76 | 2.7 77 | 78 | xml 79 | 256m 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | sdstoehr 88 | Sebastian Stöhr 89 | sebastian@sebastianstoehr.de 90 | 91 | 92 | 93 | 94 | 95 | The MIT License 96 | http://www.opensource.org/licenses/mit-license.php 97 | repo 98 | 99 | 100 | 101 | 102 | https://github.com/sdstoehr/har-reader.git 103 | scm:git:git@github.com:sdstoehr/har-reader.git 104 | scm:git:git@github.com:sdstoehr/har-reader.git 105 | 106 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarCookie.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | 7 | import java.util.Date; 8 | import java.util.Objects; 9 | 10 | /** 11 | * Information about a cookie used in request and/or response. 12 | * @see specification 13 | */ 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonIgnoreProperties(ignoreUnknown = true) 16 | public class HarCookie { 17 | 18 | private String name; 19 | private String value; 20 | private String path; 21 | private String domain; 22 | private Date expires; 23 | private Boolean httpOnly; 24 | private Boolean secure; 25 | private String comment; 26 | 27 | /** 28 | * @return Name of the cookie, null if not present. 29 | */ 30 | public String getName() { 31 | return name; 32 | } 33 | 34 | public void setName(String name) { 35 | this.name = name; 36 | } 37 | 38 | /** 39 | * @return Value of the cookie, null if not present. 40 | */ 41 | public String getValue() { 42 | return value; 43 | } 44 | 45 | public void setValue(String value) { 46 | this.value = value; 47 | } 48 | 49 | /** 50 | * @return The cookie's path, null if not present. 51 | */ 52 | public String getPath() { 53 | return path; 54 | } 55 | 56 | public void setPath(String path) { 57 | this.path = path; 58 | } 59 | 60 | /** 61 | * @return The cookie's domain, null if not present. 62 | */ 63 | public String getDomain() { 64 | return domain; 65 | } 66 | 67 | public void setDomain(String domain) { 68 | this.domain = domain; 69 | } 70 | 71 | /** 72 | * @return The cookie's expiration time, null if not present. 73 | */ 74 | @JsonFormat(shape = JsonFormat.Shape.STRING) 75 | public Date getExpires() { 76 | return expires; 77 | } 78 | 79 | public void setExpires(Date expires) { 80 | this.expires = expires; 81 | } 82 | 83 | /** 84 | * @return Whether the cookie is HTTP only, null if not present. 85 | */ 86 | public Boolean getHttpOnly() { 87 | return httpOnly; 88 | } 89 | 90 | public void setHttpOnly(Boolean httpOnly) { 91 | this.httpOnly = httpOnly; 92 | } 93 | 94 | /** 95 | * @return Whether the cookie was transmitted via SSL, null if not present. 96 | */ 97 | public Boolean getSecure() { 98 | return secure; 99 | } 100 | 101 | public void setSecure(Boolean secure) { 102 | this.secure = secure; 103 | } 104 | 105 | /** 106 | * @return Comment provided by the user or application, null if not present. 107 | */ 108 | public String getComment() { 109 | return comment; 110 | } 111 | 112 | public void setComment(String comment) { 113 | this.comment = comment; 114 | } 115 | 116 | @Override 117 | public boolean equals(Object o) { 118 | if (this == o) return true; 119 | if (o == null || getClass() != o.getClass()) return false; 120 | HarCookie harCookie = (HarCookie) o; 121 | return Objects.equals(name, harCookie.name) && 122 | Objects.equals(value, harCookie.value) && 123 | Objects.equals(path, harCookie.path) && 124 | Objects.equals(domain, harCookie.domain) && 125 | Objects.equals(expires, harCookie.expires) && 126 | Objects.equals(httpOnly, harCookie.httpOnly) && 127 | Objects.equals(secure, harCookie.secure) && 128 | Objects.equals(comment, harCookie.comment); 129 | } 130 | 131 | @Override 132 | public int hashCode() { 133 | return Objects.hash(name, value, path, domain, expires, httpOnly, secure, comment); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarTiming.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | 6 | import java.util.Objects; 7 | 8 | @JsonInclude(JsonInclude.Include.NON_NULL) 9 | @JsonIgnoreProperties(ignoreUnknown = true) 10 | public class HarTiming { 11 | 12 | protected static final Integer DEFAULT_TIME = -1; 13 | 14 | private Integer blocked; 15 | private Integer dns; 16 | private Integer connect; 17 | private Integer send; 18 | private Integer wait; 19 | private Integer receive; 20 | private Integer ssl; 21 | private String comment; 22 | 23 | /** 24 | * @return Time spent in a queue waiting for a network connection. 25 | * {@link #DEFAULT_TIME} if the timing does not apply to the current request. 26 | */ 27 | public Integer getBlocked() { 28 | if (blocked == null) { 29 | return DEFAULT_TIME; 30 | } 31 | return blocked; 32 | } 33 | 34 | public void setBlocked(Integer blocked) { 35 | this.blocked = blocked; 36 | } 37 | 38 | /** 39 | * @return DNS resolution time. The time required to resolve a host name. 40 | * {@link #DEFAULT_TIME} if the timing does not apply to the current request. 41 | */ 42 | public Integer getDns() { 43 | if (dns == null) { 44 | return DEFAULT_TIME; 45 | } 46 | return dns; 47 | } 48 | 49 | public void setDns(Integer dns) { 50 | this.dns = dns; 51 | } 52 | 53 | /** 54 | * @return Time required to create TCP connection. 55 | * {@link #DEFAULT_TIME} if the timing does not apply to the current request. 56 | */ 57 | public Integer getConnect() { 58 | if (connect == null) { 59 | return DEFAULT_TIME; 60 | } 61 | return connect; 62 | } 63 | 64 | public void setConnect(Integer connect) { 65 | this.connect = connect; 66 | } 67 | 68 | /** 69 | * @return Time required to send HTTP request to the server, null if not present. 70 | */ 71 | public Integer getSend() { 72 | return send; 73 | } 74 | 75 | public void setSend(Integer send) { 76 | this.send = send; 77 | } 78 | 79 | /** 80 | * @return Waiting for a response from the server, null if not present. 81 | */ 82 | public Integer getWait() { 83 | return wait; 84 | } 85 | 86 | public void setWait(Integer wait) { 87 | this.wait = wait; 88 | } 89 | 90 | /** 91 | * @return Time required to read entire response from the server (or cache), null if not present. 92 | */ 93 | public Integer getReceive() { 94 | return receive; 95 | } 96 | 97 | public void setReceive(Integer receive) { 98 | this.receive = receive; 99 | } 100 | 101 | /** 102 | * @return Time required for SSL/TLS negotiation. 103 | * If this field is defined then the time is also included in the connect field 104 | * (to ensure backward compatibility with HAR 1.1). 105 | * {@link #DEFAULT_TIME} if the timing does not apply to the current request. 106 | */ 107 | public Integer getSsl() { 108 | if (ssl == null) { 109 | return DEFAULT_TIME; 110 | } 111 | return ssl; 112 | } 113 | 114 | public void setSsl(Integer ssl) { 115 | this.ssl = ssl; 116 | } 117 | 118 | /** 119 | * @return Comment provided by the user or application, null if not present. 120 | */ 121 | public String getComment() { 122 | return comment; 123 | } 124 | 125 | public void setComment(String comment) { 126 | this.comment = comment; 127 | } 128 | 129 | @Override 130 | public boolean equals(Object o) { 131 | if (this == o) return true; 132 | if (o == null || getClass() != o.getClass()) return false; 133 | HarTiming harTiming = (HarTiming) o; 134 | return Objects.equals(blocked, harTiming.blocked) && 135 | Objects.equals(dns, harTiming.dns) && 136 | Objects.equals(connect, harTiming.connect) && 137 | Objects.equals(send, harTiming.send) && 138 | Objects.equals(wait, harTiming.wait) && 139 | Objects.equals(receive, harTiming.receive) && 140 | Objects.equals(ssl, harTiming.ssl) && 141 | Objects.equals(comment, harTiming.comment); 142 | } 143 | 144 | @Override 145 | public int hashCode() { 146 | return Objects.hash(blocked, dns, connect, send, wait, receive, ssl, comment); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarCache.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | 7 | import java.util.Date; 8 | import java.util.Objects; 9 | 10 | /** 11 | * Information about a request coming from browser cache. 12 | * @see specification 13 | */ 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonIgnoreProperties(ignoreUnknown = true) 16 | public class HarCache { 17 | 18 | private HarCacheInfo beforeRequest; 19 | private HarCacheInfo afterRequest; 20 | private String comment; 21 | 22 | /** 23 | * @return State of the cache entry before the request, null if not present. 24 | */ 25 | public HarCacheInfo getBeforeRequest() { 26 | return beforeRequest; 27 | } 28 | 29 | public void setBeforeRequest(HarCacheInfo beforeRequest) { 30 | this.beforeRequest = beforeRequest; 31 | } 32 | 33 | /** 34 | * @return State of the cache entry after the request, null if not present. 35 | */ 36 | public HarCacheInfo getAfterRequest() { 37 | return afterRequest; 38 | } 39 | 40 | public void setAfterRequest(HarCacheInfo afterRequest) { 41 | this.afterRequest = afterRequest; 42 | } 43 | 44 | /** 45 | * @return Comment provided by the user or application, null if not present. 46 | */ 47 | public String getComment() { 48 | return comment; 49 | } 50 | 51 | public void setComment(String comment) { 52 | this.comment = comment; 53 | } 54 | 55 | @Override 56 | public boolean equals(Object o) { 57 | if (this == o) return true; 58 | if (o == null || getClass() != o.getClass()) return false; 59 | HarCache harCache = (HarCache) o; 60 | return Objects.equals(beforeRequest, harCache.beforeRequest) && 61 | Objects.equals(afterRequest, harCache.afterRequest) && 62 | Objects.equals(comment, harCache.comment); 63 | } 64 | 65 | @Override 66 | public int hashCode() { 67 | return Objects.hash(beforeRequest, afterRequest, comment); 68 | } 69 | 70 | /** 71 | * Information about a request coming from browser cache. 72 | * @see specification 73 | */ 74 | @JsonInclude(JsonInclude.Include.NON_NULL) 75 | @JsonIgnoreProperties(ignoreUnknown = true) 76 | public static final class HarCacheInfo { 77 | 78 | private Date expires; 79 | private Date lastAccess; 80 | private String eTag; 81 | private Integer hitCount; 82 | private String comment; 83 | 84 | /** 85 | * @return Expiration time of entry, null if not present. 86 | */ 87 | @JsonFormat(shape = JsonFormat.Shape.STRING) 88 | public Date getExpires() { 89 | return expires; 90 | } 91 | 92 | public void setExpires(Date expires) { 93 | this.expires = expires; 94 | } 95 | 96 | /** 97 | * @return Last time the entry was opened, null if not present. 98 | */ 99 | @JsonFormat(shape = JsonFormat.Shape.STRING) 100 | public Date getLastAccess() { 101 | return lastAccess; 102 | } 103 | 104 | public void setLastAccess(Date lastAccess) { 105 | this.lastAccess = lastAccess; 106 | } 107 | 108 | /** 109 | * @return ETag, null if not present. 110 | */ 111 | public String geteTag() { 112 | return eTag; 113 | } 114 | 115 | public void seteTag(String eTag) { 116 | this.eTag = eTag; 117 | } 118 | 119 | /** 120 | * @return Number of times the entry has been opened, null if not present. 121 | */ 122 | public Integer getHitCount() { 123 | return hitCount; 124 | } 125 | 126 | public void setHitCount(Integer hitCount) { 127 | this.hitCount = hitCount; 128 | } 129 | 130 | /** 131 | * @return Comment provided by the user or application, null if not present. 132 | */ 133 | public String getComment() { 134 | return comment; 135 | } 136 | 137 | public void setComment(String comment) { 138 | this.comment = comment; 139 | } 140 | 141 | @Override 142 | public boolean equals(Object o) { 143 | if (this == o) return true; 144 | if (o == null || getClass() != o.getClass()) return false; 145 | HarCacheInfo that = (HarCacheInfo) o; 146 | return Objects.equals(expires, that.expires) && 147 | Objects.equals(lastAccess, that.lastAccess) && 148 | Objects.equals(eTag, that.eTag) && 149 | Objects.equals(hitCount, that.hitCount) && 150 | Objects.equals(comment, that.comment); 151 | } 152 | 153 | @Override 154 | public int hashCode() { 155 | return Objects.hash(expires, lastAccess, eTag, hitCount, comment); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarEntry.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAnyGetter; 4 | import com.fasterxml.jackson.annotation.JsonAnySetter; 5 | import com.fasterxml.jackson.annotation.JsonFormat; 6 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 7 | import com.fasterxml.jackson.annotation.JsonInclude; 8 | 9 | import java.util.Date; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | import java.util.Objects; 13 | 14 | /** 15 | * Information about a single HTTP request. 16 | * @see specification 17 | */ 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | public class HarEntry { 21 | 22 | private String pageref; 23 | private Date startedDateTime; 24 | private Integer time; 25 | private HarRequest request; 26 | private HarResponse response; 27 | private HarCache cache; 28 | private HarTiming timings; 29 | private String serverIPAddress; 30 | private String connection; 31 | private String comment; 32 | private Map additional = new HashMap<>(); 33 | 34 | /** 35 | * @return Reference to parent page, to which the request belongs to, null if not present. 36 | */ 37 | public String getPageref() { 38 | return pageref; 39 | } 40 | 41 | public void setPageref(String pageref) { 42 | this.pageref = pageref; 43 | } 44 | 45 | /** 46 | * @return Start time of request, null if not present. 47 | */ 48 | @JsonFormat(shape = JsonFormat.Shape.STRING) 49 | public Date getStartedDateTime() { 50 | return startedDateTime; 51 | } 52 | 53 | public void setStartedDateTime(Date startedDateTime) { 54 | this.startedDateTime = startedDateTime; 55 | } 56 | 57 | /** 58 | * @return Total request time (in ms), null if not present. 59 | */ 60 | public Integer getTime() { 61 | return time; 62 | } 63 | 64 | public void setTime(Integer time) { 65 | this.time = time; 66 | } 67 | 68 | /** 69 | * @return Detailed request information. 70 | */ 71 | public HarRequest getRequest() { 72 | if (request == null) { 73 | request = new HarRequest(); 74 | } 75 | return request; 76 | } 77 | 78 | public void setRequest(HarRequest request) { 79 | this.request = request; 80 | } 81 | 82 | /** 83 | * @return Detailed response information. 84 | */ 85 | public HarResponse getResponse() { 86 | if (response == null) { 87 | response = new HarResponse(); 88 | } 89 | return response; 90 | } 91 | 92 | public void setResponse(HarResponse response) { 93 | this.response = response; 94 | } 95 | 96 | /** 97 | * @return Information about cache usage. 98 | */ 99 | public HarCache getCache() { 100 | if (cache == null) { 101 | cache = new HarCache(); 102 | } 103 | return cache; 104 | } 105 | 106 | public void setCache(HarCache cache) { 107 | this.cache = cache; 108 | } 109 | 110 | /** 111 | * @return Detailed information about request/response timings. 112 | */ 113 | public HarTiming getTimings() { 114 | if (timings == null) { 115 | timings = new HarTiming(); 116 | } 117 | return timings; 118 | } 119 | 120 | public void setTimings(HarTiming timings) { 121 | this.timings = timings; 122 | } 123 | 124 | /** 125 | * @return Server IP address (result of DNS resolution), null if not present. 126 | */ 127 | public String getServerIPAddress() { 128 | return serverIPAddress; 129 | } 130 | 131 | public void setServerIPAddress(String serverIPAddress) { 132 | this.serverIPAddress = serverIPAddress; 133 | } 134 | 135 | /** 136 | * @return Unique ID of TCP/IP connection, null if not present. 137 | */ 138 | public String getConnection() { 139 | return connection; 140 | } 141 | 142 | public void setConnection(String connection) { 143 | this.connection = connection; 144 | } 145 | 146 | /** 147 | * @return Comment provided by the user or application, null if not present. 148 | */ 149 | public String getComment() { 150 | return comment; 151 | } 152 | 153 | public void setComment(String comment) { 154 | this.comment = comment; 155 | } 156 | 157 | @JsonAnyGetter 158 | public Map getAdditional() { 159 | return additional; 160 | } 161 | 162 | @JsonAnySetter 163 | public void setAdditionalField(String name, Object value) { 164 | this.additional.put(name, value); 165 | } 166 | 167 | @Override 168 | public boolean equals(Object o) { 169 | if (this == o) return true; 170 | if (o == null || getClass() != o.getClass()) return false; 171 | HarEntry harEntry = (HarEntry) o; 172 | return Objects.equals(pageref, harEntry.pageref) && 173 | Objects.equals(startedDateTime, harEntry.startedDateTime) && 174 | Objects.equals(time, harEntry.time) && 175 | Objects.equals(request, harEntry.request) && 176 | Objects.equals(response, harEntry.response) && 177 | Objects.equals(cache, harEntry.cache) && 178 | Objects.equals(timings, harEntry.timings) && 179 | Objects.equals(serverIPAddress, harEntry.serverIPAddress) && 180 | Objects.equals(connection, harEntry.connection) && 181 | Objects.equals(comment, harEntry.comment) && 182 | Objects.equals(additional, harEntry.additional); 183 | } 184 | 185 | @Override 186 | public int hashCode() { 187 | return Objects.hash(pageref, startedDateTime, time, request, response, cache, timings, serverIPAddress, 188 | connection, comment, additional); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarResponse.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAnyGetter; 4 | import com.fasterxml.jackson.annotation.JsonAnySetter; 5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 6 | import com.fasterxml.jackson.annotation.JsonInclude; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Objects; 13 | 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonIgnoreProperties(ignoreUnknown = true) 16 | public class HarResponse { 17 | 18 | protected static final Long DEFAULT_SIZE = -1L; 19 | 20 | private HttpStatus status; 21 | private String statusText; 22 | private String httpVersion; 23 | private List cookies; 24 | private List headers; 25 | private HarContent content; 26 | private String redirectURL; 27 | private Long headersSize; 28 | private Long bodySize; 29 | private String comment; 30 | private Map additional = new HashMap<>(); 31 | 32 | /** 33 | * @return Response status, null if not present. 34 | */ 35 | public int getStatus() { 36 | if (status == null) { 37 | status = HttpStatus.UNKNOWN_HTTP_STATUS; 38 | } 39 | return status.getCode(); 40 | } 41 | 42 | public void setStatus(int status) { 43 | this.status = HttpStatus.byCode(status); 44 | } 45 | 46 | /** 47 | * @return Response status description, null if not present. 48 | */ 49 | public String getStatusText() { 50 | return statusText; 51 | } 52 | 53 | public void setStatusText(String statusText) { 54 | this.statusText = statusText; 55 | } 56 | 57 | /** 58 | * @return Response HTTP Version, null if not present. 59 | */ 60 | public String getHttpVersion() { 61 | return httpVersion; 62 | } 63 | 64 | public void setHttpVersion(String httpVersion) { 65 | this.httpVersion = httpVersion; 66 | } 67 | 68 | /** 69 | * @return List of cookie objects. 70 | */ 71 | public List getCookies() { 72 | if (cookies == null) { 73 | cookies = new ArrayList<>(); 74 | } 75 | return cookies; 76 | } 77 | 78 | public void setCookies(List cookies) { 79 | this.cookies = cookies; 80 | } 81 | 82 | /** 83 | * @return List of header objects. 84 | */ 85 | public List getHeaders() { 86 | if (headers == null) { 87 | headers = new ArrayList<>(); 88 | } 89 | return headers; 90 | } 91 | 92 | public void setHeaders(List headers) { 93 | this.headers = headers; 94 | } 95 | 96 | /** 97 | * @return Details about the response body. 98 | */ 99 | public HarContent getContent() { 100 | if (content == null) { 101 | content = new HarContent(); 102 | } 103 | return content; 104 | } 105 | 106 | public void setContent(HarContent content) { 107 | this.content = content; 108 | } 109 | 110 | /** 111 | * @return Redirection target URL from the Location response header, null if not present. 112 | */ 113 | public String getRedirectURL() { 114 | return redirectURL; 115 | } 116 | 117 | public void setRedirectURL(String redirectURL) { 118 | this.redirectURL = redirectURL; 119 | } 120 | 121 | /** 122 | * @return Total number of bytes from the start of the HTTP response message until (and including) the double 123 | * CRLF before the body. {@link #DEFAULT_SIZE} if the info is not available. 124 | */ 125 | public Long getHeadersSize() { 126 | if (headersSize == null) { 127 | return DEFAULT_SIZE; 128 | } 129 | return headersSize; 130 | } 131 | 132 | public void setHeadersSize(Long headersSize) { 133 | this.headersSize = headersSize; 134 | } 135 | 136 | /** 137 | * @return Size of the received response body in bytes. 138 | * Set to zero in case of responses coming from the cache (304). 139 | * {@link #DEFAULT_SIZE} if the info is not available. 140 | */ 141 | public Long getBodySize() { 142 | if (bodySize == null) { 143 | return DEFAULT_SIZE; 144 | } 145 | return bodySize; 146 | } 147 | 148 | public void setBodySize(Long bodySize) { 149 | this.bodySize = bodySize; 150 | } 151 | 152 | /** 153 | * @return Comment provided by the user or application, null if not present. 154 | */ 155 | public String getComment() { 156 | return comment; 157 | } 158 | 159 | public void setComment(String comment) { 160 | this.comment = comment; 161 | } 162 | 163 | @JsonAnyGetter 164 | public Map getAdditional() { 165 | return additional; 166 | } 167 | 168 | @JsonAnySetter 169 | public void setAdditionalField(String name, Object value) { 170 | this.additional.put(name, value); 171 | } 172 | 173 | @Override 174 | public boolean equals(Object o) { 175 | if (this == o) return true; 176 | if (o == null || getClass() != o.getClass()) return false; 177 | HarResponse that = (HarResponse) o; 178 | return status == that.status && 179 | Objects.equals(statusText, that.statusText) && 180 | Objects.equals(httpVersion, that.httpVersion) && 181 | Objects.equals(cookies, that.cookies) && 182 | Objects.equals(headers, that.headers) && 183 | Objects.equals(content, that.content) && 184 | Objects.equals(redirectURL, that.redirectURL) && 185 | Objects.equals(headersSize, that.headersSize) && 186 | Objects.equals(bodySize, that.bodySize) && 187 | Objects.equals(comment, that.comment) && 188 | Objects.equals(additional, that.additional); 189 | } 190 | 191 | @Override 192 | public int hashCode() { 193 | return Objects.hash(status, statusText, httpVersion, cookies, headers, content, redirectURL, headersSize, 194 | bodySize, comment, additional); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/main/java/de/sstoehr/harreader/model/HarRequest.java: -------------------------------------------------------------------------------- 1 | package de.sstoehr.harreader.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAnyGetter; 4 | import com.fasterxml.jackson.annotation.JsonAnySetter; 5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 6 | import com.fasterxml.jackson.annotation.JsonInclude; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Objects; 13 | 14 | /** 15 | * Information about a performed request. 16 | * @see specification 17 | */ 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | public class HarRequest { 21 | 22 | protected static final Long DEFAULT_SIZE = -1L; 23 | 24 | private HttpMethod method; 25 | private String url; 26 | private String httpVersion; 27 | private List cookies; 28 | private List headers; 29 | private List queryString; 30 | private HarPostData postData; 31 | private Long headersSize; 32 | private Long bodySize; 33 | private String comment; 34 | private Map additional = new HashMap<>(); 35 | 36 | /** 37 | * @return Request method, null if not present. 38 | */ 39 | public HttpMethod getMethod() { 40 | return method; 41 | } 42 | 43 | public void setMethod(HttpMethod method) { 44 | this.method = method; 45 | } 46 | 47 | /** 48 | * @return Absolute URL of the request (fragments are not included), null if not present. 49 | */ 50 | public String getUrl() { 51 | return url; 52 | } 53 | 54 | public void setUrl(String url) { 55 | this.url = url; 56 | } 57 | 58 | /** 59 | * @return Request HTTP Version, null if not present. 60 | */ 61 | public String getHttpVersion() { 62 | return httpVersion; 63 | } 64 | 65 | public void setHttpVersion(String httpVersion) { 66 | this.httpVersion = httpVersion; 67 | } 68 | 69 | /** 70 | * @return List of cookie objects. 71 | */ 72 | public List getCookies() { 73 | if (cookies == null) { 74 | cookies = new ArrayList<>(); 75 | } 76 | return cookies; 77 | } 78 | 79 | public void setCookies(List cookies) { 80 | this.cookies = cookies; 81 | } 82 | 83 | /** 84 | * @return List of header objects. 85 | */ 86 | public List getHeaders() { 87 | if (headers == null) { 88 | headers = new ArrayList<>(); 89 | } 90 | return headers; 91 | } 92 | 93 | public void setHeaders(List headers) { 94 | this.headers = headers; 95 | } 96 | 97 | /** 98 | * @return List of query parameter objects. 99 | */ 100 | public List getQueryString() { 101 | if (queryString == null) { 102 | queryString = new ArrayList<>(); 103 | } 104 | return queryString; 105 | } 106 | 107 | public void setQueryString(List queryString) { 108 | this.queryString = queryString; 109 | } 110 | 111 | /** 112 | * @return Posted data info. 113 | */ 114 | public HarPostData getPostData() { 115 | if (postData == null) { 116 | postData = new HarPostData(); 117 | } 118 | return postData; 119 | } 120 | 121 | public void setPostData(HarPostData postData) { 122 | this.postData = postData; 123 | } 124 | 125 | /** 126 | * @return Total number of bytes from the start of the HTTP request message until (and including) the double 127 | * CRLF before the body. {@link #DEFAULT_SIZE} if the info is not available. 128 | */ 129 | public Long getHeadersSize() { 130 | if (headersSize == null) { 131 | return DEFAULT_SIZE; 132 | } 133 | return headersSize; 134 | } 135 | 136 | public void setHeadersSize(Long headersSize) { 137 | this.headersSize = headersSize; 138 | } 139 | 140 | /** 141 | * @return Size of the request body (POST data payload) in bytes. 142 | * {@link #DEFAULT_SIZE} if the info is not available. 143 | */ 144 | public Long getBodySize() { 145 | if (bodySize == null) { 146 | return DEFAULT_SIZE; 147 | } 148 | return bodySize; 149 | } 150 | 151 | public void setBodySize(Long bodySize) { 152 | this.bodySize = bodySize; 153 | } 154 | 155 | /** 156 | * @return Comment provided by the user or application, null if not present. 157 | */ 158 | public String getComment() { 159 | return comment; 160 | } 161 | 162 | public void setComment(String comment) { 163 | this.comment = comment; 164 | } 165 | 166 | @JsonAnyGetter 167 | public Map getAdditional() { 168 | return additional; 169 | } 170 | 171 | @JsonAnySetter 172 | public void setAdditionalField(String name, Object value) { 173 | this.additional.put(name, value); 174 | } 175 | 176 | @Override 177 | public boolean equals(Object o) { 178 | if (this == o) return true; 179 | if (o == null || getClass() != o.getClass()) return false; 180 | HarRequest that = (HarRequest) o; 181 | return method == that.method && 182 | Objects.equals(url, that.url) && 183 | Objects.equals(httpVersion, that.httpVersion) && 184 | Objects.equals(cookies, that.cookies) && 185 | Objects.equals(headers, that.headers) && 186 | Objects.equals(queryString, that.queryString) && 187 | Objects.equals(postData, that.postData) && 188 | Objects.equals(headersSize, that.headersSize) && 189 | Objects.equals(bodySize, that.bodySize) && 190 | Objects.equals(comment, that.comment) && 191 | Objects.equals(additional, that.additional); 192 | } 193 | 194 | @Override 195 | public int hashCode() { 196 | return Objects.hash(method, url, httpVersion, cookies, headers, queryString, postData, headersSize, 197 | bodySize, comment, additional); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/test/resources/sstoehr.har: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "version": "1.1", 4 | "creator": { 5 | "name": "Firebug", 6 | "version": "1.12" 7 | }, 8 | "browser": { 9 | "name": "Firefox", 10 | "version": "26.0" 11 | }, 12 | "pages": [ 13 | { 14 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 15 | "id": "page_3", 16 | "title": "Home — Sebastian Stöhr", 17 | "pageTimings": { 18 | "onContentLoad": 3910, 19 | "onLoad": -1 20 | } 21 | } 22 | ], 23 | "entries": [ 24 | { 25 | "pageref": "page_3", 26 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 27 | "time": 3804, 28 | "request": { 29 | "method": "GET", 30 | "url": "http://www.sebastianstoehr.de/assets/app-176c7b354c124a4593b8d4ac674d69e6.css", 31 | "httpVersion": "HTTP/1.1", 32 | "cookies": [], 33 | "headers": [ 34 | { 35 | "name": "Host", 36 | "value": "www.sebastianstoehr.de" 37 | }, 38 | { 39 | "name": "User-Agent", 40 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 41 | }, 42 | { 43 | "name": "Accept", 44 | "value": "text/css,*/*;q=0.1" 45 | }, 46 | { 47 | "name": "Accept-Language", 48 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 49 | }, 50 | { 51 | "name": "Accept-Encoding", 52 | "value": "gzip, deflate" 53 | }, 54 | { 55 | "name": "DNT", 56 | "value": "1" 57 | }, 58 | { 59 | "name": "Referer", 60 | "value": "http://www.sebastianstoehr.de/" 61 | }, 62 | { 63 | "name": "Connection", 64 | "value": "keep-alive" 65 | } 66 | ], 67 | "queryString": [], 68 | "headersSize": 676, 69 | "bodySize": -1 70 | }, 71 | "response": { 72 | "status": 200, 73 | "statusText": "OK", 74 | "httpVersion": "HTTP/1.1", 75 | "cookies": [], 76 | "headers": [ 77 | { 78 | "name": "Date", 79 | "value": "Sun, 26 Jan 2014 17:37:44 GMT" 80 | }, 81 | { 82 | "name": "Server", 83 | "value": "Apache" 84 | }, 85 | { 86 | "name": "Last-Modified", 87 | "value": "Fri, 10 Jan 2014 17:10:50 GMT" 88 | }, 89 | { 90 | "name": "Accept-Ranges", 91 | "value": "bytes" 92 | }, 93 | { 94 | "name": "Content-Length", 95 | "value": "3412" 96 | }, 97 | { 98 | "name": "Cache-Control", 99 | "value": "max-age=15552000" 100 | }, 101 | { 102 | "name": "Expires", 103 | "value": "Fri, 25 Jul 2014 17:37:44 GMT" 104 | }, 105 | { 106 | "name": "Vary", 107 | "value": "Accept-Encoding" 108 | }, 109 | { 110 | "name": "Keep-Alive", 111 | "value": "timeout=2, max=200" 112 | }, 113 | { 114 | "name": "Connection", 115 | "value": "Keep-Alive" 116 | }, 117 | { 118 | "name": "Content-Type", 119 | "value": "text/css" 120 | }, 121 | { 122 | "name": "Content-Encoding", 123 | "value": "gzip" 124 | } 125 | ], 126 | "content": { 127 | "mimeType": "text/css", 128 | "size": 12647, 129 | "text": "--- REMOVED ---\n" 130 | }, 131 | "redirectURL": "", 132 | "headersSize": 362, 133 | "bodySize": 3412 134 | }, 135 | "cache": {}, 136 | "timings": { 137 | "blocked": 5, 138 | "dns": 0, 139 | "connect": 0, 140 | "send": 0, 141 | "wait": 3778, 142 | "receive": 21 143 | }, 144 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 145 | "connection": "80" 146 | }, 147 | { 148 | "pageref": "page_3", 149 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 150 | "time": 9092, 151 | "request": { 152 | "method": "GET", 153 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/woeste-alumni/header-600x131-bf1788.jpg", 154 | "httpVersion": "HTTP/1.1", 155 | "cookies": [], 156 | "headers": [ 157 | { 158 | "name": "Host", 159 | "value": "www.sebastianstoehr.de" 160 | }, 161 | { 162 | "name": "User-Agent", 163 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 164 | }, 165 | { 166 | "name": "Accept", 167 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 168 | }, 169 | { 170 | "name": "Accept-Language", 171 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 172 | }, 173 | { 174 | "name": "Accept-Encoding", 175 | "value": "gzip, deflate" 176 | }, 177 | { 178 | "name": "DNT", 179 | "value": "1" 180 | }, 181 | { 182 | "name": "Referer", 183 | "value": "http://www.sebastianstoehr.de/" 184 | }, 185 | { 186 | "name": "Connection", 187 | "value": "keep-alive" 188 | } 189 | ], 190 | "queryString": [], 191 | "headersSize": 704, 192 | "bodySize": -1 193 | }, 194 | "response": { 195 | "status": 200, 196 | "statusText": "OK", 197 | "httpVersion": "HTTP/1.1", 198 | "cookies": [], 199 | "headers": [ 200 | { 201 | "name": "Date", 202 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 203 | }, 204 | { 205 | "name": "Server", 206 | "value": "Apache" 207 | }, 208 | { 209 | "name": "Last-Modified", 210 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 211 | }, 212 | { 213 | "name": "Accept-Ranges", 214 | "value": "bytes" 215 | }, 216 | { 217 | "name": "Content-Length", 218 | "value": "16997" 219 | }, 220 | { 221 | "name": "Cache-Control", 222 | "value": "max-age=15552000" 223 | }, 224 | { 225 | "name": "Expires", 226 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 227 | }, 228 | { 229 | "name": "Keep-Alive", 230 | "value": "timeout=2, max=200" 231 | }, 232 | { 233 | "name": "Connection", 234 | "value": "Keep-Alive" 235 | }, 236 | { 237 | "name": "Content-Type", 238 | "value": "image/jpeg" 239 | } 240 | ], 241 | "content": { 242 | "mimeType": "image/jpeg", 243 | "size": 132, 244 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/woeste-alumni/header-600x131-bf1788.jpg" 245 | }, 246 | "redirectURL": "", 247 | "headersSize": 318, 248 | "bodySize": 16997 249 | }, 250 | "cache": {}, 251 | "timings": { 252 | "blocked": 3804, 253 | "dns": 0, 254 | "connect": 0, 255 | "send": 0, 256 | "wait": 5209, 257 | "receive": 79 258 | }, 259 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 260 | "connection": "80" 261 | }, 262 | { 263 | "pageref": "page_3", 264 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 265 | "time": 9139, 266 | "request": { 267 | "method": "GET", 268 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/sebastian-stoehr/header-600x131-415442.jpg", 269 | "httpVersion": "HTTP/1.1", 270 | "cookies": [], 271 | "headers": [ 272 | { 273 | "name": "Host", 274 | "value": "www.sebastianstoehr.de" 275 | }, 276 | { 277 | "name": "User-Agent", 278 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 279 | }, 280 | { 281 | "name": "Accept", 282 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 283 | }, 284 | { 285 | "name": "Accept-Language", 286 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 287 | }, 288 | { 289 | "name": "Accept-Encoding", 290 | "value": "gzip, deflate" 291 | }, 292 | { 293 | "name": "DNT", 294 | "value": "1" 295 | }, 296 | { 297 | "name": "Referer", 298 | "value": "http://www.sebastianstoehr.de/" 299 | }, 300 | { 301 | "name": "Connection", 302 | "value": "keep-alive" 303 | } 304 | ], 305 | "queryString": [], 306 | "headersSize": 707, 307 | "bodySize": -1 308 | }, 309 | "response": { 310 | "status": 200, 311 | "statusText": "OK", 312 | "httpVersion": "HTTP/1.1", 313 | "cookies": [], 314 | "headers": [ 315 | { 316 | "name": "Date", 317 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 318 | }, 319 | { 320 | "name": "Server", 321 | "value": "Apache" 322 | }, 323 | { 324 | "name": "Last-Modified", 325 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 326 | }, 327 | { 328 | "name": "Accept-Ranges", 329 | "value": "bytes" 330 | }, 331 | { 332 | "name": "Content-Length", 333 | "value": "28351" 334 | }, 335 | { 336 | "name": "Cache-Control", 337 | "value": "max-age=15552000" 338 | }, 339 | { 340 | "name": "Expires", 341 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 342 | }, 343 | { 344 | "name": "Keep-Alive", 345 | "value": "timeout=2, max=200" 346 | }, 347 | { 348 | "name": "Connection", 349 | "value": "Keep-Alive" 350 | }, 351 | { 352 | "name": "Content-Type", 353 | "value": "image/jpeg" 354 | } 355 | ], 356 | "content": { 357 | "mimeType": "image/jpeg", 358 | "size": 135, 359 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/sebastian-stoehr/header-600x131-415442.jpg" 360 | }, 361 | "redirectURL": "", 362 | "headersSize": 318, 363 | "bodySize": 28351 364 | }, 365 | "cache": {}, 366 | "timings": { 367 | "blocked": 3804, 368 | "dns": 0, 369 | "connect": 0, 370 | "send": 0, 371 | "wait": 5240, 372 | "receive": 95 373 | }, 374 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 375 | "connection": "80" 376 | }, 377 | { 378 | "pageref": "page_3", 379 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 380 | "time": 9164, 381 | "request": { 382 | "method": "GET", 383 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/abizeitung/header-600x131-603283.jpg", 384 | "httpVersion": "HTTP/1.1", 385 | "cookies": [], 386 | "headers": [ 387 | { 388 | "name": "Host", 389 | "value": "www.sebastianstoehr.de" 390 | }, 391 | { 392 | "name": "User-Agent", 393 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 394 | }, 395 | { 396 | "name": "Accept", 397 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 398 | }, 399 | { 400 | "name": "Accept-Language", 401 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 402 | }, 403 | { 404 | "name": "Accept-Encoding", 405 | "value": "gzip, deflate" 406 | }, 407 | { 408 | "name": "DNT", 409 | "value": "1" 410 | }, 411 | { 412 | "name": "Referer", 413 | "value": "http://www.sebastianstoehr.de/" 414 | }, 415 | { 416 | "name": "Connection", 417 | "value": "keep-alive" 418 | } 419 | ], 420 | "queryString": [], 421 | "headersSize": 701, 422 | "bodySize": -1 423 | }, 424 | "response": { 425 | "status": 200, 426 | "statusText": "OK", 427 | "httpVersion": "HTTP/1.1", 428 | "cookies": [], 429 | "headers": [ 430 | { 431 | "name": "Date", 432 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 433 | }, 434 | { 435 | "name": "Server", 436 | "value": "Apache" 437 | }, 438 | { 439 | "name": "Last-Modified", 440 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 441 | }, 442 | { 443 | "name": "Accept-Ranges", 444 | "value": "bytes" 445 | }, 446 | { 447 | "name": "Content-Length", 448 | "value": "73064" 449 | }, 450 | { 451 | "name": "Cache-Control", 452 | "value": "max-age=15552000" 453 | }, 454 | { 455 | "name": "Expires", 456 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 457 | }, 458 | { 459 | "name": "Keep-Alive", 460 | "value": "timeout=2, max=200" 461 | }, 462 | { 463 | "name": "Connection", 464 | "value": "Keep-Alive" 465 | }, 466 | { 467 | "name": "Content-Type", 468 | "value": "image/jpeg" 469 | } 470 | ], 471 | "content": { 472 | "mimeType": "image/jpeg", 473 | "size": 129, 474 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/abizeitung/header-600x131-603283.jpg" 475 | }, 476 | "redirectURL": "", 477 | "headersSize": 318, 478 | "bodySize": 73064 479 | }, 480 | "cache": {}, 481 | "timings": { 482 | "blocked": 3804, 483 | "dns": 0, 484 | "connect": 0, 485 | "send": 0, 486 | "wait": 5217, 487 | "receive": 143 488 | }, 489 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 490 | "connection": "80" 491 | }, 492 | { 493 | "pageref": "page_3", 494 | "startedDateTime": "2014-01-26T18:37:43.814+01:00", 495 | "time": 9156, 496 | "request": { 497 | "method": "GET", 498 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/junge-liberale/header-600x131-a069fc.jpg", 499 | "httpVersion": "HTTP/1.1", 500 | "cookies": [], 501 | "headers": [ 502 | { 503 | "name": "Host", 504 | "value": "www.sebastianstoehr.de" 505 | }, 506 | { 507 | "name": "User-Agent", 508 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 509 | }, 510 | { 511 | "name": "Accept", 512 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 513 | }, 514 | { 515 | "name": "Accept-Language", 516 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 517 | }, 518 | { 519 | "name": "Accept-Encoding", 520 | "value": "gzip, deflate" 521 | }, 522 | { 523 | "name": "DNT", 524 | "value": "1" 525 | }, 526 | { 527 | "name": "Referer", 528 | "value": "http://www.sebastianstoehr.de/" 529 | }, 530 | { 531 | "name": "Connection", 532 | "value": "keep-alive" 533 | } 534 | ], 535 | "queryString": [], 536 | "headersSize": 705, 537 | "bodySize": -1 538 | }, 539 | "response": { 540 | "status": 200, 541 | "statusText": "OK", 542 | "httpVersion": "HTTP/1.1", 543 | "cookies": [], 544 | "headers": [ 545 | { 546 | "name": "Date", 547 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 548 | }, 549 | { 550 | "name": "Server", 551 | "value": "Apache" 552 | }, 553 | { 554 | "name": "Last-Modified", 555 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 556 | }, 557 | { 558 | "name": "Accept-Ranges", 559 | "value": "bytes" 560 | }, 561 | { 562 | "name": "Content-Length", 563 | "value": "49555" 564 | }, 565 | { 566 | "name": "Cache-Control", 567 | "value": "max-age=15552000" 568 | }, 569 | { 570 | "name": "Expires", 571 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 572 | }, 573 | { 574 | "name": "Keep-Alive", 575 | "value": "timeout=2, max=200" 576 | }, 577 | { 578 | "name": "Connection", 579 | "value": "Keep-Alive" 580 | }, 581 | { 582 | "name": "Content-Type", 583 | "value": "image/jpeg" 584 | } 585 | ], 586 | "content": { 587 | "mimeType": "image/jpeg", 588 | "size": 133, 589 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/junge-liberale/header-600x131-a069fc.jpg" 590 | }, 591 | "redirectURL": "", 592 | "headersSize": 318, 593 | "bodySize": 49555 594 | }, 595 | "cache": {}, 596 | "timings": { 597 | "blocked": 3803, 598 | "dns": 0, 599 | "connect": 0, 600 | "send": 0, 601 | "wait": 5222, 602 | "receive": 131 603 | }, 604 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 605 | "connection": "80" 606 | }, 607 | { 608 | "pageref": "page_3", 609 | "startedDateTime": "2014-01-26T18:37:47.701+01:00", 610 | "time": 5010, 611 | "request": { 612 | "method": "GET", 613 | "url": "http://www.sebastianstoehr.de/res/images/logo.png", 614 | "httpVersion": "HTTP/1.1", 615 | "cookies": [], 616 | "headers": [ 617 | { 618 | "name": "Host", 619 | "value": "www.sebastianstoehr.de" 620 | }, 621 | { 622 | "name": "User-Agent", 623 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 624 | }, 625 | { 626 | "name": "Accept", 627 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 628 | }, 629 | { 630 | "name": "Accept-Language", 631 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 632 | }, 633 | { 634 | "name": "Accept-Encoding", 635 | "value": "gzip, deflate" 636 | }, 637 | { 638 | "name": "DNT", 639 | "value": "1" 640 | }, 641 | { 642 | "name": "Referer", 643 | "value": "http://www.sebastianstoehr.de/assets/app-176c7b354c124a4593b8d4ac674d69e6.css" 644 | }, 645 | { 646 | "name": "Connection", 647 | "value": "keep-alive" 648 | } 649 | ], 650 | "queryString": [], 651 | "headersSize": 710, 652 | "bodySize": -1 653 | }, 654 | "response": { 655 | "status": 200, 656 | "statusText": "OK", 657 | "httpVersion": "HTTP/1.1", 658 | "cookies": [], 659 | "headers": [ 660 | { 661 | "name": "Date", 662 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 663 | }, 664 | { 665 | "name": "Server", 666 | "value": "Apache" 667 | }, 668 | { 669 | "name": "Last-Modified", 670 | "value": "Sat, 11 Jan 2014 10:56:55 GMT" 671 | }, 672 | { 673 | "name": "Accept-Ranges", 674 | "value": "bytes" 675 | }, 676 | { 677 | "name": "Content-Length", 678 | "value": "717" 679 | }, 680 | { 681 | "name": "Cache-Control", 682 | "value": "max-age=15552000" 683 | }, 684 | { 685 | "name": "Expires", 686 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 687 | }, 688 | { 689 | "name": "Keep-Alive", 690 | "value": "timeout=2, max=199" 691 | }, 692 | { 693 | "name": "Connection", 694 | "value": "Keep-Alive" 695 | }, 696 | { 697 | "name": "Content-Type", 698 | "value": "image/png" 699 | } 700 | ], 701 | "content": { 702 | "mimeType": "image/png", 703 | "size": 91, 704 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/logo.png" 705 | }, 706 | "redirectURL": "", 707 | "headersSize": 315, 708 | "bodySize": 717 709 | }, 710 | "cache": {}, 711 | "timings": { 712 | "blocked": 0, 713 | "dns": 0, 714 | "connect": 0, 715 | "send": 0, 716 | "wait": 5010, 717 | "receive": 0 718 | }, 719 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 720 | "connection": "80" 721 | }, 722 | { 723 | "pageref": "page_3", 724 | "startedDateTime": "2014-01-26T18:37:47.701+01:00", 725 | "time": 5032, 726 | "request": { 727 | "method": "GET", 728 | "url": "http://www.sebastianstoehr.de/res/images/socialbar.png", 729 | "httpVersion": "HTTP/1.1", 730 | "cookies": [], 731 | "headers": [ 732 | { 733 | "name": "Host", 734 | "value": "www.sebastianstoehr.de" 735 | }, 736 | { 737 | "name": "User-Agent", 738 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 739 | }, 740 | { 741 | "name": "Accept", 742 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 743 | }, 744 | { 745 | "name": "Accept-Language", 746 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 747 | }, 748 | { 749 | "name": "Accept-Encoding", 750 | "value": "gzip, deflate" 751 | }, 752 | { 753 | "name": "DNT", 754 | "value": "1" 755 | }, 756 | { 757 | "name": "Referer", 758 | "value": "http://www.sebastianstoehr.de/assets/app-176c7b354c124a4593b8d4ac674d69e6.css" 759 | }, 760 | { 761 | "name": "Connection", 762 | "value": "keep-alive" 763 | } 764 | ], 765 | "queryString": [], 766 | "headersSize": 715, 767 | "bodySize": -1 768 | }, 769 | "response": { 770 | "status": 200, 771 | "statusText": "OK", 772 | "httpVersion": "HTTP/1.1", 773 | "cookies": [], 774 | "headers": [ 775 | { 776 | "name": "Date", 777 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 778 | }, 779 | { 780 | "name": "Server", 781 | "value": "Apache" 782 | }, 783 | { 784 | "name": "Last-Modified", 785 | "value": "Sat, 11 Jan 2014 10:56:55 GMT" 786 | }, 787 | { 788 | "name": "Accept-Ranges", 789 | "value": "bytes" 790 | }, 791 | { 792 | "name": "Content-Length", 793 | "value": "2772" 794 | }, 795 | { 796 | "name": "Cache-Control", 797 | "value": "max-age=15552000" 798 | }, 799 | { 800 | "name": "Expires", 801 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 802 | }, 803 | { 804 | "name": "Keep-Alive", 805 | "value": "timeout=2, max=200" 806 | }, 807 | { 808 | "name": "Connection", 809 | "value": "Keep-Alive" 810 | }, 811 | { 812 | "name": "Content-Type", 813 | "value": "image/png" 814 | } 815 | ], 816 | "content": { 817 | "mimeType": "image/png", 818 | "size": 96, 819 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/socialbar.png" 820 | }, 821 | "redirectURL": "", 822 | "headersSize": 316, 823 | "bodySize": 2772 824 | }, 825 | "cache": {}, 826 | "timings": { 827 | "blocked": 0, 828 | "dns": 0, 829 | "connect": 29, 830 | "send": 0, 831 | "wait": 4980, 832 | "receive": 23 833 | }, 834 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 835 | "connection": "80" 836 | }, 837 | { 838 | "pageref": "page_3", 839 | "startedDateTime": "2014-01-26T18:37:47.711+01:00", 840 | "time": 5057, 841 | "request": { 842 | "method": "GET", 843 | "url": "http://www.sebastianstoehr.de/assets/app-d6d60b5cc834e6a9ce6fbfee22da998f.js", 844 | "httpVersion": "HTTP/1.1", 845 | "cookies": [], 846 | "headers": [ 847 | { 848 | "name": "Host", 849 | "value": "www.sebastianstoehr.de" 850 | }, 851 | { 852 | "name": "User-Agent", 853 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 854 | }, 855 | { 856 | "name": "Accept", 857 | "value": "*/*" 858 | }, 859 | { 860 | "name": "Accept-Language", 861 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 862 | }, 863 | { 864 | "name": "Accept-Encoding", 865 | "value": "gzip, deflate" 866 | }, 867 | { 868 | "name": "DNT", 869 | "value": "1" 870 | }, 871 | { 872 | "name": "Referer", 873 | "value": "http://www.sebastianstoehr.de/" 874 | }, 875 | { 876 | "name": "Connection", 877 | "value": "keep-alive" 878 | } 879 | ], 880 | "queryString": [], 881 | "headersSize": 660, 882 | "bodySize": -1 883 | }, 884 | "response": { 885 | "status": 200, 886 | "statusText": "OK", 887 | "httpVersion": "HTTP/1.1", 888 | "cookies": [], 889 | "headers": [ 890 | { 891 | "name": "Date", 892 | "value": "Sun, 26 Jan 2014 17:37:53 GMT" 893 | }, 894 | { 895 | "name": "Server", 896 | "value": "Apache" 897 | }, 898 | { 899 | "name": "Last-Modified", 900 | "value": "Fri, 10 Jan 2014 17:10:50 GMT" 901 | }, 902 | { 903 | "name": "Accept-Ranges", 904 | "value": "bytes" 905 | }, 906 | { 907 | "name": "Content-Length", 908 | "value": "4453" 909 | }, 910 | { 911 | "name": "Cache-Control", 912 | "value": "max-age=15552000" 913 | }, 914 | { 915 | "name": "Expires", 916 | "value": "Fri, 25 Jul 2014 17:37:53 GMT" 917 | }, 918 | { 919 | "name": "Vary", 920 | "value": "Accept-Encoding" 921 | }, 922 | { 923 | "name": "Keep-Alive", 924 | "value": "timeout=2, max=198" 925 | }, 926 | { 927 | "name": "Connection", 928 | "value": "Keep-Alive" 929 | }, 930 | { 931 | "name": "Content-Type", 932 | "value": "text/javascript" 933 | }, 934 | { 935 | "name": "Content-Encoding", 936 | "value": "gzip" 937 | } 938 | ], 939 | "content": { 940 | "mimeType": "text/javascript", 941 | "size": 11676, 942 | "text": "--- REMOVED ---\n" 943 | }, 944 | "redirectURL": "", 945 | "headersSize": 369, 946 | "bodySize": 4453 947 | }, 948 | "cache": {}, 949 | "timings": { 950 | "blocked": 5000, 951 | "dns": 0, 952 | "connect": 0, 953 | "send": 0, 954 | "wait": 55, 955 | "receive": 2 956 | }, 957 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 958 | "connection": "80" 959 | } 960 | ] 961 | } 962 | } -------------------------------------------------------------------------------- /src/test/resources/sstoehr.invalid-date.har: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "version": "1.1", 4 | "creator": { 5 | "name": "Firebug", 6 | "version": "1.12" 7 | }, 8 | "browser": { 9 | "name": "Firefox", 10 | "version": "26.0" 11 | }, 12 | "pages": [ 13 | { 14 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 15 | "id": "page_3", 16 | "title": "Home — Sebastian Stöhr", 17 | "pageTimings": { 18 | "onContentLoad": 3910, 19 | "onLoad": -1 20 | } 21 | } 22 | ], 23 | "entries": [ 24 | { 25 | "pageref": "page_3", 26 | "startedDateTime": "NaN-NaN-NaNTNaN:NaN:NaN.NaN+NaN:NaN", 27 | "time": 3804, 28 | "request": { 29 | "method": "GET", 30 | "url": "http://www.sebastianstoehr.de/assets/app-176c7b354c124a4593b8d4ac674d69e6.css", 31 | "httpVersion": "HTTP/1.1", 32 | "cookies": [], 33 | "headers": [ 34 | { 35 | "name": "Host", 36 | "value": "www.sebastianstoehr.de" 37 | }, 38 | { 39 | "name": "User-Agent", 40 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 41 | }, 42 | { 43 | "name": "Accept", 44 | "value": "text/css,*/*;q=0.1" 45 | }, 46 | { 47 | "name": "Accept-Language", 48 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 49 | }, 50 | { 51 | "name": "Accept-Encoding", 52 | "value": "gzip, deflate" 53 | }, 54 | { 55 | "name": "DNT", 56 | "value": "1" 57 | }, 58 | { 59 | "name": "Referer", 60 | "value": "http://www.sebastianstoehr.de/" 61 | }, 62 | { 63 | "name": "Connection", 64 | "value": "keep-alive" 65 | } 66 | ], 67 | "queryString": [], 68 | "headersSize": 676, 69 | "bodySize": -1 70 | }, 71 | "response": { 72 | "status": 200, 73 | "statusText": "OK", 74 | "httpVersion": "HTTP/1.1", 75 | "cookies": [], 76 | "headers": [ 77 | { 78 | "name": "Date", 79 | "value": "Sun, 26 Jan 2014 17:37:44 GMT" 80 | }, 81 | { 82 | "name": "Server", 83 | "value": "Apache" 84 | }, 85 | { 86 | "name": "Last-Modified", 87 | "value": "Fri, 10 Jan 2014 17:10:50 GMT" 88 | }, 89 | { 90 | "name": "Accept-Ranges", 91 | "value": "bytes" 92 | }, 93 | { 94 | "name": "Content-Length", 95 | "value": "3412" 96 | }, 97 | { 98 | "name": "Cache-Control", 99 | "value": "max-age=15552000" 100 | }, 101 | { 102 | "name": "Expires", 103 | "value": "Fri, 25 Jul 2014 17:37:44 GMT" 104 | }, 105 | { 106 | "name": "Vary", 107 | "value": "Accept-Encoding" 108 | }, 109 | { 110 | "name": "Keep-Alive", 111 | "value": "timeout=2, max=200" 112 | }, 113 | { 114 | "name": "Connection", 115 | "value": "Keep-Alive" 116 | }, 117 | { 118 | "name": "Content-Type", 119 | "value": "text/css" 120 | }, 121 | { 122 | "name": "Content-Encoding", 123 | "value": "gzip" 124 | } 125 | ], 126 | "content": { 127 | "mimeType": "text/css", 128 | "size": 12647, 129 | "text": "--- REMOVED ---\n" 130 | }, 131 | "redirectURL": "", 132 | "headersSize": 362, 133 | "bodySize": 3412 134 | }, 135 | "cache": {}, 136 | "timings": { 137 | "blocked": 5, 138 | "dns": 0, 139 | "connect": 0, 140 | "send": 0, 141 | "wait": 3778, 142 | "receive": 21 143 | }, 144 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 145 | "connection": "80" 146 | }, 147 | { 148 | "pageref": "page_3", 149 | "startedDateTime": "NaN-NaN-NaNTNaN:NaN:NaN.NaN+NaN:NaN", 150 | "time": 9092, 151 | "request": { 152 | "method": "GET", 153 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/woeste-alumni/header-600x131-bf1788.jpg", 154 | "httpVersion": "HTTP/1.1", 155 | "cookies": [], 156 | "headers": [ 157 | { 158 | "name": "Host", 159 | "value": "www.sebastianstoehr.de" 160 | }, 161 | { 162 | "name": "User-Agent", 163 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 164 | }, 165 | { 166 | "name": "Accept", 167 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 168 | }, 169 | { 170 | "name": "Accept-Language", 171 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 172 | }, 173 | { 174 | "name": "Accept-Encoding", 175 | "value": "gzip, deflate" 176 | }, 177 | { 178 | "name": "DNT", 179 | "value": "1" 180 | }, 181 | { 182 | "name": "Referer", 183 | "value": "http://www.sebastianstoehr.de/" 184 | }, 185 | { 186 | "name": "Connection", 187 | "value": "keep-alive" 188 | } 189 | ], 190 | "queryString": [], 191 | "headersSize": 704, 192 | "bodySize": -1 193 | }, 194 | "response": { 195 | "status": 200, 196 | "statusText": "OK", 197 | "httpVersion": "HTTP/1.1", 198 | "cookies": [], 199 | "headers": [ 200 | { 201 | "name": "Date", 202 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 203 | }, 204 | { 205 | "name": "Server", 206 | "value": "Apache" 207 | }, 208 | { 209 | "name": "Last-Modified", 210 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 211 | }, 212 | { 213 | "name": "Accept-Ranges", 214 | "value": "bytes" 215 | }, 216 | { 217 | "name": "Content-Length", 218 | "value": "16997" 219 | }, 220 | { 221 | "name": "Cache-Control", 222 | "value": "max-age=15552000" 223 | }, 224 | { 225 | "name": "Expires", 226 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 227 | }, 228 | { 229 | "name": "Keep-Alive", 230 | "value": "timeout=2, max=200" 231 | }, 232 | { 233 | "name": "Connection", 234 | "value": "Keep-Alive" 235 | }, 236 | { 237 | "name": "Content-Type", 238 | "value": "image/jpeg" 239 | } 240 | ], 241 | "content": { 242 | "mimeType": "image/jpeg", 243 | "size": 132, 244 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/woeste-alumni/header-600x131-bf1788.jpg" 245 | }, 246 | "redirectURL": "", 247 | "headersSize": 318, 248 | "bodySize": 16997 249 | }, 250 | "cache": {}, 251 | "timings": { 252 | "blocked": 3804, 253 | "dns": 0, 254 | "connect": 0, 255 | "send": 0, 256 | "wait": 5209, 257 | "receive": 79 258 | }, 259 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 260 | "connection": "80" 261 | }, 262 | { 263 | "pageref": "page_3", 264 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 265 | "time": 9139, 266 | "request": { 267 | "method": "GET", 268 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/sebastian-stoehr/header-600x131-415442.jpg", 269 | "httpVersion": "HTTP/1.1", 270 | "cookies": [], 271 | "headers": [ 272 | { 273 | "name": "Host", 274 | "value": "www.sebastianstoehr.de" 275 | }, 276 | { 277 | "name": "User-Agent", 278 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 279 | }, 280 | { 281 | "name": "Accept", 282 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 283 | }, 284 | { 285 | "name": "Accept-Language", 286 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 287 | }, 288 | { 289 | "name": "Accept-Encoding", 290 | "value": "gzip, deflate" 291 | }, 292 | { 293 | "name": "DNT", 294 | "value": "1" 295 | }, 296 | { 297 | "name": "Referer", 298 | "value": "http://www.sebastianstoehr.de/" 299 | }, 300 | { 301 | "name": "Connection", 302 | "value": "keep-alive" 303 | } 304 | ], 305 | "queryString": [], 306 | "headersSize": 707, 307 | "bodySize": -1 308 | }, 309 | "response": { 310 | "status": 200, 311 | "statusText": "OK", 312 | "httpVersion": "HTTP/1.1", 313 | "cookies": [], 314 | "headers": [ 315 | { 316 | "name": "Date", 317 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 318 | }, 319 | { 320 | "name": "Server", 321 | "value": "Apache" 322 | }, 323 | { 324 | "name": "Last-Modified", 325 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 326 | }, 327 | { 328 | "name": "Accept-Ranges", 329 | "value": "bytes" 330 | }, 331 | { 332 | "name": "Content-Length", 333 | "value": "28351" 334 | }, 335 | { 336 | "name": "Cache-Control", 337 | "value": "max-age=15552000" 338 | }, 339 | { 340 | "name": "Expires", 341 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 342 | }, 343 | { 344 | "name": "Keep-Alive", 345 | "value": "timeout=2, max=200" 346 | }, 347 | { 348 | "name": "Connection", 349 | "value": "Keep-Alive" 350 | }, 351 | { 352 | "name": "Content-Type", 353 | "value": "image/jpeg" 354 | } 355 | ], 356 | "content": { 357 | "mimeType": "image/jpeg", 358 | "size": 135, 359 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/sebastian-stoehr/header-600x131-415442.jpg" 360 | }, 361 | "redirectURL": "", 362 | "headersSize": 318, 363 | "bodySize": 28351 364 | }, 365 | "cache": {}, 366 | "timings": { 367 | "blocked": 3804, 368 | "dns": 0, 369 | "connect": 0, 370 | "send": 0, 371 | "wait": 5240, 372 | "receive": 95 373 | }, 374 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 375 | "connection": "80" 376 | }, 377 | { 378 | "pageref": "page_3", 379 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 380 | "time": 9164, 381 | "request": { 382 | "method": "GET", 383 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/abizeitung/header-600x131-603283.jpg", 384 | "httpVersion": "HTTP/1.1", 385 | "cookies": [], 386 | "headers": [ 387 | { 388 | "name": "Host", 389 | "value": "www.sebastianstoehr.de" 390 | }, 391 | { 392 | "name": "User-Agent", 393 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 394 | }, 395 | { 396 | "name": "Accept", 397 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 398 | }, 399 | { 400 | "name": "Accept-Language", 401 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 402 | }, 403 | { 404 | "name": "Accept-Encoding", 405 | "value": "gzip, deflate" 406 | }, 407 | { 408 | "name": "DNT", 409 | "value": "1" 410 | }, 411 | { 412 | "name": "Referer", 413 | "value": "http://www.sebastianstoehr.de/" 414 | }, 415 | { 416 | "name": "Connection", 417 | "value": "keep-alive" 418 | } 419 | ], 420 | "queryString": [], 421 | "headersSize": 701, 422 | "bodySize": -1 423 | }, 424 | "response": { 425 | "status": 200, 426 | "statusText": "OK", 427 | "httpVersion": "HTTP/1.1", 428 | "cookies": [], 429 | "headers": [ 430 | { 431 | "name": "Date", 432 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 433 | }, 434 | { 435 | "name": "Server", 436 | "value": "Apache" 437 | }, 438 | { 439 | "name": "Last-Modified", 440 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 441 | }, 442 | { 443 | "name": "Accept-Ranges", 444 | "value": "bytes" 445 | }, 446 | { 447 | "name": "Content-Length", 448 | "value": "73064" 449 | }, 450 | { 451 | "name": "Cache-Control", 452 | "value": "max-age=15552000" 453 | }, 454 | { 455 | "name": "Expires", 456 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 457 | }, 458 | { 459 | "name": "Keep-Alive", 460 | "value": "timeout=2, max=200" 461 | }, 462 | { 463 | "name": "Connection", 464 | "value": "Keep-Alive" 465 | }, 466 | { 467 | "name": "Content-Type", 468 | "value": "image/jpeg" 469 | } 470 | ], 471 | "content": { 472 | "mimeType": "image/jpeg", 473 | "size": 129, 474 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/abizeitung/header-600x131-603283.jpg" 475 | }, 476 | "redirectURL": "", 477 | "headersSize": 318, 478 | "bodySize": 73064 479 | }, 480 | "cache": {}, 481 | "timings": { 482 | "blocked": 3804, 483 | "dns": 0, 484 | "connect": 0, 485 | "send": 0, 486 | "wait": 5217, 487 | "receive": 143 488 | }, 489 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 490 | "connection": "80" 491 | }, 492 | { 493 | "pageref": "page_3", 494 | "startedDateTime": "2014-01-26T18:37:43.814+01:00", 495 | "time": 9156, 496 | "request": { 497 | "method": "GET", 498 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/junge-liberale/header-600x131-a069fc.jpg", 499 | "httpVersion": "HTTP/1.1", 500 | "cookies": [], 501 | "headers": [ 502 | { 503 | "name": "Host", 504 | "value": "www.sebastianstoehr.de" 505 | }, 506 | { 507 | "name": "User-Agent", 508 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 509 | }, 510 | { 511 | "name": "Accept", 512 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 513 | }, 514 | { 515 | "name": "Accept-Language", 516 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 517 | }, 518 | { 519 | "name": "Accept-Encoding", 520 | "value": "gzip, deflate" 521 | }, 522 | { 523 | "name": "DNT", 524 | "value": "1" 525 | }, 526 | { 527 | "name": "Referer", 528 | "value": "http://www.sebastianstoehr.de/" 529 | }, 530 | { 531 | "name": "Connection", 532 | "value": "keep-alive" 533 | } 534 | ], 535 | "queryString": [], 536 | "headersSize": 705, 537 | "bodySize": -1 538 | }, 539 | "response": { 540 | "status": 200, 541 | "statusText": "OK", 542 | "httpVersion": "HTTP/1.1", 543 | "cookies": [], 544 | "headers": [ 545 | { 546 | "name": "Date", 547 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 548 | }, 549 | { 550 | "name": "Server", 551 | "value": "Apache" 552 | }, 553 | { 554 | "name": "Last-Modified", 555 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 556 | }, 557 | { 558 | "name": "Accept-Ranges", 559 | "value": "bytes" 560 | }, 561 | { 562 | "name": "Content-Length", 563 | "value": "49555" 564 | }, 565 | { 566 | "name": "Cache-Control", 567 | "value": "max-age=15552000" 568 | }, 569 | { 570 | "name": "Expires", 571 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 572 | }, 573 | { 574 | "name": "Keep-Alive", 575 | "value": "timeout=2, max=200" 576 | }, 577 | { 578 | "name": "Connection", 579 | "value": "Keep-Alive" 580 | }, 581 | { 582 | "name": "Content-Type", 583 | "value": "image/jpeg" 584 | } 585 | ], 586 | "content": { 587 | "mimeType": "image/jpeg", 588 | "size": 133, 589 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/junge-liberale/header-600x131-a069fc.jpg" 590 | }, 591 | "redirectURL": "", 592 | "headersSize": 318, 593 | "bodySize": 49555 594 | }, 595 | "cache": {}, 596 | "timings": { 597 | "blocked": 3803, 598 | "dns": 0, 599 | "connect": 0, 600 | "send": 0, 601 | "wait": 5222, 602 | "receive": 131 603 | }, 604 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 605 | "connection": "80" 606 | }, 607 | { 608 | "pageref": "page_3", 609 | "startedDateTime": "2014-01-26T18:37:47.701+01:00", 610 | "time": 5010, 611 | "request": { 612 | "method": "GET", 613 | "url": "http://www.sebastianstoehr.de/res/images/logo.png", 614 | "httpVersion": "HTTP/1.1", 615 | "cookies": [], 616 | "headers": [ 617 | { 618 | "name": "Host", 619 | "value": "www.sebastianstoehr.de" 620 | }, 621 | { 622 | "name": "User-Agent", 623 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 624 | }, 625 | { 626 | "name": "Accept", 627 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 628 | }, 629 | { 630 | "name": "Accept-Language", 631 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 632 | }, 633 | { 634 | "name": "Accept-Encoding", 635 | "value": "gzip, deflate" 636 | }, 637 | { 638 | "name": "DNT", 639 | "value": "1" 640 | }, 641 | { 642 | "name": "Referer", 643 | "value": "http://www.sebastianstoehr.de/assets/app-176c7b354c124a4593b8d4ac674d69e6.css" 644 | }, 645 | { 646 | "name": "Connection", 647 | "value": "keep-alive" 648 | } 649 | ], 650 | "queryString": [], 651 | "headersSize": 710, 652 | "bodySize": -1 653 | }, 654 | "response": { 655 | "status": 200, 656 | "statusText": "OK", 657 | "httpVersion": "HTTP/1.1", 658 | "cookies": [], 659 | "headers": [ 660 | { 661 | "name": "Date", 662 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 663 | }, 664 | { 665 | "name": "Server", 666 | "value": "Apache" 667 | }, 668 | { 669 | "name": "Last-Modified", 670 | "value": "Sat, 11 Jan 2014 10:56:55 GMT" 671 | }, 672 | { 673 | "name": "Accept-Ranges", 674 | "value": "bytes" 675 | }, 676 | { 677 | "name": "Content-Length", 678 | "value": "717" 679 | }, 680 | { 681 | "name": "Cache-Control", 682 | "value": "max-age=15552000" 683 | }, 684 | { 685 | "name": "Expires", 686 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 687 | }, 688 | { 689 | "name": "Keep-Alive", 690 | "value": "timeout=2, max=199" 691 | }, 692 | { 693 | "name": "Connection", 694 | "value": "Keep-Alive" 695 | }, 696 | { 697 | "name": "Content-Type", 698 | "value": "image/png" 699 | } 700 | ], 701 | "content": { 702 | "mimeType": "image/png", 703 | "size": 91, 704 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/logo.png" 705 | }, 706 | "redirectURL": "", 707 | "headersSize": 315, 708 | "bodySize": 717 709 | }, 710 | "cache": {}, 711 | "timings": { 712 | "blocked": 0, 713 | "dns": 0, 714 | "connect": 0, 715 | "send": 0, 716 | "wait": 5010, 717 | "receive": 0 718 | }, 719 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 720 | "connection": "80" 721 | }, 722 | { 723 | "pageref": "page_3", 724 | "startedDateTime": "2014-01-26T18:37:47.701+01:00", 725 | "time": 5032, 726 | "request": { 727 | "method": "GET", 728 | "url": "http://www.sebastianstoehr.de/res/images/socialbar.png", 729 | "httpVersion": "HTTP/1.1", 730 | "cookies": [], 731 | "headers": [ 732 | { 733 | "name": "Host", 734 | "value": "www.sebastianstoehr.de" 735 | }, 736 | { 737 | "name": "User-Agent", 738 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 739 | }, 740 | { 741 | "name": "Accept", 742 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 743 | }, 744 | { 745 | "name": "Accept-Language", 746 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 747 | }, 748 | { 749 | "name": "Accept-Encoding", 750 | "value": "gzip, deflate" 751 | }, 752 | { 753 | "name": "DNT", 754 | "value": "1" 755 | }, 756 | { 757 | "name": "Referer", 758 | "value": "http://www.sebastianstoehr.de/assets/app-176c7b354c124a4593b8d4ac674d69e6.css" 759 | }, 760 | { 761 | "name": "Connection", 762 | "value": "keep-alive" 763 | } 764 | ], 765 | "queryString": [], 766 | "headersSize": 715, 767 | "bodySize": -1 768 | }, 769 | "response": { 770 | "status": 200, 771 | "statusText": "OK", 772 | "httpVersion": "HTTP/1.1", 773 | "cookies": [], 774 | "headers": [ 775 | { 776 | "name": "Date", 777 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 778 | }, 779 | { 780 | "name": "Server", 781 | "value": "Apache" 782 | }, 783 | { 784 | "name": "Last-Modified", 785 | "value": "Sat, 11 Jan 2014 10:56:55 GMT" 786 | }, 787 | { 788 | "name": "Accept-Ranges", 789 | "value": "bytes" 790 | }, 791 | { 792 | "name": "Content-Length", 793 | "value": "2772" 794 | }, 795 | { 796 | "name": "Cache-Control", 797 | "value": "max-age=15552000" 798 | }, 799 | { 800 | "name": "Expires", 801 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 802 | }, 803 | { 804 | "name": "Keep-Alive", 805 | "value": "timeout=2, max=200" 806 | }, 807 | { 808 | "name": "Connection", 809 | "value": "Keep-Alive" 810 | }, 811 | { 812 | "name": "Content-Type", 813 | "value": "image/png" 814 | } 815 | ], 816 | "content": { 817 | "mimeType": "image/png", 818 | "size": 96, 819 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/socialbar.png" 820 | }, 821 | "redirectURL": "", 822 | "headersSize": 316, 823 | "bodySize": 2772 824 | }, 825 | "cache": {}, 826 | "timings": { 827 | "blocked": 0, 828 | "dns": 0, 829 | "connect": 29, 830 | "send": 0, 831 | "wait": 4980, 832 | "receive": 23 833 | }, 834 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 835 | "connection": "80" 836 | }, 837 | { 838 | "pageref": "page_3", 839 | "startedDateTime": "2014-01-26T18:37:47.711+01:00", 840 | "time": 5057, 841 | "request": { 842 | "method": "GET", 843 | "url": "http://www.sebastianstoehr.de/assets/app-d6d60b5cc834e6a9ce6fbfee22da998f.js", 844 | "httpVersion": "HTTP/1.1", 845 | "cookies": [], 846 | "headers": [ 847 | { 848 | "name": "Host", 849 | "value": "www.sebastianstoehr.de" 850 | }, 851 | { 852 | "name": "User-Agent", 853 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 854 | }, 855 | { 856 | "name": "Accept", 857 | "value": "*/*" 858 | }, 859 | { 860 | "name": "Accept-Language", 861 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 862 | }, 863 | { 864 | "name": "Accept-Encoding", 865 | "value": "gzip, deflate" 866 | }, 867 | { 868 | "name": "DNT", 869 | "value": "1" 870 | }, 871 | { 872 | "name": "Referer", 873 | "value": "http://www.sebastianstoehr.de/" 874 | }, 875 | { 876 | "name": "Connection", 877 | "value": "keep-alive" 878 | } 879 | ], 880 | "queryString": [], 881 | "headersSize": 660, 882 | "bodySize": -1 883 | }, 884 | "response": { 885 | "status": 200, 886 | "statusText": "OK", 887 | "httpVersion": "HTTP/1.1", 888 | "cookies": [], 889 | "headers": [ 890 | { 891 | "name": "Date", 892 | "value": "Sun, 26 Jan 2014 17:37:53 GMT" 893 | }, 894 | { 895 | "name": "Server", 896 | "value": "Apache" 897 | }, 898 | { 899 | "name": "Last-Modified", 900 | "value": "Fri, 10 Jan 2014 17:10:50 GMT" 901 | }, 902 | { 903 | "name": "Accept-Ranges", 904 | "value": "bytes" 905 | }, 906 | { 907 | "name": "Content-Length", 908 | "value": "4453" 909 | }, 910 | { 911 | "name": "Cache-Control", 912 | "value": "max-age=15552000" 913 | }, 914 | { 915 | "name": "Expires", 916 | "value": "Fri, 25 Jul 2014 17:37:53 GMT" 917 | }, 918 | { 919 | "name": "Vary", 920 | "value": "Accept-Encoding" 921 | }, 922 | { 923 | "name": "Keep-Alive", 924 | "value": "timeout=2, max=198" 925 | }, 926 | { 927 | "name": "Connection", 928 | "value": "Keep-Alive" 929 | }, 930 | { 931 | "name": "Content-Type", 932 | "value": "text/javascript" 933 | }, 934 | { 935 | "name": "Content-Encoding", 936 | "value": "gzip" 937 | } 938 | ], 939 | "content": { 940 | "mimeType": "text/javascript", 941 | "size": 11676, 942 | "text": "--- REMOVED ---\n" 943 | }, 944 | "redirectURL": "", 945 | "headersSize": 369, 946 | "bodySize": 4453 947 | }, 948 | "cache": {}, 949 | "timings": { 950 | "blocked": 5000, 951 | "dns": 0, 952 | "connect": 0, 953 | "send": 0, 954 | "wait": 55, 955 | "receive": 2 956 | }, 957 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 958 | "connection": "80" 959 | } 960 | ] 961 | } 962 | } -------------------------------------------------------------------------------- /src/test/resources/sstoehr.invalid-integer.har: -------------------------------------------------------------------------------- 1 | { 2 | "log": { 3 | "version": "1.1", 4 | "creator": { 5 | "name": "Firebug", 6 | "version": "1.12" 7 | }, 8 | "browser": { 9 | "name": "Firefox", 10 | "version": "26.0" 11 | }, 12 | "pages": [ 13 | { 14 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 15 | "id": "page_3", 16 | "title": "Home — Sebastian Stöhr", 17 | "pageTimings": { 18 | "onContentLoad": 3910, 19 | "onLoad": -1 20 | } 21 | } 22 | ], 23 | "entries": [ 24 | { 25 | "pageref": "page_3", 26 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 27 | "time": 3804, 28 | "request": { 29 | "method": "GET", 30 | "url": "http://www.sebastianstoehr.de/assets/app-176c7b354c124a4593b8d4ac674d69e6.css", 31 | "httpVersion": "HTTP/1.1", 32 | "cookies": [], 33 | "headers": [ 34 | { 35 | "name": "Host", 36 | "value": "www.sebastianstoehr.de" 37 | }, 38 | { 39 | "name": "User-Agent", 40 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 41 | }, 42 | { 43 | "name": "Accept", 44 | "value": "text/css,*/*;q=0.1" 45 | }, 46 | { 47 | "name": "Accept-Language", 48 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 49 | }, 50 | { 51 | "name": "Accept-Encoding", 52 | "value": "gzip, deflate" 53 | }, 54 | { 55 | "name": "DNT", 56 | "value": "1" 57 | }, 58 | { 59 | "name": "Referer", 60 | "value": "http://www.sebastianstoehr.de/" 61 | }, 62 | { 63 | "name": "Connection", 64 | "value": "keep-alive" 65 | } 66 | ], 67 | "queryString": [], 68 | "headersSize": 676, 69 | "bodySize": -1 70 | }, 71 | "response": { 72 | "status": 200, 73 | "statusText": "OK", 74 | "httpVersion": "HTTP/1.1", 75 | "cookies": [], 76 | "headers": [ 77 | { 78 | "name": "Date", 79 | "value": "Sun, 26 Jan 2014 17:37:44 GMT" 80 | }, 81 | { 82 | "name": "Server", 83 | "value": "Apache" 84 | }, 85 | { 86 | "name": "Last-Modified", 87 | "value": "Fri, 10 Jan 2014 17:10:50 GMT" 88 | }, 89 | { 90 | "name": "Accept-Ranges", 91 | "value": "bytes" 92 | }, 93 | { 94 | "name": "Content-Length", 95 | "value": "3412" 96 | }, 97 | { 98 | "name": "Cache-Control", 99 | "value": "max-age=15552000" 100 | }, 101 | { 102 | "name": "Expires", 103 | "value": "Fri, 25 Jul 2014 17:37:44 GMT" 104 | }, 105 | { 106 | "name": "Vary", 107 | "value": "Accept-Encoding" 108 | }, 109 | { 110 | "name": "Keep-Alive", 111 | "value": "timeout=2, max=200" 112 | }, 113 | { 114 | "name": "Connection", 115 | "value": "Keep-Alive" 116 | }, 117 | { 118 | "name": "Content-Type", 119 | "value": "text/css" 120 | }, 121 | { 122 | "name": "Content-Encoding", 123 | "value": "gzip" 124 | } 125 | ], 126 | "content": { 127 | "mimeType": "text/css", 128 | "size": 12647, 129 | "text": "--- REMOVED ---\n" 130 | }, 131 | "redirectURL": "", 132 | "headersSize": 362, 133 | "bodySize": 3412 134 | }, 135 | "cache": {}, 136 | "timings": { 137 | "blocked": 5, 138 | "dns": 0, 139 | "connect": 0, 140 | "send": 0, 141 | "wait": 3778, 142 | "receive": 63596250212317 143 | }, 144 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 145 | "connection": "80" 146 | }, 147 | { 148 | "pageref": "page_3", 149 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 150 | "time": 9092, 151 | "request": { 152 | "method": "GET", 153 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/woeste-alumni/header-600x131-bf1788.jpg", 154 | "httpVersion": "HTTP/1.1", 155 | "cookies": [], 156 | "headers": [ 157 | { 158 | "name": "Host", 159 | "value": "www.sebastianstoehr.de" 160 | }, 161 | { 162 | "name": "User-Agent", 163 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 164 | }, 165 | { 166 | "name": "Accept", 167 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 168 | }, 169 | { 170 | "name": "Accept-Language", 171 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 172 | }, 173 | { 174 | "name": "Accept-Encoding", 175 | "value": "gzip, deflate" 176 | }, 177 | { 178 | "name": "DNT", 179 | "value": "1" 180 | }, 181 | { 182 | "name": "Referer", 183 | "value": "http://www.sebastianstoehr.de/" 184 | }, 185 | { 186 | "name": "Connection", 187 | "value": "keep-alive" 188 | } 189 | ], 190 | "queryString": [], 191 | "headersSize": 704, 192 | "bodySize": -1 193 | }, 194 | "response": { 195 | "status": 200, 196 | "statusText": "OK", 197 | "httpVersion": "HTTP/1.1", 198 | "cookies": [], 199 | "headers": [ 200 | { 201 | "name": "Date", 202 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 203 | }, 204 | { 205 | "name": "Server", 206 | "value": "Apache" 207 | }, 208 | { 209 | "name": "Last-Modified", 210 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 211 | }, 212 | { 213 | "name": "Accept-Ranges", 214 | "value": "bytes" 215 | }, 216 | { 217 | "name": "Content-Length", 218 | "value": "16997" 219 | }, 220 | { 221 | "name": "Cache-Control", 222 | "value": "max-age=15552000" 223 | }, 224 | { 225 | "name": "Expires", 226 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 227 | }, 228 | { 229 | "name": "Keep-Alive", 230 | "value": "timeout=2, max=200" 231 | }, 232 | { 233 | "name": "Connection", 234 | "value": "Keep-Alive" 235 | }, 236 | { 237 | "name": "Content-Type", 238 | "value": "image/jpeg" 239 | } 240 | ], 241 | "content": { 242 | "mimeType": "image/jpeg", 243 | "size": 132, 244 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/woeste-alumni/header-600x131-bf1788.jpg" 245 | }, 246 | "redirectURL": "", 247 | "headersSize": 318, 248 | "bodySize": 16997 249 | }, 250 | "cache": {}, 251 | "timings": { 252 | "blocked": 3804, 253 | "dns": 0, 254 | "connect": 0, 255 | "send": 0, 256 | "wait": 5209, 257 | "receive": 79 258 | }, 259 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 260 | "connection": "80" 261 | }, 262 | { 263 | "pageref": "page_3", 264 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 265 | "time": 9139, 266 | "request": { 267 | "method": "GET", 268 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/sebastian-stoehr/header-600x131-415442.jpg", 269 | "httpVersion": "HTTP/1.1", 270 | "cookies": [], 271 | "headers": [ 272 | { 273 | "name": "Host", 274 | "value": "www.sebastianstoehr.de" 275 | }, 276 | { 277 | "name": "User-Agent", 278 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 279 | }, 280 | { 281 | "name": "Accept", 282 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 283 | }, 284 | { 285 | "name": "Accept-Language", 286 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 287 | }, 288 | { 289 | "name": "Accept-Encoding", 290 | "value": "gzip, deflate" 291 | }, 292 | { 293 | "name": "DNT", 294 | "value": "1" 295 | }, 296 | { 297 | "name": "Referer", 298 | "value": "http://www.sebastianstoehr.de/" 299 | }, 300 | { 301 | "name": "Connection", 302 | "value": "keep-alive" 303 | } 304 | ], 305 | "queryString": [], 306 | "headersSize": 707, 307 | "bodySize": -1 308 | }, 309 | "response": { 310 | "status": 200, 311 | "statusText": "OK", 312 | "httpVersion": "HTTP/1.1", 313 | "cookies": [], 314 | "headers": [ 315 | { 316 | "name": "Date", 317 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 318 | }, 319 | { 320 | "name": "Server", 321 | "value": "Apache" 322 | }, 323 | { 324 | "name": "Last-Modified", 325 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 326 | }, 327 | { 328 | "name": "Accept-Ranges", 329 | "value": "bytes" 330 | }, 331 | { 332 | "name": "Content-Length", 333 | "value": "28351" 334 | }, 335 | { 336 | "name": "Cache-Control", 337 | "value": "max-age=15552000" 338 | }, 339 | { 340 | "name": "Expires", 341 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 342 | }, 343 | { 344 | "name": "Keep-Alive", 345 | "value": "timeout=2, max=200" 346 | }, 347 | { 348 | "name": "Connection", 349 | "value": "Keep-Alive" 350 | }, 351 | { 352 | "name": "Content-Type", 353 | "value": "image/jpeg" 354 | } 355 | ], 356 | "content": { 357 | "mimeType": "image/jpeg", 358 | "size": 135, 359 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/sebastian-stoehr/header-600x131-415442.jpg" 360 | }, 361 | "redirectURL": "", 362 | "headersSize": 318, 363 | "bodySize": 28351 364 | }, 365 | "cache": {}, 366 | "timings": { 367 | "blocked": 3804, 368 | "dns": 0, 369 | "connect": 0, 370 | "send": 0, 371 | "wait": 5240, 372 | "receive": 95 373 | }, 374 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 375 | "connection": "80" 376 | }, 377 | { 378 | "pageref": "page_3", 379 | "startedDateTime": "2014-01-26T18:37:43.813+01:00", 380 | "time": 9164, 381 | "request": { 382 | "method": "GET", 383 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/abizeitung/header-600x131-603283.jpg", 384 | "httpVersion": "HTTP/1.1", 385 | "cookies": [], 386 | "headers": [ 387 | { 388 | "name": "Host", 389 | "value": "www.sebastianstoehr.de" 390 | }, 391 | { 392 | "name": "User-Agent", 393 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 394 | }, 395 | { 396 | "name": "Accept", 397 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 398 | }, 399 | { 400 | "name": "Accept-Language", 401 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 402 | }, 403 | { 404 | "name": "Accept-Encoding", 405 | "value": "gzip, deflate" 406 | }, 407 | { 408 | "name": "DNT", 409 | "value": "1" 410 | }, 411 | { 412 | "name": "Referer", 413 | "value": "http://www.sebastianstoehr.de/" 414 | }, 415 | { 416 | "name": "Connection", 417 | "value": "keep-alive" 418 | } 419 | ], 420 | "queryString": [], 421 | "headersSize": 701, 422 | "bodySize": -1 423 | }, 424 | "response": { 425 | "status": 200, 426 | "statusText": "OK", 427 | "httpVersion": "HTTP/1.1", 428 | "cookies": [], 429 | "headers": [ 430 | { 431 | "name": "Date", 432 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 433 | }, 434 | { 435 | "name": "Server", 436 | "value": "Apache" 437 | }, 438 | { 439 | "name": "Last-Modified", 440 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 441 | }, 442 | { 443 | "name": "Accept-Ranges", 444 | "value": "bytes" 445 | }, 446 | { 447 | "name": "Content-Length", 448 | "value": "73064" 449 | }, 450 | { 451 | "name": "Cache-Control", 452 | "value": "max-age=15552000" 453 | }, 454 | { 455 | "name": "Expires", 456 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 457 | }, 458 | { 459 | "name": "Keep-Alive", 460 | "value": "timeout=2, max=200" 461 | }, 462 | { 463 | "name": "Connection", 464 | "value": "Keep-Alive" 465 | }, 466 | { 467 | "name": "Content-Type", 468 | "value": "image/jpeg" 469 | } 470 | ], 471 | "content": { 472 | "mimeType": "image/jpeg", 473 | "size": 129, 474 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/abizeitung/header-600x131-603283.jpg" 475 | }, 476 | "redirectURL": "", 477 | "headersSize": 318, 478 | "bodySize": 73064 479 | }, 480 | "cache": {}, 481 | "timings": { 482 | "blocked": 3804, 483 | "dns": 0, 484 | "connect": 0, 485 | "send": 0, 486 | "wait": 5217, 487 | "receive": 143 488 | }, 489 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 490 | "connection": "80" 491 | }, 492 | { 493 | "pageref": "page_3", 494 | "startedDateTime": "2014-01-26T18:37:43.814+01:00", 495 | "time": 9156, 496 | "request": { 497 | "method": "GET", 498 | "url": "http://www.sebastianstoehr.de/res/images/portfolio/junge-liberale/header-600x131-a069fc.jpg", 499 | "httpVersion": "HTTP/1.1", 500 | "cookies": [], 501 | "headers": [ 502 | { 503 | "name": "Host", 504 | "value": "www.sebastianstoehr.de" 505 | }, 506 | { 507 | "name": "User-Agent", 508 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 509 | }, 510 | { 511 | "name": "Accept", 512 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 513 | }, 514 | { 515 | "name": "Accept-Language", 516 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 517 | }, 518 | { 519 | "name": "Accept-Encoding", 520 | "value": "gzip, deflate" 521 | }, 522 | { 523 | "name": "DNT", 524 | "value": "1" 525 | }, 526 | { 527 | "name": "Referer", 528 | "value": "http://www.sebastianstoehr.de/" 529 | }, 530 | { 531 | "name": "Connection", 532 | "value": "keep-alive" 533 | } 534 | ], 535 | "queryString": [], 536 | "headersSize": 705, 537 | "bodySize": -1 538 | }, 539 | "response": { 540 | "status": 200, 541 | "statusText": "OK", 542 | "httpVersion": "HTTP/1.1", 543 | "cookies": [], 544 | "headers": [ 545 | { 546 | "name": "Date", 547 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 548 | }, 549 | { 550 | "name": "Server", 551 | "value": "Apache" 552 | }, 553 | { 554 | "name": "Last-Modified", 555 | "value": "Sat, 11 Jan 2014 10:56:50 GMT" 556 | }, 557 | { 558 | "name": "Accept-Ranges", 559 | "value": "bytes" 560 | }, 561 | { 562 | "name": "Content-Length", 563 | "value": "49555" 564 | }, 565 | { 566 | "name": "Cache-Control", 567 | "value": "max-age=15552000" 568 | }, 569 | { 570 | "name": "Expires", 571 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 572 | }, 573 | { 574 | "name": "Keep-Alive", 575 | "value": "timeout=2, max=200" 576 | }, 577 | { 578 | "name": "Connection", 579 | "value": "Keep-Alive" 580 | }, 581 | { 582 | "name": "Content-Type", 583 | "value": "image/jpeg" 584 | } 585 | ], 586 | "content": { 587 | "mimeType": "image/jpeg", 588 | "size": 133, 589 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/portfolio/junge-liberale/header-600x131-a069fc.jpg" 590 | }, 591 | "redirectURL": "", 592 | "headersSize": 318, 593 | "bodySize": 49555 594 | }, 595 | "cache": {}, 596 | "timings": { 597 | "blocked": 3803, 598 | "dns": 0, 599 | "connect": 0, 600 | "send": 0, 601 | "wait": 5222, 602 | "receive": 131 603 | }, 604 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 605 | "connection": "80" 606 | }, 607 | { 608 | "pageref": "page_3", 609 | "startedDateTime": "2014-01-26T18:37:47.701+01:00", 610 | "time": 5010, 611 | "request": { 612 | "method": "GET", 613 | "url": "http://www.sebastianstoehr.de/res/images/logo.png", 614 | "httpVersion": "HTTP/1.1", 615 | "cookies": [], 616 | "headers": [ 617 | { 618 | "name": "Host", 619 | "value": "www.sebastianstoehr.de" 620 | }, 621 | { 622 | "name": "User-Agent", 623 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 624 | }, 625 | { 626 | "name": "Accept", 627 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 628 | }, 629 | { 630 | "name": "Accept-Language", 631 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 632 | }, 633 | { 634 | "name": "Accept-Encoding", 635 | "value": "gzip, deflate" 636 | }, 637 | { 638 | "name": "DNT", 639 | "value": "1" 640 | }, 641 | { 642 | "name": "Referer", 643 | "value": "http://www.sebastianstoehr.de/assets/app-176c7b354c124a4593b8d4ac674d69e6.css" 644 | }, 645 | { 646 | "name": "Connection", 647 | "value": "keep-alive" 648 | } 649 | ], 650 | "queryString": [], 651 | "headersSize": 710, 652 | "bodySize": -1 653 | }, 654 | "response": { 655 | "status": 200, 656 | "statusText": "OK", 657 | "httpVersion": "HTTP/1.1", 658 | "cookies": [], 659 | "headers": [ 660 | { 661 | "name": "Date", 662 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 663 | }, 664 | { 665 | "name": "Server", 666 | "value": "Apache" 667 | }, 668 | { 669 | "name": "Last-Modified", 670 | "value": "Sat, 11 Jan 2014 10:56:55 GMT" 671 | }, 672 | { 673 | "name": "Accept-Ranges", 674 | "value": "bytes" 675 | }, 676 | { 677 | "name": "Content-Length", 678 | "value": "717" 679 | }, 680 | { 681 | "name": "Cache-Control", 682 | "value": "max-age=15552000" 683 | }, 684 | { 685 | "name": "Expires", 686 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 687 | }, 688 | { 689 | "name": "Keep-Alive", 690 | "value": "timeout=2, max=199" 691 | }, 692 | { 693 | "name": "Connection", 694 | "value": "Keep-Alive" 695 | }, 696 | { 697 | "name": "Content-Type", 698 | "value": "image/png" 699 | } 700 | ], 701 | "content": { 702 | "mimeType": "image/png", 703 | "size": 91, 704 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/logo.png" 705 | }, 706 | "redirectURL": "", 707 | "headersSize": 315, 708 | "bodySize": 717 709 | }, 710 | "cache": {}, 711 | "timings": { 712 | "blocked": 0, 713 | "dns": 0, 714 | "connect": 0, 715 | "send": 0, 716 | "wait": 5010, 717 | "receive": 0 718 | }, 719 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 720 | "connection": "80" 721 | }, 722 | { 723 | "pageref": "page_3", 724 | "startedDateTime": "2014-01-26T18:37:47.701+01:00", 725 | "time": 5032, 726 | "request": { 727 | "method": "GET", 728 | "url": "http://www.sebastianstoehr.de/res/images/socialbar.png", 729 | "httpVersion": "HTTP/1.1", 730 | "cookies": [], 731 | "headers": [ 732 | { 733 | "name": "Host", 734 | "value": "www.sebastianstoehr.de" 735 | }, 736 | { 737 | "name": "User-Agent", 738 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 739 | }, 740 | { 741 | "name": "Accept", 742 | "value": "image/png,image/*;q=0.8,*/*;q=0.5" 743 | }, 744 | { 745 | "name": "Accept-Language", 746 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 747 | }, 748 | { 749 | "name": "Accept-Encoding", 750 | "value": "gzip, deflate" 751 | }, 752 | { 753 | "name": "DNT", 754 | "value": "1" 755 | }, 756 | { 757 | "name": "Referer", 758 | "value": "http://www.sebastianstoehr.de/assets/app-176c7b354c124a4593b8d4ac674d69e6.css" 759 | }, 760 | { 761 | "name": "Connection", 762 | "value": "keep-alive" 763 | } 764 | ], 765 | "queryString": [], 766 | "headersSize": 715, 767 | "bodySize": -1 768 | }, 769 | "response": { 770 | "status": 200, 771 | "statusText": "OK", 772 | "httpVersion": "HTTP/1.1", 773 | "cookies": [], 774 | "headers": [ 775 | { 776 | "name": "Date", 777 | "value": "Sun, 26 Jan 2014 17:37:48 GMT" 778 | }, 779 | { 780 | "name": "Server", 781 | "value": "Apache" 782 | }, 783 | { 784 | "name": "Last-Modified", 785 | "value": "Sat, 11 Jan 2014 10:56:55 GMT" 786 | }, 787 | { 788 | "name": "Accept-Ranges", 789 | "value": "bytes" 790 | }, 791 | { 792 | "name": "Content-Length", 793 | "value": "2772" 794 | }, 795 | { 796 | "name": "Cache-Control", 797 | "value": "max-age=15552000" 798 | }, 799 | { 800 | "name": "Expires", 801 | "value": "Fri, 25 Jul 2014 17:37:48 GMT" 802 | }, 803 | { 804 | "name": "Keep-Alive", 805 | "value": "timeout=2, max=200" 806 | }, 807 | { 808 | "name": "Connection", 809 | "value": "Keep-Alive" 810 | }, 811 | { 812 | "name": "Content-Type", 813 | "value": "image/png" 814 | } 815 | ], 816 | "content": { 817 | "mimeType": "image/png", 818 | "size": 96, 819 | "text": "Die Quelle von diesem URL ist kein Text:: http://www.sebastianstoehr.de/res/images/socialbar.png" 820 | }, 821 | "redirectURL": "", 822 | "headersSize": 316, 823 | "bodySize": 2772 824 | }, 825 | "cache": {}, 826 | "timings": { 827 | "blocked": 0, 828 | "dns": 0, 829 | "connect": 29, 830 | "send": 0, 831 | "wait": 4980, 832 | "receive": 23 833 | }, 834 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 835 | "connection": "80" 836 | }, 837 | { 838 | "pageref": "page_3", 839 | "startedDateTime": "2014-01-26T18:37:47.711+01:00", 840 | "time": 5057, 841 | "request": { 842 | "method": "GET", 843 | "url": "http://www.sebastianstoehr.de/assets/app-d6d60b5cc834e6a9ce6fbfee22da998f.js", 844 | "httpVersion": "HTTP/1.1", 845 | "cookies": [], 846 | "headers": [ 847 | { 848 | "name": "Host", 849 | "value": "www.sebastianstoehr.de" 850 | }, 851 | { 852 | "name": "User-Agent", 853 | "value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0" 854 | }, 855 | { 856 | "name": "Accept", 857 | "value": "*/*" 858 | }, 859 | { 860 | "name": "Accept-Language", 861 | "value": "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3" 862 | }, 863 | { 864 | "name": "Accept-Encoding", 865 | "value": "gzip, deflate" 866 | }, 867 | { 868 | "name": "DNT", 869 | "value": "1" 870 | }, 871 | { 872 | "name": "Referer", 873 | "value": "http://www.sebastianstoehr.de/" 874 | }, 875 | { 876 | "name": "Connection", 877 | "value": "keep-alive" 878 | } 879 | ], 880 | "queryString": [], 881 | "headersSize": 660, 882 | "bodySize": -1 883 | }, 884 | "response": { 885 | "status": 200, 886 | "statusText": "OK", 887 | "httpVersion": "HTTP/1.1", 888 | "cookies": [], 889 | "headers": [ 890 | { 891 | "name": "Date", 892 | "value": "Sun, 26 Jan 2014 17:37:53 GMT" 893 | }, 894 | { 895 | "name": "Server", 896 | "value": "Apache" 897 | }, 898 | { 899 | "name": "Last-Modified", 900 | "value": "Fri, 10 Jan 2014 17:10:50 GMT" 901 | }, 902 | { 903 | "name": "Accept-Ranges", 904 | "value": "bytes" 905 | }, 906 | { 907 | "name": "Content-Length", 908 | "value": "4453" 909 | }, 910 | { 911 | "name": "Cache-Control", 912 | "value": "max-age=15552000" 913 | }, 914 | { 915 | "name": "Expires", 916 | "value": "Fri, 25 Jul 2014 17:37:53 GMT" 917 | }, 918 | { 919 | "name": "Vary", 920 | "value": "Accept-Encoding" 921 | }, 922 | { 923 | "name": "Keep-Alive", 924 | "value": "timeout=2, max=198" 925 | }, 926 | { 927 | "name": "Connection", 928 | "value": "Keep-Alive" 929 | }, 930 | { 931 | "name": "Content-Type", 932 | "value": "text/javascript" 933 | }, 934 | { 935 | "name": "Content-Encoding", 936 | "value": "gzip" 937 | } 938 | ], 939 | "content": { 940 | "mimeType": "text/javascript", 941 | "size": 11676, 942 | "text": "--- REMOVED ---\n" 943 | }, 944 | "redirectURL": "", 945 | "headersSize": 369, 946 | "bodySize": 4453 947 | }, 948 | "cache": {}, 949 | "timings": { 950 | "blocked": 5000, 951 | "dns": 0, 952 | "connect": 0, 953 | "send": 0, 954 | "wait": 55, 955 | "receive": 2 956 | }, 957 | "serverIPAddress": "2001:8d8:1000:30ff:1bb6:8823:a9a3:a003", 958 | "connection": "80" 959 | } 960 | ] 961 | } 962 | } --------------------------------------------------------------------------------