Skip to content

Commit fa60226

Browse files
committed
http additions
file system server add NotFound, Redirect functions method on a string R=r DELTA=212 (199 added, 4 deleted, 9 changed) OCL=27467 CL=27471
1 parent c956e90 commit fa60226

File tree

3 files changed

+209
-13
lines changed

3 files changed

+209
-13
lines changed

src/lib/http/Makefile

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,22 @@ coverage: packages
3232
$(AS) $*.s
3333

3434
O1=\
35-
url.$O\
3635
status.$O\
36+
url.$O\
3737

3838
O2=\
3939
request.$O\
4040

4141
O3=\
4242
server.$O\
4343

44-
http.a: a1 a2 a3
44+
O4=\
45+
fs.$O\
46+
47+
http.a: a1 a2 a3 a4
4548

4649
a1: $(O1)
47-
$(AR) grc http.a url.$O status.$O
50+
$(AR) grc http.a status.$O url.$O
4851
rm -f $(O1)
4952

5053
a2: $(O2)
@@ -55,12 +58,17 @@ a3: $(O3)
5558
$(AR) grc http.a server.$O
5659
rm -f $(O3)
5760

61+
a4: $(O4)
62+
$(AR) grc http.a fs.$O
63+
rm -f $(O4)
64+
5865
newpkg: clean
5966
$(AR) grc http.a
6067

6168
$(O1): newpkg
6269
$(O2): a1
6370
$(O3): a2
71+
$(O4): a3
6472

6573
nuke: clean
6674
rm -f $(GOROOT)/pkg/http.a

src/lib/http/fs.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright 2009 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// HTTP file system request handler
6+
7+
package http
8+
9+
import (
10+
"fmt";
11+
"http";
12+
"io";
13+
"os";
14+
"path";
15+
"strings";
16+
"utf8";
17+
)
18+
19+
// TODO this should be in a mime package somewhere
20+
var contentByExt = map[string] string {
21+
".css": "text/css",
22+
".gif": "image/gif",
23+
".html": "text/html; charset=utf-8",
24+
".jpg": "image/jpeg",
25+
".js": "application/x-javascript",
26+
".png": "image/png",
27+
}
28+
29+
// Heuristic: b is text if it is valid UTF-8 and doesn't
30+
// contain any unprintable ASCII or Unicode characters.
31+
func isText(b []byte) bool {
32+
for len(b) > 0 && utf8.FullRune(b) {
33+
rune, size := utf8.DecodeRune(b);
34+
if size == 1 && rune == utf8.RuneError {
35+
// decoding error
36+
return false;
37+
}
38+
if 0x80 <= rune && rune <= 0x9F {
39+
return false;
40+
}
41+
if rune < ' ' {
42+
switch rune {
43+
case '\n', '\r', '\t':
44+
// okay
45+
default:
46+
// binary garbage
47+
return false;
48+
}
49+
}
50+
b = b[size:len(b)];
51+
}
52+
return true;
53+
}
54+
55+
func dirList(c *Conn, f *os.File) {
56+
fmt.Fprintf(c, "<pre>\n");
57+
for {
58+
dirs, err := f.Readdir(100);
59+
if err != nil || len(dirs) == 0 {
60+
break
61+
}
62+
for i, d := range dirs {
63+
name := d.Name;
64+
if d.IsDirectory() {
65+
name += "/"
66+
}
67+
// TODO htmlescape
68+
fmt.Fprintf(c, "<a href=\"%s\">%s</a>\n", name, name);
69+
}
70+
}
71+
fmt.Fprintf(c, "</pre>\n");
72+
}
73+
74+
75+
func serveFileInternal(c *Conn, r *Request, name string, redirect bool) {
76+
const indexPage = "/index.html";
77+
78+
// redirect to strip off any index.html
79+
n := len(name) - len(indexPage);
80+
if n >= 0 && name[n:len(name)] == indexPage {
81+
http.Redirect(c, name[0:n+1]);
82+
return;
83+
}
84+
85+
f, err := os.Open(name, os.O_RDONLY, 0);
86+
if err != nil {
87+
// TODO expose actual error?
88+
NotFound(c, r);
89+
return;
90+
}
91+
defer f.Close();
92+
93+
d, err1 := f.Stat();
94+
if err1 != nil {
95+
// TODO expose actual error?
96+
NotFound(c, r);
97+
return;
98+
}
99+
100+
if redirect {
101+
// redirect to canonical path: / at end of directory url
102+
// r.Url.Path always begins with /
103+
url := r.Url.Path;
104+
if d.IsDirectory() {
105+
if url[len(url)-1] != '/' {
106+
http.Redirect(c, url + "/");
107+
return;
108+
}
109+
} else {
110+
if url[len(url)-1] == '/' {
111+
http.Redirect(c, url[0:len(url)-1]);
112+
return;
113+
}
114+
}
115+
}
116+
117+
// use contents of index.html for directory, if present
118+
if d.IsDirectory() {
119+
index := name + indexPage;
120+
ff, err := os.Open(index, os.O_RDONLY, 0);
121+
if err == nil {
122+
defer ff.Close();
123+
dd, err := ff.Stat();
124+
if err == nil {
125+
name = index;
126+
d = dd;
127+
f = ff;
128+
}
129+
}
130+
}
131+
132+
if d.IsDirectory() {
133+
dirList(c, f);
134+
return;
135+
}
136+
137+
// serve file
138+
// use extension to find content type.
139+
ext := path.Ext(name);
140+
if ctype, ok := contentByExt[ext]; ok {
141+
c.SetHeader("Content-Type", ctype);
142+
} else {
143+
// read first chunk to decide between utf-8 text and binary
144+
var buf [1024]byte;
145+
n, err := io.Readn(f, buf);
146+
b := buf[0:n];
147+
if isText(b) {
148+
c.SetHeader("Content-Type", "text-plain; charset=utf-8");
149+
} else {
150+
c.SetHeader("Content-Type", "application/octet-stream"); // generic binary
151+
}
152+
c.Write(b);
153+
}
154+
io.Copy(f, c);
155+
}
156+
157+
// ServeFile replies to the request with the contents of the named file or directory.
158+
func ServeFile(c *Conn, r *Request, name string) {
159+
serveFileInternal(c, r, name, false);
160+
}
161+
162+
type fileHandler struct {
163+
root string;
164+
prefix string;
165+
}
166+
167+
// FileServer returns a handler that serves HTTP requests
168+
// with the contents of the file system rooted at root.
169+
// It strips prefix from the incoming requests before
170+
// looking up the file name in the file system.
171+
func FileServer(root, prefix string) Handler {
172+
return &fileHandler{root, prefix};
173+
}
174+
175+
func (f *fileHandler) ServeHTTP(c *Conn, r *Request) {
176+
path := r.Url.Path;
177+
if !strings.HasPrefix(path, f.prefix) {
178+
NotFound(c, r);
179+
return;
180+
}
181+
path = path[len(f.prefix):len(path)];
182+
serveFileInternal(c, r, f.root + "/" + path, true);
183+
}
184+

src/lib/http/server.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ func (f HandlerFunc) ServeHTTP(c *Conn, req *Request) {
253253

254254
// Helper handlers
255255

256-
// 404 not found
257-
func notFound(c *Conn, req *Request) {
256+
// NotFound replies to the request with an HTTP 404 not found error.
257+
func NotFound(c *Conn, req *Request) {
258258
c.SetHeader("Content-Type", "text/plain; charset=utf-8");
259259
c.WriteHeader(StatusNotFound);
260260
io.WriteString(c, "404 page not found\n");
@@ -263,22 +263,26 @@ func notFound(c *Conn, req *Request) {
263263
// NotFoundHandler returns a simple request handler
264264
// that replies to each request with a ``404 page not found'' reply.
265265
func NotFoundHandler() Handler {
266-
return HandlerFunc(notFound)
266+
return HandlerFunc(NotFound)
267267
}
268268

269-
// Redirect to a fixed URL
270-
type redirectHandler struct {
271-
to string;
272-
}
273-
func (h *redirectHandler) ServeHTTP(c *Conn, req *Request) {
274-
c.SetHeader("Location", h.to);
269+
// Redirect replies to the request with a redirect to url,
270+
// which may be a path relative to the request path.
271+
func Redirect(c *Conn, url string) {
272+
c.SetHeader("Location", url);
275273
c.WriteHeader(StatusMovedPermanently);
276274
}
277275

276+
// Redirect to a fixed URL
277+
type redirectHandler string
278+
func (url redirectHandler) ServeHTTP(c *Conn, req *Request) {
279+
Redirect(c, url);
280+
}
281+
278282
// RedirectHandler returns a request handler that redirects
279283
// each request it receives to the given url.
280284
func RedirectHandler(url string) Handler {
281-
return &redirectHandler{url};
285+
return redirectHandler(url);
282286
}
283287

284288
// ServeMux is an HTTP request multiplexer.

0 commit comments

Comments
 (0)