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
    Body, Entry, HurlFile, Method, Request, Response, SourceInfo, Status, StatusValue, Version,
21
    VersionValue,
22
};
23
use crate::combinator::{optional, zero_or_more};
24
use crate::parser::bytes::bytes;
25
use crate::parser::primitives::{
26
    eof, key_value, line_terminator, one_or_more_spaces, optional_line_terminators, try_literal,
27
    zero_or_more_spaces,
28
};
29
use crate::parser::sections::{request_sections, response_sections};
30
use crate::parser::string::unquoted_template;
31
use crate::parser::{ParseError, ParseErrorKind, ParseResult};
32
use crate::reader::Reader;
33

            
34
4005
pub fn hurl_file(reader: &mut Reader) -> ParseResult<HurlFile> {
35
4005
    let entries = zero_or_more(entry, reader)?;
36
3785
    let line_terminators = optional_line_terminators(reader)?;
37
3785
    eof(reader)?;
38
3785
    Ok(HurlFile {
39
3785
        entries,
40
3785
        line_terminators,
41
3785
    })
42
}
43

            
44
15145
fn entry(reader: &mut Reader) -> ParseResult<Entry> {
45
15145
    let req = request(reader)?;
46
14025
    let resp = optional(response, reader)?;
47
13960
    Ok(Entry {
48
13960
        request: req,
49
13960
        response: resp,
50
13960
    })
51
}
52

            
53
15145
fn request(reader: &mut Reader) -> ParseResult<Request> {
54
15145
    let start = reader.cursor();
55
15145
    let line_terminators = optional_line_terminators(reader)?;
56
15145
    let space0 = zero_or_more_spaces(reader)?;
57
15145
    let m = method(reader)?;
58
14160
    let space1 = one_or_more_spaces(reader)?;
59
14150
    let url = unquoted_template(reader)?;
60
14145
    let line_terminator0 = line_terminator(reader)?;
61
14145
    let headers = zero_or_more(key_value, reader)?;
62
14145
    let sections = request_sections(reader)?;
63
14095
    let b = optional(body, reader)?;
64
14030
    let source_info = SourceInfo::new(start.pos, reader.cursor().pos);
65
14030

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

            
80
14025
    Ok(Request {
81
14025
        line_terminators,
82
14025
        space0,
83
14025
        method: m,
84
14025
        space1,
85
14025
        url,
86
14025
        line_terminator0,
87
14025
        headers,
88
14025
        sections,
89
14025
        body: b,
90
14025
        source_info,
91
14025
    })
92
}
93

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

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

            
136
14025
fn version(reader: &mut Reader) -> ParseResult<Version> {
137
14025
    let start = reader.cursor();
138
14025
    try_literal("HTTP", reader)?;
139

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

            
168
13000
fn status(reader: &mut Reader) -> ParseResult<Status> {
169
13000
    let start = reader.cursor();
170
13000
    let value = match try_literal("*", reader) {
171
150
        Ok(_) => StatusValue::Any,
172
        Err(_) => {
173
12850
            if reader.is_eof() {
174
                let kind = ParseErrorKind::Status;
175
                return Err(ParseError::new(start.pos, false, kind));
176
            }
177
53955
            let s = reader.read_while(|c| c.is_ascii_digit());
178
12850
            if s.is_empty() {
179
5
                let kind = ParseErrorKind::Status;
180
5
                return Err(ParseError::new(start.pos, false, kind));
181
            }
182
12845
            match s.to_string().parse() {
183
12845
                Ok(value) => StatusValue::Specific(value),
184
                Err(_) => {
185
                    let kind = ParseErrorKind::Status;
186
                    return Err(ParseError::new(start.pos, false, kind));
187
                }
188
            }
189
        }
190
    };
191
12995
    let end = reader.cursor();
192
12995
    Ok(Status {
193
12995
        value,
194
12995
        source_info: SourceInfo::new(start.pos, end.pos),
195
12995
    })
196
}
197

            
198
27035
fn body(reader: &mut Reader) -> ParseResult<Body> {
199
    //  let start = reader.state.clone();
200
27035
    let line_terminators = optional_line_terminators(reader)?;
201
27035
    let space0 = zero_or_more_spaces(reader)?;
202
27035
    let value = bytes(reader)?;
203
3425
    let line_terminator0 = line_terminator(reader)?;
204
3425
    Ok(Body {
205
3425
        line_terminators,
206
3425
        space0,
207
3425
        value,
208
3425
        line_terminator0,
209
3425
    })
210
}
211

            
212
#[cfg(test)]
213
mod tests {
214
    use super::*;
215
    use crate::ast::{
216
        Bytes, Comment, JsonListElement, JsonValue, LineTerminator, MultilineString,
217
        MultilineStringKind, Template, TemplateElement, Text, Whitespace,
218
    };
219
    use crate::reader::Pos;
220

            
221
    #[test]
222
    fn test_hurl_file() {
223
        let mut reader = Reader::new("GET http://google.fr");
224
        let hurl_file = hurl_file(&mut reader).unwrap();
225
        assert_eq!(hurl_file.entries.len(), 1);
226
    }
227

            
228
    #[test]
229
    fn test_entry() {
230
        let mut reader = Reader::new("GET http://google.fr");
231
        let e = entry(&mut reader).unwrap();
232
        assert_eq!(e.request.method, Method("GET".to_string()));
233
        assert_eq!(reader.cursor().index, 20);
234
    }
235

            
236
    #[test]
237
    fn test_several_entry() {
238
        let mut reader = Reader::new("GET http://google.fr\nGET http://google.fr");
239

            
240
        let e = entry(&mut reader).unwrap();
241
        assert_eq!(e.request.method, Method("GET".to_string()));
242
        assert_eq!(reader.cursor().index, 21);
243
        assert_eq!(reader.cursor().pos.line, 2);
244

            
245
        let e = entry(&mut reader).unwrap();
246
        assert_eq!(e.request.method, Method("GET".to_string()));
247
        assert_eq!(reader.cursor().index, 41);
248
        assert_eq!(reader.cursor().pos.line, 2);
249

            
250
        let mut reader =
251
            Reader::new("GET http://google.fr # comment1\nGET http://google.fr # comment2");
252

            
253
        let e = entry(&mut reader).unwrap();
254
        assert_eq!(e.request.method, Method("GET".to_string()));
255
        assert_eq!(reader.cursor().index, 32);
256
        assert_eq!(reader.cursor().pos.line, 2);
257

            
258
        let e = entry(&mut reader).unwrap();
259
        assert_eq!(e.request.method, Method("GET".to_string()));
260
        assert_eq!(reader.cursor().index, 63);
261
        assert_eq!(reader.cursor().pos.line, 2);
262
    }
263

            
264
    #[test]
265
    fn test_entry_with_response() {
266
        let mut reader = Reader::new("GET http://google.fr\nHTTP/1.1 200");
267
        let e = entry(&mut reader).unwrap();
268
        assert_eq!(e.request.method, Method("GET".to_string()));
269
        assert_eq!(e.response.unwrap().status.value, StatusValue::Specific(200));
270
    }
271

            
272
    #[test]
273
    fn test_request() {
274
        let mut reader = Reader::new("GET http://google.fr");
275
        let default_request = Request {
276
            line_terminators: vec![],
277
            space0: Whitespace {
278
                value: String::new(),
279
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
280
            },
281
            method: Method("GET".to_string()),
282
            space1: Whitespace {
283
                value: " ".to_string(),
284
                source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 5)),
285
            },
286
            url: Template {
287
                elements: vec![TemplateElement::String {
288
                    value: String::from("http://google.fr"),
289
                    encoded: String::from("http://google.fr"),
290
                }],
291
                delimiter: None,
292
                source_info: SourceInfo::new(Pos::new(1, 5), Pos::new(1, 21)),
293
            },
294
            line_terminator0: LineTerminator {
295
                space0: Whitespace {
296
                    value: String::new(),
297
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 21)),
298
                },
299
                comment: None,
300
                newline: Whitespace {
301
                    value: String::new(),
302
                    source_info: SourceInfo::new(Pos::new(1, 21), Pos::new(1, 21)),
303
                },
304
            },
305
            headers: vec![],
306
            sections: vec![],
307
            body: None,
308
            source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 21)),
309
        };
310
        assert_eq!(request(&mut reader).unwrap(), default_request);
311
        assert_eq!(reader.cursor().index, 20);
312

            
313
        let mut reader = Reader::new("GET  http://google.fr # comment");
314
        let default_request = Request {
315
            line_terminators: vec![],
316
            space0: Whitespace {
317
                value: String::new(),
318
                source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
319
            },
320
            method: Method("GET".to_string()),
321
            space1: Whitespace {
322
                value: "  ".to_string(),
323
                source_info: SourceInfo::new(Pos::new(1, 4), Pos::new(1, 6)),
324
            },
325
            url: Template {
326
                elements: vec![TemplateElement::String {
327
                    value: String::from("http://google.fr"),
328
                    encoded: String::from("http://google.fr"),
329
                }],
330
                delimiter: None,
331
                source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 22)),
332
            },
333
            line_terminator0: LineTerminator {
334
                space0: Whitespace {
335
                    value: " ".to_string(),
336
                    source_info: SourceInfo::new(Pos::new(1, 22), Pos::new(1, 23)),
337
                },
338
                comment: Some(Comment {
339
                    value: " comment".to_string(),
340
                    source_info: SourceInfo::new(Pos::new(1, 24), Pos::new(1, 32)),
341
                }),
342
                newline: Whitespace {
343
                    value: String::new(),
344
                    source_info: SourceInfo::new(Pos::new(1, 32), Pos::new(1, 32)),
345
                },
346
            },
347
            headers: vec![],
348
            sections: vec![],
349
            body: None,
350
            source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 32)),
351
        };
352
        assert_eq!(request(&mut reader).unwrap(), default_request);
353
        assert_eq!(reader.cursor().index, 31);
354

            
355
        let mut reader = Reader::new("GET http://google.fr\nGET http://google.fr");
356
        let r = request(&mut reader).unwrap();
357
        assert_eq!(r.method, Method("GET".to_string()));
358
        assert_eq!(reader.cursor().index, 21);
359
        let r = request(&mut reader).unwrap();
360
        assert_eq!(r.method, Method("GET".to_string()));
361
    }
362

            
363
    #[test]
364
    fn test_request_multilines() {
365
        // GET http://google.fr
366
        // ```
367
        // Hello World!
368
        // ```
369
        let mut reader = Reader::new("GET http://google.fr\n```\nHello World!\n```");
370
        let req = request(&mut reader).unwrap();
371
        assert_eq!(
372
            req.body.unwrap(),
373
            Body {
374
                line_terminators: vec![],
375
                space0: Whitespace {
376
                    value: String::new(),
377
                    source_info: SourceInfo::new(Pos::new(2, 1), Pos::new(2, 1)),
378
                },
379
                value: Bytes::MultilineString(MultilineString {
380
                    kind: MultilineStringKind::Text(Text {
381
                        space: Whitespace {
382
                            value: String::new(),
383
                            source_info: SourceInfo::new(Pos::new(2, 4), Pos::new(2, 4)),
384
                        },
385
                        newline: Whitespace {
386
                            source_info: SourceInfo::new(Pos::new(2, 4), Pos::new(3, 1)),
387
                            value: "\n".to_string(),
388
                        },
389
                        value: Template {
390
                            elements: vec![TemplateElement::String {
391
                                value: String::from("Hello World!\n"),
392
                                encoded: String::from("Hello World!\n"),
393
                            }],
394
                            delimiter: None,
395
                            source_info: SourceInfo::new(Pos::new(3, 1), Pos::new(4, 1)),
396
                        },
397
                    }),
398
                    attributes: vec![]
399
                }),
400
                line_terminator0: LineTerminator {
401
                    space0: Whitespace {
402
                        value: String::new(),
403
                        source_info: SourceInfo::new(Pos::new(4, 4), Pos::new(4, 4)),
404
                    },
405
                    comment: None,
406
                    newline: Whitespace {
407
                        value: String::new(),
408
                        source_info: SourceInfo::new(Pos::new(4, 4), Pos::new(4, 4)),
409
                    },
410
                },
411
            }
412
        );
413
    }
414

            
415
    #[test]
416
    fn test_request_post_json() {
417
        let mut reader = Reader::new("POST http://localhost:8000/post-json-array\n[1,2,3]");
418
        let r = request(&mut reader).unwrap();
419
        assert_eq!(r.method, Method("POST".to_string()));
420
        assert_eq!(
421
            r.body.unwrap().value,
422
            Bytes::Json(JsonValue::List {
423
                space0: String::new(),
424
                elements: vec![
425
                    JsonListElement {
426
                        space0: String::new(),
427
                        value: JsonValue::Number("1".to_string()),
428
                        space1: String::new(),
429
                    },
430
                    JsonListElement {
431
                        space0: String::new(),
432
                        value: JsonValue::Number("2".to_string()),
433
                        space1: String::new(),
434
                    },
435
                    JsonListElement {
436
                        space0: String::new(),
437
                        value: JsonValue::Number("3".to_string()),
438
                        space1: String::new(),
439
                    },
440
                ],
441
            })
442
        );
443

            
444
        let mut reader = Reader::new("POST http://localhost:8000/post-json-string\n\"Hello\"");
445
        let r = request(&mut reader).unwrap();
446
        assert_eq!(r.method, Method("POST".to_string()));
447
        assert_eq!(
448
            r.body.unwrap().value,
449
            Bytes::Json(JsonValue::String(Template {
450
                delimiter: Some('"'),
451
                elements: vec![TemplateElement::String {
452
                    value: "Hello".to_string(),
453
                    encoded: "Hello".to_string(),
454
                }],
455
                source_info: SourceInfo::new(Pos::new(2, 2), Pos::new(2, 7)),
456
            }))
457
        );
458

            
459
        let mut reader = Reader::new("POST http://localhost:8000/post-json-number\n100");
460
        let r = request(&mut reader).unwrap();
461
        assert_eq!(r.method, Method("POST".to_string()));
462
        assert_eq!(
463
            r.body.unwrap().value,
464
            Bytes::Json(JsonValue::Number("100".to_string()))
465
        );
466
    }
467

            
468
    #[test]
469
    fn test_request_error() {
470
        let mut reader = Reader::new("xxx");
471
        let error = request(&mut reader).err().unwrap();
472
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
473
    }
474

            
475
    #[test]
476
    fn test_response() {
477
        let mut reader = Reader::new("HTTP/1.1 200");
478
        //println!("{:?}", response(&mut reader));
479
        let r = response(&mut reader).unwrap();
480

            
481
        assert_eq!(r.version.value, VersionValue::Version11);
482
        assert_eq!(r.status.value, StatusValue::Specific(200));
483
    }
484

            
485
    #[test]
486
    fn test_method() {
487
        let mut reader = Reader::new("xxx ");
488
        let error = method(&mut reader).err().unwrap();
489
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
490
        assert_eq!(reader.cursor().index, 3);
491

            
492
        let mut reader = Reader::new("");
493
        let error = method(&mut reader).err().unwrap();
494
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
495
        assert_eq!(reader.cursor().index, 0);
496

            
497
        let mut reader = Reader::new("GET ");
498
        assert_eq!(method(&mut reader).unwrap(), Method("GET".to_string()));
499
        assert_eq!(reader.cursor().index, 3);
500

            
501
        let mut reader = Reader::new("CUSTOM");
502
        assert_eq!(method(&mut reader).unwrap(), Method("CUSTOM".to_string()));
503
        assert_eq!(reader.cursor().index, 6);
504
    }
505

            
506
    #[test]
507
    fn test_version() {
508
        let mut reader = Reader::new("HTTP 200");
509
        assert_eq!(version(&mut reader).unwrap().value, VersionAny);
510
        assert_eq!(reader.cursor().index, 4);
511

            
512
        let mut reader = Reader::new("HTTP\t200");
513
        assert_eq!(version(&mut reader).unwrap().value, VersionAny);
514
        assert_eq!(reader.cursor().index, 4);
515

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

            
519
        let mut reader = Reader::new("HTTP/1. 200");
520
        let error = version(&mut reader).err().unwrap();
521
        assert_eq!(error.pos, Pos { line: 1, column: 1 });
522
    }
523

            
524
    #[test]
525
    fn test_status() {
526
        let mut reader = Reader::new("*");
527
        let s = status(&mut reader).unwrap();
528
        assert_eq!(s.value, StatusValue::Any);
529

            
530
        let mut reader = Reader::new("200");
531
        let s = status(&mut reader).unwrap();
532
        assert_eq!(s.value, StatusValue::Specific(200));
533

            
534
        let mut reader = Reader::new("xxx");
535
        let result = status(&mut reader);
536
        assert!(result.is_err());
537
    }
538

            
539
    #[test]
540
    fn test_body_json() {
541
        let mut reader = Reader::new("[1,2,3] ");
542
        let b = body(&mut reader).unwrap();
543
        assert_eq!(b.line_terminators.len(), 0);
544
        assert_eq!(
545
            b.value,
546
            Bytes::Json(JsonValue::List {
547
                space0: String::new(),
548
                elements: vec![
549
                    JsonListElement {
550
                        space0: String::new(),
551
                        value: JsonValue::Number("1".to_string()),
552
                        space1: String::new(),
553
                    },
554
                    JsonListElement {
555
                        space0: String::new(),
556
                        value: JsonValue::Number("2".to_string()),
557
                        space1: String::new(),
558
                    },
559
                    JsonListElement {
560
                        space0: String::new(),
561
                        value: JsonValue::Number("3".to_string()),
562
                        space1: String::new(),
563
                    },
564
                ],
565
            })
566
        );
567
        assert_eq!(reader.cursor().index, 8);
568

            
569
        let mut reader = Reader::new("{}");
570
        let b = body(&mut reader).unwrap();
571
        assert_eq!(b.line_terminators.len(), 0);
572
        assert_eq!(
573
            b.value,
574
            Bytes::Json(JsonValue::Object {
575
                space0: String::new(),
576
                elements: vec![],
577
            })
578
        );
579
        assert_eq!(reader.cursor().index, 2);
580

            
581
        let mut reader = Reader::new("# comment\n {} # comment\nxxx");
582
        let b = body(&mut reader).unwrap();
583
        assert_eq!(b.line_terminators.len(), 1);
584
        assert_eq!(
585
            b.value,
586
            Bytes::Json(JsonValue::Object {
587
                space0: String::new(),
588
                elements: vec![],
589
            })
590
        );
591
        assert_eq!(reader.cursor().index, 24);
592

            
593
        let mut reader = Reader::new("{x");
594
        let error = body(&mut reader).err().unwrap();
595
        assert_eq!(error.pos, Pos { line: 1, column: 2 });
596
        assert!(!error.recoverable);
597
    }
598
}