diff --git a/exec.go b/exec.go index 97c599b..986104b 100644 --- a/exec.go +++ b/exec.go @@ -40,12 +40,15 @@ func setupInitialCgiEnviron() []string { } } -func executeCgi(request *Request) *GophorError { +func executeCgi(responder *Responder) *GophorError { + /* Easier if we grab a pointer to the request here */ + request := responder.Request + /* Get initial CgiEnv variables */ cgiEnv := Config.CgiEnv - cgiEnv = append(cgiEnv, envKeyValue("SERVER_NAME", request.Host.Name())) /* MUST be set to name of server host client is connecting to */ - cgiEnv = append(cgiEnv, envKeyValue("SERVER_PORT", request.Host.Port())) /* MUST be set to the server port that client is connecting to */ - cgiEnv = append(cgiEnv, envKeyValue("REMOTE_ADDR", request.Client.Ip())) /* Remote client addr, MUST be set */ + cgiEnv = append(cgiEnv, envKeyValue("SERVER_NAME", responder.Host.Name())) /* MUST be set to name of server host client is connecting to */ + cgiEnv = append(cgiEnv, envKeyValue("SERVER_PORT", responder.Host.Port())) /* MUST be set to the server port that client is connecting to */ + cgiEnv = append(cgiEnv, envKeyValue("REMOTE_ADDR", responder.Client.Ip())) /* Remote client addr, MUST be set */ /* We store the query string in Parameters[0]. Ensure we git without initial delimiter */ var queryString string @@ -62,8 +65,8 @@ func executeCgi(request *Request) *GophorError { cgiEnv = append(cgiEnv, envKeyValue("REQUEST_URI", "/"+request.RelPath()+request.Parameters[0])) /* Fuck it. For now, we don't support PATH_INFO. It's a piece of shit variable */ -// cgiEnv = append(cgiEnv, envKeyValue("PATH_INFO", request.Parameters[0])) /* Sub-resource to be fetched by script, derived from path hierarch portion of URI. NOT URL encoded */ -// cgiEnv = append(cgiEnv, envKeyValue("PATH_TRANSLATED", request.AbsPath())) /* Take PATH_INFO, parse as local URI and append root dir */ +// cgiEnv = append(cgiEnv, envKeyValue("PATH_INFO", responder.Parameters[0])) /* Sub-resource to be fetched by script, derived from path hierarch portion of URI. NOT URL encoded */ +// cgiEnv = append(cgiEnv, envKeyValue("PATH_TRANSLATED", responder.AbsPath())) /* Take PATH_INFO, parse as local URI and append root dir */ /* We ignore these due to just CBA and we're not implementing authorization yet */ // cgiEnv = append(cgiEnv, envKeyValue("AUTH_TYPE", "")) /* Any method used my server to authenticate user, MUST be set if auth'd */ @@ -72,18 +75,18 @@ func executeCgi(request *Request) *GophorError { // cgiEnv = append(cgiEnv, envKeyValue("REMOTE_HOST", "")) /* Remote client domain name */ // cgiEnv = append(cgiEnv, envKeyValue("REMOTE_USER", "")) /* Remote user ID, if AUTH_TYPE, MUST be set */ - return execute(request.Writer, cgiEnv, request.AbsPath(), nil) + return execute(responder.Writer, cgiEnv, request.AbsPath(), nil) } -func executeFile(request *Request) *GophorError { - return execute(request.Writer, Config.Env, request.AbsPath(), request.Parameters) +func executeFile(responder *Responder) *GophorError { + return execute(responder.Writer, Config.Env, responder.Request.AbsPath(), responder.Request.Parameters) } -func executeCommand(request *Request) *GophorError { - if isRestrictedCommand(request.AbsPath()) { +func executeCommand(responder *Responder) *GophorError { + if isRestrictedCommand(responder.Request.AbsPath()) { return &GophorError{ RestrictedCommandErr, nil } } - return execute(request.Writer, Config.Env, request.AbsPath(), request.Parameters) + return execute(responder.Writer, Config.Env, responder.Request.AbsPath(), responder.Request.Parameters) } func execute(writer io.Writer, env []string, path string, args []string) *GophorError { @@ -148,7 +151,7 @@ func execute(writer io.Writer, env []string, path string, args []string) *Gophor if exitCode != 0 { /* If non-zero exit code return error */ - Config.SysLog.Error("", "Error executing: %s\n", cmd.String()) + Config.SysLog.Error("", "Error executing: %s\n", path) return &GophorError{ CommandExitCodeErr, err } } else { return nil diff --git a/filecontents.go b/filecontents.go index 91a1516..13a1ecf 100644 --- a/filecontents.go +++ b/filecontents.go @@ -11,8 +11,8 @@ type FileContents interface { * for holding onto some level of information about the * contents of a file. */ - Render(*Request) *GophorError - Load() *GophorError + Render(*Responder) *GophorError + Load() *GophorError Clear() } @@ -20,8 +20,8 @@ type GeneratedFileContents struct { Contents []byte /* Generated file contents as byte slice */ } -func (fc *GeneratedFileContents) Render(request *Request) *GophorError { - return request.WriteFlush(fc.Contents) +func (fc *GeneratedFileContents) Render(responder *Responder) *GophorError { + return responder.WriteFlush(fc.Contents) } func (fc *GeneratedFileContents) Load() *GophorError { @@ -34,12 +34,12 @@ func (fc *GeneratedFileContents) Clear() { } type RegularFileContents struct { - Request *Request /* Stored filesystem request */ - Contents []byte /* File contents as byte slice */ + Request *Request /* Stored filesystem request */ + Contents []byte /* File contents as byte slice */ } -func (fc *RegularFileContents) Render(request *Request) *GophorError { - return request.WriteFlush(fc.Contents) +func (fc *RegularFileContents) Render(responder *Responder) *GophorError { + return responder.WriteFlush(fc.Contents) } func (fc *RegularFileContents) Load() *GophorError { @@ -58,18 +58,18 @@ type GophermapContents struct { Sections []GophermapSection /* Slice to hold differing gophermap sections */ } -func (gc *GophermapContents) Render(request *Request) *GophorError { +func (gc *GophermapContents) Render(responder *Responder) *GophorError { /* Render and send each of the gophermap sections */ var gophorErr *GophorError for _, line := range gc.Sections { - gophorErr = line.Render(request) + gophorErr = line.Render(responder) if gophorErr != nil { Config.SysLog.Error("", "Error executing gophermap contents: %s\n", gophorErr.Error()) } } /* End on footer text (including lastline) */ - return request.WriteFlush(Config.FooterText) + return responder.WriteFlush(Config.FooterText) } func (gc *GophermapContents) Load() *GophorError { @@ -88,48 +88,58 @@ type GophermapSection interface { * sections and render when necessary */ - Render(*Request) *GophorError + Render(*Responder) *GophorError } type GophermapText struct { Contents []byte /* Text contents */ } -func (s *GophermapText) Render(request *Request) *GophorError { - return request.Write(replaceStrings(string(s.Contents), request.Host)) +func (s *GophermapText) Render(responder *Responder) *GophorError { + return responder.Write(replaceStrings(string(s.Contents), responder.Host)) } type GophermapDirListing struct { - Request *Request /* Stored filesystem request */ - Hidden map[string]bool /* Hidden files map parsed from gophermap */ + Request *Request /* Stored filesystem request */ + Hidden map[string]bool /* Hidden files map parsed from gophermap */ } -func (g *GophermapDirListing) Render(request *Request) *GophorError { +func (g *GophermapDirListing) Render(responder *Responder) *GophorError { /* Create new filesystem request from mixture of stored + supplied */ return listDir( - &Request{ - request.Host, - request.Client, - request.Writer, - g.Request.Path, - g.Request.Parameters, + &Responder{ + responder.Host, + responder.Client, + responder.Writer, + g.Request, }, g.Hidden, ) } +type GophermapFile struct { + Request *Request +} + +func (g *GophermapFile) Render(responder *Responder) *GophorError { + fileContents, gophorErr := readIntoGophermap(g.Request.AbsPath()) + if gophorErr != nil { + return gophorErr + } + return responder.Write(fileContents) +} + type GophermapExecCgi struct { Request *Request /* Stored file system request */ } -func (g *GophermapExecCgi) Render(request *Request) *GophorError { +func (g *GophermapExecCgi) Render(responder *Responder) *GophorError { /* Create new filesystem request from mixture of stored + supplied */ - return executeCgi(&Request{ - request.Host, - request.Client, - request.Writer, - g.Request.Path, - g.Request.Parameters, + return executeCgi(&Responder{ + responder.Host, + responder.Client, + responder.Writer, + g.Request, }) } @@ -137,13 +147,12 @@ type GophermapExecFile struct { Request *Request /* Stored file system request */ } -func (g *GophermapExecFile) Render(request *Request) *GophorError { - return executeFile(&Request{ - request.Host, - request.Client, - request.Writer, - g.Request.Path, - g.Request.Parameters, +func (g *GophermapExecFile) Render(responder *Responder) *GophorError { + return executeFile(&Responder{ + responder.Host, + responder.Client, + responder.Writer, + g.Request, }) } @@ -151,13 +160,12 @@ type GophermapExecCommand struct { Request *Request } -func (g *GophermapExecCommand) Render(request *Request) *GophorError { - return executeCommand(&Request{ - request.Host, - request.Client, - request.Writer, - g.Request.Path, - g.Request.Parameters, +func (g *GophermapExecCommand) Render(responder *Responder) *GophorError { + return executeCommand(&Responder{ + responder.Host, + responder.Client, + responder.Writer, + g.Request, }) } @@ -203,8 +211,8 @@ func readGophermap(request *Request) ([]GophermapSection, *GophorError) { case TypeSubGophermap: /* Parse new requestPath and parameters (this automatically sanitizes requestPath) */ - subPath, subParameters := parseLineRequestString(request.Path, line[1:]) - subRequest := NewRequest(nil, nil, nil, subPath, subParameters) + subRelPath, subParameters := parseLineRequestString(request.Path, line[1:]) + subRequest := &Request{ subRelPath, subParameters } if !subRequest.PathHasAbsPrefix("/") { if Config.CgiEnabled { @@ -245,10 +253,7 @@ func readGophermap(request *Request) ([]GophermapSection, *GophorError) { if Config.CgiEnabled && subRequest.PathHasRelPrefix(CgiBinDirStr) { sections = append(sections, &GophermapExecCgi{ subRequest }) } else { - fileContents, gophorErr := readIntoGophermap(subRequest.AbsPath()) - if gophorErr == nil { - sections = append(sections, &GophermapText{ fileContents }) - } + sections = append(sections, &GophermapFile{ subRequest }) } } @@ -261,8 +266,7 @@ func readGophermap(request *Request) ([]GophermapSection, *GophorError) { case TypeEndBeginList: /* Create GophermapDirListing object then break out at end of loop */ - dirPath := NewRequestPath(request.RootDir(), request.PathTrimRelSuffix(GophermapFileStr)) - dirRequest := NewRequest(nil, nil, nil, dirPath, request.Parameters) + dirRequest := &Request{ NewRequestPath(request.RootDir(), request.PathTrimRelSuffix(GophermapFileStr)), request.Parameters } dirListing = &GophermapDirListing{ dirRequest, hidden } return false diff --git a/filesystem.go b/filesystem.go index 3dc3dae..abea917 100644 --- a/filesystem.go +++ b/filesystem.go @@ -32,18 +32,18 @@ func (fs *FileSystem) Init(size int, fileSizeMax float64) { fs.CacheFileMax = int64(BytesInMegaByte * fileSizeMax) } -func (fs *FileSystem) HandleRequest(request *Request) *GophorError { +func (fs *FileSystem) HandleRequest(responder *Responder) *GophorError { /* Check if restricted file */ - if isRestrictedFile(request.AbsPath()) { + if isRestrictedFile(responder.Request.AbsPath()) { return &GophorError{ IllegalPathErr, nil } } /* Get filesystem stat, check it exists! */ - stat, err := os.Stat(request.AbsPath()) + stat, err := os.Stat(responder.Request.AbsPath()) if err != nil { /* Check file isn't in cache before throwing in the towel */ fs.CacheMutex.RLock() - file := fs.CacheMap.Get(request.AbsPath()) + file := fs.CacheMap.Get(responder.Request.AbsPath()) if file == nil { fs.CacheMutex.RUnlock() return &GophorError{ FileStatErr, err } @@ -51,7 +51,7 @@ func (fs *FileSystem) HandleRequest(request *Request) *GophorError { /* It's there! Get contents, unlock and return */ file.Mutex.RLock() - gophorErr := file.WriteContents(request) + gophorErr := file.WriteContents(responder) file.Mutex.RUnlock() fs.CacheMutex.RUnlock() @@ -63,43 +63,44 @@ func (fs *FileSystem) HandleRequest(request *Request) *GophorError { /* Directory */ case stat.Mode() & os.ModeDir != 0: /* Ignore anything under cgi-bin directory */ - if request.PathHasRelPrefix(CgiBinDirStr) { + if responder.Request.PathHasRelPrefix(CgiBinDirStr) { return &GophorError{ IllegalPathErr, nil } } /* Check Gophermap exists */ - gophermapPath := NewRequestPath(request.RootDir(), request.PathJoinRel(GophermapFileStr)) + gophermapPath := NewRequestPath(responder.Request.RootDir(), responder.Request.PathJoinRel(GophermapFileStr)) stat, err = os.Stat(gophermapPath.Absolute()) if err == nil { /* Gophermap exists! If executable and CGI enabled execute, else serve. */ - gophermapRequest := NewRequest(request.Host, request.Client, request.Writer, gophermapPath, request.Parameters) + gophermapRequest := &Request{ gophermapPath, responder.Request.Parameters } + responder.Request = gophermapRequest if stat.Mode().Perm() & 0100 != 0 { if Config.CgiEnabled { - return gophermapRequest.SafeFlush(executeFile(gophermapRequest)) + return responder.SafeFlush(executeFile(responder)) } else { return &GophorError{ CgiDisabledErr, nil } } } else { - return fs.FetchFile(gophermapRequest) + return fs.FetchFile(responder) } } else { /* No gophermap, serve directory listing */ - return listDir(request, map[string]bool{}) + return listDir(responder, map[string]bool{}) } /* Regular file */ case stat.Mode() & os.ModeType == 0: /* If cgi-bin and CGI enabled, return executed contents. Else, fetch */ - if request.PathHasRelPrefix(CgiBinDirStr) { + if responder.Request.PathHasRelPrefix(CgiBinDirStr) { if Config.CgiEnabled { - return request.SafeFlush(executeCgi(request)) + return responder.SafeFlush(executeCgi(responder)) } else { return &GophorError{ CgiDisabledErr, nil } } } else { - return fs.FetchFile(request) + return fs.FetchFile(responder) } /* Unsupported type */ @@ -108,10 +109,10 @@ func (fs *FileSystem) HandleRequest(request *Request) *GophorError { } } -func (fs *FileSystem) FetchFile(request *Request) *GophorError { +func (fs *FileSystem) FetchFile(responder *Responder) *GophorError { /* Get cache map read lock then check if file in cache map */ fs.CacheMutex.RLock() - file := fs.CacheMap.Get(request.AbsPath()) + file := fs.CacheMap.Get(responder.Request.AbsPath()) if file != nil { /* File in cache -- before doing anything get file read lock */ @@ -140,7 +141,7 @@ func (fs *FileSystem) FetchFile(request *Request) *GophorError { /* Open file here, to check it exists, ready for file stat * and in case file is too big we pass it as a raw response */ - fd, err := os.Open(request.AbsPath()) + fd, err := os.Open(responder.Request.AbsPath()) if err != nil { /* Error stat'ing file, unlock read mutex then return error */ fs.CacheMutex.RUnlock() @@ -159,22 +160,22 @@ func (fs *FileSystem) FetchFile(request *Request) *GophorError { if stat.Size() > fs.CacheFileMax { /* Unlock the read mutex, we don't need it where we're going... returning, we're returning. */ fs.CacheMutex.RUnlock() - return request.WriteRaw(fd) + return responder.WriteRaw(fd) } /* Create new file contents */ var contents FileContents - if request.PathHasAbsSuffix("/"+GophermapFileStr) { - contents = &GophermapContents{ request.CachedRequest(), nil } + if responder.Request.PathHasAbsSuffix("/"+GophermapFileStr) { + contents = &GophermapContents{ responder.Request, nil } } else { - contents = &RegularFileContents{ request.CachedRequest(), nil } + contents = &RegularFileContents{ responder.Request, nil } } /* Compare file size (in MB) to CacheFileSizeMax. If larger, just send file raw */ if stat.Size() > fs.CacheFileMax { /* Unlock the read mutex, we don't need it where we're going... returning, we're returning. */ fs.CacheMutex.RUnlock() - return contents.Render(request) + return contents.Render(responder) } /* Create new file wrapper around contents */ @@ -193,7 +194,7 @@ func (fs *FileSystem) FetchFile(request *Request) *GophorError { fs.CacheMutex.Lock() /* Put file in the FixedMap */ - fs.CacheMap.Put(request.AbsPath(), file) + fs.CacheMap.Put(responder.Request.AbsPath(), file) /* Before unlocking cache mutex, lock file read for upcoming call to .Contents() */ file.Mutex.RLock() @@ -204,7 +205,7 @@ func (fs *FileSystem) FetchFile(request *Request) *GophorError { } /* Read file contents into new variable for return, then unlock file read lock */ - gophorErr := file.WriteContents(request) + gophorErr := file.WriteContents(responder) file.Mutex.RUnlock() /* Finally we can unlock the cache map read lock, we are done :) */ @@ -225,8 +226,8 @@ type File struct { LastRefresh int64 } -func (f *File) WriteContents(request *Request) *GophorError { - return f.Content.Render(request) +func (f *File) WriteContents(responder *Responder) *GophorError { + return f.Content.Render(responder) } func (f *File) CacheContents() *GophorError { diff --git a/filesystem_read.go b/filesystem_read.go index ad219dd..f9fd8ee 100644 --- a/filesystem_read.go +++ b/filesystem_read.go @@ -111,21 +111,21 @@ func unixLineEndSplitter(data []byte, atEOF bool) (advance int, token []byte, er } /* List the files in a directory, hiding those requested */ -func listDir(request *Request, hidden map[string]bool) *GophorError { +func listDir(responder *Responder, hidden map[string]bool) *GophorError { /* Open directory file descriptor */ - fd, err := os.Open(request.AbsPath()) + fd, err := os.Open(responder.Request.AbsPath()) if err != nil { - Config.SysLog.Error("", "failed to open %s: %s\n", request.AbsPath(), err.Error()) + Config.SysLog.Error("", "failed to open %s: %s\n", responder.Request.AbsPath(), err.Error()) return &GophorError{ FileOpenErr, err } } /* Read files in directory */ files, err := fd.Readdir(-1) if err != nil { - Config.SysLog.Error("", "failed to enumerate dir %s: %s\n", request.AbsPath(), err.Error()) + Config.SysLog.Error("", "failed to enumerate dir %s: %s\n", responder.Request.AbsPath(), err.Error()) return &GophorError{ DirListErr, err } } - + /* Sort the files by name */ sort.Sort(byName(files)) @@ -133,11 +133,11 @@ func listDir(request *Request, hidden map[string]bool) *GophorError { dirContents := make([]byte, 0) /* First add a title + a space */ - dirContents = append(dirContents, buildLine(TypeInfo, "[ "+request.Host.Name()+request.SelectorPath()+" ]", "TITLE", NullHost, NullPort)...) + dirContents = append(dirContents, buildLine(TypeInfo, "[ "+responder.Host.Name()+responder.Request.SelectorPath()+" ]", "TITLE", NullHost, NullPort)...) dirContents = append(dirContents, buildInfoLine("")...) /* Add a 'back' entry. GoLang Readdir() seems to miss this */ - dirContents = append(dirContents, buildLine(TypeDirectory, "..", request.PathJoinSelector(".."), request.Host.Name(), request.Host.Port())...) + dirContents = append(dirContents, buildLine(TypeDirectory, "..", responder.Request.PathJoinSelector(".."), responder.Host.Name(), responder.Host.Port())...) /* Walk through files :D */ for _, file := range files { @@ -150,14 +150,14 @@ func listDir(request *Request, hidden map[string]bool) *GophorError { switch { case file.Mode() & os.ModeDir != 0: /* Directory -- create directory listing */ - itemPath := request.PathJoinSelector(file.Name()) - dirContents = append(dirContents, buildLine(TypeDirectory, file.Name(), itemPath, request.Host.Name(), request.Host.Port())...) + itemPath := responder.Request.PathJoinSelector(file.Name()) + dirContents = append(dirContents, buildLine(TypeDirectory, file.Name(), itemPath, responder.Host.Name(), responder.Host.Port())...) case file.Mode() & os.ModeType == 0: /* Regular file -- find item type and creating listing */ - itemPath := request.PathJoinSelector(file.Name()) + itemPath := responder.Request.PathJoinSelector(file.Name()) itemType := getItemType(itemPath) - dirContents = append(dirContents, buildLine(itemType, file.Name(), itemPath, request.Host.Name(), request.Host.Port())...) + dirContents = append(dirContents, buildLine(itemType, file.Name(), itemPath, responder.Host.Name(), responder.Host.Port())...) default: /* Ignore */ @@ -165,7 +165,7 @@ func listDir(request *Request, hidden map[string]bool) *GophorError { } /* Append the footer (including lastline), write and flush! */ - return request.WriteFlush(append(dirContents, Config.FooterText...)) + return responder.WriteFlush(append(dirContents, Config.FooterText...)) } /* Took a leaf out of go-gopher's book here. */ diff --git a/request.go b/request.go index b4da442..2253696 100644 --- a/request.go +++ b/request.go @@ -48,87 +48,8 @@ func (rp *RequestPath) Selector() string { } type Request struct { - /* A gophor request containing any data necessary. - * Either handled through FileSystem or to direct function like listDir(). - */ - - /* Can be nil */ - Host *ConnHost - Client *ConnClient - - /* MUST be set */ - Writer *bufio.Writer Path *RequestPath - Parameters []string /* CGI-bin params will be 1 length slice, shell commands populate >=1 */ -} - -func NewSanitizedRequest(conn *GophorConn, requestStr string) *Request { - /* Split dataStr into request path and parameter string (if pressent) */ - relPath, parameters := parseRequestString(requestStr) - relPath = sanitizeRelativePath(conn.RootDir(), relPath) - bufWriter := bufio.NewWriterSize(conn.Conn, SocketWriteBufSize) - requestPath := NewRequestPath(conn.RootDir(), relPath) - return NewRequest(conn.Host, conn.Client, bufWriter, requestPath, parameters) -} - -func NewRequest(host *ConnHost, client *ConnClient, writer *bufio.Writer, path *RequestPath, parameters []string) *Request { - return &Request{ - host, - client, - writer, - path, - parameters, - } -} - -func (r *Request) AccessLogInfo(format string, args ...interface{}) { - /* You HAVE to be sure that r.Conn is NOT nil before calling this */ - Config.AccLog.Info("("+r.Client.AddrStr()+") ", format, args...) -} - -func (r *Request) AccessLogError(format string, args ...interface{}) { - /* You HAVE to be sure that r.Conn is NOT nil before calling this */ - Config.AccLog.Error("("+r.Client.AddrStr()+") ", format, args...) -} - -func (r *Request) Write(data []byte) *GophorError { - _, err := r.Writer.Write(data) - if err != nil { - return &GophorError{ BufferedWriteErr, err } - } - return nil -} - -func (r *Request) WriteFlush(data []byte) *GophorError { - _, err := r.Writer.Write(data) - if err != nil { - return &GophorError{ BufferedWriteErr, err } - } - return r.Flush() -} - -func (r *Request) Flush() *GophorError { - err := r.Writer.Flush() - if err != nil { - return &GophorError{ BufferedWriteFlushErr, err } - } - return nil -} - -func (r *Request) SafeFlush(gophorErr *GophorError) *GophorError { - if gophorErr != nil { - return gophorErr - } else { - return r.Flush() - } -} - -func (r *Request) WriteRaw(reader io.Reader) *GophorError { - _, err := r.Writer.ReadFrom(reader) - if err != nil { - return &GophorError{ BufferedWriteReadErr, err } - } - return r.Flush() + Parameters []string } func (r *Request) RootDir() string { @@ -187,8 +108,70 @@ func (r *Request) PathJoinRootDir(extPath string) string { return path.Join(r.Path.RootDir(), extPath) } -func (r *Request) CachedRequest() *Request { - return NewRequest(nil, nil, nil, r.Path, r.Parameters) +type Responder struct { + Host *ConnHost + Client *ConnClient + Writer *bufio.Writer + Request *Request +} + +func NewSanitizedRequest(conn *GophorConn, requestStr string) *Request { + relPath, paramaters := parseRequestString(requestStr) + relPath = sanitizeRelativePath(conn.RootDir(), relPath) + return &Request{ NewRequestPath(conn.RootDir(), relPath), paramaters } +} + +func NewResponder(conn *GophorConn, request *Request) *Responder { + bufWriter := bufio.NewWriterSize(conn.Conn, SocketWriteBufSize) + return &Responder{ conn.Host, conn.Client, bufWriter, request } +} + +func (r *Responder) AccessLogInfo(format string, args ...interface{}) { + Config.AccLog.Info("("+r.Client.AddrStr()+") ", format, args...) +} + +func (r *Responder) AccessLogError(format string, args ...interface{}) { + Config.AccLog.Error("("+r.Client.AddrStr()+") ", format, args...) +} + +func (r *Responder) Write(data []byte) *GophorError { + _, err := r.Writer.Write(data) + if err != nil { + return &GophorError{ BufferedWriteErr, err } + } + return nil +} + +func (r *Responder) WriteFlush(data []byte) *GophorError { + _, err := r.Writer.Write(data) + if err != nil { + return &GophorError{ BufferedWriteErr, err } + } + return r.Flush() +} + +func (r *Responder) Flush() *GophorError { + err := r.Writer.Flush() + if err != nil { + return &GophorError{ BufferedWriteFlushErr, err } + } + return nil +} + +func (r *Responder) SafeFlush(gophorErr *GophorError) *GophorError { + if gophorErr != nil { + return gophorErr + } else { + return r.Flush() + } +} + +func (r *Responder) WriteRaw(reader io.Reader) *GophorError { + _, err := r.Writer.ReadFrom(reader) + if err != nil { + return &GophorError{ BufferedWriteReadErr, err } + } + return r.Flush() } /* Sanitize a request path string */ diff --git a/worker.go b/worker.go index 988baac..1613e93 100644 --- a/worker.go +++ b/worker.go @@ -89,8 +89,11 @@ func (worker *Worker) Serve() { /* Create new request from dataStr */ request := NewSanitizedRequest(worker.Conn, received) + /* Create new responder from request */ + responder := NewResponder(worker.Conn, request) + /* Handle request */ - gophorErr := Config.FileSystem.HandleRequest(request) + gophorErr := Config.FileSystem.HandleRequest(responder) /* Handle any error */ if gophorErr != nil { @@ -98,17 +101,17 @@ func (worker *Worker) Serve() { Config.SysLog.Error("", gophorErr.Error()) /* Generate response bytes from error code */ - response := generateGopherErrorResponseFromCode(gophorErr.Code) + errResponse := generateGopherErrorResponseFromCode(gophorErr.Code) /* If we got response bytes to send? SEND 'EM! */ - if response != nil { + if errResponse != nil { /* No gods. No masters. We don't care about error checking here */ - request.WriteFlush(response) + responder.WriteFlush(errResponse) } - request.AccessLogError("Failed to serve: %s\n", request.AbsPath()) + responder.AccessLogError("Failed to serve: %s\n", request.AbsPath()) } else { /* Log served */ - request.AccessLogInfo("Served: %s\n", request.AbsPath()) + responder.AccessLogInfo("Served: %s\n", request.AbsPath()) } }