1
/*
2
 * Hurl (https://hurl.dev)
3
 * Copyright (C) 2024 Orange
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *          http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
use crate::ast::VersionValue::VersionAny;
19
use crate::ast::*;
20
use crate::combinator::{optional, zero_or_more};
21
use crate::parser::bytes::*;
22
use crate::parser::error::*;
23
use crate::parser::number::natural;
24
use crate::parser::primitives::*;
25
use crate::parser::sections::*;
26
use crate::parser::ParseResult;
27
use crate::reader::Reader;
28

            
29
use super::string::unquoted_template;
30

            
31
3975
pub fn hurl_file(reader: &mut Reader) -> ParseResult<HurlFile> {
32
3975
    let entries = zero_or_more(entry, reader)?;
33
3755
    let line_terminators = optional_line_terminators(reader)?;
34
3755
    eof(reader)?;
35
3755
    Ok(HurlFile {
36
3755
        entries,
37
3755
        line_terminators,
38
3755
    })
39
}
40

            
41
15125
fn entry(reader: &mut Reader) -> ParseResult<Entry> {
42
15125
    let req = request(reader)?;
43
14005
    let resp = optional(response, reader)?;
44
13940
    Ok(Entry {
45
13940
        request: req,
46
13940
        response: resp,
47
13940
    })
48
}
49

            
50
15125
fn request(reader: &mut Reader) -> ParseResult<Request> {
51
15125
    let start = reader.cursor();
52
15125
    let line_terminators = optional_line_terminators(reader)?;
53
15125
    let space0 = zero_or_more_spaces(reader)?;
54
15125
    let m = method(reader)?;
55
14140
    let space1 = one_or_more_spaces(reader)?;
56
14130
    let url = unquoted_template(reader)?;
57
14125
    let line_terminator0 = line_terminator(reader)?;
58
14125
    let headers = zero_or_more(key_value, reader)?;
59
14125
    let sections = request_sections(reader)?;
60
14075
    let b = optional(body, reader)?;
61
14010
    let source_info = SourceInfo::new(start.pos, reader.cursor().pos);
62
14010

            
63
14010
    // Check duplicated section
64
14010
    let mut section_names = vec![];
65
14010
    for section in sections.clone() {
66
2350
        if section_names.contains(&section.name().to_string()) {
67
5
            return Err(ParseError::new(
68
5
                section.source_info.start,
69
5
                false,
70
5
                ParseErrorKind::DuplicateSection,
71
5
            ));
72
2345
        } else {
73
2345
            section_names.push(section.name().to_string());
74
        }
75
    }
76

            
77
14005
    Ok(Request {
78
14005
        line_terminators,
79
14005
        space0,
80
14005
        method: m,
81
14005
        space1,
82
14005
        url,
83
14005
        line_terminator0,
84
14005
        headers,
85
14005
        sections,
86
14005
        body: b,
87
14005
        source_info,
88
14005
    })
89
}
90

            
91
14005
fn response(reader: &mut Reader) -> ParseResult<Response> {
92
14005
    let start = reader.cursor();
93
14005
    let line_terminators = optional_line_terminators(reader)?;
94
14005
    let space0 = zero_or_more_spaces(reader)?;
95
14005
    let _version = version(reader)?;
96
12945
    let space1 = one_or_more_spaces(reader)?;
97
12945
    let _status = status(reader)?;
98
12940
    let line_terminator0 = line_terminator(reader)?;
99
12940
    let headers = zero_or_more(key_value, reader)?;
100
12940
    let sections = response_sections(reader)?;
101
12885
    let b = optional(body, reader)?;
102
12885
    Ok(Response {
103
12885
        line_terminators,
104
12885
        space0,
105
12885
        version: _version,
106
12885
        space1,
107
12885
        status: _status,
108
12885
        line_terminator0,
109
12885
        headers,
110
12885
        sections,
111
12885
        body: b,
112
12885
        source_info: SourceInfo::new(start.pos, reader.cursor().pos),
113
12885
    })
114
}
115

            
116
15125
fn method(reader: &mut Reader) -> ParseResult<Method> {
117
15125
    if reader.is_eof() {
118
965
        let kind = ParseErrorKind::Method {
119
965
            name: "<EOF>".to_string(),
120
965
        };
121
965
        return Err(ParseError::new(reader.cursor().pos, true, kind));
122
    }
123
14160
    let start = reader.cursor();
124
62057
    let name = reader.read_while(|c| c.is_ascii_alphabetic());
125
14160
    if name.is_empty() || name.to_uppercase() != name {
126
20
        let kind = ParseErrorKind::Method { name };
127
20
        Err(ParseError::new(start.pos, false, kind))
128
    } else {
129
14140
        Ok(Method(name))
130
    }
131
}
132

            
133
14005
fn version(reader: &mut Reader) -> ParseResult<Version> {
134
14005
    let start = reader.cursor();
135
14005
    try_literal("HTTP", reader)?;
136

            
137
12950
    let next_c = reader.peek();
138
12950
    match next_c {
139
        Some('/') => {
140
220
            let available_version = [
141
220
                ("/1.0", VersionValue::Version1),
142
220
                ("/1.1", VersionValue::Version11),
143
220
                ("/2", VersionValue::Version2),
144
220
                ("/3", VersionValue::Version3),
145
220
                ("/*", VersionValue::VersionAnyLegacy),
146
220
            ];
147
635
            for (s, value) in available_version.iter() {
148
635
                if try_literal(s, reader).is_ok() {
149
215
                    return Ok(Version {
150
215
                        value: value.clone(),
151
215
                        source_info: SourceInfo::new(start.pos, reader.cursor().pos),
152
215
                    });
153
                }
154
            }
155
5
            Err(ParseError::new(start.pos, false, ParseErrorKind::Version))
156
        }
157
12730
        Some(' ') | Some('\t') => Ok(Version {
158
12730
            value: VersionAny,
159
12730
            source_info: SourceInfo::new(start.pos, reader.cursor().pos),
160
12730
        }),
161
        _ => Err(ParseError::new(start.pos, false, ParseErrorKind::Version)),
162
    }
163
}
164

            
165
12945
fn status(reader: &mut Reader) -> ParseResult<Status> {
166
12945
    let start = reader.cursor();
167
12945
    let value = match try_literal("*", reader) {
168
135
        Ok(_) => StatusValue::Any,
169
12810
        Err(_) => match natural(reader) {
170
12805
            Ok(value) => StatusValue::Specific(value),
171
5
            Err(_) => return Err(ParseError::new(start.pos, false, ParseErrorKind::Status)),
172
        },
173
    };
174
12940
    let end = reader.cursor();
175
12940
    Ok(Status {
176
12940
        value,
177
12940
        source_info: SourceInfo::new(start.pos, end.pos),
178
12940
    })
179
}
180

            
181
26960
fn body(reader: &mut Reader) -> ParseResult<Body> {
182
    //  let start = reader.state.clone();
183
26960
    let line_terminators = optional_line_terminators(reader)?;
184
26960
    let space0 = zero_or_more_spaces(reader)?;
185
26960
    let value = bytes(reader)?;
186
3410
    let line_terminator0 = line_terminator(reader)?;
187
3410
    Ok(Body {
188
3410
        line_terminators,
189
3410
        space0,
190
3410
        value,
191
3410
        line_terminator0,
192
3410
    })
193
}
194

            
195
#[cfg(test)]
196
mod tests {
197
    use super::*;
198
    use crate::reader::Pos;
199

            
200
    #[test]
201
    fn test_hurl_file() {
202
        let mut reader = Reader::new("GET http://google.fr");
203
        let hurl_file = hurl_file(&mut reader).unwrap();
204
        assert_eq!(hurl_file.entries.len(), 1);
205
    }
206

            
207
    #[test]
208
    fn test_entry() {
209
        let mut reader = Reader::new("GET http://google.fr");
210
        let e = entry(&mut reader).unwrap();
211
        assert_eq!(e.request.method, Method("GET".to_string()));
212
        assert_eq!(reader.cursor().index, 20);
213
    }
214

            
215
    #[test]
216
    fn test_several_entry() {
217
        let mut reader = Reader::new("GET http://google.fr\nGET http://google.fr");
218

            
219
        let e = entry(&mut reader).unwrap();
220
        assert_eq!(e.request.method, Method("GET".to_string()));
221
        assert_eq!(reader.cursor().index, 21);
222
        assert_eq!(reader.cursor().pos.line, 2);
223

            
224
        let e = entry(&mut reader).unwrap();
225
        assert_eq!(e.request.method, Method("GET".to_string()));
226
        assert_eq!(reader.cursor().index, 41);
227
        assert_eq!(reader.cursor().pos.line, 2);
228

            
229
        let mut reader =
230
            Reader::new("GET http://google.fr # comment1\nGET http://google.fr # comment2");
231

            
232
        let e = entry(&mut reader).unwrap();
233
        assert_eq!(e.request.method, Method("GET".to_string()));
234
        assert_eq!(reader.cursor().index, 32);
235
        assert_eq!(reader.cursor().pos.line, 2);
236

            
237
        let e = entry(&mut reader).unwrap();
238
        assert_eq!(e.request.method, Method("GET".to_string()));
239
        assert_eq!(reader.cursor().index, 63);
240
        assert_eq!(reader.cursor().pos.line, 2);
241
    }
242

            
243
    #[test]
244
    fn test_entry_with_response() {
245
        let mut reader = Reader::new("GET http://google.fr\nHTTP/1.1 200");
246
        let e = entry(&mut reader).unwrap();
247
        assert_eq!(e.request.method, Method("GET".to_string()));
248
        assert_eq!(e.response.unwrap().status.value, StatusValue::Specific(200));
249
    }
250

            
251
    #[test]
252
    fn test_request() {
253
        let mut reader = Reader::new("GET http://google.fr");
254
        let default_request = Request {
255
            line_terminators: vec![],
256
            space0: Whitespace {
257
                value: String::new(),
258
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
259
            },
260
            method: Method("GET".to_string()),
261
            space1: Whitespace {
262
                value: " ".to_string(),
263
                source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 5)),
264
            },
265
            url: Template {
266
                elements: vec![TemplateElement::String {
267
                    value: String::from("http://google.fr"),
268
                    encoded: String::from("http://google.fr"),
269
                }],
270
                delimiter: None,
271
                source_info: SourceInfo::new(Pos::new(1, 5), Pos::new(1, 21)),
272
            },
273
            line_terminator0: LineTerminator {
274
                space0: Whitespace {
275
                    value: String::new(),
276
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 21)),
277
                },
278
                comment: None,
279
                newline: Whitespace {
280
                    value: String::new(),
281
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 21)),
282
                },
283
            },
284
            headers: vec![],
285
            sections: vec![],
286
            body: None,
287
            source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 21)),
288
        };
289
        assert_eq!(request(&mut reader).unwrap(), default_request);
290
        assert_eq!(reader.cursor().index, 20);
291

            
292
        let mut reader = Reader::new("GET  http://google.fr # comment");
293
        let default_request = Request {
294
            line_terminators: vec![],
295
            space0: Whitespace {
296
                value: String::new(),
297
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
298
            },
299
            method: Method("GET".to_string()),
300
            space1: Whitespace {
301
                value: "  ".to_string(),
302
                source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 6)),
303
            },
304
            url: Template {
305
                elements: vec![TemplateElement::String {
306
                    value: String::from("http://google.fr"),
307
                    encoded: String::from("http://google.fr"),
308
                }],
309
                delimiter: None,
310
                source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 22)),
311
            },
312
            line_terminator0: LineTerminator {
313
                space0: Whitespace {
314
                    value: " ".to_string(),
315
                    source_info: SourceInfo::new(Pos::new(1, 22), Pos::new(1, 23)),
316
                },
317
                comment: Some(Comment {
318
                    value: " comment".to_string(),
319
                    source_info: SourceInfo::new(Pos::new(1, 24), Pos::new(1, 32)),
320
                }),
321
                newline: Whitespace {
322
                    value: String::new(),
323
                    source_info: SourceInfo::new(Pos::new(1, 32), Pos::new(1, 32)),
324
                },
325
            },
326
            headers: vec![],
327
            sections: vec![],
328
            body: None,
329
            source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 32)),
330
        };
331
        assert_eq!(request(&mut reader).unwrap(), default_request);
332
        assert_eq!(reader.cursor().index, 31);
333

            
334
        let mut reader = Reader::new("GET http://google.fr\nGET http://google.fr");
335
        let r = request(&mut reader).unwrap();
336
        assert_eq!(r.method, Method("GET".to_string()));
337
        assert_eq!(reader.cursor().index, 21);
338
        let r = request(&mut reader).unwrap();
339
        assert_eq!(r.method, Method("GET".to_string()));
340
    }
341

            
342
    #[test]
343
    fn test_request_multilines() {
344
        // GET http://google.fr
345
        // ```
346
        // Hello World!
347
        // ```
348
        let mut reader = Reader::new("GET http://google.fr\n```\nHello World!\n```");
349
        let req = request(&mut reader).unwrap();
350
        assert_eq!(
351
            req.body.unwrap(),
352
            Body {
353
                line_terminators: vec![],
354
                space0: Whitespace {
355
                    value: String::new(),
356
                    source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 1)),
357
                },
358
                value: Bytes::MultilineString(MultilineString {
359
                    kind: MultilineStringKind::Text(Text {
360
                        space: Whitespace {
361
                            value: String::new(),
362
                            source_info: SourceInfo::new(Pos::new(2, 4), Pos::new(2, 4)),
363
                        },
364
                        newline: Whitespace {
365
                            source_info: SourceInfo::new(Pos::new(2, 4), Pos::new(3, 1)),
366
                            value: "\n".to_string(),
367
                        },
368
                        value: Template {
369
                            elements: vec![TemplateElement::String {
370
                                value: String::from("Hello World!\n"),
371
                                encoded: String::from("Hello World!\n"),
372
                            }],
373
                            delimiter: None,
374
                            source_info: SourceInfo::new(Pos::new(3, 1), Pos::new(4, 1)),
375
                        },
376
                    }),
377
                    attributes: vec![]
378
                }),
379
                line_terminator0: LineTerminator {
380
                    space0: Whitespace {
381
                        value: String::new(),
382
                        source_info: SourceInfo::new(Pos::new(4, 4), Pos::new(4, 4)),
383
                    },
384
                    comment: None,
385
                    newline: Whitespace {
386
                        value: String::new(),
387
                        source_info: SourceInfo::new(Pos::new(4, 4), Pos::new(4, 4)),
388
                    },
389
                },
390
            }
391
        );
392
    }
393

            
394
    #[test]
395
    fn test_request_post_json() {
396
        let mut reader = Reader::new("POST http://localhost:8000/post-json-array\n[1,2,3]");
397
        let r = request(&mut reader).unwrap();
398
        assert_eq!(r.method, Method("POST".to_string()));
399
        assert_eq!(
400
            r.body.unwrap().value,
401
            Bytes::Json(JsonValue::List {
402
                space0: String::new(),
403
                elements: vec![
404
                    JsonListElement {
405
                        space0: String::new(),
406
                        value: JsonValue::Number("1".to_string()),
407
                        space1: String::new(),
408
                    },
409
                    JsonListElement {
410
                        space0: String::new(),
411
                        value: JsonValue::Number("2".to_string()),
412
                        space1: String::new(),
413
                    },
414
                    JsonListElement {
415
                        space0: String::new(),
416
                        value: JsonValue::Number("3".to_string()),
417
                        space1: String::new(),
418
                    },
419
                ],
420
            })
421
        );
422

            
423
        let mut reader = Reader::new("POST http://localhost:8000/post-json-string\n\"Hello\"");
424
        let r = request(&mut reader).unwrap();
425
        assert_eq!(r.method, Method("POST".to_string()));
426
        assert_eq!(
427
            r.body.unwrap().value,
428
            Bytes::Json(JsonValue::String(Template {
429
                delimiter: Some('"'),
430
                elements: vec![TemplateElement::String {
431
                    value: "Hello".to_string(),
432
                    encoded: "Hello".to_string(),
433
                }],
434
                source_info: SourceInfo::new(Pos::new(2, 2), Pos::new(2, 7)),
435
            }))
436
        );
437

            
438
        let mut reader = Reader::new("POST http://localhost:8000/post-json-number\n100");
439
        let r = request(&mut reader).unwrap();
440
        assert_eq!(r.method, Method("POST".to_string()));
441
        assert_eq!(
442
            r.body.unwrap().value,
443
            Bytes::Json(JsonValue::Number("100".to_string()))
444
        );
445
    }
446

            
447
    #[test]
448
    fn test_request_error() {
449
        let mut reader = Reader::new("xxx");
450
        let error = request(&mut reader).err().unwrap();
451
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
452
    }
453

            
454
    #[test]
455
    fn test_response() {
456
        let mut reader = Reader::new("HTTP/1.1 200");
457
        //println!("{:?}", response(&mut reader));
458
        let r = response(&mut reader).unwrap();
459

            
460
        assert_eq!(r.version.value, VersionValue::Version11);
461
        assert_eq!(r.status.value, StatusValue::Specific(200));
462
    }
463

            
464
    #[test]
465
    fn test_method() {
466
        let mut reader = Reader::new("xxx ");
467
        let error = method(&mut reader).err().unwrap();
468
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
469
        assert_eq!(reader.cursor().index, 3);
470

            
471
        let mut reader = Reader::new("");
472
        let error = method(&mut reader).err().unwrap();
473
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
474
        assert_eq!(reader.cursor().index, 0);
475

            
476
        let mut reader = Reader::new("GET ");
477
        assert_eq!(method(&mut reader).unwrap(), Method("GET".to_string()));
478
        assert_eq!(reader.cursor().index, 3);
479

            
480
        let mut reader = Reader::new("CUSTOM");
481
        assert_eq!(method(&mut reader).unwrap(), Method("CUSTOM".to_string()));
482
        assert_eq!(reader.cursor().index, 6);
483
    }
484

            
485
    #[test]
486
    fn test_version() {
487
        let mut reader = Reader::new("HTTP 200");
488
        assert_eq!(
489
            version(&mut reader).unwrap().value,
490
            VersionValue::VersionAny
491
        );
492
        assert_eq!(reader.cursor().index, 4);
493

            
494
        let mut reader = Reader::new("HTTP\t200");
495
        assert_eq!(
496
            version(&mut reader).unwrap().value,
497
            VersionValue::VersionAny
498
        );
499
        assert_eq!(reader.cursor().index, 4);
500

            
501
        let mut reader = Reader::new("HTTP/1.1 200");
502
        assert_eq!(version(&mut reader).unwrap().value, VersionValue::Version11);
503

            
504
        let mut reader = Reader::new("HTTP/1. 200");
505
        let error = version(&mut reader).err().unwrap();
506
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
507
    }
508

            
509
    #[test]
510
    fn test_status() {
511
        let mut reader = Reader::new("*");
512
        let s = status(&mut reader).unwrap();
513
        assert_eq!(s.value, StatusValue::Any);
514

            
515
        let mut reader = Reader::new("200");
516
        let s = status(&mut reader).unwrap();
517
        assert_eq!(s.value, StatusValue::Specific(200));
518

            
519
        let mut reader = Reader::new("xxx");
520
        let result = status(&mut reader);
521
        assert!(result.is_err());
522
    }
523

            
524
    #[test]
525
    fn test_body_json() {
526
        let mut reader = Reader::new("[1,2,3] ");
527
        let b = body(&mut reader).unwrap();
528
        assert_eq!(b.line_terminators.len(), 0);
529
        assert_eq!(
530
            b.value,
531
            Bytes::Json(JsonValue::List {
532
                space0: String::new(),
533
                elements: vec![
534
                    JsonListElement {
535
                        space0: String::new(),
536
                        value: JsonValue::Number("1".to_string()),
537
                        space1: String::new(),
538
                    },
539
                    JsonListElement {
540
                        space0: String::new(),
541
                        value: JsonValue::Number("2".to_string()),
542
                        space1: String::new(),
543
                    },
544
                    JsonListElement {
545
                        space0: String::new(),
546
                        value: JsonValue::Number("3".to_string()),
547
                        space1: String::new(),
548
                    },
549
                ],
550
            })
551
        );
552
        assert_eq!(reader.cursor().index, 8);
553

            
554
        let mut reader = Reader::new("{}");
555
        let b = body(&mut reader).unwrap();
556
        assert_eq!(b.line_terminators.len(), 0);
557
        assert_eq!(
558
            b.value,
559
            Bytes::Json(JsonValue::Object {
560
                space0: String::new(),
561
                elements: vec![],
562
            })
563
        );
564
        assert_eq!(reader.cursor().index, 2);
565

            
566
        let mut reader = Reader::new("# comment\n {} # comment\nxxx");
567
        let b = body(&mut reader).unwrap();
568
        assert_eq!(b.line_terminators.len(), 1);
569
        assert_eq!(
570
            b.value,
571
            Bytes::Json(JsonValue::Object {
572
                space0: String::new(),
573
                elements: vec![],
574
            })
575
        );
576
        assert_eq!(reader.cursor().index, 24);
577

            
578
        let mut reader = Reader::new("{x");
579
        let error = body(&mut reader).err().unwrap();
580
        assert_eq!(error.pos, Pos { line: 1, column: 2 });
581
        assert!(!error.recoverable);
582
    }
583
}