-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathparser.go
More file actions
373 lines (323 loc) · 10.4 KB
/
Copy pathparser.go
File metadata and controls
373 lines (323 loc) · 10.4 KB
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
package har
import (
"encoding/json"
"fmt"
"net/url"
"os"
"strings"
)
// ParseHarWithOptions 解析HAR格式的字节数据,使用自定义解析选项
func ParseHarWithOptions(harFileBytes []byte, options ParseOptions) (*Har, error) {
if len(harFileBytes) == 0 {
return nil, NewInvalidFormatError("输入为空")
}
// 检查文件是否是JSON格式
if !isJSONContent(harFileBytes) {
return nil, NewInvalidFormatError("输入不是有效的JSON格式")
}
// 如果是严格模式,直接解析
if !options.Lenient {
har := new(Har)
err := json.Unmarshal(harFileBytes, har)
if err != nil {
return nil, WrapJSONUnmarshalError(err)
}
// 如果需要验证
if !options.SkipValidation {
if err := validateHar(har); err != nil {
return nil, err
}
}
return har, nil
}
// 宽松模式(Lenient):尝试解析尽可能多的内容
return parseLenient(harFileBytes, options)
}
// ParseHarFileWithOptions 解析HAR格式的文件,使用自定义解析选项
func ParseHarFileWithOptions(harFilePath string, options ParseOptions) (*Har, error) {
harFileBytes, err := os.ReadFile(harFilePath)
if err != nil {
return nil, NewFileSystemError(fmt.Sprintf("无法读取文件 '%s'", harFilePath), err)
}
har, err := ParseHarWithOptions(harFileBytes, options)
if err != nil {
harErr, ok := err.(*HarError)
if ok {
_ = harErr.WithMetadata("filePath", harFilePath)
}
return nil, err
}
return har, nil
}
// ParseHarEnhanced 增强版HAR解析,提供详细错误信息
func ParseHarEnhanced(harFileBytes []byte) (*Har, *HarError) {
har, err := ParseHarWithOptions(harFileBytes, DefaultParseOptions())
if err != nil {
// ParseHarWithOptions 的所有错误路径均返回 *HarError
return nil, err.(*HarError)
}
return har, nil
}
// ParseHarFileEnhanced 增强版HAR文件解析,提供详细错误信息
func ParseHarFileEnhanced(harFilePath string) (*Har, *HarError) {
har, err := ParseHarFileWithOptions(harFilePath, DefaultParseOptions())
if err != nil {
// ParseHarFileWithOptions 的所有错误路径均返回 *HarError
return nil, err.(*HarError)
}
return har, nil
}
// ParseHarLenient 宽松模式解析HAR文件内容
func ParseHarLenient(harFileBytes []byte) (*Har, error) {
options := DefaultParseOptions()
options.Lenient = true
options.CollectWarnings = true
return ParseHarWithOptions(harFileBytes, options)
}
// ParseHarFileLenient 宽松模式解析HAR文件
func ParseHarFileLenient(harFilePath string) (*Har, error) {
options := DefaultParseOptions()
options.Lenient = true
options.CollectWarnings = true
return ParseHarFileWithOptions(harFilePath, options)
}
// isJSONContent 检查内容是否是JSON格式
func isJSONContent(content []byte) bool {
trimmed := strings.TrimSpace(string(content))
return (strings.HasPrefix(trimmed, "{") && strings.HasSuffix(trimmed, "}")) ||
(strings.HasPrefix(trimmed, "[") && strings.HasSuffix(trimmed, "]"))
}
// validateHar 验证HAR对象内容的有效性
// 该函数现在转发到validator.go中实现的ValidateHarFile
func validateHar(har *Har) error {
if har == nil {
return NewInvalidFormatError("HAR对象为空")
}
return ValidateHarFile(har)
}
// parseLenient 宽松模式解析,尝试解析尽可能多的内容
func parseLenient(harFileBytes []byte, options ParseOptions) (*Har, error) {
// 创建一个空的HAR对象
har := &Har{
Log: Log{
Entries: []Entries{},
Pages: []Pages{},
},
}
// 使用map来进行初步解析,这样即使部分字段无效也能解析其他部分
var rawData map[string]json.RawMessage
if err := json.Unmarshal(harFileBytes, &rawData); err != nil {
return nil, WrapJSONUnmarshalError(err)
}
// 跟踪所有错误
rootError := &HarError{
Code: ErrCodeJSONParse,
Message: "HAR解析过程中发生错误,但部分内容已成功解析",
}
// 解析log字段
if logBytes, ok := rawData["log"]; ok {
var logData map[string]json.RawMessage
if err := json.Unmarshal(logBytes, &logData); err != nil {
_ = rootError.AddPartialError(
NewJSONParseError("无法解析log字段", err).WithField("log"))
} else {
// 解析version字段
if versionBytes, ok := logData["version"]; ok {
var version string
if err := json.Unmarshal(versionBytes, &version); err == nil {
har.Log.Version = version
} else {
_ = rootError.AddPartialError(
NewJSONParseError("无法解析version字段", err).WithField("log.version"))
}
}
// 解析creator字段
if creatorBytes, ok := logData["creator"]; ok {
var creator Creator
if err := json.Unmarshal(creatorBytes, &creator); err == nil {
har.Log.Creator = creator
} else {
_ = rootError.AddPartialError(
NewJSONParseError("无法解析creator字段", err).WithField("log.creator"))
}
}
// 解析pages字段
if pagesBytes, ok := logData["pages"]; ok {
var pages []json.RawMessage
if err := json.Unmarshal(pagesBytes, &pages); err == nil {
for i, pageBytes := range pages {
var page Pages
if err := json.Unmarshal(pageBytes, &page); err == nil {
har.Log.Pages = append(har.Log.Pages, page)
} else {
_ = rootError.AddPartialError(
NewJSONParseError(
fmt.Sprintf("无法解析第%d个page", i+1), err).
WithField(fmt.Sprintf("log.pages[%d]", i)))
}
}
} else {
_ = rootError.AddPartialError(
NewJSONParseError("无法解析pages字段", err).WithField("log.pages"))
}
}
// 解析entries字段,这是最重要的部分
if entriesBytes, ok := logData["entries"]; ok {
var entries []json.RawMessage
if err := json.Unmarshal(entriesBytes, &entries); err == nil {
for i, entryBytes := range entries {
var entry Entries
if err := json.Unmarshal(entryBytes, &entry); err == nil {
har.Log.Entries = append(har.Log.Entries, entry)
} else {
_ = rootError.AddPartialError(
NewJSONParseError(
fmt.Sprintf("无法解析第%d个entry", i+1), err).
WithField(fmt.Sprintf("log.entries[%d]", i)))
}
}
} else {
_ = rootError.AddPartialError(
NewJSONParseError("无法解析entries字段", err).WithField("log.entries"))
}
}
}
} else {
_ = rootError.AddPartialError(NewMissingFieldError("log"))
}
// 如果有错误,并且选项指定收集警告
if rootError.HasPartialErrors() && options.CollectWarnings {
// 如果解析了部分内容,返回HAR对象和错误
if har.Log.Version != "" || len(har.Log.Entries) > 0 || len(har.Log.Pages) > 0 {
return har, rootError
}
// 否则认为解析完全失败
return nil, rootError
} else if rootError.HasPartialErrors() {
// 如果有错误但不收集警告,只返回错误
return nil, rootError
}
return har, nil
}
// Result 解析结果,包含HAR对象和可能的警告
type Result struct {
Har *Har
Warnings []*HarError
}
// ParseHarWithWarnings 解析HAR文件同时返回警告信息
// 该函数使用宽松模式解析,并收集所有警告而不是直接失败
func ParseHarWithWarnings(harFileBytes []byte) (*Result, error) {
// 使用宽松模式和警告收集
options := DefaultParseOptions()
options.Lenient = true
options.CollectWarnings = true
// 解析HAR数据
har, err := ParseHarWithOptions(harFileBytes, options)
// 初始化结果对象
result := &Result{
Har: har,
Warnings: []*HarError{},
}
// 处理解析阶段的警告
if err != nil {
if harErr, ok := err.(*HarError); ok && har != nil {
// 在宽松模式下,将解析错误转换为警告
result.Warnings = appendWarnings(result.Warnings, harErr.GetPartialErrors())
} else {
// 解析完全失败的情况
return nil, err
}
}
// 执行URL验证
urlWarnings := validateURLs(har)
if len(urlWarnings) > 0 {
result.Warnings = appendWarnings(result.Warnings, urlWarnings)
}
// 如果仍未找到警告,尝试运行完整验证
if len(result.Warnings) == 0 {
validationWarnings := performFullValidation(har)
result.Warnings = appendWarnings(result.Warnings, validationWarnings)
}
return result, nil
}
// validateURLs 验证所有条目中的URL字段
func validateURLs(har *Har) []*HarError {
if har == nil || len(har.Log.Entries) == 0 {
return nil
}
var warnings []*HarError
for i, entry := range har.Log.Entries {
if entry.Request.URL == "" {
continue
}
// 严格URL验证
if _, err := url.Parse(entry.Request.URL); err != nil {
urlError := NewValidationError(
fmt.Sprintf("无效的URL格式: %s", err.Error()),
fmt.Sprintf("log.entries[%d].request.url", i),
)
warnings = append(warnings, urlError)
continue
}
// 额外检查常见URL问题
if strings.Contains(entry.Request.URL, " ") {
urlError := NewValidationError(
fmt.Sprintf("URL包含空格: %s", entry.Request.URL),
fmt.Sprintf("log.entries[%d].request.url", i),
)
warnings = append(warnings, urlError)
}
if !strings.Contains(entry.Request.URL, "://") {
urlError := NewValidationError(
fmt.Sprintf("URL缺少协议: %s", entry.Request.URL),
fmt.Sprintf("log.entries[%d].request.url", i),
)
warnings = append(warnings, urlError)
}
}
return warnings
}
// performFullValidation 执行完整的HAR验证,并将错误转换为警告
func performFullValidation(har *Har) []*HarError {
if har == nil {
return nil
}
validationErr := ValidateHarFile(har)
if validationErr == nil {
return nil
}
// ValidateHarFile 的所有错误路径均返回 *HarError
return validationErr.(*HarError).GetPartialErrors()
}
// appendWarnings 将新警告追加到现有警告列表,避免重复
func appendWarnings(existing []*HarError, newWarnings []*HarError) []*HarError {
if len(newWarnings) == 0 {
return existing
}
if existing == nil {
return newWarnings
}
// 使用映射检测重复
warningMap := make(map[string]bool)
for _, warn := range existing {
key := warn.Field + ":" + warn.Message
warningMap[key] = true
}
// 添加非重复的警告
for _, warn := range newWarnings {
key := warn.Field + ":" + warn.Message
if !warningMap[key] {
existing = append(existing, warn)
warningMap[key] = true
}
}
return existing
}
// ParseHarFileWithWarnings 解析HAR文件同时返回警告信息
func ParseHarFileWithWarnings(harFilePath string) (*Result, error) {
harFileBytes, err := os.ReadFile(harFilePath)
if err != nil {
return nil, NewFileSystemError(fmt.Sprintf("无法读取文件 '%s'", harFilePath), err)
}
return ParseHarWithWarnings(harFileBytes)
}