cogo_http/header/common/
link.rs

1use std::fmt;
2use std::borrow::Cow;
3use std::str::FromStr;
4
5#[allow(unused_imports)]
6use std::ascii::AsciiExt;
7
8use mime::Mime;
9use language_tags::LanguageTag;
10
11use crate::header::parsing;
12use crate::header::{Header, HeaderFormat};
13
14/// The `Link` header, defined in
15/// [RFC5988](http://tools.ietf.org/html/rfc5988#section-5)
16///
17/// # ABNF
18/// ```plain
19/// Link           = "Link" ":" #link-value
20/// link-value     = "<" URI-Reference ">" *( ";" link-param )
21/// link-param     = ( ( "rel" "=" relation-types )
22///                | ( "anchor" "=" <"> URI-Reference <"> )
23///                | ( "rev" "=" relation-types )
24///                | ( "hreflang" "=" Language-Tag )
25///                | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) )
26///                | ( "title" "=" quoted-string )
27///                | ( "title*" "=" ext-value )
28///                | ( "type" "=" ( media-type | quoted-mt ) )
29///                | ( link-extension ) )
30/// link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] )
31///                | ( ext-name-star "=" ext-value )
32/// ext-name-star  = parmname "*" ; reserved for RFC2231-profiled
33/// ; extensions.  Whitespace NOT
34/// ; allowed in between.
35/// ptoken         = 1*ptokenchar
36/// ptokenchar     = "!" | "#" | "$" | "%" | "&" | "'" | "("
37///                | ")" | "*" | "+" | "-" | "." | "/" | DIGIT
38///                | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA
39///                | "[" | "]" | "^" | "_" | "`" | "{" | "|"
40///                | "}" | "~"
41/// media-type     = type-name "/" subtype-name
42/// quoted-mt      = <"> media-type <">
43/// relation-types = relation-type
44///                | <"> relation-type *( 1*SP relation-type ) <">
45/// relation-type  = reg-rel-type | ext-rel-type
46/// reg-rel-type   = LOALPHA *( LOALPHA | DIGIT | "." | "-" )
47/// ext-rel-type   = URI
48/// ```
49///
50/// # Example values
51///
52/// `Link: <http://example.com/TheBook/chapter2>; rel="previous";
53///        title="previous chapter"`
54///
55/// `Link: </TheBook/chapter2>; rel="previous"; title*=UTF-8'de'letztes%20Kapitel,
56///        </TheBook/chapter4>; rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel`
57///
58/// # Examples
59/// ```
60/// use cogo_http::header::{Headers, Link, LinkValue, RelationType};
61///
62/// let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
63///     .push_rel(RelationType::Previous)
64///     .set_title("previous chapter");
65///
66/// let mut headers = Headers::new();
67/// headers.set(
68///     Link::new(vec![link_value])
69/// );
70/// ```
71#[derive(Clone, PartialEq, Debug)]
72pub struct Link {
73    /// A list of the `link-value`s of the Link entity-header.
74    values: Vec<LinkValue>
75}
76
77/// A single `link-value` of a `Link` header, based on:
78/// [RFC5988](http://tools.ietf.org/html/rfc5988#section-5)
79#[derive(Clone, PartialEq, Debug)]
80pub struct LinkValue {
81    /// Target IRI: `link-value`.
82    link: Cow<'static, str>,
83
84    /// Forward Relation Types: `rel`.
85    rel: Option<Vec<RelationType>>,
86
87    /// Context IRI: `anchor`.
88    anchor: Option<String>,
89
90    /// Reverse Relation Types: `rev`.
91    rev: Option<Vec<RelationType>>,
92
93    /// Hint on the language of the result of dereferencing
94    /// the link: `hreflang`.
95    href_lang: Option<Vec<LanguageTag>>,
96
97    /// Destination medium or media: `media`.
98    media_desc: Option<Vec<MediaDesc>>,
99
100    /// Label of the destination of a Link: `title`.
101    title: Option<String>,
102
103    /// The `title` encoded in a different charset: `title*`.
104    title_star: Option<String>,
105
106    /// Hint on the media type of the result of dereferencing
107    /// the link: `type`.
108    media_type: Option<Mime>,
109}
110
111/// A Media Descriptors Enum based on:
112/// https://www.w3.org/TR/html401/types.html#h-6.13
113#[derive(Clone, PartialEq, Debug)]
114pub enum MediaDesc {
115    /// screen.
116    Screen,
117    /// tty.
118    Tty,
119    /// tv.
120    Tv,
121    /// projection.
122    Projection,
123    /// handheld.
124    Handheld,
125    /// print.
126    Print,
127    /// braille.
128    Braille,
129    /// aural.
130    Aural,
131    /// all.
132    All,
133    /// Unrecognized media descriptor extension.
134    Extension(String)
135}
136
137/// A Link Relation Type Enum based on:
138/// [RFC5988](https://tools.ietf.org/html/rfc5988#section-6.2.2)
139#[derive(Clone, PartialEq, Debug)]
140pub enum RelationType {
141    /// alternate.
142    Alternate,
143    /// appendix.
144    Appendix,
145    /// bookmark.
146    Bookmark,
147    /// chapter.
148    Chapter,
149    /// contents.
150    Contents,
151    /// copyright.
152    Copyright,
153    /// current.
154    Current,
155    /// describedby.
156    DescribedBy,
157    /// edit.
158    Edit,
159    /// edit-media.
160    EditMedia,
161    /// enclosure.
162    Enclosure,
163    /// first.
164    First,
165    /// glossary.
166    Glossary,
167    /// help.
168    Help,
169    /// hub.
170    Hub,
171    /// index.
172    Index,
173    /// last.
174    Last,
175    /// latest-version.
176    LatestVersion,
177    /// license.
178    License,
179    /// next.
180    Next,
181    /// next-archive.
182    NextArchive,
183    /// payment.
184    Payment,
185    /// prev.
186    Prev,
187    /// predecessor-version.
188    PredecessorVersion,
189    /// previous.
190    Previous,
191    /// prev-archive.
192    PrevArchive,
193    /// related.
194    Related,
195    /// replies.
196    Replies,
197    /// section.
198    Section,
199    /// self.
200    RelationTypeSelf,
201    /// service.
202    Service,
203    /// start.
204    Start,
205    /// stylesheet.
206    Stylesheet,
207    /// subsection.
208    Subsection,
209    /// successor-version.
210    SuccessorVersion,
211    /// up.
212    Up,
213    /// versionHistory.
214    VersionHistory,
215    /// via.
216    Via,
217    /// working-copy.
218    WorkingCopy,
219    /// working-copy-of.
220    WorkingCopyOf,
221    /// ext-rel-type.
222    ExtRelType(String)
223}
224
225////////////////////////////////////////////////////////////////////////////////
226// Struct methods
227////////////////////////////////////////////////////////////////////////////////
228
229impl Link {
230    /// Create `Link` from a `Vec<LinkValue>`.
231    pub fn new(link_values: Vec<LinkValue>) -> Link {
232        Link { values: link_values }
233    }
234
235    /// Get the `Link` header's `LinkValue`s.
236    pub fn values(&self) -> &[LinkValue] {
237        self.values.as_ref()
238    }
239
240    /// Add a `LinkValue` instance to the `Link` header's values.
241    pub fn push_value(&mut self, link_value: LinkValue) {
242        self.values.push(link_value);
243    }
244}
245
246impl LinkValue {
247    /// Create `LinkValue` from URI-Reference.
248    pub fn new<T>(uri: T) -> LinkValue
249        where T: Into<Cow<'static, str>> {
250        LinkValue {
251            link: uri.into(),
252            rel: None,
253            anchor: None,
254            rev: None,
255            href_lang: None,
256            media_desc: None,
257            title: None,
258            title_star: None,
259            media_type: None,
260        }
261    }
262
263    /// Get the `LinkValue`'s value.
264    pub fn link(&self) -> &str {
265        self.link.as_ref()
266    }
267
268    /// Get the `LinkValue`'s `rel` parameter(s).
269    pub fn rel(&self) -> Option<&[RelationType]> {
270        self.rel.as_ref().map(AsRef::as_ref)
271    }
272
273    /// Get the `LinkValue`'s `anchor` parameter.
274    pub fn anchor(&self) -> Option<&str> {
275        self.anchor.as_ref().map(AsRef::as_ref)
276    }
277
278    /// Get the `LinkValue`'s `rev` parameter(s).
279    pub fn rev(&self) -> Option<&[RelationType]> {
280        self.rev.as_ref().map(AsRef::as_ref)
281    }
282
283    /// Get the `LinkValue`'s `hreflang` parameter(s).
284    pub fn href_lang(&self) -> Option<&[LanguageTag]> {
285        self.href_lang.as_ref().map(AsRef::as_ref)
286    }
287
288    /// Get the `LinkValue`'s `media` parameter(s).
289    pub fn media_desc(&self) -> Option<&[MediaDesc]> {
290        self.media_desc.as_ref().map(AsRef::as_ref)
291    }
292
293    /// Get the `LinkValue`'s `title` parameter.
294    pub fn title(&self) -> Option<&str> {
295        self.title.as_ref().map(AsRef::as_ref)
296    }
297
298    /// Get the `LinkValue`'s `title*` parameter.
299    pub fn title_star(&self) -> Option<&str> {
300        self.title_star.as_ref().map(AsRef::as_ref)
301    }
302
303    /// Get the `LinkValue`'s `type` parameter.
304    pub fn media_type(&self) -> Option<&Mime> {
305        self.media_type.as_ref()
306    }
307
308    /// Add a `RelationType` to the `LinkValue`'s `rel` parameter.
309    pub fn push_rel(mut self, rel: RelationType) -> LinkValue {
310        let mut v = self.rel.take().unwrap_or(Vec::new());
311
312        v.push(rel);
313
314        self.rel = Some(v);
315
316        self
317    }
318
319    /// Set `LinkValue`'s `anchor` parameter.
320    pub fn set_anchor<T: Into<String>>(mut self, anchor: T) -> LinkValue {
321        self.anchor = Some(anchor.into());
322
323        self
324    }
325
326    /// Add a `RelationType` to the `LinkValue`'s `rev` parameter.
327    pub fn push_rev(mut self, rev: RelationType) -> LinkValue {
328        let mut v = self.rev.take().unwrap_or(Vec::new());
329
330        v.push(rev);
331
332        self.rev = Some(v);
333
334        self
335    }
336
337    /// Add a `LanguageTag` to the `LinkValue`'s `hreflang` parameter.
338    pub fn push_href_lang(mut self, language_tag: LanguageTag) -> LinkValue {
339        let mut v = self.href_lang.take().unwrap_or(Vec::new());
340
341        v.push(language_tag);
342
343        self.href_lang = Some(v);
344
345        self
346    }
347
348    /// Add a `MediaDesc` to the `LinkValue`'s `media_desc` parameter.
349    pub fn push_media_desc(mut self, media_desc: MediaDesc) -> LinkValue {
350        let mut v = self.media_desc.take().unwrap_or(Vec::new());
351
352        v.push(media_desc);
353
354        self.media_desc = Some(v);
355
356        self
357    }
358
359    /// Set `LinkValue`'s `title` parameter.
360    pub fn set_title<T: Into<String>>(mut self, title: T) -> LinkValue {
361        self.title = Some(title.into());
362
363        self
364    }
365
366    /// Set `LinkValue`'s `title*` parameter.
367    pub fn set_title_star<T: Into<String>>(mut self, title_star: T) -> LinkValue {
368        self.title_star = Some(title_star.into());
369
370        self
371    }
372
373    /// Set `LinkValue`'s `type` parameter.
374    pub fn set_media_type(mut self, media_type: Mime) -> LinkValue {
375        self.media_type = Some(media_type);
376
377        self
378    }
379}
380
381////////////////////////////////////////////////////////////////////////////////
382// Trait implementations
383////////////////////////////////////////////////////////////////////////////////
384
385impl Header for Link {
386    fn header_name() -> &'static str {
387        static NAME: &'static str = "Link";
388        NAME
389    }
390
391    fn parse_header(raw: &[Vec<u8>]) -> crate::Result<Link> {
392        // If more that one `Link` headers are present in a request's
393        // headers they are combined in a single `Link` header containing
394        // all the `link-value`s present in each of those `Link` headers.
395        raw.iter()
396            .map(|v| parsing::from_raw_str::<Link>(&v))
397            .fold(None, |p, c| match (p, c) {
398                (None, c) => Some(c),
399                (e @ Some(Err(_)), _) => e,
400                (Some(Ok(mut p)), Ok(c)) => {
401                    p.values.extend(c.values);
402
403                    Some(Ok(p))
404                }
405                _ => Some(Err(crate::Error::Header)),
406            })
407            .unwrap_or(Err(crate::Error::Header))
408    }
409}
410impl HeaderFormat for Link {
411    fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
412        fmt_delimited(f, self.values.as_slice(), ", ", ("", ""))
413    }
414}
415
416impl fmt::Display for Link {
417    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
418        self.fmt_header(f)
419    }
420}
421
422impl fmt::Display for LinkValue {
423    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
424        r#try!(write!(f, "<{}>", self.link));
425
426        if let Some(ref rel) = self.rel {
427            r#try!(fmt_delimited(f, rel.as_slice(), " ", ("; rel=\"", "\"")));
428        }
429        if let Some(ref anchor) = self.anchor {
430            r#try!(write!(f, "; anchor=\"{}\"", anchor));
431        }
432        if let Some(ref rev) = self.rev {
433            r#try!(fmt_delimited(f, rev.as_slice(), " ", ("; rev=\"", "\"")));
434        }
435        if let Some(ref href_lang) = self.href_lang {
436            for tag in href_lang {
437                r#try!(write!(f, "; hreflang={}", tag));
438            }
439        }
440        if let Some(ref media_desc) = self.media_desc {
441            r#try!(fmt_delimited(f, media_desc.as_slice(), ", ", ("; media=\"", "\"")));
442        }
443        if let Some(ref title) = self.title {
444            r#try!(write!(f, "; title=\"{}\"", title));
445        }
446        if let Some(ref title_star) = self.title_star {
447            r#try!(write!(f, "; title*={}", title_star));
448        }
449        if let Some(ref media_type) = self.media_type {
450            r#try!(write!(f, "; type=\"{}\"", media_type));
451        }
452
453        Ok(())
454    }
455}
456
457impl FromStr for Link {
458    type Err = crate::Error;
459
460    fn from_str(s: &str) -> crate::Result<Link> {
461        // Create a split iterator with delimiters: `;`, `,`
462        let link_split = SplitAsciiUnquoted::new(s, ";,");
463
464        let mut link_values: Vec<LinkValue> = Vec::new();
465
466        // Loop over the splits parsing the Link header into
467        // a `Vec<LinkValue>`
468        for segment in link_split {
469            // Parse the `Target IRI`
470            // https://tools.ietf.org/html/rfc5988#section-5.1
471            if segment.trim().starts_with('<') {
472                link_values.push(
473                    match verify_and_trim(segment.trim(), (b'<', b'>')) {
474                        Err(_) => return Err(crate::Error::Header),
475                        Ok(s) => {
476                            LinkValue {
477                                link: s.to_owned().into(),
478                                rel: None,
479                                anchor: None,
480                                rev: None,
481                                href_lang: None,
482                                media_desc: None,
483                                title: None,
484                                title_star: None,
485                                media_type: None,
486                            }
487                        },
488                    }
489                );
490            } else {
491                // Parse the current link-value's parameters
492                let mut link_param_split = segment.splitn(2, '=');
493
494                let link_param_name = match link_param_split.next() {
495                    None => return Err(crate::Error::Header),
496                    Some(p) => p.trim(),
497                };
498
499                let link_header = match link_values.last_mut() {
500                    None => return Err(crate::Error::Header),
501                    Some(l) => l,
502                };
503
504                if "rel".eq_ignore_ascii_case(link_param_name) {
505                    // Parse relation type: `rel`.
506                    // https://tools.ietf.org/html/rfc5988#section-5.3
507                    if link_header.rel.is_none() {
508                        link_header.rel = match link_param_split.next() {
509                            None => return Err(crate::Error::Header),
510                            Some("") =>  return Err(crate::Error::Header),
511                            Some(s) => {
512                                s.trim_matches(|c: char| c == '"' || c.is_whitespace())
513                                    .split(' ')
514                                    .map(|t| t.trim().parse())
515                                    .collect::<Result<Vec<RelationType>, _>>()
516                                    .or_else(|_| return Err(crate::Error::Header))
517                                    .ok()
518                            },
519                        };
520                    }
521                } else if "anchor".eq_ignore_ascii_case(link_param_name) {
522                    // Parse the `Context IRI`.
523                    // https://tools.ietf.org/html/rfc5988#section-5.2
524                    link_header.anchor = match link_param_split.next() {
525                        None => return Err(crate::Error::Header),
526                        Some("") =>  return Err(crate::Error::Header),
527                        Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) {
528                            Err(_) => return Err(crate::Error::Header),
529                            Ok(a) => Some(String::from(a)),
530                        },
531                    };
532                } else if "rev".eq_ignore_ascii_case(link_param_name) {
533                    // Parse relation type: `rev`.
534                    // https://tools.ietf.org/html/rfc5988#section-5.3
535                    if link_header.rev.is_none() {
536                        link_header.rev = match link_param_split.next() {
537                            None => return Err(crate::Error::Header),
538                            Some("") =>  return Err(crate::Error::Header),
539                            Some(s) => {
540                                s.trim_matches(|c: char| c == '"' || c.is_whitespace())
541                                    .split(' ')
542                                    .map(|t| t.trim().parse())
543                                    .collect::<Result<Vec<RelationType>, _>>()
544                                    .or_else(|_| return Err(crate::Error::Header))
545                                    .ok()
546                            },
547                        }
548                    }
549                } else if "hreflang".eq_ignore_ascii_case(link_param_name) {
550                    // Parse target attribute: `hreflang`.
551                    // https://tools.ietf.org/html/rfc5988#section-5.4
552                    let mut v = link_header.href_lang.take().unwrap_or(Vec::new());
553
554                    v.push(
555                        match link_param_split.next() {
556                            None => return Err(crate::Error::Header),
557                            Some("") =>  return Err(crate::Error::Header),
558                            Some(s) => match s.trim().parse() {
559                                Err(_) => return Err(crate::Error::Header),
560                                Ok(t) => t,
561                            },
562                        }
563                    );
564
565                    link_header.href_lang = Some(v);
566                } else if "media".eq_ignore_ascii_case(link_param_name) {
567                    // Parse target attribute: `media`.
568                    // https://tools.ietf.org/html/rfc5988#section-5.4
569                    if link_header.media_desc.is_none() {
570                        link_header.media_desc = match link_param_split.next() {
571                            None => return Err(crate::Error::Header),
572                            Some("") =>  return Err(crate::Error::Header),
573                            Some(s) => {
574                                s.trim_matches(|c: char| c == '"' || c.is_whitespace())
575                                    .split(',')
576                                    .map(|t| t.trim().parse())
577                                    .collect::<Result<Vec<MediaDesc>, _>>()
578                                    .or_else(|_| return Err(crate::Error::Header))
579                                    .ok()
580                            },
581                        };
582                    }
583                } else if "title".eq_ignore_ascii_case(link_param_name) {
584                    // Parse target attribute: `title`.
585                    // https://tools.ietf.org/html/rfc5988#section-5.4
586                    if link_header.title.is_none() {
587                        link_header.title = match link_param_split.next() {
588                            None => return Err(crate::Error::Header),
589                            Some("") =>  return Err(crate::Error::Header),
590                            Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) {
591                                Err(_) => return Err(crate::Error::Header),
592                                Ok(t) => Some(String::from(t)),
593                            },
594                        };
595                    }
596                } else if "title*".eq_ignore_ascii_case(link_param_name) {
597                    // Parse target attribute: `title*`.
598                    // https://tools.ietf.org/html/rfc5988#section-5.4
599                    //
600                    // Definition of `ext-value`:
601                    //       https://tools.ietf.org/html/rfc5987#section-3.2.1
602                    if link_header.title_star.is_none() {
603                        link_header.title_star = match link_param_split.next() {
604                            None => return Err(crate::Error::Header),
605                            Some("") =>  return Err(crate::Error::Header),
606                            Some(s) => Some(String::from(s.trim())),
607                        };
608                    }
609                } else if "type".eq_ignore_ascii_case(link_param_name) {
610                    // Parse target attribute: `type`.
611                    // https://tools.ietf.org/html/rfc5988#section-5.4
612                    if link_header.media_type.is_none() {
613                        link_header.media_type = match link_param_split.next() {
614                            None => return Err(crate::Error::Header),
615                            Some("") =>  return Err(crate::Error::Header),
616                            Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) {
617                                Err(_) => return Err(crate::Error::Header),
618                                Ok(t) => match t.parse() {
619                                    Err(_) => return Err(crate::Error::Header),
620                                    Ok(m) => Some(m),
621                                },
622                            },
623
624                        };
625                    }
626                } else {
627                    return Err(crate::Error::Header);
628                }
629            }
630        }
631
632        Ok(Link::new(link_values))
633    }
634}
635
636impl fmt::Display for MediaDesc {
637    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
638        match *self {
639            MediaDesc::Screen => write!(f, "screen"),
640            MediaDesc::Tty => write!(f, "tty"),
641            MediaDesc::Tv => write!(f, "tv"),
642            MediaDesc::Projection => write!(f, "projection"),
643            MediaDesc::Handheld => write!(f, "handheld"),
644            MediaDesc::Print => write!(f, "print"),
645            MediaDesc::Braille => write!(f, "braille"),
646            MediaDesc::Aural => write!(f, "aural"),
647            MediaDesc::All => write!(f, "all"),
648            MediaDesc::Extension(ref other) => write!(f, "{}", other),
649         }
650    }
651}
652
653impl FromStr for MediaDesc {
654    type Err = crate::Error;
655
656    fn from_str(s: &str) -> crate::Result<MediaDesc> {
657        match s {
658            "screen" => Ok(MediaDesc::Screen),
659            "tty" => Ok(MediaDesc::Tty),
660            "tv" => Ok(MediaDesc::Tv),
661            "projection" => Ok(MediaDesc::Projection),
662            "handheld" => Ok(MediaDesc::Handheld),
663            "print" => Ok(MediaDesc::Print),
664            "braille" => Ok(MediaDesc::Braille),
665            "aural" => Ok(MediaDesc::Aural),
666            "all" => Ok(MediaDesc::All),
667             _ => Ok(MediaDesc::Extension(String::from(s))),
668        }
669    }
670}
671
672impl fmt::Display for RelationType {
673    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
674        match *self {
675            RelationType::Alternate => write!(f, "alternate"),
676            RelationType::Appendix => write!(f, "appendix"),
677            RelationType::Bookmark => write!(f, "bookmark"),
678            RelationType::Chapter => write!(f, "chapter"),
679            RelationType::Contents => write!(f, "contents"),
680            RelationType::Copyright => write!(f, "copyright"),
681            RelationType::Current => write!(f, "current"),
682            RelationType::DescribedBy => write!(f, "describedby"),
683            RelationType::Edit => write!(f, "edit"),
684            RelationType::EditMedia => write!(f, "edit-media"),
685            RelationType::Enclosure => write!(f, "enclosure"),
686            RelationType::First => write!(f, "first"),
687            RelationType::Glossary => write!(f, "glossary"),
688            RelationType::Help => write!(f, "help"),
689            RelationType::Hub => write!(f, "hub"),
690            RelationType::Index => write!(f, "index"),
691            RelationType::Last => write!(f, "last"),
692            RelationType::LatestVersion => write!(f, "latest-version"),
693            RelationType::License => write!(f, "license"),
694            RelationType::Next => write!(f, "next"),
695            RelationType::NextArchive => write!(f, "next-archive"),
696            RelationType::Payment => write!(f, "payment"),
697            RelationType::Prev => write!(f, "prev"),
698            RelationType::PredecessorVersion => write!(f, "predecessor-version"),
699            RelationType::Previous => write!(f, "previous"),
700            RelationType::PrevArchive => write!(f, "prev-archive"),
701            RelationType::Related => write!(f, "related"),
702            RelationType::Replies => write!(f, "replies"),
703            RelationType::Section => write!(f, "section"),
704            RelationType::RelationTypeSelf => write!(f, "self"),
705            RelationType::Service => write!(f, "service"),
706            RelationType::Start => write!(f, "start"),
707            RelationType::Stylesheet => write!(f, "stylesheet"),
708            RelationType::Subsection => write!(f, "subsection"),
709            RelationType::SuccessorVersion => write!(f, "successor-version"),
710            RelationType::Up => write!(f, "up"),
711            RelationType::VersionHistory => write!(f, "version-history"),
712            RelationType::Via => write!(f, "via"),
713            RelationType::WorkingCopy => write!(f, "working-copy"),
714            RelationType::WorkingCopyOf => write!(f, "working-copy-of"),
715            RelationType::ExtRelType(ref uri) => write!(f, "{}", uri),
716         }
717    }
718}
719
720impl FromStr for RelationType {
721    type Err = crate::Error;
722
723    fn from_str(s: &str) -> crate::Result<RelationType> {
724        if "alternate".eq_ignore_ascii_case(s) {
725            Ok(RelationType::Alternate)
726        } else if "appendix".eq_ignore_ascii_case(s) {
727            Ok(RelationType::Appendix)
728        } else if "bookmark".eq_ignore_ascii_case(s) {
729            Ok(RelationType::Bookmark)
730        } else if "chapter".eq_ignore_ascii_case(s) {
731            Ok(RelationType::Chapter)
732        } else if "contents".eq_ignore_ascii_case(s) {
733            Ok(RelationType::Contents)
734        } else if "copyright".eq_ignore_ascii_case(s) {
735            Ok(RelationType::Copyright)
736        } else if "current".eq_ignore_ascii_case(s) {
737            Ok(RelationType::Current)
738        } else if "describedby".eq_ignore_ascii_case(s) {
739            Ok(RelationType::DescribedBy)
740        } else if "edit".eq_ignore_ascii_case(s) {
741            Ok(RelationType::Edit)
742        } else if "edit-media".eq_ignore_ascii_case(s) {
743            Ok(RelationType::EditMedia)
744        } else if "enclosure".eq_ignore_ascii_case(s) {
745            Ok(RelationType::Enclosure)
746        } else if "first".eq_ignore_ascii_case(s) {
747            Ok(RelationType::First)
748        } else if "glossary".eq_ignore_ascii_case(s) {
749            Ok(RelationType::Glossary)
750        } else if "help".eq_ignore_ascii_case(s) {
751            Ok(RelationType::Help)
752        } else if "hub".eq_ignore_ascii_case(s) {
753            Ok(RelationType::Hub)
754        } else if "index".eq_ignore_ascii_case(s) {
755            Ok(RelationType::Index)
756        } else if "last".eq_ignore_ascii_case(s) {
757            Ok(RelationType::Last)
758        } else if "latest-version".eq_ignore_ascii_case(s) {
759            Ok(RelationType::LatestVersion)
760        } else if "license".eq_ignore_ascii_case(s) {
761            Ok(RelationType::License)
762        } else if "next".eq_ignore_ascii_case(s) {
763            Ok(RelationType::Next)
764        } else if "next-archive".eq_ignore_ascii_case(s) {
765            Ok(RelationType::NextArchive)
766        } else if "payment".eq_ignore_ascii_case(s) {
767            Ok(RelationType::Payment)
768        } else if "prev".eq_ignore_ascii_case(s) {
769            Ok(RelationType::Prev)
770        } else if "predecessor-version".eq_ignore_ascii_case(s) {
771            Ok(RelationType::PredecessorVersion)
772        } else if "previous".eq_ignore_ascii_case(s) {
773            Ok(RelationType::Previous)
774        } else if "prev-archive".eq_ignore_ascii_case(s) {
775            Ok(RelationType::PrevArchive)
776        } else if "related".eq_ignore_ascii_case(s) {
777            Ok(RelationType::Related)
778        } else if "replies".eq_ignore_ascii_case(s) {
779            Ok(RelationType::Replies)
780        } else if "section".eq_ignore_ascii_case(s) {
781            Ok(RelationType::Section)
782        } else if "self".eq_ignore_ascii_case(s) {
783            Ok(RelationType::RelationTypeSelf)
784        } else if "service".eq_ignore_ascii_case(s) {
785            Ok(RelationType::Service)
786        } else if "start".eq_ignore_ascii_case(s) {
787            Ok(RelationType::Start)
788        } else if "stylesheet".eq_ignore_ascii_case(s) {
789            Ok(RelationType::Stylesheet)
790        } else if "subsection".eq_ignore_ascii_case(s) {
791            Ok(RelationType::Subsection)
792        } else if "successor-version".eq_ignore_ascii_case(s) {
793            Ok(RelationType::SuccessorVersion)
794        } else if "up".eq_ignore_ascii_case(s) {
795            Ok(RelationType::Up)
796        } else if "version-history".eq_ignore_ascii_case(s) {
797            Ok(RelationType::VersionHistory)
798        } else if "via".eq_ignore_ascii_case(s) {
799            Ok(RelationType::Via)
800        } else if "working-copy".eq_ignore_ascii_case(s) {
801            Ok(RelationType::WorkingCopy)
802        } else if "working-copy-of".eq_ignore_ascii_case(s) {
803            Ok(RelationType::WorkingCopyOf)
804        } else {
805            Ok(RelationType::ExtRelType(String::from(s)))
806        }
807    }
808}
809
810////////////////////////////////////////////////////////////////////////////////
811// Utilities
812////////////////////////////////////////////////////////////////////////////////
813
814struct SplitAsciiUnquoted<'a> {
815    src: &'a str,
816    pos: usize,
817    del: &'a str
818}
819
820impl<'a> SplitAsciiUnquoted<'a> {
821    fn new(s: &'a str, d: &'a str) -> SplitAsciiUnquoted<'a> {
822        SplitAsciiUnquoted{
823            src: s,
824            pos: 0,
825            del: d,
826        }
827    }
828}
829
830impl<'a> Iterator for SplitAsciiUnquoted<'a> {
831    type Item = &'a str;
832
833    fn next(&mut self) -> Option<&'a str> {
834        if self.pos < self.src.len() {
835            let prev_pos = self.pos;
836            let mut pos = self.pos;
837
838            let mut in_quotes = false;
839
840            for c in self.src[prev_pos..].as_bytes().iter() {
841                in_quotes ^= *c == b'"';
842
843                // Ignore `c` if we're `in_quotes`.
844                if !in_quotes && self.del.as_bytes().contains(c) {
845                    break;
846                }
847
848                pos += 1;
849            }
850
851            self.pos = pos + 1;
852
853            Some(&self.src[prev_pos..pos])
854        } else {
855            None
856        }
857    }
858}
859
860fn fmt_delimited<T: fmt::Display>(f: &mut fmt::Formatter, p: &[T], d: &str, b: (&str, &str)) -> fmt::Result {
861    if p.len() != 0 {
862        // Write a starting string `b.0` before the first element
863        r#try!(write!(f, "{}{}", b.0, p[0]));
864
865        for i in &p[1..] {
866            // Write the next element preceded by the delimiter `d`
867            r#try!(write!(f, "{}{}", d, i));
868        }
869
870        // Write a ending string `b.1` before the first element
871        r#try!(write!(f, "{}", b.1));
872    }
873
874    Ok(())
875}
876
877fn verify_and_trim(s: &str, b: (u8, u8)) -> crate::Result<&str> {
878    let length = s.len();
879    let byte_array = s.as_bytes();
880
881    // Verify that `s` starts with `b.0` and ends with `b.1` and return
882    // the contained substring after triming whitespace.
883    if length > 1 && b.0 == byte_array[0] && b.1 == byte_array[length - 1] {
884        Ok(s.trim_matches(
885            |c: char| c == b.0 as char || c == b.1 as char || c.is_whitespace())
886        )
887    } else {
888        Err(crate::Error::Header)
889    }
890}
891
892////////////////////////////////////////////////////////////////////////////////
893// Tests
894////////////////////////////////////////////////////////////////////////////////
895
896#[cfg(test)]
897mod tests {
898    use std::fmt;
899    use std::fmt::Write;
900
901    use super::{Link, LinkValue, MediaDesc, RelationType, SplitAsciiUnquoted};
902    use super::{fmt_delimited, verify_and_trim};
903
904    use crate::header::Header;
905
906    use crate::buffer::BufReader;
907    use crate::mock::MockStream;
908    use crate::http::h1::parse_request;
909
910    use mime::Mime;
911    use mime::TopLevel::Text;
912    use mime::SubLevel::Plain;
913
914    #[test]
915    fn test_link() {
916        let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
917            .push_rel(RelationType::Previous)
918            .push_rev(RelationType::Next)
919            .set_title("previous chapter");
920
921        let link_header = b"<http://example.com/TheBook/chapter2>; \
922            rel=\"previous\"; rev=next; title=\"previous chapter\"";
923
924        let expected_link = Link::new(vec![link_value]);
925
926        let link = Header::parse_header(&vec![link_header.to_vec()]);
927        assert_eq!(link.ok(), Some(expected_link));
928    }
929
930    #[test]
931    fn test_link_multiple_values() {
932        let first_link = LinkValue::new("/TheBook/chapter2")
933            .push_rel(RelationType::Previous)
934            .set_title_star("UTF-8'de'letztes%20Kapitel");
935
936        let second_link = LinkValue::new("/TheBook/chapter4")
937            .push_rel(RelationType::Next)
938            .set_title_star("UTF-8'de'n%c3%a4chstes%20Kapitel");
939
940        let link_header = b"</TheBook/chapter2>; \
941            rel=\"previous\"; title*=UTF-8'de'letztes%20Kapitel, \
942            </TheBook/chapter4>; \
943            rel=\"next\"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel";
944
945        let expected_link = Link::new(vec![first_link, second_link]);
946
947        let link = Header::parse_header(&vec![link_header.to_vec()]);
948        assert_eq!(link.ok(), Some(expected_link));
949    }
950
951    #[test]
952    fn test_link_all_attributes() {
953        let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
954            .push_rel(RelationType::Previous)
955            .set_anchor("../anchor/example/")
956            .push_rev(RelationType::Next)
957            .push_href_lang(langtag!(de))
958            .push_media_desc(MediaDesc::Screen)
959            .set_title("previous chapter")
960            .set_title_star("title* unparsed")
961            .set_media_type(Mime(Text, Plain, vec![]));
962
963        let link_header = b"<http://example.com/TheBook/chapter2>; \
964            rel=\"previous\"; anchor=\"../anchor/example/\"; \
965            rev=\"next\"; hreflang=de; media=\"screen\"; \
966            title=\"previous chapter\"; title*=title* unparsed; \
967            type=\"text/plain\"";
968
969        let expected_link = Link::new(vec![link_value]);
970
971        let link = Header::parse_header(&vec![link_header.to_vec()]);
972        assert_eq!(link.ok(), Some(expected_link));
973    }
974
975    #[test]
976    fn test_link_multiple_link_headers() {
977        let first_link = LinkValue::new("/TheBook/chapter2")
978            .push_rel(RelationType::Previous)
979            .set_title_star("UTF-8'de'letztes%20Kapitel");
980
981        let second_link = LinkValue::new("/TheBook/chapter4")
982            .push_rel(RelationType::Next)
983            .set_title_star("UTF-8'de'n%c3%a4chstes%20Kapitel");
984
985        let third_link = LinkValue::new("http://example.com/TheBook/chapter2")
986            .push_rel(RelationType::Previous)
987            .push_rev(RelationType::Next)
988            .set_title("previous chapter");
989
990        let expected_link = Link::new(vec![first_link, second_link, third_link]);
991
992        let mut raw = MockStream::with_input(b"GET /super_short_uri/and_whatever HTTP/1.1\r\nHost: \
993                                  hyper.rs\r\nAccept: a lot of things\r\nAccept-Charset: \
994                                  utf8\r\nAccept-Encoding: *\r\nLink: </TheBook/chapter2>; \
995                                  rel=\"previous\"; title*=UTF-8'de'letztes%20Kapitel, \
996                                  </TheBook/chapter4>; rel=\"next\"; title*=\
997                                  UTF-8'de'n%c3%a4chstes%20Kapitel\r\n\
998                                  Access-Control-Allow-Credentials: None\r\nLink: \
999                                  <http://example.com/TheBook/chapter2>; \
1000                                  rel=\"previous\"; rev=next; title=\"previous chapter\"\
1001                                  \r\n\r\n");
1002
1003        let mut buf = BufReader::new(&mut raw);
1004        let res = parse_request(&mut buf).unwrap();
1005
1006        let link = res.headers.get::<Link>().unwrap();
1007
1008        assert_eq!(*link, expected_link);
1009    }
1010
1011    #[test]
1012    fn test_link_display() {
1013        let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
1014            .push_rel(RelationType::Previous)
1015            .set_anchor("/anchor/example/")
1016            .push_rev(RelationType::Next)
1017            .push_href_lang(langtag!(de))
1018            .push_media_desc(MediaDesc::Screen)
1019            .set_title("previous chapter")
1020            .set_title_star("title* unparsed")
1021            .set_media_type(Mime(Text, Plain, vec![]));
1022
1023        let link = Link::new(vec![link_value]);
1024
1025        let mut link_header = String::new();
1026        write!(&mut link_header, "{}", link).unwrap();
1027
1028        let expected_link_header = "<http://example.com/TheBook/chapter2>; \
1029            rel=\"previous\"; anchor=\"/anchor/example/\"; \
1030            rev=\"next\"; hreflang=de; media=\"screen\"; \
1031            title=\"previous chapter\"; title*=title* unparsed; \
1032            type=\"text/plain\"";
1033
1034        assert_eq!(link_header, expected_link_header);
1035    }
1036
1037    #[test]
1038    fn test_link_parsing_errors() {
1039        let link_a = b"http://example.com/TheBook/chapter2; \
1040            rel=\"previous\"; rev=next; title=\"previous chapter\"";
1041
1042        let mut err: Result<Link, _> = Header::parse_header(&vec![link_a.to_vec()]);
1043        assert_eq!(err.is_err(), true);
1044
1045        let link_b = b"<http://example.com/TheBook/chapter2>; \
1046            =\"previous\"; rev=next; title=\"previous chapter\"";
1047
1048        err = Header::parse_header(&vec![link_b.to_vec()]);
1049        assert_eq!(err.is_err(), true);
1050
1051        let link_c = b"<http://example.com/TheBook/chapter2>; \
1052            rel=; rev=next; title=\"previous chapter\"";
1053
1054        err = Header::parse_header(&vec![link_c.to_vec()]);
1055        assert_eq!(err.is_err(), true);
1056
1057        let link_d = b"<http://example.com/TheBook/chapter2>; \
1058            rel=\"previous\"; rev=next; title=";
1059
1060        err = Header::parse_header(&vec![link_d.to_vec()]);
1061        assert_eq!(err.is_err(), true);
1062
1063        let link_e = b"<http://example.com/TheBook/chapter2>; \
1064            rel=\"previous\"; rev=next; attr=unknown";
1065
1066        err = Header::parse_header(&vec![link_e.to_vec()]);
1067        assert_eq!(err.is_err(), true);
1068    }
1069
1070    #[test]
1071    fn test_link_split_ascii_unquoted_iterator() {
1072        let string = "some, text; \"and, more; in quotes\", or not";
1073        let mut string_split = SplitAsciiUnquoted::new(string, ";,");
1074
1075        assert_eq!(Some("some"), string_split.next());
1076        assert_eq!(Some(" text"), string_split.next());
1077        assert_eq!(Some(" \"and, more; in quotes\""), string_split.next());
1078        assert_eq!(Some(" or not"), string_split.next());
1079        assert_eq!(None, string_split.next());
1080    }
1081
1082    #[test]
1083    fn test_link_fmt_delimited() {
1084        struct TestFormatterStruct<'a> { v: Vec<&'a str> };
1085
1086        impl<'a> fmt::Display for TestFormatterStruct<'a> {
1087            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1088                fmt_delimited(f, self.v.as_slice(), ", ", (">>", "<<"))
1089            }
1090        }
1091
1092        let test_formatter = TestFormatterStruct { v: vec!["first", "second"] };
1093
1094        let mut string = String::new();
1095        write!(&mut string, "{}", test_formatter).unwrap();
1096
1097        let expected_string = ">>first, second<<";
1098
1099        assert_eq!(string, expected_string);
1100    }
1101
1102    #[test]
1103    fn test_link_verify_and_trim() {
1104        let string = verify_and_trim(">  some string   <", (b'>', b'<'));
1105        assert_eq!(string.ok(), Some("some string"));
1106
1107        let err = verify_and_trim(" >  some string   <", (b'>', b'<'));
1108        assert_eq!(err.is_err(), true);
1109    }
1110}
1111
1112bench_header!(bench_link, Link, { vec![b"<http://example.com/TheBook/chapter2>; rel=\"previous\"; rev=next; title=\"previous chapter\"; type=\"text/html\"; media=\"screen, tty\"".to_vec()] });