22// Use of this source code is governed by a MIT-style
33// license that can be found in the LICENSE.txt file.
44
5- package main
5+ package htex
66
77import (
88 "bufio"
99 "bytes"
10- "flag"
11- "fmt"
1210 "html"
1311 "log"
1412 "net/http"
1513 "os"
1614 "path"
17- "path/filepath"
1815 "strings"
1916)
2017
21- var localRoot string
22- var verbose bool
23-
2418type ElemKind int
2519
2620const (
@@ -44,7 +38,7 @@ type HtexFile struct {
4438 layout * HtexFile
4539}
4640
47- func SplitHtexTokens () func ([]byte , bool ) (int , []byte , error ) {
41+ func splitHtexTokens () func ([]byte , bool ) (int , []byte , error ) {
4842 insideHtexElem := false
4943 closingHtexElem := false
5044 return func (data []byte , atEOF bool ) (int , []byte , error ) {
@@ -95,19 +89,25 @@ func SplitHtexTokens() func([]byte, bool) (int, []byte, error) {
9589 }
9690}
9791
92+ type Htex struct {
93+ localRoot string
94+ verbose bool
95+ HttpHandler http.Handler
96+ }
97+
9898// relativeTo is a path to the current local filename that is being
9999// processed (so relative URLs will be relative to the directory of
100100// this file)
101- func SolveUrlPathToLocalPath (relativeTo string , urlPath string ) string {
101+ func ( h * Htex ) solveUrlPathToLocalPath (relativeTo string , urlPath string ) string {
102102 if urlPath [0 ] == '/' {
103- return path .Join (localRoot , urlPath )
103+ return path .Join (h . localRoot , urlPath )
104104 } else {
105105 return path .Join (path .Dir (relativeTo ), urlPath )
106106 }
107107}
108108
109- func ParseHtexFile (r * http.Request , fn string ) * HtexFile {
110- if verbose {
109+ func ( h * Htex ) parseHtexFile (r * http.Request , fn string ) * HtexFile {
110+ if h . verbose {
111111 log .Println (" -> parse file" , fn )
112112 }
113113
@@ -122,7 +122,7 @@ func ParseHtexFile(r *http.Request, fn string) *HtexFile {
122122 insideHtexElem := false
123123 var tok string
124124 scanner := bufio .NewScanner (file )
125- scanner .Split (SplitHtexTokens ())
125+ scanner .Split (splitHtexTokens ())
126126 for scanner .Scan () {
127127 elem := Elem {ElemNone , "" }
128128 tok = scanner .Text ()
@@ -134,8 +134,8 @@ func ParseHtexFile(r *http.Request, fn string) *HtexFile {
134134 insideHtexElem = true
135135 if strings .HasPrefix (tok , "<!layout" ) {
136136 scanner .Scan ()
137- layoutFn := SolveUrlPathToLocalPath (fn , scanner .Text ())
138- layout := ParseHtexFile (r , layoutFn )
137+ layoutFn := h . solveUrlPathToLocalPath (fn , scanner .Text ())
138+ layout := h . parseHtexFile (r , layoutFn )
139139 if layout != nil {
140140 htexFile .layout = layout
141141 }
@@ -175,10 +175,12 @@ func ParseHtexFile(r *http.Request, fn string) *HtexFile {
175175 return htexFile
176176}
177177
178- func WriteHtexFile (w http.ResponseWriter , r * http.Request , htexFile * HtexFile , layout * HtexFile , content func (http.ResponseWriter , * http.Request )) {
178+ func ( h * Htex ) writeHtexFile (w http.ResponseWriter , r * http.Request , htexFile * HtexFile , layout * HtexFile , content func (http.ResponseWriter , * http.Request )) {
179179 if layout != nil {
180- WriteHtexFile (w , r , layout , layout .layout ,
181- func (w http.ResponseWriter , r * http.Request ) { WriteHtexFile (w , r , htexFile , nil , content ) })
180+ h .writeHtexFile (w , r , layout , layout .layout ,
181+ func (w http.ResponseWriter , r * http.Request ) {
182+ h .writeHtexFile (w , r , htexFile , nil , content )
183+ })
182184 return
183185 }
184186
@@ -208,7 +210,7 @@ func WriteHtexFile(w http.ResponseWriter, r *http.Request, htexFile *HtexFile, l
208210 w .Write ([]byte (r .Form [elem .text ][0 ]))
209211 }
210212 } else if elem .kind == ElemIncludeRaw || elem .kind == ElemIncludeEscaped {
211- fn := SolveUrlPathToLocalPath (htexFile .fn , elem .text )
213+ fn := h . solveUrlPathToLocalPath (htexFile .fn , elem .text )
212214 content , err := os .ReadFile (fn )
213215 if elem .kind == ElemIncludeEscaped {
214216 content = []byte (html .EscapeString (string (content )))
@@ -224,16 +226,14 @@ func WriteHtexFile(w http.ResponseWriter, r *http.Request, htexFile *HtexFile, l
224226 }
225227}
226228
227- type HtexHandler struct {
228- }
229-
230- func (hh * HtexHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
229+ func (h * Htex ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
230+ verbose := h .verbose
231231 url := path .Clean (r .URL .String ())
232232 if verbose {
233233 log .Println (r .RemoteAddr , r .Method , r .URL , url )
234234 }
235235
236- fn := path .Join (localRoot , url )
236+ fn := path .Join (h . localRoot , url )
237237 base := path .Base (fn )
238238
239239 if base == "." {
@@ -278,63 +278,26 @@ func (hh *HtexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
278278 fn = fn + ".htex"
279279 s , _ = os .Stat (fn )
280280 if s != nil && s .Mode ().IsRegular () {
281- h := w .Header ()
282- h .Set ("Content-Type" , "text/html; charset=utf-8" )
283- if verbose {
281+ hdr := w .Header ()
282+ hdr .Set ("Content-Type" , "text/html; charset=utf-8" )
283+ if h . verbose {
284284 log .Println (" -> dynamic file" , fn )
285285 }
286- htexFile := ParseHtexFile (r , fn )
286+ htexFile := h . parseHtexFile (r , fn )
287287 if htexFile != nil {
288288 r .ParseForm ()
289- WriteHtexFile (w , r , htexFile , htexFile .layout , nil )
289+ h . writeHtexFile (w , r , htexFile , htexFile .layout , nil )
290290 }
291291 return
292292 }
293293
294294 http .NotFound (w , r )
295295}
296296
297- func main () {
298- var fullchain , privkey string
299- var port int
300- flag .BoolVar (& verbose , "verbose" , false , "verbose output" )
301- flag .StringVar (& fullchain , "fullchain" , "" , "TLS certificate" )
302- flag .StringVar (& privkey , "privkey" , "" , "private key for the TLS certificate" )
303- flag .IntVar (& port , "port" , 0 , "port to listen (80 or 443 by default)" )
304- flag .Parse ()
305- n := len (flag .Args ())
306- if n > 0 {
307- localRoot , _ = filepath .Abs (flag .Args ()[0 ])
308- } else {
309- localRoot , _ = filepath .Abs ("public" )
310- }
311-
312- s , err := os .Stat (localRoot )
313- if err != nil || s == nil || ! s .Mode ().IsDir () {
314- log .Fatalln ("cannot open directory:" , localRoot )
315- }
316-
317- var handler http.Handler
297+ func NewHtex (localRoot string , verbose bool ) * Htex {
298+ h := & Htex {localRoot , verbose , nil }
318299 if verbose {
319- handler = & LogHtexHandler {}
320- } else {
321- handler = & HtexHandler {}
322- }
323-
324- if fullchain != "" && privkey != "" {
325- // Start HTTPS server
326- if port == 0 {
327- port = 443
328- }
329- log .Printf ("htex server running https://localhost:%d\n -> local dir %s\n " , port , localRoot )
330- log .Fatal (http .ListenAndServeTLS (
331- fmt .Sprint (":" , port ), fullchain , privkey , handler ))
332- } else {
333- // Start HTTP server
334- if port == 0 {
335- port = 80
336- }
337- log .Printf ("htex server running https://localhost:%d\n -> local dir %s\n " , port , localRoot )
338- log .Fatal (http .ListenAndServe (fmt .Sprint (":" , port ), handler ))
300+ h .HttpHandler = & LogHtexHandler {handler : h }
339301 }
302+ return h
340303}
0 commit comments