Skip to content

Commit 53bd117

Browse files
authored
Merge pull request #91 from wader/inject-only-html
Only inject if content type is text/html
2 parents 93e3141 + 4f588db commit 53bd117

File tree

8 files changed

+88
-31
lines changed

8 files changed

+88
-31
lines changed

fileserver/fileserver.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func serveContent(ci inject.CopyInject, w http.ResponseWriter, r *http.Request,
126126
ctype = ctypes[0]
127127
}
128128

129-
injector, err := ci.Sniff(content)
129+
injector, err := ci.Sniff(content, ctype)
130130
if err != nil {
131131
http.Error(w, err.Error(), http.StatusInternalServerError)
132132
return err
@@ -138,7 +138,7 @@ func serveContent(ci inject.CopyInject, w http.ResponseWriter, r *http.Request,
138138
return err
139139
}
140140

141-
if injector.Found {
141+
if injector.Found() {
142142
size = size + int64(injector.Extra())
143143
}
144144

inject/inject.go

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,65 @@ import (
1515
"io"
1616
"net/http"
1717
"regexp"
18+
"strings"
1819
)
1920

2021
// CopyInject copies data, and injects a payload before a specified marker
2122
type CopyInject struct {
2223
// Number of initial bytes within which to search for marker
2324
Within int
25+
// Only inject in responses with this content type
26+
ContentType string
2427
// A marker, BEFORE which the payload is inserted
2528
Marker *regexp.Regexp
2629
// The payload to be inserted
2730
Payload []byte
2831
}
2932

30-
// Injector keeps injection state
31-
type Injector struct {
32-
// Has the marker been found?
33-
Found bool
33+
type Injector interface {
34+
Copy(dst io.Writer) (int64, error)
35+
Extra() int
36+
Found() bool
37+
}
3438

39+
// realInjector keeps injection state
40+
type realInjector struct {
41+
// Has the marker been found?
42+
found bool
3543
conf *CopyInject
3644
src io.Reader
3745
offset int
3846
sniffedData []byte
3947
}
4048

49+
type nopInjector struct {
50+
src io.Reader
51+
}
52+
53+
func (injector *nopInjector) Copy(dst io.Writer) (int64, error) {
54+
return io.Copy(dst, injector.src)
55+
}
56+
57+
func (injector *nopInjector) Extra() int {
58+
return 0
59+
}
60+
61+
func (injector *nopInjector) Found() bool {
62+
return false
63+
}
64+
4165
// Extra reports the number of extra bytes that will be injected
42-
func (injector *Injector) Extra() int {
43-
if injector.Found {
66+
func (injector *realInjector) Extra() int {
67+
if injector.found {
4468
return len(injector.conf.Payload)
4569
}
4670
return 0
4771
}
4872

73+
func (injector *realInjector) Found() bool {
74+
return injector.found
75+
}
76+
4977
func min(a int, b int) int {
5078
if a > b {
5179
return b
@@ -55,8 +83,12 @@ func min(a int, b int) int {
5583

5684
// Sniff reads the first SniffLen bytes of the source, and checks for the
5785
// marker. Returns an Injector instance.
58-
func (ci *CopyInject) Sniff(src io.Reader) (*Injector, error) {
59-
injector := &Injector{
86+
func (ci *CopyInject) Sniff(src io.Reader, contentType string) (Injector, error) {
87+
if !strings.Contains(contentType, ci.ContentType) {
88+
return &nopInjector{src: src}, nil
89+
}
90+
91+
injector := &realInjector{
6092
conf: ci,
6193
src: src,
6294
}
@@ -74,7 +106,7 @@ func (ci *CopyInject) Sniff(src io.Reader) (*Injector, error) {
74106
}
75107
loc := ci.Marker.FindIndex(injector.sniffedData[:min(n, ci.Within)])
76108
if loc != nil {
77-
injector.Found = true
109+
injector.found = true
78110
injector.offset = loc[0]
79111
}
80112
return injector, nil
@@ -89,7 +121,7 @@ func (ci *CopyInject) ServeTemplate(statuscode int, w http.ResponseWriter, t *te
89121
}
90122

91123
length := buff.Len()
92-
inj, err := ci.Sniff(buff)
124+
inj, err := ci.Sniff(buff, "text/html")
93125
if err != nil {
94126
return err
95127
}
@@ -106,9 +138,9 @@ func (ci *CopyInject) ServeTemplate(statuscode int, w http.ResponseWriter, t *te
106138

107139
// Copy copies the data from src to dst, injecting the Payload if Sniff found
108140
// the marker.
109-
func (injector *Injector) Copy(dst io.Writer) (int64, error) {
141+
func (injector *realInjector) Copy(dst io.Writer) (int64, error) {
110142
var preludeLen int64
111-
if injector.Found {
143+
if injector.found {
112144
startn, err := io.Copy(
113145
dst,
114146
bytes.NewBuffer(

inject/inject_test.go

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,31 @@ import (
77
"testing"
88
)
99

10-
func inject(ci CopyInject, data string) (found bool, dstdata string, err error) {
10+
func inject(ci CopyInject, data string, contentType string) (found bool, dstdata string, err error) {
1111
src := bytes.NewBuffer([]byte(data))
1212
dst := bytes.NewBuffer(make([]byte, 0))
13-
injector, err := ci.Sniff(src)
13+
injector, err := ci.Sniff(src, contentType)
1414
if err != nil {
1515
return false, "", err
1616
}
1717
_, err = injector.Copy(dst)
1818
if err != nil {
1919
return false, "", err
2020
}
21-
return injector.Found, string(dst.Bytes()), nil
21+
return injector.Found(), string(dst.Bytes()), nil
22+
}
23+
24+
func TestReverseProxyNoInject(t *testing.T) {
25+
ci := CopyInject{
26+
Within: 100,
27+
ContentType: "text/html",
28+
Marker: regexp.MustCompile("mark"),
29+
Payload: []byte("inject"),
30+
}
31+
found, dst, err := inject(ci, "imark", "text/plain")
32+
if err != nil || found || dst != "imark" {
33+
t.Errorf("Unexpected, found:%v dst:%v error:%v", dst, found, err)
34+
}
2235
}
2336

2437
func TestReverseProxy(t *testing.T) {
@@ -43,11 +56,12 @@ func TestReverseProxy(t *testing.T) {
4356
}
4457
for i, tt := range sniffTests {
4558
ci := CopyInject{
46-
Within: tt.snifflen,
47-
Marker: regexp.MustCompile(tt.marker),
48-
Payload: []byte(tt.payload),
59+
Within: tt.snifflen,
60+
ContentType: "text/html",
61+
Marker: regexp.MustCompile(tt.marker),
62+
Payload: []byte(tt.payload),
4963
}
50-
found, dst, err := inject(ci, tt.src)
64+
found, dst, err := inject(ci, tt.src, "text/html")
5165

5266
// Sanity checkss
5367
if err != nil {
@@ -74,7 +88,7 @@ func TestReverseProxy(t *testing.T) {
7488
}
7589

7690
// Idempotence
77-
found, dst2, err := inject(ci, dst)
91+
found, dst2, err := inject(ci, dst, "text/html")
7892
if err != nil {
7993
t.Errorf("Test %d, unexpected error:\n%s\n", i, err)
8094
}
@@ -91,8 +105,8 @@ func TestNil(t *testing.T) {
91105
ci := CopyInject{}
92106
val := "onetwothree"
93107
src := bytes.NewBuffer([]byte(val))
94-
injector, err := ci.Sniff(src)
95-
if injector.Found || err != nil {
108+
injector, err := ci.Sniff(src, "")
109+
if injector.Found() || err != nil {
96110
t.Error("Unexpected")
97111
}
98112
dst := bytes.NewBuffer(make([]byte, 0))

livereload/livereload.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ const (
3131

3232
// Injector for the livereload script
3333
var Injector = inject.CopyInject{
34-
Within: 1024 * 30,
35-
Marker: regexp.MustCompile(`<\/head>`),
36-
Payload: []byte(`<script src="/.devd.livereload.js"></script>`),
34+
Within: 1024 * 30,
35+
ContentType: "text/html",
36+
Marker: regexp.MustCompile(`<\/head>`),
37+
Payload: []byte(`<script src="/.devd.livereload.js"></script>`),
3738
}
3839

3940
// Server implements a Livereload server

responselogger.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
type ResponseLogWriter struct {
1616
Log termlog.Logger
1717
Resp http.ResponseWriter
18+
Flusher http.Flusher
1819
Timer *timer.Timer
1920
wroteHeader bool
2021
}
@@ -80,3 +81,9 @@ func (rl *ResponseLogWriter) WriteHeader(code int) {
8081
rl.Resp.WriteHeader(code)
8182
rl.Timer.ResponseDone()
8283
}
84+
85+
func (rl *ResponseLogWriter) Flush() {
86+
if rl.Flusher != nil {
87+
rl.Flusher.Flush()
88+
}
89+
}

reverseproxy/reverseproxy.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,14 @@ func (p *ReverseProxy) ServeHTTPContext(
173173
log.Say(fmt.Sprintf("%s uploaded", humanize.Bytes(uint64(req.ContentLength))))
174174
}
175175

176-
inject, err := p.Inject.Sniff(res.Body)
176+
inject, err := p.Inject.Sniff(res.Body, res.Header.Get("Content-Type"))
177177
if err != nil {
178178
log.Shout("reverse proxy error: %v", err)
179179
rw.WriteHeader(http.StatusInternalServerError)
180180
return
181181
}
182182

183-
if inject.Found {
183+
if inject.Found() {
184184
cl, err := strconv.ParseInt(res.Header.Get("Content-Length"), 10, 32)
185185
if err == nil {
186186
cl = cl + int64(inject.Extra())
@@ -196,7 +196,7 @@ func (p *ReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
196196
p.ServeHTTPContext(context.Background(), w, r)
197197
}
198198

199-
func (p *ReverseProxy) copyResponse(ctx context.Context, dst io.Writer, inject *inject.Injector) {
199+
func (p *ReverseProxy) copyResponse(ctx context.Context, dst io.Writer, inject inject.Injector) {
200200
log := termlog.FromContext(ctx)
201201
if p.FlushInterval != 0 {
202202
if wf, ok := dst.(writeFlusher); ok {

route.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"html/template"
88
"net/http"
99
"net/url"
10+
"time"
1011

1112
"github.com/cortesi/devd/fileserver"
1213
"github.com/cortesi/devd/httpctx"
@@ -31,6 +32,7 @@ func (ep forwardEndpoint) Handler(prefix string, templates *template.Template, c
3132
rp.Transport = &http.Transport{
3233
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
3334
}
35+
rp.FlushInterval = 200 * time.Millisecond
3436
return httpctx.StripPrefix(prefix, rp)
3537
}
3638

server.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,10 @@ func (dd *Devd) WrapHandler(log termlog.TermLog, next httpctx.Handler) http.Hand
192192
}
193193
}
194194
}
195+
flusher, _ := w.(http.Flusher)
195196
next.ServeHTTPContext(
196197
ctx,
197-
&ResponseLogWriter{Log: sublog, Resp: w, Timer: &timr},
198+
&ResponseLogWriter{Log: sublog, Resp: w, Flusher: flusher, Timer: &timr},
198199
r,
199200
)
200201
})

0 commit comments

Comments
 (0)