Giter Site home page Giter Site logo

node-http-link-header's Introduction

HTTP Link Header

npm npm license npm downloads

Parse & format HTTP link headers according to RFC 8288

Install via npm

$ npm install --save http-link-header

Deviations from the RFC

Link Target

While RFC 8288, Section 3.1 states that relative URI-References MUST be resolved by the parsers – this library DOES NOT. This is due to the parser not having an input for the absolute or canonical URI of the related document. Currently there are no plans to add this, and it is left to the user whether or not to resolve relative URIs.

Usage

var LinkHeader = require( 'http-link-header' )

Parsing a HTTP link header

var link = LinkHeader.parse(
  '<example.com>; rel="example"; title="Example Website", ' +
  '<example-01.com>; rel="alternate"; title="Alternate Example Domain"'
)

> Link {
  refs: [
    { uri: 'example.com', rel: 'example', title: 'Example Website' },
    { uri: 'example-01.com', rel: 'alternate', title: 'Alternate Example Domain' },
  ]
}

Checking whether it has a reference with a given attribute & value

link.has( 'rel', 'alternate' )
> true

Retrieving a reference with a given attribute & value

link.get( 'rel', 'alternate' )
> [
  { uri: 'example-01.com', rel: 'alternate', title: 'Alternate Example Domain' }
]
// Shorthand for `rel` attributes
link.rel( 'alternate' )
> [
  { uri: 'example-01.com', rel: 'alternate', title: 'Alternate Example Domain' }
]

Setting references

link.set({ uri: 'https://example.com/next', rel: 'next' })
> Link {
  refs: [
    { uri: 'example.com', rel: 'example', title: 'Example Website' },
    { uri: 'example-01.com', rel: 'alternate', title: 'Alternate Example Domain' },
    { rel: 'next', uri: 'https://example.com/next' }
  ]
}

Setting a unique reference

link.setUnique({
  uri: 'https://example.com/image.png',
  rel: 'preload',
  as: 'image',
  type: 'image/png'
})
> Link {
  refs: [
    { uri: 'https://example.com/image.png', rel: 'preload', as: 'image', type: 'image/png' }
  ]
}

link.setUnique({
  uri: 'https://example.com/image.png',
  rel: 'preload',
  as: 'image',
  type: 'image/png'
})
> Link {
  refs: [
    { uri: 'https://example.com/image.png', rel: 'preload', as: 'image', type: 'image/png' }
  ]
}

Parsing multiple headers

var link = new LinkHeader()

link.parse( '<example.com>; rel="example"; title="Example Website"' )
> Link {
  refs: [
    { uri: 'example.com', rel: 'example', title: 'Example Website' },
  ]
}

link.parse( '<example-01.com>; rel="alternate"; title="Alternate Example Domain"' )
> Link {
  refs: [
    { uri: 'example.com', rel: 'example', title: 'Example Website' },
    { uri: 'example-01.com', rel: 'alternate', title: 'Alternate Example Domain' },
  ]
}

link.parse( '<example-02.com>; rel="alternate"; title="Second Alternate Example Domain"' )
> Link {
  refs: [
    { uri: 'example.com', rel: 'example', title: 'Example Website' },
    { uri: 'example-01.com', rel: 'alternate', title: 'Alternate Example Domain' },
    { uri: 'example-02.com', rel: 'alternate', title: 'Second Alternate Example Domain' },
  ]
}

Handling extended attributes

link.parse( '</extended-attr-example>; rel=start; title*=UTF-8\'en\'%E2%91%A0%E2%93%AB%E2%85%93%E3%8F%A8%E2%99%B3%F0%9D%84%9E%CE%BB' )
> Link {
  refs: [
    { uri: '/extended-attr-example', rel: 'start', 'title*': { language: 'en', encoding: null, value: '①⓫⅓㏨♳𝄞λ' } }
  ]
}

Stringifying to HTTP header format

link.toString()
> '<example.com>; rel=example; title="Example Website", <example-01.com>; rel=alternate; title="Alternate Example Domain"'

Speed

$ npm run benchmark
# http-link-header .parse() ⨉ 1000000
ok ~1.29 s (1 s + 289696759 ns)

# http-link-header #toString() ⨉ 1000000
ok ~554 ms (0 s + 553782657 ns)

node-http-link-header's People

Contributors

adamraine avatar angelo-v avatar csarven avatar dhui avatar jaydenseric avatar jhermsmeier avatar qubyte avatar xadhoom avatar ykzts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

node-http-link-header's Issues

Responsible disclosure policy

Hey there!

I belong to an open source security research community, and a member (@yetingli) has found an issue, but doesn’t know the best way to disclose it.

If not a hassle, might you kindly add a SECURITY.md file with an email, or another contact method? GitHub recommends this best practice to ensure security issues are responsibly disclosed, and it would serve as a simple instruction for security researchers in the future.

Thank you for your consideration, and I look forward to hearing from you!

(cc @huntr-helper)

rel can have multiple values

RFC 8288 says:

The rel parameter can, however, contain multiple link relation types. When this occurs, it establishes multiple links that share the same context, target, and target attributes.

And also:

Note that link-values can convey multiple links between the same link target and link context; for example:

Link: <http://example.org/>;
      rel="start http://example.net/relation/other"

Here, the link to “http://example.org/” has the registered relation type “start” and the extension relation type “http://example.net/relation/other”.

However, if I were to parse that header using http-link-header and then call .get('rel', 'start') or .rel('start'), I get an empty array. The expected result is [{rel: "start http://example.net/relation/other", uri: "http://example.org/"}], at least for the .rel call. See https://runkit.com/embed/trgz6e845y89

Multiple usage of link header `rel`

I have a question regarding the following statement in the README:

NOTE: According to RFC 5988 only one reference with the same rel attribute is allowed, so the following effectively replaces an existing reference

I read the RFC twice now and cannot find any indication of this being specified there.

Especially setting multiple preload headers makes sense.
And there is the PubSubHubbub draft which specifically states, that there may be more than one rel=hub headers:

The HTTP [RFC2616] response from the publisher MUST include at least one Link Header [RFC5988] with rel=hub (a hub link header) as well as exactly one Link Header [RFC5988] with rel=self (the self link header)

So I was wondering if an API change to accommodate multiple link headers would make sense for 1.0.0

Link.has(attr, value) always returns true

Problem

Link.has('someattr', 'somevalue') always returns true, even if 'someattr' and 'somevalue' are not present in link header.

Technical description

This is what Link.has(attr, value) (Line #101) looks like:

has( attr, value ) {
  return this.get( attr, value ) != null
}

It checks if Link.get(attr, value) returned null.

Link.get(attr, value) (Line #83) always returns an Array, never null. In case the provided attr/value pair is not present, an empty array is returned. An empty array is truthy and not null, thus Link.has always returns true.

Solution

Either change Link.has to check for this.get( attr, value ).length or update Link.get to return null when the array is empty. I tend to prefer the latter, because then the returned value is falsy.

ref storage

We can improve the performance of ref lookups by using a map

The Map object holds key-value pairs and remembers the original insertion order of the keys. Any value (both objects and primitive values) may be used as either a key or a value.

Support serialization of extended attributes

{
  uri: '/risk-mitigation',
  rel: 'start',
  'title*': {
    language: 'en',
    encoding: null,
    value: '①⓫⅓㏨♳𝄞λ',
  }
}
title*=UTF-8\'en\'%E2%91%A0%E2%93%AB%E2%85%93%E3%8F%A8%E2%99%B3%F0%9D%84%9E%CE%BB

Attributes containing "/" should be quoted?

I'm not sure if the type value is required by spec to be quoted, because it has a "/":

- <https://gravatar.com/avatar/66129e417727c0749bdfb82cc222cf09.jpg?d=mp&s=450>; rel=preload; as=image; type=image/jpeg
+ <https://gravatar.com/avatar/66129e417727c0749bdfb82cc222cf09.jpg?d=mp&s=450>; rel=preload; as=image; type="image/jpeg"

Currently this lib does not quote values containing "/".

The "type" header field parameter now needs to be quoted (as "token" does not allow "/").
https://tools.ietf.org/html/rfc8288#appendix-C

A "token" is a value without quotes. I've gone around in circles in the IETF specs trying to see exactly where the definition of a "token" is, and specifically what strings are forbidden from occuring in a token.

Although it would be nice if this lib would automatically apply quotes as needed, is there a way to force quotes for particular values? I considered using quotes in the strings, e.g. '"image/jpeg"' but then maybe this lib might add an extra layer of quotes on top if the value contains , or ; or something else that may trigger quotes.

URI relations should not be lowercased

The rel can be a URI:

The ABNF for the rel and rev parameters’ values is:

relation-type *( 1*SP relation-type )

where:

relation-type  = reg-rel-type / ext-rel-type
reg-rel-type   = LOALPHA *( LOALPHA / DIGIT / "." / "-" )
ext-rel-type   = URI ; Section 3 of [RFC3986]

https://httpwg.org/specs/rfc8288.html#header-type

Looking at that RFC3986, while the scheme and the host are case insensitive, the other parts can be case sensitive, depending on the scheme used:

When a URI uses components of the generic syntax, the component
syntax equivalence rules always apply; namely, that the scheme and
host are case-insensitive and therefore should be normalized to
lowercase. For example, the URI HTTP://www.EXAMPLE.com/ is
equivalent to http://www.example.com/. The other generic syntax
components are assumed to be case-sensitive unless specifically
defined otherwise by the scheme (see Section 6.2.3).

https://tools.ietf.org/html/rfc3986#section-6.2.2.1

However, when encountering a rel with non-lowercase characters in the other components, http-link-header converts them to lowercase:

const httpLinkHeader = require("http-link-header")

const links = httpLinkHeader.parse('<https://some.url>; rel="http://some.rel/caseSensitive"');

console.log(links.refs);
// => [Object {rel: "http://some.rel/casesensitive", uri: "https://some.url"}]

https://codesandbox.io/s/charming-tdd-syip6

(Apologies if I misinterpreted any of the above RFCs.)

Check on extended attribute formats & usage

There needs to be some more investigation into extended attributes and their usage in the wild.
So far I've only come across text* in the spec, but haven't seen it used anywhere.

Error on link header with `nopush`

Downstream bug: GoogleChrome/lighthouse#14934

The parse function throws a Expected attribute delimiter at offset 94 when nopush is in a link header. nopush should be valid syntax here.

Repro script:

import LinkHeader from 'http-link-header';

const headerValue = '<https://assets.calendly.com/assets/booking/css/booking-d0ac32b1.css>; rel=preload; as=style; nopush';
const refs = LinkHeader.parse(headerValue);

Make parse fn robust when undefined

When you do something like LinkHeader.parse(undefined) it crashes with:

Uncaught TypeError: Cannot read property 'replace' of undefined

I'd make this a bit more robust and would just return undefined as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.