1
2
3
4
5
6
7
8
9
10 package http
11
12 import (
13 "bufio"
14 "bytes"
15 "crypto/rand"
16 "crypto/tls"
17 "fmt"
18 "io"
19 "log"
20 "net"
21 "os"
22 "path"
23 "runtime/debug"
24 "strconv"
25 "strings"
26 "sync"
27 "time"
28 "url"
29 )
30
31
32 var (
33 ErrWriteAfterFlush = os.NewError("Conn.Write called after Flush")
34 ErrBodyNotAllowed = os.NewError("http: response status code does not allow body")
35 ErrHijacked = os.NewError("Conn has been hijacked")
36 ErrContentLength = os.NewError("Conn.Write wrote more than the declared Content-Length")
37 )
38
39
40
41
42
43
44
45
46
47 type Handler interface {
48 ServeHTTP(ResponseWriter, *Request)
49 }
50
51
52
53 type ResponseWriter interface {
54
55
56
57 Header() Header
58
59
60
61
62 Write([]byte) (int, os.Error)
63
64
65
66
67
68
69 WriteHeader(int)
70 }
71
72
73
74
75
76
77
78
79 type Flusher interface {
80
81 Flush()
82 }
83
84
85
86 type Hijacker interface {
87
88
89
90
91
92 Hijack() (net.Conn, *bufio.ReadWriter, os.Error)
93 }
94
95
96 type conn struct {
97 remoteAddr string
98 server *Server
99 rwc net.Conn
100 lr *io.LimitedReader
101 buf *bufio.ReadWriter
102 hijacked bool
103 tlsState *tls.ConnectionState
104 body []byte
105 }
106
107
108 type response struct {
109 conn *conn
110 req *Request
111 chunking bool
112 wroteHeader bool
113 wroteContinue bool
114 header Header
115 written int64
116 contentLength int64
117 status int
118 needSniff bool
119
120
121
122
123
124 closeAfterReply bool
125 }
126
127 type writerOnly struct {
128 io.Writer
129 }
130
131 func (r *response) ReadFrom(src io.Reader) (n int64, err os.Error) {
132
133
134
135 r.Flush()
136 if !r.chunking && r.bodyAllowed() && !r.needSniff {
137 if rf, ok := r.conn.rwc.(io.ReaderFrom); ok {
138 n, err = rf.ReadFrom(src)
139 r.written += n
140 return
141 }
142 }
143
144
145 return io.Copy(writerOnly{r}, src)
146 }
147
148
149 const noLimit int64 = (1 << 63) - 1
150
151
152 func (srv *Server) newConn(rwc net.Conn) (c *conn, err os.Error) {
153 c = new(conn)
154 c.remoteAddr = rwc.RemoteAddr().String()
155 c.server = srv
156 c.rwc = rwc
157 c.body = make([]byte, sniffLen)
158 c.lr = io.LimitReader(rwc, noLimit).(*io.LimitedReader)
159 br := bufio.NewReader(c.lr)
160 bw := bufio.NewWriter(rwc)
161 c.buf = bufio.NewReadWriter(br, bw)
162
163 if tlsConn, ok := rwc.(*tls.Conn); ok {
164 tlsConn.Handshake()
165 c.tlsState = new(tls.ConnectionState)
166 *c.tlsState = tlsConn.ConnectionState()
167 }
168
169 return c, nil
170 }
171
172
173
174
175 const DefaultMaxHeaderBytes = 1 << 20
176
177 func (srv *Server) maxHeaderBytes() int {
178 if srv.MaxHeaderBytes > 0 {
179 return srv.MaxHeaderBytes
180 }
181 return DefaultMaxHeaderBytes
182 }
183
184
185
186 type expectContinueReader struct {
187 resp *response
188 readCloser io.ReadCloser
189 closed bool
190 }
191
192 func (ecr *expectContinueReader) Read(p []byte) (n int, err os.Error) {
193 if ecr.closed {
194 return 0, os.NewError("http: Read after Close on request Body")
195 }
196 if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked {
197 ecr.resp.wroteContinue = true
198 io.WriteString(ecr.resp.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n")
199 ecr.resp.conn.buf.Flush()
200 }
201 return ecr.readCloser.Read(p)
202 }
203
204 func (ecr *expectContinueReader) Close() os.Error {
205 ecr.closed = true
206 return ecr.readCloser.Close()
207 }
208
209
210
211
212
213 const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
214
215 var errTooLarge = os.NewError("http: request too large")
216
217
218 func (c *conn) readRequest() (w *response, err os.Error) {
219 if c.hijacked {
220 return nil, ErrHijacked
221 }
222 c.lr.N = int64(c.server.maxHeaderBytes()) + 4096
223 var req *Request
224 if req, err = ReadRequest(c.buf.Reader); err != nil {
225 if c.lr.N == 0 {
226 return nil, errTooLarge
227 }
228 return nil, err
229 }
230 c.lr.N = noLimit
231
232 req.RemoteAddr = c.remoteAddr
233 req.TLS = c.tlsState
234
235 w = new(response)
236 w.conn = c
237 w.req = req
238 w.header = make(Header)
239 w.contentLength = -1
240 c.body = c.body[:0]
241 return w, nil
242 }
243
244 func (w *response) Header() Header {
245 return w.header
246 }
247
248 func (w *response) WriteHeader(code int) {
249 if w.conn.hijacked {
250 log.Print("http: response.WriteHeader on hijacked connection")
251 return
252 }
253 if w.wroteHeader {
254 log.Print("http: multiple response.WriteHeader calls")
255 return
256 }
257
258
259
260 if w.req.ContentLength != 0 {
261 ecr, isExpecter := w.req.Body.(*expectContinueReader)
262 if !isExpecter || ecr.resp.wroteContinue {
263 w.req.Body.Close()
264 }
265 }
266
267 w.wroteHeader = true
268 w.status = code
269 if code == StatusNotModified {
270
271 for _, header := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} {
272 if w.header.Get(header) != "" {
273
274
275
276 log.Printf("http: StatusNotModified response with header %q defined", header)
277 w.header.Del(header)
278 }
279 }
280 } else {
281
282 if w.header.Get("Content-Type") == "" {
283 w.needSniff = true
284 }
285 }
286
287 if _, ok := w.header["Date"]; !ok {
288 w.Header().Set("Date", time.UTC().Format(TimeFormat))
289 }
290
291
292 var hasCL bool
293 var contentLength int64
294 if clenStr := w.header.Get("Content-Length"); clenStr != "" {
295 var err os.Error
296 contentLength, err = strconv.Atoi64(clenStr)
297 if err == nil {
298 hasCL = true
299 } else {
300 log.Printf("http: invalid Content-Length of %q sent", clenStr)
301 w.header.Del("Content-Length")
302 }
303 }
304
305 te := w.header.Get("Transfer-Encoding")
306 hasTE := te != ""
307 if hasCL && hasTE && te != "identity" {
308
309
310 log.Printf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d",
311 te, contentLength)
312 w.header.Del("Content-Length")
313 hasCL = false
314 }
315
316 if w.req.Method == "HEAD" || code == StatusNotModified {
317
318 } else if hasCL {
319 w.contentLength = contentLength
320 w.header.Del("Transfer-Encoding")
321 } else if w.req.ProtoAtLeast(1, 1) {
322
323
324
325
326
327 w.chunking = true
328 w.header.Set("Transfer-Encoding", "chunked")
329 } else {
330
331
332
333 w.closeAfterReply = true
334 w.header.Del("Transfer-Encoding")
335 }
336
337 if w.req.wantsHttp10KeepAlive() && (w.req.Method == "HEAD" || hasCL) {
338 _, connectionHeaderSet := w.header["Connection"]
339 if !connectionHeaderSet {
340 w.header.Set("Connection", "keep-alive")
341 }
342 } else if !w.req.ProtoAtLeast(1, 1) {
343
344 w.closeAfterReply = true
345 }
346
347 if w.header.Get("Connection") == "close" {
348 w.closeAfterReply = true
349 }
350
351
352 if w.chunking {
353 w.header.Del("Content-Length")
354 }
355 if !w.req.ProtoAtLeast(1, 0) {
356 return
357 }
358 proto := "HTTP/1.0"
359 if w.req.ProtoAtLeast(1, 1) {
360 proto = "HTTP/1.1"
361 }
362 codestring := strconv.Itoa(code)
363 text, ok := statusText[code]
364 if !ok {
365 text = "status code " + codestring
366 }
367 io.WriteString(w.conn.buf, proto+" "+codestring+" "+text+"\r\n")
368 w.header.Write(w.conn.buf)
369
370
371
372 if !w.needSniff {
373 io.WriteString(w.conn.buf, "\r\n")
374 }
375 }
376
377
378
379
380 func (w *response) sniff() {
381 if !w.needSniff {
382 return
383 }
384 w.needSniff = false
385
386 data := w.conn.body
387 fmt.Fprintf(w.conn.buf, "Content-Type: %s\r\n\r\n", DetectContentType(data))
388
389 if len(data) == 0 {
390 return
391 }
392 if w.chunking {
393 fmt.Fprintf(w.conn.buf, "%x\r\n", len(data))
394 }
395 _, err := w.conn.buf.Write(data)
396 if w.chunking && err == nil {
397 io.WriteString(w.conn.buf, "\r\n")
398 }
399 }
400
401
402
403 func (w *response) bodyAllowed() bool {
404 if !w.wroteHeader {
405 panic("")
406 }
407 return w.status != StatusNotModified && w.req.Method != "HEAD"
408 }
409
410 func (w *response) Write(data []byte) (n int, err os.Error) {
411 if w.conn.hijacked {
412 log.Print("http: response.Write on hijacked connection")
413 return 0, ErrHijacked
414 }
415 if !w.wroteHeader {
416 w.WriteHeader(StatusOK)
417 }
418 if len(data) == 0 {
419 return 0, nil
420 }
421 if !w.bodyAllowed() {
422 return 0, ErrBodyNotAllowed
423 }
424
425 w.written += int64(len(data))
426 if w.contentLength != -1 && w.written > w.contentLength {
427 return 0, ErrContentLength
428 }
429
430 var m int
431 if w.needSniff {
432
433
434
435
436 m := cap(w.conn.body) - len(w.conn.body)
437 if m > len(data) {
438 m = len(data)
439 }
440 w.conn.body = append(w.conn.body, data[:m]...)
441 data = data[m:]
442 if len(data) == 0 {
443
444
445 return m, nil
446 }
447
448
449
450
451
452
453 w.sniff()
454 }
455
456
457
458
459 if w.chunking {
460 fmt.Fprintf(w.conn.buf, "%x\r\n", len(data))
461 }
462 n, err = w.conn.buf.Write(data)
463 if err == nil && w.chunking {
464 if n != len(data) {
465 err = io.ErrShortWrite
466 }
467 if err == nil {
468 io.WriteString(w.conn.buf, "\r\n")
469 }
470 }
471
472 return m + n, err
473 }
474
475
476
477
478
479
480
481
482 func errorKludge(w *response) {
483 const min = 1024
484
485
486 if kind := w.status / 100; kind != 4 && kind != 5 {
487 return
488 }
489
490
491 if w.written == 0 || w.written >= min {
492 return
493 }
494
495
496 var msg string
497 switch agent := w.req.UserAgent(); {
498 case strings.Contains(agent, "MSIE"):
499 msg = "Internet Explorer"
500 case strings.Contains(agent, "Chrome/"):
501 msg = "Chrome"
502 default:
503 return
504 }
505 msg += " would ignore this error page if this text weren't here.\n"
506
507
508 baseType := strings.SplitN(w.header.Get("Content-Type"), ";", 2)[0]
509 switch baseType {
510 case "text/html":
511 io.WriteString(w, "<!-- ")
512 for w.written < min {
513 io.WriteString(w, msg)
514 }
515 io.WriteString(w, " -->")
516 case "text/plain":
517 io.WriteString(w, "\n")
518 for w.written < min {
519 io.WriteString(w, msg)
520 }
521 }
522 }
523
524 func (w *response) finishRequest() {
525
526
527 if w.req.wantsHttp10KeepAlive() {
528 sentLength := w.header.Get("Content-Length") != ""
529 if sentLength && w.header.Get("Connection") == "keep-alive" {
530 w.closeAfterReply = false
531 }
532 }
533 if !w.wroteHeader {
534 w.WriteHeader(StatusOK)
535 }
536 if w.needSniff {
537 w.sniff()
538 }
539 errorKludge(w)
540 if w.chunking {
541 io.WriteString(w.conn.buf, "0\r\n")
542
543 io.WriteString(w.conn.buf, "\r\n")
544 }
545 w.conn.buf.Flush()
546 w.req.Body.Close()
547 if w.req.MultipartForm != nil {
548 w.req.MultipartForm.RemoveAll()
549 }
550
551 if w.contentLength != -1 && w.contentLength != w.written {
552
553 w.closeAfterReply = true
554 }
555 }
556
557 func (w *response) Flush() {
558 if !w.wroteHeader {
559 w.WriteHeader(StatusOK)
560 }
561 w.sniff()
562 w.conn.buf.Flush()
563 }
564
565
566 func (c *conn) close() {
567 if c.buf != nil {
568 c.buf.Flush()
569 c.buf = nil
570 }
571 if c.rwc != nil {
572 c.rwc.Close()
573 c.rwc = nil
574 }
575 }
576
577
578 func (c *conn) serve() {
579 defer func() {
580 err := recover()
581 if err == nil {
582 return
583 }
584 c.rwc.Close()
585
586 var buf bytes.Buffer
587 fmt.Fprintf(&buf, "http: panic serving %v: %v\n", c.remoteAddr, err)
588 buf.Write(debug.Stack())
589 log.Print(buf.String())
590 }()
591
592 for {
593 w, err := c.readRequest()
594 if err != nil {
595 if err == errTooLarge {
596
597
598
599
600
601 fmt.Fprintf(c.rwc, "HTTP/1.1 400 Request Too Large\r\n\r\n")
602 }
603 break
604 }
605
606
607 req := w.req
608 if req.expectsContinue() {
609 if req.ProtoAtLeast(1, 1) {
610
611 req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
612 }
613 if req.ContentLength == 0 {
614 w.Header().Set("Connection", "close")
615 w.WriteHeader(StatusBadRequest)
616 w.finishRequest()
617 break
618 }
619 req.Header.Del("Expect")
620 } else if req.Header.Get("Expect") != "" {
621
622
623
624
625
626
627
628
629
630
631
632
633 w.Header().Set("Connection", "close")
634 w.WriteHeader(StatusExpectationFailed)
635 w.finishRequest()
636 break
637 }
638
639 handler := c.server.Handler
640 if handler == nil {
641 handler = DefaultServeMux
642 }
643
644
645
646
647
648
649 handler.ServeHTTP(w, w.req)
650 if c.hijacked {
651 return
652 }
653 w.finishRequest()
654 if w.closeAfterReply {
655 break
656 }
657 }
658 c.close()
659 }
660
661
662
663 func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err os.Error) {
664 if w.conn.hijacked {
665 return nil, nil, ErrHijacked
666 }
667 w.conn.hijacked = true
668 rwc = w.conn.rwc
669 buf = w.conn.buf
670 w.conn.rwc = nil
671 w.conn.buf = nil
672 return
673 }
674
675
676
677
678
679 type HandlerFunc func(ResponseWriter, *Request)
680
681
682 func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
683 f(w, r)
684 }
685
686
687
688
689 func Error(w ResponseWriter, error string, code int) {
690 w.Header().Set("Content-Type", "text/plain; charset=utf-8")
691 w.WriteHeader(code)
692 fmt.Fprintln(w, error)
693 }
694
695
696 func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) }
697
698
699
700 func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
701
702
703
704
705
706
707 func StripPrefix(prefix string, h Handler) Handler {
708 return HandlerFunc(func(w ResponseWriter, r *Request) {
709 if !strings.HasPrefix(r.URL.Path, prefix) {
710 NotFound(w, r)
711 return
712 }
713 r.URL.Path = r.URL.Path[len(prefix):]
714 h.ServeHTTP(w, r)
715 })
716 }
717
718
719
720 func Redirect(w ResponseWriter, r *Request, urlStr string, code int) {
721 if u, err := url.Parse(urlStr); err == nil {
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738 oldpath := r.URL.Path
739 if oldpath == "" {
740 oldpath = "/"
741 }
742 if u.Scheme == "" {
743
744 if urlStr == "" || urlStr[0] != '/' {
745
746 olddir, _ := path.Split(oldpath)
747 urlStr = olddir + urlStr
748 }
749
750 var query string
751 if i := strings.Index(urlStr, "?"); i != -1 {
752 urlStr, query = urlStr[:i], urlStr[i:]
753 }
754
755
756 trailing := urlStr[len(urlStr)-1] == '/'
757 urlStr = path.Clean(urlStr)
758 if trailing && urlStr[len(urlStr)-1] != '/' {
759 urlStr += "/"
760 }
761 urlStr += query
762 }
763 }
764
765 w.Header().Set("Location", urlStr)
766 w.WriteHeader(code)
767
768
769
770
771 if r.Method == "GET" {
772 note := "<a href=\"" + htmlEscape(urlStr) + "\">" + statusText[code] + "</a>.\n"
773 fmt.Fprintln(w, note)
774 }
775 }
776
777 func htmlEscape(s string) string {
778 s = strings.Replace(s, "&", "&", -1)
779 s = strings.Replace(s, "<", "<", -1)
780 s = strings.Replace(s, ">", ">", -1)
781 s = strings.Replace(s, "\"", """, -1)
782 s = strings.Replace(s, "'", "'", -1)
783 return s
784 }
785
786
787 type redirectHandler struct {
788 url string
789 code int
790 }
791
792 func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request) {
793 Redirect(w, r, rh.url, rh.code)
794 }
795
796
797
798
799 func RedirectHandler(url string, code int) Handler {
800 return &redirectHandler{url, code}
801 }
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826 type ServeMux struct {
827 m map[string]Handler
828 }
829
830
831 func NewServeMux() *ServeMux { return &ServeMux{make(map[string]Handler)} }
832
833
834 var DefaultServeMux = NewServeMux()
835
836
837 func pathMatch(pattern, path string) bool {
838 if len(pattern) == 0 {
839
840 return false
841 }
842 n := len(pattern)
843 if pattern[n-1] != '/' {
844 return pattern == path
845 }
846 return len(path) >= n && path[0:n] == pattern
847 }
848
849
850 func cleanPath(p string) string {
851 if p == "" {
852 return "/"
853 }
854 if p[0] != '/' {
855 p = "/" + p
856 }
857 np := path.Clean(p)
858
859
860 if p[len(p)-1] == '/' && np != "/" {
861 np += "/"
862 }
863 return np
864 }
865
866
867
868 func (mux *ServeMux) match(path string) Handler {
869 var h Handler
870 var n = 0
871 for k, v := range mux.m {
872 if !pathMatch(k, path) {
873 continue
874 }
875 if h == nil || len(k) > n {
876 n = len(k)
877 h = v
878 }
879 }
880 return h
881 }
882
883
884
885 func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
886
887 if p := cleanPath(r.URL.Path); p != r.URL.Path {
888 w.Header().Set("Location", p)
889 w.WriteHeader(StatusMovedPermanently)
890 return
891 }
892
893 h := mux.match(r.Host + r.URL.Path)
894 if h == nil {
895 h = mux.match(r.URL.Path)
896 }
897 if h == nil {
898 h = NotFoundHandler()
899 }
900 h.ServeHTTP(w, r)
901 }
902
903
904 func (mux *ServeMux) Handle(pattern string, handler Handler) {
905 if pattern == "" {
906 panic("http: invalid pattern " + pattern)
907 }
908
909 mux.m[pattern] = handler
910
911
912
913 n := len(pattern)
914 if n > 0 && pattern[n-1] == '/' {
915 mux.m[pattern[0:n-1]] = RedirectHandler(pattern, StatusMovedPermanently)
916 }
917 }
918
919
920 func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
921 mux.Handle(pattern, HandlerFunc(handler))
922 }
923
924
925
926
927 func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
928
929
930
931
932 func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
933 DefaultServeMux.HandleFunc(pattern, handler)
934 }
935
936
937
938
939
940 func Serve(l net.Listener, handler Handler) os.Error {
941 srv := &Server{Handler: handler}
942 return srv.Serve(l)
943 }
944
945
946 type Server struct {
947 Addr string
948 Handler Handler
949 ReadTimeout int64
950 WriteTimeout int64
951 MaxHeaderBytes int
952 }
953
954
955
956
957 func (srv *Server) ListenAndServe() os.Error {
958 addr := srv.Addr
959 if addr == "" {
960 addr = ":http"
961 }
962 l, e := net.Listen("tcp", addr)
963 if e != nil {
964 return e
965 }
966 return srv.Serve(l)
967 }
968
969
970
971
972 func (srv *Server) Serve(l net.Listener) os.Error {
973 defer l.Close()
974 for {
975 rw, e := l.Accept()
976 if e != nil {
977 if ne, ok := e.(net.Error); ok && ne.Temporary() {
978 log.Printf("http: Accept error: %v", e)
979 continue
980 }
981 return e
982 }
983 if srv.ReadTimeout != 0 {
984 rw.SetReadTimeout(srv.ReadTimeout)
985 }
986 if srv.WriteTimeout != 0 {
987 rw.SetWriteTimeout(srv.WriteTimeout)
988 }
989 c, err := srv.newConn(rw)
990 if err != nil {
991 continue
992 }
993 go c.serve()
994 }
995 panic("not reached")
996 }
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025 func ListenAndServe(addr string, handler Handler) os.Error {
1026 server := &Server{Addr: addr, Handler: handler}
1027 return server.ListenAndServe()
1028 }
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058 func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) os.Error {
1059 server := &Server{Addr: addr, Handler: handler}
1060 return server.ListenAndServeTLS(certFile, keyFile)
1061 }
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072 func (s *Server) ListenAndServeTLS(certFile, keyFile string) os.Error {
1073 addr := s.Addr
1074 if addr == "" {
1075 addr = ":https"
1076 }
1077 config := &tls.Config{
1078 Rand: rand.Reader,
1079 Time: time.Seconds,
1080 NextProtos: []string{"http/1.1"},
1081 }
1082
1083 var err os.Error
1084 config.Certificates = make([]tls.Certificate, 1)
1085 config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
1086 if err != nil {
1087 return err
1088 }
1089
1090 conn, err := net.Listen("tcp", addr)
1091 if err != nil {
1092 return err
1093 }
1094
1095 tlsListener := tls.NewListener(conn, config)
1096 return s.Serve(tlsListener)
1097 }
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107 func TimeoutHandler(h Handler, ns int64, msg string) Handler {
1108 f := func() <-chan int64 {
1109 return time.After(ns)
1110 }
1111 return &timeoutHandler{h, f, msg}
1112 }
1113
1114
1115
1116 var ErrHandlerTimeout = os.NewError("http: Handler timeout")
1117
1118 type timeoutHandler struct {
1119 handler Handler
1120 timeout func() <-chan int64
1121 body string
1122 }
1123
1124 func (h *timeoutHandler) errorBody() string {
1125 if h.body != "" {
1126 return h.body
1127 }
1128 return "<html><head><title>Timeout</title></head><body><h1>Timeout</h1></body></html>"
1129 }
1130
1131 func (h *timeoutHandler) ServeHTTP(w ResponseWriter, r *Request) {
1132 done := make(chan bool)
1133 tw := &timeoutWriter{w: w}
1134 go func() {
1135 h.handler.ServeHTTP(tw, r)
1136 done <- true
1137 }()
1138 select {
1139 case <-done:
1140 return
1141 case <-h.timeout():
1142 tw.mu.Lock()
1143 defer tw.mu.Unlock()
1144 if !tw.wroteHeader {
1145 tw.w.WriteHeader(StatusServiceUnavailable)
1146 tw.w.Write([]byte(h.errorBody()))
1147 }
1148 tw.timedOut = true
1149 }
1150 }
1151
1152 type timeoutWriter struct {
1153 w ResponseWriter
1154
1155 mu sync.Mutex
1156 timedOut bool
1157 wroteHeader bool
1158 }
1159
1160 func (tw *timeoutWriter) Header() Header {
1161 return tw.w.Header()
1162 }
1163
1164 func (tw *timeoutWriter) Write(p []byte) (int, os.Error) {
1165 tw.mu.Lock()
1166 timedOut := tw.timedOut
1167 tw.mu.Unlock()
1168 if timedOut {
1169 return 0, ErrHandlerTimeout
1170 }
1171 return tw.w.Write(p)
1172 }
1173
1174 func (tw *timeoutWriter) WriteHeader(code int) {
1175 tw.mu.Lock()
1176 if tw.timedOut || tw.wroteHeader {
1177 tw.mu.Unlock()
1178 return
1179 }
1180 tw.wroteHeader = true
1181 tw.mu.Unlock()
1182 tw.w.WriteHeader(code)
1183 }