-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathschema.go
476 lines (445 loc) · 18.6 KB
/
schema.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
// MIT License
//
// Copyright (c) 2024 chaunsin
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
package go_har
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"unicode/utf8"
)
// Har HTTP Archive (HAR) format
// https://w3c.github.io/web-performance/specs/HAR/Overview.html
// This specification defines an archival format for HTTP transactions that can be
// used by a web browser to export detailed performance data about web pages it loads.
type Har struct {
Log *Log `json:"log"`
}
func (h *Har) Validate() error {
return nil
}
// Log This object represents the root of the exported data.
// This object MUST be present and its name MUST be "log".
// The object contains the following name/value pairs:
type Log struct {
// Required. Version number of the format.
Version string `json:"version"`
// Required. An object of type creator that contains the name and version
// information of the log creator application.
Creator *Creator `json:"creator"`
// [optional]. An object of type browser that contains the name and version
// information of the user agent.
Browser *Browser `json:"browser,omitempty"`
// [optional]. An array of objects of type page, each representing one exported
// (tracked) page. Leave out this field if the application does not support
// grouping by pages.
Pages []*Page `json:"pages,omitempty"`
// Required. An array of objects of type entry, each representing one
// exported (tracked) HTTP request.
Entries []*Entry `json:"entries"`
// [optional]. A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}
// Creator This object contains information about the log creator application and contains the following name/value pairs:
type Creator struct {
// Required. The name of the application that created the log.
Name string `json:"name"`
// Required. The version number of the application that created the log.
Version string `json:"version"`
// [optional]. A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}
// Browser This object contains information about the browser that
// created the log and contains the following name/value pairs:
type Browser struct {
// Required. The name of the browser that created the log.
Name string `json:"name"`
// Required. The version number of the browser that created the log.
Version string `json:"version"`
// [optional]. A comment provided by the user or the browser.
Comment string `json:"comment,omitempty"`
}
// Page There is one <page> object for every exported web page and
// one <entry> object for every HTTP request. In case when an HTTP
// trace tool isn't able to group requests by a page, the <pages>
// object is empty and individual requests don't have a parent page.
type Page struct {
// Date and time stamp for the beginning of the page load
// (ISO 8601 YYYY-MM-DDThh:mm:ss.sTZD, e.g., 2009-07-24T19:20:30.45+01:00).
StartedDateTime string `json:"startedDateTime"`
// Unique identifier of a page within the . Entries use it to refer the parent page.
ID string `json:"id"`
// Page title.
Title string `json:"title"`
// Detailed timing info about a page load.
PageTimings *PageTimings `json:"pageTimings"`
// (new in 1.2) A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}
// PageTimings describes timings for various events (states) fired during the page load.
// All times are specified in milliseconds. If time info is not available, the appropriate field is set to -1.
type PageTimings struct {
// Content of the page loaded. Number of milliseconds since a page load started
// (page.startedDateTime). Use -1 if the timing does not apply to the current
// request.
// Depending on the browser, onContentLoad property represents DOMContentLoad
// event or document.readyState == interactive.
OnContentLoad float64 `json:"onContentLoad"`
// Page is loaded (onLoad event fired). Number of milliseconds since a page
// load started (page.startedDateTime). Use -1 if the timing does not apply
// to the current request.
OnLoad float64 `json:"onLoad"`
// (new in 1.2) A comment provided by the user or the application.
Comment string `json:"comment"`
}
// Entry This object represents an array with all exported HTTP requests.
// Sorting entries by startedDateTime (starting from the oldest) is
// the preferred way how to export data since it can make importing faster.
// However, the reader application should always make sure the array is sorted (if required for the import)
type Entry struct {
// [string, unique, optional] Reference to the parent page.
// Leave out this field if the application does not support grouping by pages.
PageRef string `json:"pageref,omitempty"`
// Date and time stamp of the request start (ISO 8601 YYYY-MM-DDThh:mm:ss.sTZD).
StartedDateTime string `json:"startedDateTime"`
// Total elapsed time of the request in milliseconds. This is the sum of all
// timings available in the timing object (i.e., not including -1 values).
Time float64 `json:"time"`
// Detailed info about the request.
Request *Request `json:"request"`
// Detailed info about the response.
Response *Response `json:"response"`
// Info about cache usage.
Cache *Cache `json:"cache"`
// Detailed timing info about a request/response round trip.
Timings *Timings `json:"timings"`
// [optional] (new in 1.2) IP address of the server that was connected
// (result of DNS resolution).
ServerIPAddress string `json:"serverIPAddress,omitempty"`
// [optional] (new in 1.2) Unique ID of the parent TCP/IP connection, can be
// the client port number. Note that a port number doesn't have to be a unique
// identifier in cases where the port is shared for more connections. If the
// port isn't available for the application, any other unique connection ID
// can be used instead (e.g., connection index). Leave out this field if the
// application doesn't support this info.
Connection string `json:"connection,omitempty"`
// (new in 1.2) A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}
// Request contains detailed info about performed request.
type Request struct {
// Request method (GET, POST, ...).
Method string `json:"method"`
// Absolute URL of the request (fragments are not included).
URL string `json:"url"`
// Request HTTP Version.
HTTPVersion string `json:"httpVersion"`
// List of cookie objects.
Cookies []*Cookie `json:"cookies"`
// List of header objects.
Headers []*NVP `json:"headers"`
// List of query parameter objects.
QueryString []*NVP `json:"queryString"`
// Posted data info.
PostData *PostData `json:"postData"`
// Total number of bytes from the start of the HTTP request message until
// (and including) the double CRLF before the body. Set to -1 if the info
// is not available.
HeaderSize int64 `json:"headerSize"`
// Size of the request body (POST data payload) in bytes. Set to -1 if the
// info is not available.
BodySize int64 `json:"bodySize"`
// (new in 1.2) A comment provided by the user or the application.
Comment string `json:"comment"`
}
// Response contains detailed info about the response.
// The total response size received can be computed as follows (if both values are available):
// var totalSize = entry.response.headersSize + entry.response.bodySize;
type Response struct {
// Response status.
Status int `json:"status"`
// Response status description.
StatusText string `json:"statusText"`
// Response HTTP Version.
HTTPVersion string `json:"httpVersion"`
// List of cookie objects.
Cookies []*Cookie `json:"cookies"`
// List of header objects.
Headers []*NVP `json:"headers"`
// Details about the response body.
Content *Content `json:"content"`
// Redirection target URL from the Location response header.
RedirectURL string `json:"redirectURL"`
// Total number of bytes from the start of the HTTP response message until
// (and including) the double CRLF before the body. Set to -1 if the info is
// not available.
// The size of received response-headers is computed only from headers that
// are really received from the server. Additional headers appended by the
// browser are not included in this number, but they appear in the list of
// header objects.
HeadersSize int64 `json:"headersSize"`
// Size of the received response body in bytes. Set to zero in case of
// responses coming from the cache (304). Set to -1 if the info is not
// available.
BodySize int64 `json:"bodySize"`
// [optional] (new in 1.2) A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}
// Cookie contains list of all cookies (used in <request> and <response> objects).
type Cookie struct {
// The name of the cookie.
Name string `json:"name"`
// The cookie value.
Value string `json:"value"`
// [optional] The path pertaining to the cookie.
Path string `json:"path,omitempty"`
// [optional] The host of the cookie.
Domain string `json:"domain,omitempty"`
// [optional] Cookie expiration time.
// (ISO 8601 YYYY-MM-DDThh:mm:ss.sTZD, e.g., 2009-07-24T19:20:30.123+02:00).
Expires string `json:"expires,omitempty"`
// [optional] Set to true if the cookie is HTTP only, false otherwise.
HTTPOnly bool `json:"httpOnly,omitempty"`
// [optional] (new in 1.2) True if the cookie was transmitted over ssl, false otherwise.
Secure bool `json:"secure,omitempty"`
// [optional] (new in 1.2) A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}
// NVP is simply a name/value pair with a comment
type NVP struct {
Name string `json:"name"`
Value string `json:"value"`
Comment string `json:"comment,omitempty"`
}
// PostData describes posted data, if any (embedded in <request> object).
type PostData struct {
// Mime type of posted data.
MimeType string `json:"mimeType"`
// List of posted parameters (in case of URL encoded parameters).
Params []*PostParam `json:"params"`
// Plain text posted data
Text string `json:"text"`
// [optional] (new in 1.2) A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}
// pdBinary is the JSON representation of binary PostData.
type pdBinary struct {
MimeType string `json:"mimeType"`
// Params is a list of posted parameters (in case of URL encoded parameters).
Params []*PostParam `json:"params"`
Text []byte `json:"text"`
Encoding string `json:"encoding"`
}
// MarshalJSON returns a JSON representation of binary PostData.
func (p *PostData) MarshalJSON() ([]byte, error) {
if utf8.ValidString(p.Text) {
// avoid infinite recursion
type noMethod PostData
return json.Marshal((*noMethod)(p))
}
return json.Marshal(pdBinary{
MimeType: p.MimeType,
Params: p.Params,
Text: []byte(p.Text),
Encoding: "base64",
})
}
// UnmarshalJSON populates PostData based on the []byte representation of
// the binary PostData.
func (p *PostData) UnmarshalJSON(data []byte) error {
// conform to json.Unmarshaler spec
if bytes.Equal(data, []byte("null")) {
return nil
}
var enc struct {
Encoding string `json:"encoding"`
}
if err := json.Unmarshal(data, &enc); err != nil {
return err
}
if enc.Encoding != "base64" {
// avoid infinite recursion
type noMethod PostData
return json.Unmarshal(data, (*noMethod)(p))
}
var pb pdBinary
if err := json.Unmarshal(data, &pb); err != nil {
return err
}
p.MimeType = pb.MimeType
p.Params = pb.Params
p.Text = string(pb.Text)
return nil
}
// PostParam is a list of posted parameters, if any (embedded in <postData> object).
type PostParam struct {
// name of a posted parameter.
Name string `json:"name"`
// [optional] value of a posted parameter or content of a posted file.
Value string `json:"value,omitempty"`
// [optional] name of a posted file.
FileName string `json:"fileName,omitempty"`
// [optional] content type of posted file.
ContentType string `json:"contentType,omitempty"`
// [optional] (new in 1.2) A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}
// Content describes details about response content (embedded in <response> object).
type Content struct {
// Length of the returned content in bytes. Should be equal to
// response.bodySize if there is no compression and bigger when the content
// has been compressed.
Size int64 `json:"size"`
// [optional] Number of bytes saved. Leave out this field if the information
// is not available.
Compression int64 `json:"compression,omitempty"`
// MIME type of the response text (value of the Content-Type response
// header). The charset attribute of the MIME type is included (if
// available).
MimeType string `json:"mimeType"`
// [optional] Response body sent from the server or loaded from the browser
// cache. This field is populated with textual content only. The text field
// is either HTTP decoded text or an encoded (e.g. "base64") representation of
// the response body. Leave out this field if the information is not
// available.
Text []byte `json:"text,omitempty"`
// [optional] (new in 1.2) Encoding used for response text field e.g.
// "base64". Leave out this field if the text field is HTTP decoded
// (decompressed & unchunked), then trans-coded from its original character
// set into UTF-8.
Encoding string `json:"encoding,omitempty"`
// [optional] (new in 1.2) A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}
// For marshaling Content to and from json. This works around the json library's
// default conversion of []byte to base64 encoded string.
type contentJSON struct {
Size int64 `json:"size"`
MimeType string `json:"mimeType"`
// Text contains the response body sent from the server or loaded from the
// browser cache. This field is populated with textual content only. The text
// field is either HTTP decoded text or an encoded (e.g. "base64")
// representation of the response body. Leave out this field if the
// information is not available.
Text string `json:"text,omitempty"`
// Encoding used for response text field e.g "base64". Leave out this field
// if the text field is HTTP decoded (decompressed & unchunked), than
// trans-coded from its original character set into UTF-8.
Encoding string `json:"encoding,omitempty"`
}
// MarshalJSON marshals the byte slice into json after encoding based on c.Encoding.
func (c *Content) MarshalJSON() ([]byte, error) {
var txt string
switch c.Encoding {
case "base64":
txt = base64.StdEncoding.EncodeToString(c.Text)
case "":
txt = string(c.Text)
default:
return nil, fmt.Errorf("unsupported encoding for Content.Text: %s", c.Encoding)
}
cj := contentJSON{
Size: c.Size,
MimeType: c.MimeType,
Text: txt,
Encoding: c.Encoding,
}
return json.Marshal(cj)
}
// UnmarshalJSON unmarshal the bytes slice into Content.
func (c *Content) UnmarshalJSON(data []byte) error {
var cj contentJSON
if err := json.Unmarshal(data, &cj); err != nil {
return err
}
var (
txt []byte
err error
)
switch cj.Encoding {
case "base64":
txt, err = base64.StdEncoding.DecodeString(cj.Text)
if err != nil {
return fmt.Errorf("failed to decode base64-encoded Content.Text: %v", err)
}
case "":
txt = []byte(cj.Text)
default:
return fmt.Errorf("unsupported encoding for Content.Text: %s", cj.Encoding)
}
c.Size = cj.Size
c.MimeType = cj.MimeType
c.Text = txt
c.Encoding = cj.Encoding
return nil
}
// Cache contains info about a request coming from browser cache.
type Cache struct {
// [optional] State of a cache entry before the request. Leave out this field
// if the information is not available.
BeforeRequest *CacheObject `json:"beforeRequest,omitempty"`
// [optional] State of a cache entry after the request. Leave out this field if
// the information is not available.
AfterRequest *CacheObject `json:"afterRequest,omitempty"`
// [optional] (new in 1.2) A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}
// CacheObject is used by both beforeRequest and afterRequest
type CacheObject struct {
// [optional] - Expiration time of the cache entry.
Expires string `json:"expires,omitempty"`
// The last time the cache entry was opened.
LastAccess string `json:"lastAccess"`
// Etag
ETag string `json:"eTag"`
// The number of times the cache entry has been opened.
HitCount int64 `json:"hitCount"`
// [optional] (new in 1.2) A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}
// Timings describe various phases within a request-response round trip.
// All times are specified in milliseconds.
type Timings struct {
// [optional] Time spent in a queue waiting for a network connection. Use -1
// if the timing does not apply to the current request.
Blocked float64 `json:"blocked,omitempty"`
// [optional] - DNS resolution time. The time required to resolve a host name.
// Use -1 if the timing does not apply to the current request.
DNS float64 `json:"dns,omitempty"`
// [optional] - Time required to create TCP connection. Use -1 if the timing
// does not apply to the current request.
Connect float64 `json:"connect,omitempty"`
// Time required to send an HTTP request to the server.
Send float64 `json:"send"`
// Waiting for a response from the server.
Wait float64 `json:"wait"`
// Time required to read the entire response from the server (or cache).
Receive float64 `json:"receive"`
// [optional] (new in 1.2) - Time required for SSL/TLS negotiation. If this
// field is defined, then the time is also included in the connected field (to
// ensure backward compatibility with HAR 1.1). Use -1 if the timing does not
// apply to the current request.
Ssl float64 `json:"ssl,omitempty"`
// [optional] (new in 1.2) - A comment provided by the user or the application.
Comment string `json:"comment,omitempty"`
}