From beaa8b1bcabd4be25ac26bea39ab9f7290147e67 Mon Sep 17 00:00:00 2001 From: bep Date: Sat, 16 May 2015 00:11:39 +0200 Subject: Add support for URLs relative to context root Setting `RelativeURLs` to `true` will make all relative URLs in the site *really* relative. And will do so with speed. So: In `/post/myblogpost.html`: `/mycss.css` becomes `../mycss.css` The same in `/index.html` will become: `./mycss.css` etc. Note that absolute URLs will not be touched (either external resources, or URLs constructed with `BaseURL`). The speediness is about the same as before: ``` benchmark old ns/op new ns/op delta BenchmarkAbsURL 17462 18164 +4.02% BenchmarkAbsURLSrcset 18842 19632 +4.19% BenchmarkXMLAbsURLSrcset 18643 19313 +3.59% BenchmarkXMLAbsURL 9283 9656 +4.02% benchmark old allocs new allocs delta BenchmarkAbsURL 24 28 +16.67% BenchmarkAbsURLSrcset 29 32 +10.34% BenchmarkXMLAbsURLSrcset 27 30 +11.11% BenchmarkXMLAbsURL 12 14 +16.67% benchmark old bytes new bytes delta BenchmarkAbsURL 3154 3404 +7.93% BenchmarkAbsURLSrcset 2376 2573 +8.29% BenchmarkXMLAbsURLSrcset 2569 2763 +7.55% BenchmarkXMLAbsURL 1888 1998 +5.83% ``` Fixes #1104 Fixes #622 Fixes #937 Fixes #157 --- transform/absurl.go | 57 ++++----------------------------------------- transform/absurlreplacer.go | 29 +++++++++++------------ transform/chain.go | 10 ++++++-- transform/chain_test.go | 53 ++++++++++++++++++++++++----------------- 4 files changed, 59 insertions(+), 90 deletions(-) (limited to 'transform') diff --git a/transform/absurl.go b/transform/absurl.go index 75dbe4db3..7f06056f0 100644 --- a/transform/absurl.go +++ b/transform/absurl.go @@ -1,58 +1,11 @@ package transform -import ( - "github.com/spf13/viper" - "sync" -) +var ar *absURLReplacer = newAbsURLReplacer() -// to be used in tests; the live site will get its value from Viper. -var AbsBaseUrl string - -var absURLInit sync.Once -var ar *absURLReplacer - -func AbsURL() (trs []link, err error) { - initAbsURLReplacer() - return absURLFromReplacer(ar) -} - -func absURLFromURL(URL string) (trs []link, err error) { - return absURLFromReplacer(newAbsURLReplacer(URL)) -} - -func absURLFromReplacer(ar *absURLReplacer) (trs []link, err error) { - trs = append(trs, func(ct contentTransformer) { - ar.replaceInHTML(ct) - }) - return -} - -func AbsURLInXML() (trs []link, err error) { - initAbsURLReplacer() - return absURLInXMLFromReplacer(ar) -} - -func absURLInXMLFromURL(URL string) (trs []link, err error) { - return absURLInXMLFromReplacer(newAbsURLReplacer(URL)) +var AbsURL = func(ct contentTransformer) { + ar.replaceInHTML(ct) } -func absURLInXMLFromReplacer(ar *absURLReplacer) (trs []link, err error) { - trs = append(trs, func(ct contentTransformer) { - ar.replaceInXML(ct) - }) - return -} - -func initAbsURLReplacer() { - absURLInit.Do(func() { - var url string - - if AbsBaseUrl != "" { - url = AbsBaseUrl - } else { - url = viper.GetString("BaseURL") - } - - ar = newAbsURLReplacer(url) - }) +var AbsURLInXML = func(ct contentTransformer) { + ar.replaceInXML(ct) } diff --git a/transform/absurlreplacer.go b/transform/absurlreplacer.go index e1ba9e54c..35f02ade3 100644 --- a/transform/absurlreplacer.go +++ b/transform/absurlreplacer.go @@ -3,8 +3,6 @@ package transform import ( "bytes" "io" - "net/url" - "strings" "unicode/utf8" ) @@ -23,6 +21,9 @@ type absurllexer struct { // the target for the new absurlified content w io.Writer + // path may be set to a "." relative path + path []byte + pos int // input position start int // item start position width int // width of last element @@ -54,9 +55,8 @@ var prefixes = []*prefix{ } type absURLMatcher struct { - match []byte - quote []byte - replacementURL []byte + match []byte + quote []byte } // match check rune inside word. Will be != ' '. @@ -147,7 +147,7 @@ func checkCandidateBase(l *absurllexer) { } l.pos += len(m.match) l.w.Write(m.quote) - l.w.Write(m.replacementURL) + l.w.Write(l.path) l.start = l.pos } } @@ -188,7 +188,7 @@ func checkCandidateSrcset(l *absurllexer) { l.w.Write([]byte(m.quote)) for i, f := range fields { if f[0] == '/' { - l.w.Write(m.replacementURL) + l.w.Write(l.path) l.w.Write(f[1:]) } else { @@ -252,9 +252,11 @@ func (l *absurllexer) replace() { } func doReplace(ct contentTransformer, matchers []absURLMatcher) { + lexer := &absurllexer{ content: ct.Content(), w: ct, + path: ct.Path(), matchers: matchers} lexer.replace() @@ -265,9 +267,7 @@ type absURLReplacer struct { xmlMatchers []absURLMatcher } -func newAbsURLReplacer(baseURL string) *absURLReplacer { - u, _ := url.Parse(baseURL) - base := []byte(strings.TrimRight(u.String(), "/") + "/") +func newAbsURLReplacer() *absURLReplacer { // HTML dqHTMLMatch := []byte("\"/") @@ -285,14 +285,13 @@ func newAbsURLReplacer(baseURL string) *absURLReplacer { return &absURLReplacer{ htmlMatchers: []absURLMatcher{ - {dqHTMLMatch, dqHTML, base}, - {sqHTMLMatch, sqHTML, base}, + {dqHTMLMatch, dqHTML}, + {sqHTMLMatch, sqHTML}, }, xmlMatchers: []absURLMatcher{ - {dqXMLMatch, dqXML, base}, - {sqXMLMatch, sqXML, base}, + {dqXMLMatch, dqXML}, + {sqXMLMatch, sqXML}, }} - } func (au *absURLReplacer) replaceInHTML(ct contentTransformer) { diff --git a/transform/chain.go b/transform/chain.go index ab26a350e..4019dca25 100644 --- a/transform/chain.go +++ b/transform/chain.go @@ -23,6 +23,7 @@ func NewEmptyTransforms() []link { // contentTransformer is an interface that enables rotation of pooled buffers // in the transformer chain. type contentTransformer interface { + Path() []byte Content() []byte io.Writer } @@ -30,10 +31,15 @@ type contentTransformer interface { // Implements contentTransformer // Content is read from the from-buffer and rewritten to to the to-buffer. type fromToBuffer struct { + path []byte from *bytes.Buffer to *bytes.Buffer } +func (ft fromToBuffer) Path() []byte { + return ft.path +} + func (ft fromToBuffer) Write(p []byte) (n int, err error) { return ft.to.Write(p) } @@ -42,7 +48,7 @@ func (ft fromToBuffer) Content() []byte { return ft.from.Bytes() } -func (c *chain) Apply(w io.Writer, r io.Reader) error { +func (c *chain) Apply(w io.Writer, r io.Reader, p []byte) error { b1 := bp.GetBuffer() defer bp.PutBuffer(b1) @@ -57,7 +63,7 @@ func (c *chain) Apply(w io.Writer, r io.Reader) error { b2 := bp.GetBuffer() defer bp.PutBuffer(b2) - fb := &fromToBuffer{from: b1, to: b2} + fb := &fromToBuffer{path: p, from: b1, to: b2} for i, tr := range *c { if i > 0 { diff --git a/transform/chain_test.go b/transform/chain_test.go index cd6dfcde9..808c20121 100644 --- a/transform/chain_test.go +++ b/transform/chain_test.go @@ -3,6 +3,7 @@ package transform import ( "bytes" "github.com/spf13/hugo/helpers" + "path/filepath" "strings" "testing" ) @@ -62,6 +63,11 @@ schemaless: <img srcset='//img.jpg' src='//basic.jpg'> schemaless2: <img srcset="//img.jpg" src="//basic.jpg2> POST ` +const REL_PATH_VARIATIONS = `PRE. a href="/img/small.jpg" POST.` +const REL_PATH_VARIATIONS_CORRECT = `PRE. a href="../../img/small.jpg" POST.` + +const testBaseURL = "http://base/" + var abs_url_bench_tests = []test{ {H5_JS_CONTENT_DOUBLE_QUOTE, CORRECT_OUTPUT_SRC_HREF_DQ}, {H5_JS_CONTENT_SINGLE_QUOTE, CORRECT_OUTPUT_SRC_HREF_SQ}, @@ -85,11 +91,13 @@ var srcset_xml_tests = []test{ {SRCSET_XML_SINGLE_QUOTE, SRCSET_XML_SINGLE_QUOTE_CORRECT}, {SRCSET_XML_VARIATIONS, SRCSET_XML_VARIATIONS_CORRECT}} +var relurl_tests = []test{{REL_PATH_VARIATIONS, REL_PATH_VARIATIONS_CORRECT}} + func TestChainZeroTransformers(t *testing.T) { tr := NewChain() in := new(bytes.Buffer) out := new(bytes.Buffer) - if err := tr.Apply(in, out); err != nil { + if err := tr.Apply(in, out, []byte("")); err != nil { t.Errorf("A zero transformer chain returned an error.") } } @@ -112,7 +120,7 @@ func TestChaingMultipleTransformers(t *testing.T) { tr := NewChain(f1, f2, f3, f4) out := new(bytes.Buffer) - if err := tr.Apply(out, helpers.StringToReader("Test: f4 f3 f1 f2 f1 The End.")); err != nil { + if err := tr.Apply(out, helpers.StringToReader("Test: f4 f3 f1 f2 f1 The End."), []byte("")); err != nil { t.Errorf("Multi transformer chain returned an error: %s", err) } @@ -124,8 +132,7 @@ func TestChaingMultipleTransformers(t *testing.T) { } func BenchmarkAbsURL(b *testing.B) { - absURL, _ := absURLFromURL("http://base") - tr := NewChain(absURL...) + tr := NewChain(AbsURL) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -134,8 +141,7 @@ func BenchmarkAbsURL(b *testing.B) { } func BenchmarkAbsURLSrcset(b *testing.B) { - absURL, _ := absURLFromURL("http://base") - tr := NewChain(absURL...) + tr := NewChain(AbsURL) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -144,8 +150,7 @@ func BenchmarkAbsURLSrcset(b *testing.B) { } func BenchmarkXMLAbsURLSrcset(b *testing.B) { - absXMLURL, _ := absURLInXMLFromURL("http://base") - tr := NewChain(absXMLURL...) + tr := NewChain(AbsURLInXML) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -154,31 +159,33 @@ func BenchmarkXMLAbsURLSrcset(b *testing.B) { } func TestAbsURL(t *testing.T) { - absURL, _ := absURLFromURL("http://base") - tr := NewChain(absURL...) + tr := NewChain(AbsURL) apply(t.Errorf, tr, abs_url_tests) } +func TestRelativeURL(t *testing.T) { + tr := NewChain(AbsURL) + + applyWithPath(t.Errorf, tr, relurl_tests, helpers.GetDottedRelativePath(filepath.FromSlash("/post/sub/"))) + +} + func TestAbsURLSrcSet(t *testing.T) { - absURL, _ := absURLFromURL("http://base") - tr := NewChain(absURL...) + tr := NewChain(AbsURL) apply(t.Errorf, tr, srcset_tests) } func TestAbsXMLURLSrcSet(t *testing.T) { - absURLInXML, _ := absURLInXMLFromURL("http://base") - tr := NewChain(absURLInXML...) + tr := NewChain(AbsURLInXML) apply(t.Errorf, tr, srcset_xml_tests) - } func BenchmarkXMLAbsURL(b *testing.B) { - absURLInXML, _ := absURLInXMLFromURL("http://base") - tr := NewChain(absURLInXML...) + tr := NewChain(AbsURLInXML) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -187,17 +194,17 @@ func BenchmarkXMLAbsURL(b *testing.B) { } func TestXMLAbsURL(t *testing.T) { - absURLInXML, _ := absURLInXMLFromURL("http://base") - tr := NewChain(absURLInXML...) + tr := NewChain(AbsURLInXML) apply(t.Errorf, tr, xml_abs_url_tests) } type errorf func(string, ...interface{}) -func apply(ef errorf, tr chain, tests []test) { +func applyWithPath(ef errorf, tr chain, tests []test, path string) { for _, test := range tests { out := new(bytes.Buffer) - err := tr.Apply(out, strings.NewReader(test.content)) + var err error + err = tr.Apply(out, strings.NewReader(test.content), []byte(path)) if err != nil { ef("Unexpected error: %s", err) } @@ -207,6 +214,10 @@ func apply(ef errorf, tr chain, tests []test) { } } +func apply(ef errorf, tr chain, tests []test) { + applyWithPath(ef, tr, tests, testBaseURL) +} + type test struct { content string expected string -- cgit v1.2.3