Skip to content

Commit f5858d0

Browse files
committed
Split htex cmd with htex library
1 parent 772f0b0 commit f5858d0

5 files changed

Lines changed: 96 additions & 74 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
htex
1+
/htex
22
*.exe

cmd/htex/main.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2025 David Capello. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE.txt file.
4+
5+
package main
6+
7+
import (
8+
"flag"
9+
"fmt"
10+
"log"
11+
"net/http"
12+
"os"
13+
"path/filepath"
14+
15+
"htex.dev"
16+
)
17+
18+
var localRoot string
19+
var verbose bool
20+
21+
func main() {
22+
var fullchain, privkey string
23+
var port int
24+
flag.BoolVar(&verbose, "verbose", false, "verbose output")
25+
flag.StringVar(&fullchain, "fullchain", "", "TLS certificate")
26+
flag.StringVar(&privkey, "privkey", "", "private key for the TLS certificate")
27+
flag.IntVar(&port, "port", 0, "port to listen (80 or 443 by default)")
28+
flag.Parse()
29+
n := len(flag.Args())
30+
if n > 0 {
31+
localRoot, _ = filepath.Abs(flag.Args()[0])
32+
} else {
33+
localRoot, _ = filepath.Abs("public")
34+
}
35+
36+
s, err := os.Stat(localRoot)
37+
if err != nil || s == nil || !s.Mode().IsDir() {
38+
log.Fatalln("cannot open directory:", localRoot)
39+
}
40+
41+
h := htex.NewHtex(localRoot, verbose)
42+
43+
if fullchain != "" && privkey != "" {
44+
// Start HTTPS server
45+
if port == 0 {
46+
port = 443
47+
}
48+
log.Printf("htex server running https://localhost:%d\n -> local dir %s\n", port, localRoot)
49+
log.Fatal(http.ListenAndServeTLS(
50+
fmt.Sprint(":", port), fullchain, privkey, h.HttpHandler))
51+
} else {
52+
// Start HTTP server
53+
if port == 0 {
54+
port = 80
55+
}
56+
log.Printf("htex server running http://localhost:%d\n -> local dir %s\n", port, localRoot)
57+
log.Fatal(http.ListenAndServe(fmt.Sprint(":", port), h.HttpHandler))
58+
}
59+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
module github.com/dacap/htex
1+
module htex.dev
22

33
go 1.24

htex.go

Lines changed: 33 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,19 @@
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

77
import (
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-
2418
type ElemKind int
2519

2620
const (
@@ -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
}

log.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
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

77
import (
88
"log"
@@ -29,7 +29,7 @@ func (w *logResponseWriter) WriteHeader(statusCode int) {
2929
}
3030

3131
type LogHtexHandler struct {
32-
handler HtexHandler
32+
handler http.Handler
3333
w logResponseWriter
3434
}
3535

0 commit comments

Comments
 (0)