// proxyWebsocket copies data between websocket client and server until one side // closes the connection. (ReverseProxy doesn't work with websocket requests.) func proxyWebsocket(w http.ResponseWriter, r *http.Request, host string) { d, err := net.Dial("tcp", host) if err != nil { http.Error(w, "Error contacting backend server.", 500) glog.Errorf("Error dialing websocket backend %s: %v", host, err) return } hj, ok := w.(http.Hijacker) if !ok { http.Error(w, "Not a hijacker?", 500) return } nc, _, err := hj.Hijack() if err != nil { glog.Errorf("Hijack error: %v", err) return } defer nc.Close() defer d.Close() err = r.Write(d) if err != nil { glog.Errorf("Error copying request to target: %v", err) return } errc := make(chan error, 2) cp := func(dst io.Writer, src io.Reader) { _, err := io.Copy(dst, src) errc <- err } go cp(d, nc) go cp(nc, d) <-errc }
func (r *RenderTemplateResult) render(req *Request, resp *Response, wr io.Writer) { err := r.Template.Execute(wr, r.RenderArgs) if err == nil { return } var templateContent []string templateName, line, description := parseTemplateError(err) if templateName == "" { templateName = r.Template.Name() templateContent = MainTemplateLoader.SourceLines(templateName) } else { if tmpl, _ := MainTemplateLoader.Template(templateName); tmpl != nil { templateContent = MainTemplateLoader.SourceLines(templateName) } } compileError := &Error{ Title: "Template Execution Error", Path: templateName, Description: description, Line: line, SourceLines: templateContent, } resp.Status = 500 glog.Errorf("Template Execution Error (in %s): %s", templateName, description) ErrorResult{r.RenderArgs, compileError}.Apply(req, resp) }
func Unbind(output map[string]string, name string, val interface{}) { if binder, found := binderForType(reflect.TypeOf(val)); found { if binder.Unbind != nil { binder.Unbind(output, name, val) } else { glog.Errorf("revel/binder: can not unbind %s=%s", name, val) } } }
func (c *Controller) RenderError(err error) Result { // If it's a 5xx error, also log it to error. status := c.Response.Status if status == 0 { status = http.StatusInternalServerError } if status/100 == 5 { glog.Errorf("%d %s: %s", status, http.StatusText(status), err) } return ErrorResult{c.RenderArgs, err} }
// Deserialize transforms bytes produced by Serialize back into a Go object, // storing it into "ptr", which must be a pointer to the value type. func Deserialize(byt []byte, ptr interface{}) (err error) { if bytes, ok := ptr.(*[]byte); ok { *bytes = byt return } if v := reflect.ValueOf(ptr); v.Kind() == reflect.Ptr { switch p := v.Elem(); p.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var i int64 i, err = strconv.ParseInt(string(byt), 10, 64) if err != nil { glog.Errorf("revel/cache: failed to parse int '%s': %s", string(byt), err) } else { p.SetInt(i) } return case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: var i uint64 i, err = strconv.ParseUint(string(byt), 10, 64) if err != nil { glog.Errorf("revel/cache: failed to parse uint '%s': %s", string(byt), err) } else { p.SetUint(i) } return } } b := bytes.NewBuffer(byt) decoder := gob.NewDecoder(b) if err = decoder.Decode(ptr); err != nil { glog.Errorf("revel/cache: gob decoding failed: %s", err) return } return }
// Return nil if no match. func (r *Route) Match(method string, reqPath string) *RouteMatch { // Check the Method if r.Method != "*" && method != r.Method && !(method == "HEAD" && r.Method == "GET") { return nil } // Check the Path var matches []string = r.pathPattern.FindStringSubmatch(reqPath) if len(matches) == 0 || len(matches[0]) != len(reqPath) { return nil } // Figure out the Param names. params := make(map[string]string) for i, m := range matches[1:] { params[r.pathPattern.SubexpNames()[i+1]] = m } // If the action is variablized, replace into it with the captured args. action := r.Action if strings.Contains(action, "{") { for key, value := range params { action = strings.Replace(action, "{"+key+"}", value, -1) } } // Special handling for explicit 404's. if action == "404" { return &RouteMatch{ Action: "404", } } // Split the action into controller and method actionSplit := strings.Split(action, ".") if len(actionSplit) != 2 { glog.Errorf("Failed to split action: %s (matching route: %s)", action, r.Action) return nil } return &RouteMatch{ Action: action, ControllerName: actionSplit[0], MethodName: actionSplit[1], Params: params, FixedParams: r.FixedParams, } }
// Serialize transforms the given value into bytes following these rules: // - If value is a byte array, it is returned as-is. // - If value is an int or uint type, it is returned as the ASCII representation // - Else, encoding/gob is used to serialize func Serialize(value interface{}) ([]byte, error) { if bytes, ok := value.([]byte); ok { return bytes, nil } switch v := reflect.ValueOf(value); v.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return []byte(strconv.FormatInt(v.Int(), 10)), nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return []byte(strconv.FormatUint(v.Uint(), 10)), nil } var b bytes.Buffer encoder := gob.NewEncoder(&b) if err := encoder.Encode(value); err != nil { glog.Errorf("revel/cache: gob encoding '%s' failed: %s", value, err) return nil, err } return b.Bytes(), nil }
// This method handles requests for files. The supplied prefix may be absolute // or relative. If the prefix is relative it is assumed to be relative to the // application directory. The filepath may either be just a file or an // additional filepath to search for the given file. This response may return // the following responses in the event of an error or invalid request; // 403(Forbidden): If the prefix filepath combination results in a directory. // 404(Not found): If the prefix and filepath combination results in a non-existent file. // 500(Internal Server Error): There are a few edge cases that would likely indicate some configuration error outside of revel. // // Note that when defining routes in routes/conf the parameters must not have // spaces around the comma. // Bad: Static.Serve("public/img", "favicon.png") // Good: Static.Serve("public/img","favicon.png") // // Examples: // Serving a directory // Route (conf/routes): // GET /public/{<.*>filepath} Static.Serve("public") // Request: // public/js/sessvars.js // Calls // Static.Serve("public","js/sessvars.js") // // Serving a file // Route (conf/routes): // GET /favicon.ico Static.Serve("public/img","favicon.png") // Request: // favicon.ico // Calls: // Static.Serve("public/img", "favicon.png") func (c Static) Serve(prefix, filepath string) revel.Result { // Fix for #503. prefix = c.Params.Fixed.Get("prefix") if prefix == "" { return c.NotFound("") } var basePath string if !fpath.IsAbs(prefix) { basePath = revel.BasePath } basePathPrefix := fpath.Join(basePath, fpath.FromSlash(prefix)) fname := fpath.Join(basePathPrefix, fpath.FromSlash(filepath)) if !strings.HasPrefix(fname, basePathPrefix) { glog.Warningf("Attempted to read file outside of base path: %s", fname) return c.NotFound("") } finfo, err := os.Stat(fname) if err != nil { if os.IsNotExist(err) { glog.Warningf("File not found (%s): %s ", fname, err) return c.NotFound("File not found") } glog.Errorf("Error trying to get fileinfo for '%s': %s", fname, err) return c.RenderError(err) } if finfo.Mode().IsDir() { glog.Warningf("Attempted directory listing of %s", fname) return c.Forbidden("Directory listing not allowed") } file, err := os.Open(fname) return c.RenderFile(file, revel.Inline) }
// Prepares the route to be used in matching. func NewRoute(method, path, action, fixedArgs string) (r *Route) { // Handle fixed arguments argsReader := strings.NewReader(fixedArgs) csv := csv.NewReader(argsReader) fargs, err := csv.Read() if err != nil && err != io.EOF { glog.Errorf("Invalid fixed parameters (%v): for string '%v'", err.Error(), fixedArgs) } r = &Route{ Method: strings.ToUpper(method), Path: path, Action: action, FixedParams: fargs, } // URL pattern // TODO: Support non-absolute paths if !strings.HasPrefix(r.Path, "/") { glog.Error("Absolute URL required.") return } // Handle embedded arguments // Convert path arguments with unspecified regexes to standard form. // e.g. "/customer/{id}" => "/customer/{<[^/]+>id} normPath := nakedPathParamRegex.ReplaceAllStringFunc(r.Path, func(m string) string { var argMatches []string = nakedPathParamRegex.FindStringSubmatch(m) return "{<[^/]+>" + argMatches[1] + "}" }) // Go through the arguments r.args = make([]*arg, 0, 3) for i, m := range argsPattern.FindAllStringSubmatch(normPath, -1) { r.args = append(r.args, &arg{ name: string(m[2]), index: i, constraint: regexp.MustCompile(string(m[1])), }) } // Now assemble the entire path regex, including the embedded parameters. // e.g. /app/{<[^/]+>id} => /app/(?P<id>[^/]+) pathPatternStr := argsPattern.ReplaceAllStringFunc(normPath, func(m string) string { var argMatches []string = argsPattern.FindStringSubmatch(m) return "(?P<" + argMatches[2] + ">" + argMatches[1] + ")" }) r.pathPattern = regexp.MustCompile(pathPatternStr + "$") // Handle action var actionPatternStr string = strings.Replace(r.Action, ".", `\.`, -1) for _, arg := range r.args { var argName string = "{" + arg.name + "}" if argIndex := strings.Index(actionPatternStr, argName); argIndex != -1 { actionPatternStr = strings.Replace(actionPatternStr, argName, "(?P<"+arg.name+">"+arg.constraint.String()+")", -1) } } r.actionPattern = regexp.MustCompile(actionPatternStr) return }
// Refresh scans the views directory and parses all templates using the // configured TemplateEngines. If a template fails to parse, the error is set // on the loader (and returned). func (loader *TemplateLoader) Refresh() *Error { glog.V(1).Infof("Refreshing templates from %s", loader.paths) loader.compileError = nil loader.templatePaths = map[string]string{} // Set the template delimiters for the project if present, then split into left // and right delimiters around a space character var splitDelims []string if delims := Config.StringDefault("template.delimiters", ""); delims != "" { splitDelims = strings.Split(delims, " ") if len(splitDelims) != 2 { glog.Fatalln("app.conf: Incorrect format for template.delimiters") } } loader.defaultEngine = NewTextTemplateEngine() loader.engines = map[string]TemplateEngine{ ".html": NewHtmlTemplateEngine(), ".xml": NewHtmlTemplateEngine(), ".json": NewTextTemplateEngine(), ".txt": NewTextTemplateEngine(), } // Walk through the template loader's paths and pass each template to the // appropriate engine. for _, basePath := range loader.paths { // Walk only returns an error if the template loader is completely unusable // (namely, if one of the TemplateFuncs does not have an acceptable signature). funcErr := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) (walkErr error) { defer func() { if err := recover(); err != nil { walkErr = &Error{ Title: "Panic (Template Loader)", Description: fmt.Sprintln(err), } } }() if err != nil { glog.Errorln("error walking templates:", err) return nil } // Walk into directories. if info.IsDir() { if !loader.WatchDir(info.Name()) { return filepath.SkipDir } return nil } if !loader.WatchFile(info.Name()) { return nil } // Convert template names to use forward slashes, even on Windows. templateName := path[len(basePath)+1:] if os.PathSeparator == '\\' { templateName = strings.Replace(templateName, "\\", "/", -1) } // If we already loaded a template of this name, skip it. if _, ok := loader.templatePaths[templateName]; ok { return nil } loader.templatePaths[templateName] = path fileBytes, err := ioutil.ReadFile(path) if err != nil { glog.Errorln("Failed reading file:", path) return nil } ext := filepath.Ext(templateName) engine, ok := loader.engines[ext] if !ok { engine = loader.defaultEngine } // If alternate delimiters set for the project, change them for this template. if splitDelims != nil { if strings.HasPrefix(path, ViewsPath) { engine.Delims(splitDelims[0], splitDelims[1]) } else { engine.Delims("", "") } } err = engine.Parse(templateName, string(fileBytes)) // Store / report the first error encountered. if err != nil && loader.compileError == nil { _, line, description := parseTemplateError(err) loader.compileError = &Error{ Title: "Template Compilation Error", Path: templateName, Description: description, Line: line, SourceLines: strings.Split(string(fileBytes), "\n"), } glog.Errorf("Template compilation error (In %s around line %d):\n%s", templateName, line, description) } return nil }) // If there was an error with the Funcs, set it and return immediately. if funcErr != nil { loader.compileError = funcErr.(*Error) return loader.compileError } } return loader.compileError }