/src/fastweb/fastweb.go

http://go-fastweb.googlecode.com/ · Go · 1004 lines · 873 code · 124 blank · 7 comment · 201 complexity · bc8730bf888b79b24bb644ed9845558d MD5 · raw file

  1. // vim: set syntax=go autoindent:
  2. // Copyright 2010 Ivan Wong. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. package fastweb
  6. import (
  7. "bufio"
  8. "bytes"
  9. "container/vector"
  10. "go-fastcgi.googlecode.com/svn/trunk/src/fastcgi"
  11. "fmt"
  12. "url"
  13. "io"
  14. "io/ioutil"
  15. "log"
  16. "os"
  17. "rand"
  18. "reflect"
  19. "regexp"
  20. "strconv"
  21. "strings"
  22. "time"
  23. "unsafe"
  24. )
  25. const (
  26. IntParam = 1
  27. StrParam = 2
  28. )
  29. type ControllerInterface interface {
  30. Init()
  31. DefaultAction() string
  32. SetEnv(env *env)
  33. PreFilter()
  34. Render()
  35. SetContext(ctxt ControllerInterface)
  36. StartSession()
  37. CloseSession()
  38. }
  39. type Error interface {
  40. os.Error
  41. Type() string
  42. }
  43. type ErrorStruct struct {
  44. typ string
  45. message string
  46. }
  47. type methodInfo struct {
  48. name string
  49. method reflect.Value
  50. nparams int
  51. paramTypes []int
  52. }
  53. type controllerInfo struct {
  54. name string
  55. controller ControllerInterface
  56. controllerType reflect.Type
  57. controllerPtrType reflect.Type
  58. methodMap map[string]*methodInfo
  59. }
  60. type Application struct {
  61. controllerMap map[string]*controllerInfo
  62. defaultController string
  63. }
  64. type env struct {
  65. path string
  66. controller string
  67. lcontroller string
  68. action string
  69. laction string
  70. params []string
  71. request *fastcgi.Request
  72. body string
  73. form map[string][]string
  74. upload map[string][](*Upload)
  75. cookies map[string]string
  76. }
  77. type Upload struct {
  78. File *os.File
  79. Filename string
  80. }
  81. type cookie struct {
  82. value string
  83. expire *time.Time
  84. path string
  85. domain string
  86. secure bool
  87. httpOnly bool
  88. }
  89. type Controller struct {
  90. Path string
  91. Name string
  92. LName string
  93. Action string
  94. LAction string
  95. Params []string
  96. PageTitle string
  97. Layout string
  98. ContentType string
  99. Body string
  100. Form map[string][]string
  101. Upload map[string][]*Upload
  102. Cookies map[string]string
  103. Session *Session
  104. setCookies map[string]*cookie
  105. ctxt ControllerInterface
  106. Request *fastcgi.Request
  107. preRenered bool
  108. }
  109. func NewError(typ string, message string) *ErrorStruct {
  110. return &ErrorStruct{typ, message}
  111. }
  112. func (e *ErrorStruct) String() string { return e.message }
  113. func (e *ErrorStruct) Type() string { return e.typ }
  114. func (c *Controller) Init() {
  115. c.PageTitle = ""
  116. c.Layout = "default"
  117. c.ContentType = "text/html; charset=utf-8"
  118. }
  119. func (c *Controller) DefaultAction() string { return "Index" }
  120. func (c *Controller) SetEnv(env *env) {
  121. c.Name = env.controller
  122. c.LName = env.lcontroller
  123. c.Action = env.action
  124. c.LAction = env.laction
  125. c.Path = env.path
  126. c.Params = env.params
  127. c.Request = env.request
  128. c.Body = env.body
  129. c.Form = env.form
  130. c.Upload = env.upload
  131. c.Cookies = env.cookies
  132. }
  133. func (c *Controller) PreFilter() {}
  134. type tmplInfo struct {
  135. tmpl *Template
  136. mtime int64
  137. }
  138. var tmplCache map[string]*tmplInfo = make(map[string]*tmplInfo)
  139. func loadTemplate(fname string) (*Template, os.Error) {
  140. dir, e := os.Stat(fname)
  141. if e != nil {
  142. return nil, e
  143. }
  144. if !dir.IsRegular() {
  145. return nil, NewError("Generic", "'"+fname+"' is not a regular file")
  146. }
  147. ti, _ := tmplCache[fname]
  148. if ti == nil || dir.Mtime_ns > ti.mtime {
  149. bytes, e := ioutil.ReadFile(fname)
  150. if e != nil {
  151. return nil, e
  152. }
  153. t, e := Parse(string(bytes), nil)
  154. if e != nil {
  155. return nil, e
  156. }
  157. ti = &tmplInfo{
  158. mtime: dir.Mtime_ns,
  159. tmpl: t,
  160. }
  161. tmplCache[fname] = ti
  162. }
  163. return ti.tmpl, nil
  164. }
  165. func (c *Controller) SetContext(ctxt ControllerInterface) {
  166. if c.ctxt == nil {
  167. c.ctxt = ctxt
  168. }
  169. }
  170. func (c *Controller) preRender() {
  171. if !c.preRenered {
  172. io.WriteString(c.Request.Stdout, "Content-Type: "+c.ContentType+"\r\n")
  173. if c.setCookies != nil {
  174. for k, ck := range c.setCookies {
  175. s := "Set-Cookie: " + k + "=" + url.QueryEscape(ck.value)
  176. if ck.expire != nil {
  177. s += "; expire=" + ck.expire.Format(time.RFC1123)
  178. }
  179. if ck.path != "" {
  180. s += "; path=" + ck.path
  181. }
  182. if ck.domain != "" {
  183. s += "; path=" + ck.domain
  184. }
  185. if ck.secure {
  186. s += "; secure"
  187. }
  188. if ck.httpOnly {
  189. s += "; HttpOnly"
  190. }
  191. io.WriteString(c.Request.Stdout, s+"\r\n")
  192. }
  193. }
  194. io.WriteString(c.Request.Stdout, "\r\n")
  195. c.preRenered = true
  196. }
  197. }
  198. func executeTemplate(fname string, t *Template, w io.Writer, data interface{}) {
  199. e := t.Execute(w, data)
  200. if e != nil {
  201. log.Printf("error occurred during executing template %s: %s", fname, e)
  202. }
  203. }
  204. func (c *Controller) RenderContent() string {
  205. c.preRender()
  206. fname := "views/" + c.LName + "/" + c.LAction + ".tpl"
  207. t, e := loadTemplate(fname)
  208. if e != nil {
  209. // Dump c.ctxt contents
  210. }
  211. if t != nil {
  212. executeTemplate(fname, t, c.Request.Stdout, c.ctxt)
  213. }
  214. return ""
  215. }
  216. func (c *Controller) renderTemplate(fname string) {
  217. t, e := loadTemplate(fname)
  218. if e == nil {
  219. executeTemplate(fname, t, c.Request.Stdout, c.ctxt)
  220. } else {
  221. log.Printf("failed to load template %s: %s", fname, e)
  222. }
  223. }
  224. func (c *Controller) RenderElement(name string) string {
  225. c.renderTemplate("views/elements/" + name + ".tpl")
  226. return ""
  227. }
  228. func (c *Controller) RenderControllerElement(name string) string {
  229. c.renderTemplate("views/" + c.LName + "/elements/" + name + ".tpl")
  230. return ""
  231. }
  232. func (c *Controller) Render() {
  233. c.preRender()
  234. if len(c.Layout) == 0 {
  235. c.RenderContent()
  236. return
  237. }
  238. fname := "views/layouts/" + c.Layout + ".tpl"
  239. t, e := loadTemplate(fname)
  240. if e != nil {
  241. log.Printf("failed to load layout template %s: %s", fname, e)
  242. c.RenderContent()
  243. } else {
  244. executeTemplate(fname, t, c.Request.Stdout, c.ctxt)
  245. }
  246. }
  247. func (c *Controller) SetCookie(key string, value string) {
  248. c.SetCookieFull(key, value, nil, "", "", false, false)
  249. }
  250. func (c *Controller) SetCookieFull(key string, value string, expire *time.Time, path string, domain string, secure bool, httpOnly bool) {
  251. if c.setCookies == nil {
  252. c.setCookies = make(map[string]*cookie)
  253. }
  254. c.setCookies[key] = &cookie{
  255. value: value,
  256. expire: expire,
  257. path: path,
  258. domain: domain,
  259. secure: secure,
  260. httpOnly: httpOnly,
  261. }
  262. }
  263. func (c *Controller) StartSession() {
  264. if c.Session != nil {
  265. return
  266. }
  267. c.Session = GetSession(c)
  268. }
  269. func (c *Controller) CloseSession() {
  270. if c.Session == nil {
  271. return
  272. }
  273. c.Session.Close()
  274. }
  275. type ErrorHandler struct {
  276. Controller
  277. typ string
  278. }
  279. func NewErrorHandler(e Error, r *fastcgi.Request) *ErrorHandler {
  280. eh := &ErrorHandler{
  281. typ: e.Type(),
  282. }
  283. eh.Request = r
  284. eh.Init()
  285. eh.SetContext(eh)
  286. return eh
  287. }
  288. func (eh *ErrorHandler) RenderContent() string {
  289. eh.preRender()
  290. fname := "views/errors/" + eh.typ + ".tpl"
  291. t, e := loadTemplate(fname)
  292. if e != nil {
  293. var msg string
  294. switch eh.typ {
  295. case "PageNotFound":
  296. msg = "Hmm, the page you’re looking for can't be found."
  297. default:
  298. msg = "We're sorry, but there was an error processing your request. Please try again later."
  299. }
  300. fmt.Fprintf(eh.Request.Stdout, "%s", msg)
  301. } else {
  302. t.Execute(eh.Request.Stdout, eh)
  303. executeTemplate(fname, t, eh.Request.Stdout, eh)
  304. }
  305. return ""
  306. }
  307. func titleCase(s string) string {
  308. if len(s) > 0 {
  309. parts := strings.Split(s, "_")
  310. for i, p := range parts {
  311. l := len(p)
  312. if l > 0 {
  313. parts[i] = strings.ToUpper(string(p[0])) + p[1:len(p)]
  314. } else {
  315. parts[i] = ""
  316. }
  317. }
  318. return strings.Join(parts, "")
  319. }
  320. return s
  321. }
  322. func deTitleCase(s string) string {
  323. if len(s) > 0 {
  324. var o string
  325. for i, c := range s {
  326. if c >= 'A' && c <= 'Z' {
  327. if i > 0 {
  328. o += "_"
  329. }
  330. o += strings.ToLower(string(c))
  331. } else {
  332. o += string(c)
  333. }
  334. }
  335. return o
  336. }
  337. return s
  338. }
  339. func parseKeyValueString(m map[string]*vector.StringVector, s string) os.Error {
  340. if s == "" {
  341. return nil
  342. }
  343. // copied from pkg/http/request.go
  344. for _, kv := range strings.Split(s, "&") {
  345. if kv == "" {
  346. continue
  347. }
  348. kvPair := strings.SplitN(kv, "=", 2)
  349. var key, value string
  350. var e os.Error
  351. key, e = url.QueryUnescape(kvPair[0])
  352. if e == nil && len(kvPair) > 1 {
  353. value, e = url.QueryUnescape(kvPair[1])
  354. }
  355. if e != nil {
  356. return e
  357. }
  358. vec, ok := m[key]
  359. if !ok {
  360. vec = new(vector.StringVector)
  361. m[key] = vec
  362. }
  363. vec.Push(value)
  364. }
  365. return nil
  366. }
  367. var boundaryRE, _ = regexp.Compile("boundary=\"?([^\";,]+)\"?")
  368. type multipartReader struct {
  369. rd io.Reader
  370. bd []byte
  371. buf []byte
  372. head int
  373. tail int
  374. eof bool
  375. done bool
  376. }
  377. func newMultipartReader(rd io.Reader, bd string) *multipartReader {
  378. return &multipartReader{
  379. rd: rd,
  380. bd: []byte("\r\n--" + bd),
  381. buf: make([]byte, 4096),
  382. head: 0,
  383. tail: 0,
  384. }
  385. }
  386. func (md *multipartReader) finished() bool { return md.done }
  387. func (md *multipartReader) read(delim []byte) ([]byte, os.Error) {
  388. if md.done {
  389. return nil, os.EOF
  390. }
  391. if !md.eof && md.tail < len(md.buf) {
  392. n, e := md.rd.Read(md.buf[md.tail:len(md.buf)])
  393. if e != nil {
  394. if e != os.EOF {
  395. return nil, e
  396. }
  397. md.eof = true
  398. }
  399. md.tail += n
  400. }
  401. if i := bytes.Index(md.buf[md.head:md.tail], delim); i >= 0 {
  402. s := make([]byte, i)
  403. copy(s, md.buf[md.head:md.head+i])
  404. md.head += i + len(delim)
  405. return s, os.EOF
  406. }
  407. if md.eof {
  408. md.done = true
  409. return md.buf[md.head:md.tail], os.EOF
  410. }
  411. bf := md.tail - md.head
  412. keep := len(delim) - 1
  413. if keep > bf {
  414. keep = bf
  415. }
  416. stop := md.tail - keep
  417. n := stop - md.head
  418. s := make([]byte, n)
  419. if n > 0 {
  420. copy(s, md.buf[md.head:stop])
  421. }
  422. copy(md.buf[0:keep], md.buf[stop:md.tail])
  423. md.head = 0
  424. md.tail = keep
  425. return s, nil
  426. }
  427. func (md *multipartReader) readFirstLine() { md.readStringUntil(md.bd[2:len(md.bd)], false) }
  428. var crlf2 = []byte{'\r', '\n', '\r', '\n'}
  429. type byteConsumer func([]byte) os.Error
  430. func (md *multipartReader) readUntil(delim []byte, checkEnd bool, f byteConsumer) os.Error {
  431. for {
  432. b, e := md.read(delim)
  433. if b != nil {
  434. if e := f(b); e != nil {
  435. return e
  436. }
  437. }
  438. if e != nil {
  439. if e == os.EOF {
  440. if checkEnd && md.tail-md.head >= 2 && string(md.buf[md.head:md.head+2]) == "--" {
  441. md.done = true
  442. }
  443. break
  444. }
  445. return e
  446. }
  447. }
  448. return nil
  449. }
  450. func (md *multipartReader) readStringUntil(delim []byte, checkEnd bool) (string, os.Error) {
  451. var s string
  452. e := md.readUntil(delim, checkEnd, func(b []byte) os.Error {
  453. s += string(b)
  454. return nil
  455. })
  456. return s, e
  457. }
  458. type hdrInfo struct {
  459. key string
  460. val string
  461. attribs map[string]string
  462. }
  463. func parseHeader(line string) *hdrInfo {
  464. var key, attrib string
  465. var hdr *hdrInfo
  466. var attribs map[string]string
  467. var j int
  468. phase := 0
  469. line += ";"
  470. for i, c := range line {
  471. switch phase {
  472. case 0:
  473. if c == ':' {
  474. key = strings.TrimSpace(line[0:i])
  475. phase++
  476. j = i + 1
  477. }
  478. case 1:
  479. if c == ';' {
  480. attribs = make(map[string]string)
  481. hdr = &hdrInfo{
  482. key: key,
  483. val: strings.TrimSpace(line[j:i]),
  484. attribs: attribs,
  485. }
  486. phase++
  487. j = i + 1
  488. }
  489. case 2:
  490. if c == '=' {
  491. attrib = strings.TrimSpace(line[j:i])
  492. phase++
  493. j = i + 1
  494. }
  495. case 3:
  496. if c == '"' {
  497. phase++
  498. j = i + 1
  499. } else if c == ';' {
  500. attribs[attrib] = strings.TrimSpace(line[j:i])
  501. phase = 2
  502. j = i + 1
  503. }
  504. case 4:
  505. if c == '\\' {
  506. phase++
  507. } else if c == '"' {
  508. attribs[attrib] = line[j:i]
  509. phase += 2
  510. }
  511. case 5:
  512. phase--
  513. case 6:
  514. if c == ';' {
  515. phase = 2
  516. j = i + 1
  517. }
  518. }
  519. }
  520. return hdr
  521. }
  522. func (md *multipartReader) readHeaders() (map[string]*hdrInfo, os.Error) {
  523. s, _ := md.readStringUntil(crlf2, false)
  524. lines := strings.Split(s[2:len(s)], "\r\n")
  525. hdrs := make(map[string]*hdrInfo)
  526. for _, line := range lines {
  527. if hdr := parseHeader(line); hdr != nil {
  528. hdrs[hdr.key] = hdr
  529. }
  530. }
  531. return hdrs, nil
  532. }
  533. func (md *multipartReader) readBody() (string, os.Error) {
  534. return md.readStringUntil(md.bd, true)
  535. }
  536. var pads = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  537. func tempfile() (*os.File, os.Error) {
  538. tmpdir := os.Getenv("TMPDIR")
  539. if tmpdir == "" {
  540. tmpdir = "/tmp"
  541. }
  542. for {
  543. var s string
  544. for i := 0; i < 10; i++ {
  545. s += string(pads[rand.Int()%len(pads)])
  546. }
  547. file, e := os.OpenFile(tmpdir+"/fastweb."+s, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
  548. if e == nil {
  549. return file, e
  550. }
  551. pe, ok := e.(*os.PathError)
  552. if !ok || pe.Error != os.EEXIST {
  553. return nil, e
  554. }
  555. }
  556. return nil, nil
  557. }
  558. func parseMultipartForm(m map[string]*vector.StringVector, u map[string]*vector.Vector, r *fastcgi.Request) os.Error {
  559. ct := r.Params["CONTENT_TYPE"]
  560. a := boundaryRE.FindStringSubmatchIndex(ct)
  561. if len(a) < 4 {
  562. return os.NewError("can't find boundary in content type")
  563. }
  564. b := ct[a[2]:a[3]]
  565. md := newMultipartReader(r.Stdin, b)
  566. md.readFirstLine()
  567. for !md.finished() {
  568. hdrs, e := md.readHeaders()
  569. if e != nil {
  570. return e
  571. }
  572. cd, ok := hdrs["Content-Disposition"]
  573. if !ok {
  574. return os.NewError("can't find Content-Disposition")
  575. }
  576. name, ok := cd.attribs["name"]
  577. if !ok {
  578. return os.NewError("can't find attrib 'name' in Content-Disposition")
  579. }
  580. filename, ok := cd.attribs["filename"]
  581. if ok {
  582. vec, ok := u[name]
  583. if !ok {
  584. vec = new(vector.Vector)
  585. u[name] = vec
  586. }
  587. file, e := tempfile()
  588. if e != nil {
  589. return e
  590. }
  591. wr := bufio.NewWriter(file)
  592. fname := file.Name()
  593. md.readUntil(md.bd, true, func(b []byte) os.Error {
  594. if _, e := wr.Write(b); e != nil {
  595. return e
  596. }
  597. return nil
  598. })
  599. wr.Flush()
  600. // to flush (system) buffer, re-open immediately
  601. file.Close()
  602. file, _ = os.Open(fname)
  603. vec.Push(&Upload{
  604. File: file,
  605. Filename: filename,
  606. })
  607. } else {
  608. vec, ok := m[name]
  609. if !ok {
  610. vec = new(vector.StringVector)
  611. m[name] = vec
  612. }
  613. s, e := md.readBody()
  614. if e != nil {
  615. return e
  616. }
  617. vec.Push(s)
  618. }
  619. }
  620. return nil
  621. }
  622. func parseForm(r *fastcgi.Request) (string, map[string][]string, map[string][]*Upload, os.Error) {
  623. m := make(map[string]*vector.StringVector)
  624. u := make(map[string]*vector.Vector)
  625. var body string
  626. s := r.Params["QUERY_STRING"]
  627. if s != "" {
  628. e := parseKeyValueString(m, s)
  629. if e != nil {
  630. return body, nil, nil, e
  631. }
  632. }
  633. if r.Params["REQUEST_METHOD"] == "POST" {
  634. switch ct := r.Params["CONTENT_TYPE"]; true {
  635. case strings.HasPrefix(ct, "application/x-www-form-urlencoded") && (len(ct) == 33 || ct[33] == ';'):
  636. var b []byte
  637. var e os.Error
  638. if b, e = ioutil.ReadAll(r.Stdin); e != nil {
  639. return body, nil, nil, e
  640. }
  641. body = string(b)
  642. e = parseKeyValueString(m, body)
  643. if e != nil {
  644. return body, nil, nil, e
  645. }
  646. case strings.HasPrefix(ct, "multipart/form-data"):
  647. e := parseMultipartForm(m, u, r)
  648. if e != nil {
  649. return body, nil, nil, e
  650. }
  651. default:
  652. log.Printf("unknown content type '%s'", ct)
  653. }
  654. }
  655. form := make(map[string][]string)
  656. for k, vec := range m {
  657. form[k] = vec.Copy()
  658. }
  659. upload := make(map[string][]*Upload)
  660. for k, vec := range u {
  661. d := vec.Copy()
  662. v := make([]*Upload, len(d))
  663. for i, u := range d {
  664. v[i] = u.(*Upload)
  665. }
  666. upload[k] = v
  667. }
  668. return body, form, upload, nil
  669. }
  670. func parseCookies(r *fastcgi.Request) (map[string]string, os.Error) {
  671. cookies := make(map[string]string)
  672. if s, ok := r.Params["HTTP_COOKIE"]; ok {
  673. var key string
  674. phase := 0
  675. j := 0
  676. s += ";"
  677. for i, c := range s {
  678. switch phase {
  679. case 0:
  680. if c == '=' {
  681. key = strings.TrimSpace(s[j:i])
  682. j = i + 1
  683. phase++
  684. }
  685. case 1:
  686. if c == ';' {
  687. v, e := url.QueryUnescape(s[j:i])
  688. if e != nil {
  689. return cookies, e
  690. }
  691. cookies[key] = v
  692. phase = 0
  693. j = i + 1
  694. }
  695. }
  696. }
  697. }
  698. return cookies, nil
  699. }
  700. func (a *Application) getEnv(r *fastcgi.Request) *env {
  701. var params []string
  702. var lname string
  703. var laction string
  704. path, _ := r.Params["REQUEST_URI"]
  705. p := strings.SplitN(path, "?", 2)
  706. if len(p) > 1 {
  707. path = p[0]
  708. r.Params["QUERY_STRING"] = p[1]
  709. }
  710. pparts := strings.Split(path, "/")
  711. n := len(pparts)
  712. if n > 1 {
  713. lname = pparts[1]
  714. if n > 2 {
  715. laction = pparts[2]
  716. if n > 3 {
  717. if pparts[n-1] == "" {
  718. n--
  719. }
  720. params = pparts[3:n]
  721. }
  722. }
  723. }
  724. name := titleCase(lname)
  725. action := titleCase(laction)
  726. body, form, upload, e := parseForm(r)
  727. if e != nil {
  728. log.Printf("failed to parse form: %s", e.String())
  729. }
  730. cookies, e := parseCookies(r)
  731. if e != nil {
  732. log.Printf("failed to parse cookies: %s", e.String())
  733. }
  734. return &env{
  735. path: path,
  736. controller: name,
  737. lcontroller: lname,
  738. action: action,
  739. laction: laction,
  740. params: params,
  741. request: r,
  742. body: body,
  743. form: form,
  744. upload: upload,
  745. cookies: cookies,
  746. }
  747. }
  748. func (a *Application) route(r *fastcgi.Request) os.Error {
  749. env := a.getEnv(r)
  750. if env.controller == "" {
  751. env.controller = a.defaultController
  752. env.lcontroller = deTitleCase(env.controller)
  753. }
  754. cinfo, _ := a.controllerMap[env.controller]
  755. if cinfo == nil {
  756. return NewError("PageNotFound", "controller class '"+env.controller+"' not found")
  757. }
  758. vc := reflect.New(cinfo.controllerType)
  759. vi := reflect.New(cinfo.controllerPtrType).Elem()
  760. vi.Set(vc.Elem().Addr())
  761. c := vi.Interface().(ControllerInterface)
  762. if env.action == "" {
  763. env.action = c.DefaultAction()
  764. env.laction = deTitleCase(env.action)
  765. }
  766. minfo, _ := cinfo.methodMap[env.action]
  767. if minfo == nil {
  768. return NewError("PageNotFound", "action '"+env.action+"' is not implemented in controller '"+env.controller+"'")
  769. }
  770. if minfo.nparams > len(env.params) {
  771. return NewError("PageNotFound", "not enough parameter")
  772. }
  773. pv := make([]reflect.Value, minfo.nparams+1)
  774. pv[0] = vc
  775. for i := 0; i < minfo.nparams; i++ {
  776. p := env.params[i]
  777. switch minfo.paramTypes[i] {
  778. case StrParam:
  779. pv[i+1] = reflect.ValueOf(p)
  780. case IntParam:
  781. x, e2 := strconv.Atoi(p)
  782. if e2 != nil {
  783. return NewError("PageNotFound", fmt.Sprintf("parameter %d must be an integer, input: %s", i+1, p))
  784. }
  785. pv[i+1] = reflect.ValueOf(x)
  786. }
  787. }
  788. c.Init()
  789. c.SetEnv(env)
  790. c.PreFilter()
  791. eval := minfo.method.Call(pv)[0]
  792. if !eval.IsNil() {
  793. elemval := eval.Elem()
  794. return unsafe.Unreflect(elemval.Type(), unsafe.Pointer(elemval.UnsafeAddr())).(os.Error)
  795. }
  796. c.SetContext(c)
  797. c.Render()
  798. c.CloseSession()
  799. return nil
  800. }
  801. func (a *Application) Handle(r *fastcgi.Request) bool {
  802. e := a.route(r)
  803. if e != nil {
  804. var ee Error
  805. if e, ok := e.(Error); ok {
  806. ee = e
  807. } else {
  808. ee = NewError("Generic", e.String())
  809. }
  810. log.Printf("%s", e.String())
  811. eh := NewErrorHandler(ee, r)
  812. eh.Render()
  813. }
  814. return true
  815. }
  816. func NewApplication() *Application {
  817. return &Application{
  818. controllerMap: make(map[string]*controllerInfo),
  819. defaultController: "Default",
  820. }
  821. }
  822. func (a *Application) RegisterController(c ControllerInterface) {
  823. v := reflect.ValueOf(c)
  824. pt := v.Type()
  825. t := v.Elem().Type()
  826. name := t.Name()
  827. mmap := make(map[string]*methodInfo)
  828. n := pt.NumMethod()
  829. for i := 0; i < n; i++ {
  830. m := pt.Method(i)
  831. name := m.Name
  832. switch name {
  833. case "SetEnv", "Render", "DefaultAction", "Init", "PreFilter", "SetContext":
  834. continue
  835. }
  836. mt := m.Type
  837. if mt.NumOut() != 1 {
  838. continue
  839. }
  840. mo := mt.Out(0)
  841. switch mo.Kind() {
  842. case reflect.Interface:
  843. if mo.PkgPath() != "os" || mo.Name() != "Error" {
  844. continue
  845. }
  846. default:
  847. continue
  848. }
  849. nin := mt.NumIn() - 1
  850. ptypes := make([]int, nin)
  851. for j := 0; j < nin; j++ {
  852. in := mt.In(j + 1)
  853. switch in.Kind() {
  854. case reflect.Int:
  855. ptypes[j] = IntParam
  856. case reflect.String:
  857. ptypes[j] = StrParam
  858. default:
  859. continue
  860. }
  861. }
  862. mmap[name] = &methodInfo{
  863. name: name,
  864. method: m.Func,
  865. nparams: nin,
  866. paramTypes: ptypes,
  867. }
  868. }
  869. a.controllerMap[name] = &controllerInfo{
  870. name: name,
  871. controller: c,
  872. controllerType: t,
  873. controllerPtrType: pt,
  874. methodMap: mmap,
  875. }
  876. }
  877. func (a *Application) Run(addr string) os.Error {
  878. rand.Seed(time.Nanoseconds())
  879. return fastcgi.RunStandalone(addr, a)
  880. }