har 0.0.1.dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +24 -0
  5. data/README.mdown +36 -0
  6. data/Rakefile +28 -0
  7. data/bin/har +4 -0
  8. data/har.gemspec +27 -0
  9. data/lib/har.rb +32 -0
  10. data/lib/har/archive.rb +135 -0
  11. data/lib/har/page.rb +19 -0
  12. data/lib/har/schema_type.rb +78 -0
  13. data/lib/har/schemas/browserType +19 -0
  14. data/lib/har/schemas/cacheEntryType +25 -0
  15. data/lib/har/schemas/cacheType +15 -0
  16. data/lib/har/schemas/contentType +26 -0
  17. data/lib/har/schemas/cookieType +32 -0
  18. data/lib/har/schemas/creatorType +18 -0
  19. data/lib/har/schemas/entryType +46 -0
  20. data/lib/har/schemas/logType +40 -0
  21. data/lib/har/schemas/pageTimingsType +17 -0
  22. data/lib/har/schemas/pageType +29 -0
  23. data/lib/har/schemas/postDataType +38 -0
  24. data/lib/har/schemas/recordType +17 -0
  25. data/lib/har/schemas/requestType +53 -0
  26. data/lib/har/schemas/responseType +51 -0
  27. data/lib/har/schemas/timingsType +43 -0
  28. data/lib/har/serializable.rb +22 -0
  29. data/lib/har/version.rb +3 -0
  30. data/lib/har/viewer.rb +99 -0
  31. data/lib/har/viewer/css/SyntaxHighlighter.css +1 -0
  32. data/lib/har/viewer/css/aboutTab.css +1 -0
  33. data/lib/har/viewer/css/domTab.css +1 -0
  34. data/lib/har/viewer/css/domTree.css +1 -0
  35. data/lib/har/viewer/css/harPreview.css +1 -0
  36. data/lib/har/viewer/css/harView.css +1 -0
  37. data/lib/har/viewer/css/harViewer.css +1 -0
  38. data/lib/har/viewer/css/homeTab.css +1 -0
  39. data/lib/har/viewer/css/images/ajax-loader.gif +0 -0
  40. data/lib/har/viewer/css/images/bg-button.gif +0 -0
  41. data/lib/har/viewer/css/images/blank.gif +0 -0
  42. data/lib/har/viewer/css/images/button-background.png +0 -0
  43. data/lib/har/viewer/css/images/checkmark.gif +0 -0
  44. data/lib/har/viewer/css/images/checkmark.png +0 -0
  45. data/lib/har/viewer/css/images/close-sprites.png +0 -0
  46. data/lib/har/viewer/css/images/contextMenuTarget.png +0 -0
  47. data/lib/har/viewer/css/images/contextMenuTargetHover.png +0 -0
  48. data/lib/har/viewer/css/images/download-sprites.png +0 -0
  49. data/lib/har/viewer/css/images/downloadButtons-aero.png +0 -0
  50. data/lib/har/viewer/css/images/group.gif +0 -0
  51. data/lib/har/viewer/css/images/loading_16.gif +0 -0
  52. data/lib/har/viewer/css/images/menu/Thumbs.db +0 -0
  53. data/lib/har/viewer/css/images/menu/shadowAlpha.png +0 -0
  54. data/lib/har/viewer/css/images/menu/tabMenuCheckbox.png +0 -0
  55. data/lib/har/viewer/css/images/menu/tabMenuPin.png +0 -0
  56. data/lib/har/viewer/css/images/menu/tabMenuRadio.png +0 -0
  57. data/lib/har/viewer/css/images/netBarBlocking.gif +0 -0
  58. data/lib/har/viewer/css/images/netBarBlocking2.gif +0 -0
  59. data/lib/har/viewer/css/images/netBarCached.gif +0 -0
  60. data/lib/har/viewer/css/images/netBarConnecting.gif +0 -0
  61. data/lib/har/viewer/css/images/netBarLoaded.gif +0 -0
  62. data/lib/har/viewer/css/images/netBarReceiving.gif +0 -0
  63. data/lib/har/viewer/css/images/netBarResolving.gif +0 -0
  64. data/lib/har/viewer/css/images/netBarResponded.gif +0 -0
  65. data/lib/har/viewer/css/images/netBarSending.gif +0 -0
  66. data/lib/har/viewer/css/images/netBarWaiting.gif +0 -0
  67. data/lib/har/viewer/css/images/page-timeline.png +0 -0
  68. data/lib/har/viewer/css/images/save.png +0 -0
  69. data/lib/har/viewer/css/images/spriteArrows.gif +0 -0
  70. data/lib/har/viewer/css/images/spriteArrows.png +0 -0
  71. data/lib/har/viewer/css/images/tabEnabled.png +0 -0
  72. data/lib/har/viewer/css/images/timeline-sprites.png +0 -0
  73. data/lib/har/viewer/css/images/tooltipConnectorUp.png +0 -0
  74. data/lib/har/viewer/css/images/twisty-sprites.png +0 -0
  75. data/lib/har/viewer/css/images/twistyClosed.png +0 -0
  76. data/lib/har/viewer/css/images/twistyOpen.png +0 -0
  77. data/lib/har/viewer/css/infoTip.css +1 -0
  78. data/lib/har/viewer/css/pageList.css +1 -0
  79. data/lib/har/viewer/css/pageStats.css +1 -0
  80. data/lib/har/viewer/css/pageTimeline.css +1 -0
  81. data/lib/har/viewer/css/popupMenu.css +1 -0
  82. data/lib/har/viewer/css/previewTab.css +1 -0
  83. data/lib/har/viewer/css/requestBody.css +1 -0
  84. data/lib/har/viewer/css/requestList.css +1 -0
  85. data/lib/har/viewer/css/schemaTab.css +1 -0
  86. data/lib/har/viewer/css/tabView.css +1 -0
  87. data/lib/har/viewer/css/toolTip.css +1 -0
  88. data/lib/har/viewer/css/toolbar.css +1 -0
  89. data/lib/har/viewer/css/xhrSpy.css +1 -0
  90. data/lib/har/viewer/index.html +13 -0
  91. data/lib/har/viewer/scripts/app.build.js +0 -0
  92. data/lib/har/viewer/scripts/core/cookies.js +2 -0
  93. data/lib/har/viewer/scripts/core/lib.js +23 -0
  94. data/lib/har/viewer/scripts/core/trace.js +26 -0
  95. data/lib/har/viewer/scripts/domplate/domTree.js +5 -0
  96. data/lib/har/viewer/scripts/domplate/domplate.js +263 -0
  97. data/lib/har/viewer/scripts/domplate/infoTip.js +4 -0
  98. data/lib/har/viewer/scripts/domplate/popupMenu.js +14 -0
  99. data/lib/har/viewer/scripts/domplate/tabView.js +6 -0
  100. data/lib/har/viewer/scripts/domplate/toolTip.js +3 -0
  101. data/lib/har/viewer/scripts/domplate/toolbar.js +3 -0
  102. data/lib/har/viewer/scripts/downloadify/LICENSE.txt +22 -0
  103. data/lib/har/viewer/scripts/downloadify/README.textile +89 -0
  104. data/lib/har/viewer/scripts/downloadify/js/downloadify.min.js +6 -0
  105. data/lib/har/viewer/scripts/downloadify/js/swfobject.js +18 -0
  106. data/lib/har/viewer/scripts/downloadify/media/downloadify.swf +0 -0
  107. data/lib/har/viewer/scripts/downloadify/src/Downloadify.as +162 -0
  108. data/lib/har/viewer/scripts/downloadify/src/downloadify.js +6 -0
  109. data/lib/har/viewer/scripts/excanvas/excanvas.js +21 -0
  110. data/lib/har/viewer/scripts/harPreview.js +149 -0
  111. data/lib/har/viewer/scripts/harViewer.js +244 -0
  112. data/lib/har/viewer/scripts/jquery-plugins/jquery.json.js +6 -0
  113. data/lib/har/viewer/scripts/nls/harViewer.js +1 -0
  114. data/lib/har/viewer/scripts/nls/homeTab.js +1 -0
  115. data/lib/har/viewer/scripts/nls/pageStats.js +2 -0
  116. data/lib/har/viewer/scripts/nls/pageTimeline.js +1 -0
  117. data/lib/har/viewer/scripts/nls/previewTab.js +1 -0
  118. data/lib/har/viewer/scripts/nls/requestBody.js +1 -0
  119. data/lib/har/viewer/scripts/nls/requestList.js +2 -0
  120. data/lib/har/viewer/scripts/preview/harModel.js +7 -0
  121. data/lib/har/viewer/scripts/preview/harSchema.js +308 -0
  122. data/lib/har/viewer/scripts/preview/jsonSchema.js +7 -0
  123. data/lib/har/viewer/scripts/preview/pageList.js +4 -0
  124. data/lib/har/viewer/scripts/preview/ref.js +5 -0
  125. data/lib/har/viewer/scripts/preview/requestBody.js +11 -0
  126. data/lib/har/viewer/scripts/preview/requestList.js +27 -0
  127. data/lib/har/viewer/scripts/requireplugins-jquery-1.4.2.js +204 -0
  128. data/lib/har/viewer/scripts/syntax-highlighter/README.txt +9 -0
  129. data/lib/har/viewer/scripts/syntax-highlighter/shCore.js +20 -0
  130. data/lib/har/viewer/scripts/tabs/aboutTab.html +93 -0
  131. data/lib/har/viewer/scripts/tabs/aboutTab.js +2 -0
  132. data/lib/har/viewer/scripts/tabs/domTab.js +1 -0
  133. data/lib/har/viewer/scripts/tabs/homeTab.html +34 -0
  134. data/lib/har/viewer/scripts/tabs/homeTab.js +5 -0
  135. data/lib/har/viewer/scripts/tabs/pageStats.js +14 -0
  136. data/lib/har/viewer/scripts/tabs/pageTimeline.js +10 -0
  137. data/lib/har/viewer/scripts/tabs/previewTab.js +6 -0
  138. data/lib/har/viewer/scripts/tabs/schemaTab.js +2 -0
  139. data/lib/har/viewer/scripts/xhr-spy/spy.js +2 -0
  140. data/lib/har/viewer/scripts/xhr-spy/spy.php +13 -0
  141. data/lib/har/viewer/scripts/xhr-spy/xhr.js +6 -0
  142. data/lib/har/viewer/scripts/xhr-spy/xhrSpyBookmarklet.js +1 -0
  143. data/schema.json +201 -0
  144. data/spec/fixtures/entry1.json +124 -0
  145. data/spec/fixtures/hars/bad.har +2563 -0
  146. data/spec/fixtures/hars/browser-blocking-time.har +2567 -0
  147. data/spec/fixtures/hars/google.com.har +612 -0
  148. data/spec/fixtures/hars/inline-scripts-block.har +1066 -0
  149. data/spec/fixtures/hars/softwareishard.com.har +5323 -0
  150. data/spec/har/archive_spec.rb +130 -0
  151. data/spec/har/entry_spec.rb +16 -0
  152. data/spec/har/page_spec.rb +24 -0
  153. data/spec/har/viewer_spec.rb +17 -0
  154. data/spec/spec_helper.rb +34 -0
  155. metadata +283 -0
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in har.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2010 Jari Bakken
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ harviewer by Jan Odvarko is covered by the New BSD License
23
+ http://www.opensource.org/licenses/bsd-license.php
24
+ http://code.google.com/p/harviewer/
@@ -0,0 +1,36 @@
1
+ HAR
2
+ ===
3
+
4
+ Ruby library to work with and view HTTP archives.
5
+ This includes [harviewer](http://code.google.com/p/harviewer/), which can be launched through the "har" command.
6
+
7
+ Usage
8
+ =============
9
+
10
+ HAR::Archive.from_string(json) #=> #<Har::Archive:0x5e7cac>
11
+ HAR::Archive.from_file(path) #=> #<Har::Archive:0x5e7cac>
12
+
13
+ Or from command the command line:
14
+
15
+ har /path/to/some.har
16
+
17
+ Links
18
+ =====
19
+
20
+ * [harviewer](http://code.google.com/p/harviewer/)
21
+
22
+ Note on Patches/Pull Requests
23
+ =============================
24
+
25
+ * Fork the project.
26
+ * Make your feature addition or bug fix.
27
+ * Add tests for it. This is important so I don't break it in a
28
+ future version unintentionally.
29
+ * Commit, do not mess with rakefile, version, or history.
30
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
31
+ * Send me a pull request. Bonus points for topic branches.
32
+
33
+ Copyright
34
+ =========
35
+
36
+ Copyright (c) 2010 Jari Bakken. See LICENSE for details.
@@ -0,0 +1,28 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new
6
+
7
+ desc 'Regenerate the schema files'
8
+ task :schema do
9
+ require "json"
10
+
11
+ out = File.expand_path("../lib/har/schemas", __FILE__)
12
+ schema = JSON.parse(File.read("schema.json"))
13
+
14
+ # cleanup
15
+ Dir[File.join(out, "*.json")].each { |f|
16
+ rm f
17
+ }
18
+
19
+ # generate
20
+ schema.each do |type, schema|
21
+ path = File.join(out, type)
22
+ puts path
23
+
24
+ File.open(path, "w") { |file|
25
+ file << JSON.pretty_generate(schema)
26
+ }
27
+ end
28
+ end
data/bin/har ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "har"
4
+ HAR::Viewer.new(ARGV).show
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "har/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "har"
7
+ s.version = HAR::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Jari Bakken"]
10
+ s.email = ["jari.bakken@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Ruby library to work with HTTP archives}
13
+ s.description = %q{Ruby library to work with HTTP archives}
14
+
15
+ s.rubyforge_project = "har"
16
+
17
+ s.add_dependency "json"
18
+ s.add_dependency "json-schema"
19
+ s.add_dependency "launchy"
20
+
21
+ s.add_development_dependency "rspec", "~> 2.0"
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
@@ -0,0 +1,32 @@
1
+ require 'json'
2
+ require 'json-schema'
3
+ require 'time'
4
+
5
+ module HAR
6
+ class Error < StandardError; end
7
+ class ValidationError < StandardError; end
8
+ end
9
+
10
+ require 'har/serializable'
11
+ require 'har/schema_type'
12
+ require 'har/page'
13
+ require 'har/archive'
14
+ require 'har/viewer'
15
+
16
+ module Enumerable
17
+ def group_by
18
+ assoc = {}
19
+
20
+ each do |element|
21
+ key = yield(element)
22
+
23
+ if assoc.has_key?(key)
24
+ assoc[key] << element
25
+ else
26
+ assoc[key] = [element]
27
+ end
28
+ end
29
+
30
+ assoc
31
+ end unless [].respond_to?(:group_by)
32
+ end
@@ -0,0 +1,135 @@
1
+ module HAR
2
+ class Archive
3
+ include Serializable
4
+
5
+ def self.from_string(str, uri = nil)
6
+ new JSON.parse(str), uri
7
+ end
8
+
9
+ def self.from_file(path)
10
+ from_string File.read(path), path
11
+ end
12
+
13
+ attr_reader :uri
14
+
15
+ def initialize(input, uri = nil)
16
+ @data = input
17
+ @uri = uri
18
+ end
19
+
20
+ def pages
21
+ @pages ||= raw_log.fetch('pages').map { |page|
22
+ Page.new page, entries_for(page['id'])
23
+ }
24
+ end
25
+
26
+ def entries
27
+ @entries ||= raw_entries.map { |e| Entry.new(e) }
28
+ end
29
+
30
+ # create a new archive by merging this and another archive
31
+
32
+ def merge(other)
33
+ assert_archive other
34
+
35
+ data = deep_clone(@data)
36
+ merge_data data, other.as_json, other.uri
37
+
38
+ self.class.new data
39
+ end
40
+
41
+ # destructively merge this with the given archive
42
+
43
+ def merge!(other)
44
+ assert_archive other
45
+ clear_caches
46
+
47
+ merge_data @data, other.as_json, other.uri
48
+ nil
49
+ end
50
+
51
+ def save_to(path)
52
+ File.open(path, "w") { |io| io << @data.to_json }
53
+ end
54
+
55
+ def valid?
56
+ JSON::Validator.validate schema_file, @data
57
+ end
58
+
59
+ def validate!
60
+ JSON::Validator.validate2 schema_file, @data
61
+ rescue JSON::ValidationError => ex
62
+ # add archive uri to the message
63
+ if @uri
64
+ raise ValidationError, "#{@uri}: #{ex.message}"
65
+ else
66
+ raise ValidationError, ex.message
67
+ end
68
+ end
69
+
70
+ protected
71
+
72
+ private
73
+
74
+ def schema_file
75
+ @schema_file ||= File.expand_path("../schemas/logType", __FILE__)
76
+ end
77
+
78
+ def merge_data(left, right, uri)
79
+ log = left.fetch('log')
80
+ other_log = right.fetch('log')
81
+
82
+ pages = log.fetch('pages')
83
+ entries = log.fetch('entries')
84
+
85
+ other_pages = other_log.fetch("pages")
86
+ other_entries = other_log.fetch("entries")
87
+
88
+ if uri
89
+ deep_clone(other_pages).each do |page|
90
+ c = page['comment'] ||= ''
91
+ c << "(merged from #{File.basename uri})"
92
+
93
+ pages << page
94
+ end
95
+ else
96
+ pages.concat other_pages
97
+ end
98
+
99
+ entries.concat other_entries
100
+ end
101
+
102
+ def assert_archive(other)
103
+ unless other.kind_of?(self.class)
104
+ raise TypeError, "expected #{self.class}"
105
+ end
106
+ end
107
+
108
+ def clear_caches
109
+ @raw_entries = @entries_map = @pages = @entries = @log = nil
110
+ end
111
+
112
+ def entries_for(page_id)
113
+ entries = entries_map[page_id] || []
114
+ entries.map { |e| Entry.new(e) }
115
+ end
116
+
117
+ def raw_entries
118
+ @raw_entries ||= raw_log.fetch('entries')
119
+ end
120
+
121
+ def raw_log
122
+ @raw_log ||= @data.fetch 'log'
123
+ end
124
+
125
+ def entries_map
126
+ @entries_map ||= raw_entries.group_by { |e| e['pageref'] }
127
+ end
128
+
129
+ def deep_clone(obj)
130
+ Marshal.load Marshal.dump(obj)
131
+ end
132
+
133
+ end # Archive
134
+ end # HAR
135
+
@@ -0,0 +1,19 @@
1
+ module HAR
2
+ class Page < SchemaType
3
+
4
+ attr_reader :entries
5
+
6
+ def initialize(input, entries)
7
+ super(input)
8
+ @entries = entries
9
+ end
10
+
11
+ def title
12
+ @data.fetch 'title'
13
+ end
14
+
15
+ # a little sugar
16
+ alias_method :timings, :page_timings
17
+
18
+ end
19
+ end
@@ -0,0 +1,78 @@
1
+ require "pp"
2
+
3
+ module HAR
4
+ class SchemaType
5
+ include Serializable
6
+
7
+ class << self
8
+ private
9
+
10
+ def load_from(file)
11
+ schema = JSON.parse(File.read(file))
12
+
13
+ klass = Class.new(self) do
14
+ schema['properties'].each do |name, definition|
15
+ define_property name, definition
16
+ end
17
+ end
18
+
19
+ HAR.const_set type_name_from(schema['id']), klass
20
+ end
21
+
22
+ def define_property(name, definition)
23
+ required = definition['required']
24
+ type = definition['type']
25
+
26
+ wrapper = extract_wrapper(name, definition)
27
+
28
+ fetch_code = required ? "@data.fetch(#{name.inspect})" : "@data[#{name.inspect}]"
29
+ fetch_code = "#{wrapper} #{fetch_code}" if wrapper
30
+
31
+ class_eval "
32
+ def #{snake_case name}
33
+ @#{name} ||= #{fetch_code}
34
+ end
35
+ "
36
+ end
37
+
38
+ def extract_wrapper(name, definition)
39
+ type = definition['type']
40
+
41
+ case type
42
+ when 'object'
43
+ return "#{name.capitalize}.new"
44
+ when 'string'
45
+ if definition['format'] == 'date-time'
46
+ return 'Time.parse'
47
+ end
48
+ when Hash
49
+ ref = type['$ref']
50
+ when nil
51
+ ref = definition['$ref']
52
+ end
53
+
54
+ "#{type_name_from(ref)}.new" if ref
55
+ end
56
+
57
+ def type_name_from(str)
58
+ t = str[/^(.+)Type/, 1] or raise Error, "could not extract type name from #{str.inspect}"
59
+ t[0] = t[0].chr.upcase
60
+
61
+ t
62
+ end
63
+
64
+ def snake_case(str)
65
+ str.gsub(/\B[A-Z][^A-Z]/, '_\&').downcase.gsub(' ', '_')
66
+ end
67
+ end
68
+
69
+ def initialize(data)
70
+ @data = data
71
+ end
72
+
73
+ # load in schemas
74
+ Dir[File.expand_path("../schemas/*", __FILE__)].each do |file|
75
+ load_from file
76
+ end
77
+ end # SchemaType
78
+ end # HAR
@@ -0,0 +1,19 @@
1
+ {
2
+ "id": "browserType",
3
+ "description": "Name and version info of used browser.",
4
+ "type": "object",
5
+ "optional": true,
6
+ "properties": {
7
+ "name": {
8
+ "type": "string",
9
+ "required": true
10
+ },
11
+ "version": {
12
+ "type": "string",
13
+ "required": true
14
+ },
15
+ "comment": {
16
+ "type": "string"
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "id": "cacheEntryType",
3
+ "optional": true,
4
+ "description": "Info about cache entry.",
5
+ "properties": {
6
+ "expires": {
7
+ "type": "string"
8
+ },
9
+ "lastAccess": {
10
+ "type": "string",
11
+ "required": true
12
+ },
13
+ "eTag": {
14
+ "type": "string",
15
+ "required": true
16
+ },
17
+ "hitCount": {
18
+ "type": "integer",
19
+ "required": true
20
+ },
21
+ "comment": {
22
+ "type": "string"
23
+ }
24
+ }
25
+ }