massively improve code commenting, fix SkipPrefixWriter bug

Signed-off-by: kim (grufwub) <grufwub@gmail.com>
master
kim (grufwub) 4 years ago
parent 45e77db911
commit 43ddf00b36

@ -100,6 +100,9 @@ func (l *GophorListener) Accept() (*GophorConn, error) {
}
type DeadlineConn struct {
/* Simple wrapper to net.Conn that's sets deadlines
* on each call to Read() / Write()
*/
conn net.Conn
}
@ -117,12 +120,11 @@ func (c *DeadlineConn) Write(b []byte) (int, error) {
func (c *DeadlineConn) Close() error {
/* Implements closer */
return c.conn.Close()
}
type GophorConn struct {
/* Simple net.Conn wrapper with virtual host and client info */
/* Wrap DeadlineConn with other connection details */
Conn *DeadlineConn
Host *ConnHost

@ -28,7 +28,6 @@ const (
InvalidGophermapErr ErrorCode = iota
/* Executing */
BufferReadErr ErrorCode = iota
CommandStartErr ErrorCode = iota
CommandExitCodeErr ErrorCode = iota
CgiOutputErr ErrorCode = iota
@ -83,13 +82,9 @@ func (e *GophorError) Error() string {
case InvalidRequestErr:
str = "invalid request data"
case EmptyItemTypeErr:
str = "line string provides no dir entity type"
case InvalidGophermapErr:
str = "invalid gophermap"
case BufferReadErr:
str = "buffer read fail"
case CommandStartErr:
str = "command start fail"
case CgiOutputErr:
@ -141,13 +136,9 @@ func gophorErrorToResponseCode(code ErrorCode) ErrorResponseCode {
case InvalidRequestErr:
return ErrorResponse400
case EmptyItemTypeErr:
return ErrorResponse500
case InvalidGophermapErr:
return ErrorResponse500
case BufferReadErr:
return ErrorResponse500
case CommandStartErr:
return ErrorResponse500
case CommandExitCodeErr:

@ -9,20 +9,22 @@ import (
"io"
)
/* Setup initial (i.e. constant) gophermap / command environment variables */
func setupExecEnviron(path string) []string {
return []string {
envKeyValue("PATH", path),
}
}
/* Setup initial (i.e. constant) CGI environment variables */
func setupInitialCgiEnviron(path string) []string {
return []string{
/* RFC 3875 standard */
envKeyValue("GATEWAY_INTERFACE", "CGI/1.1"), /* MUST be set to the dialect of CGI being used by the server */
envKeyValue("GATEWAY_INTERFACE", "CGI/1.1"), /* MUST be set to the dialect of CGI being used by the server */
envKeyValue("SERVER_SOFTWARE", "gophor/"+GophorVersion), /* MUST be set to name and version of server software serving this request */
envKeyValue("SERVER_PROTOCOL", "RFC1436"), /* MUST be set to name and version of application protocol used for this request */
envKeyValue("CONTENT_LENGTH", "0"), /* Contains size of message-body attached (always 0 so we set here) */
envKeyValue("REQUEST_METHOD", "GET"), /* MUST be set to method by which script should process request. Always GET */
envKeyValue("SERVER_PROTOCOL", "RFC1436"), /* MUST be set to name and version of application protocol used for this request */
envKeyValue("CONTENT_LENGTH", "0"), /* Contains size of message-body attached (always 0 so we set here) */
envKeyValue("REQUEST_METHOD", "GET"), /* MUST be set to method by which script should process request. Always GET */
/* Non-standard */
envKeyValue("PATH", path),
@ -31,6 +33,7 @@ func setupInitialCgiEnviron(path string) []string {
}
}
/* Execute a CGI script */
func executeCgi(responder *Responder) *GophorError {
/* Get initial CgiEnv variables */
cgiEnv := Config.CgiEnv
@ -45,16 +48,16 @@ func executeCgi(responder *Responder) *GophorError {
} else {
queryString = responder.Request.Parameters[0]
}
cgiEnv = append(cgiEnv, envKeyValue("QUERY_STRING", queryString)) /* URL encoded search or parameter string, MUST be set even if empty */
cgiEnv = append(cgiEnv, envKeyValue("SCRIPT_NAME", "/"+responder.Request.RelPath())) /* URI path (not URL encoded) which could identify the CGI script (rather than script's output) */
cgiEnv = append(cgiEnv, envKeyValue("SCRIPT_FILENAME", responder.Request.AbsPath())) /* Basically SCRIPT_NAME absolute path */
cgiEnv = append(cgiEnv, envKeyValue("SELECTOR", responder.Request.SelectorPath()))
cgiEnv = append(cgiEnv, envKeyValue("DOCUMENT_ROOT", responder.Request.RootDir()))
cgiEnv = append(cgiEnv, envKeyValue("REQUEST_URI", "/"+responder.Request.RelPath()+responder.Request.Parameters[0]))
cgiEnv = append(cgiEnv, envKeyValue("QUERY_STRING", queryString)) /* URL encoded search or parameter string, MUST be set even if empty */
cgiEnv = append(cgiEnv, envKeyValue("SCRIPT_NAME", "/"+responder.Request.Path.Relative())) /* URI path (not URL encoded) which could identify the CGI script (rather than script's output) */
cgiEnv = append(cgiEnv, envKeyValue("SCRIPT_FILENAME", responder.Request.Path.Absolute())) /* Basically SCRIPT_NAME absolute path */
cgiEnv = append(cgiEnv, envKeyValue("SELECTOR", responder.Request.Path.Selector()))
cgiEnv = append(cgiEnv, envKeyValue("DOCUMENT_ROOT", responder.Request.Path.RootDir()))
cgiEnv = append(cgiEnv, envKeyValue("REQUEST_URI", "/"+responder.Request.Path.Relative()+responder.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", 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 */
// 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 */
@ -63,44 +66,42 @@ func executeCgi(responder *Responder) *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 */
contentTypeReached := true
/* Create nwe SkipPrefixwriter from underlying response writer set to skip up to:
* \r\n\r\n
* Then checks if it contains a valid 'content-type:' header, if so it strips these.
*/
skipPrefixWriter := NewSkipPrefixWriter(
responder.Writer,
[]byte(DOSLineEnd+DOSLineEnd),
func(skipBuffer []byte) bool {
split := bytes.Split(skipBuffer, []byte(DOSLineEnd))
for _, header := range split {
for _, header := range bytes.Split(skipBuffer, []byte(DOSLineEnd)) {
header = bytes.ToLower(header)
if bytes.HasPrefix(header, []byte("content-type:")) {
contentTypeReached = true
break
if bytes.Contains(header, []byte("content-type:")) {
return false
}
}
return contentTypeReached
return true
},
)
gophorErr := execute(skipPrefixWriter, cgiEnv, responder.Request.AbsPath(), nil)
if gophorErr != nil {
return gophorErr
} else if !contentTypeReached {
return &GophorError{ CgiOutputErr, nil }
} else {
return nil
}
/* Execute the CGI script using the new SkipPrefixWriter and above environment */
return execute(skipPrefixWriter, cgiEnv, responder.Request.Path.Absolute(), nil)
}
/* Execute any file (though only allowed are gophermaps) */
func executeFile(responder *Responder) *GophorError {
return execute(responder.Writer, Config.Env, responder.Request.AbsPath(), responder.Request.Parameters)
return execute(responder.Writer, Config.Env, responder.Request.Path.Absolute(), responder.Request.Parameters)
}
/* Check command is not restricted then execute */
func executeCommand(responder *Responder) *GophorError {
if isRestrictedCommand(responder.Request.AbsPath()) {
if isRestrictedCommand(responder.Request.Path.Absolute()) {
return &GophorError{ RestrictedCommandErr, nil }
}
return execute(responder.Writer, Config.Env, responder.Request.AbsPath(), responder.Request.Parameters)
return execute(responder.Writer, Config.Env, responder.Request.Path.Absolute(), responder.Request.Parameters)
}
/* Execute a supplied path with arguments and environment, to writer */
func execute(writer io.Writer, env []string, path string, args []string) *GophorError {
/* If CGI disbabled, just return error */
if !Config.CgiEnabled {
@ -175,6 +176,7 @@ func execute(writer io.Writer, env []string, path string, args []string) *Gophor
}
}
/* Just neatens creating an environment KEY=VALUE string */
func envKeyValue(key, value string) string {
return key+"="+value
}

@ -17,7 +17,9 @@ type FileContents interface {
}
type GeneratedFileContents struct {
Contents []byte /* Generated file contents as byte slice */
/* Super simple, holds onto a slice of bytes */
Contents []byte
}
func (fc *GeneratedFileContents) Render(responder *Responder) *GophorError {
@ -34,8 +36,12 @@ func (fc *GeneratedFileContents) Clear() {
}
type RegularFileContents struct {
Request *Request /* Stored filesystem request */
Contents []byte /* File contents as byte slice */
/* Simple implemention that holds onto a RequestPath
* and slice containing cache'd content
*/
Path *RequestPath
Contents []byte
}
func (fc *RegularFileContents) Render(responder *Responder) *GophorError {
@ -45,7 +51,7 @@ func (fc *RegularFileContents) Render(responder *Responder) *GophorError {
func (fc *RegularFileContents) Load() *GophorError {
/* Load the file into memory */
var gophorErr *GophorError
fc.Contents, gophorErr = bufferedRead(fc.Request.AbsPath())
fc.Contents, gophorErr = bufferedRead(fc.Path.Absolute())
return gophorErr
}
@ -54,8 +60,12 @@ func (fc *RegularFileContents) Clear() {
}
type GophermapContents struct {
Request *Request /* Stored filesystem request */
Sections []GophermapSection /* Slice to hold differing gophermap sections */
/* Holds onto a RequestPath and slice containing individually
* renderable sections of the gophermap.
*/
Path *RequestPath
Sections []GophermapSection
}
func (gc *GophermapContents) Render(responder *Responder) *GophorError {
@ -75,7 +85,7 @@ func (gc *GophermapContents) Render(responder *Responder) *GophorError {
func (gc *GophermapContents) Load() *GophorError {
/* Load the gophermap into memory as gophermap sections */
var gophorErr *GophorError
gc.Sections, gophorErr = readGophermap(gc.Request)
gc.Sections, gophorErr = readGophermap(gc.Path)
return gophorErr
}
@ -85,14 +95,14 @@ func (gc *GophermapContents) Clear() {
type GophermapSection interface {
/* Interface for storing differring types of gophermap
* sections and render when necessary
* sections to render when necessary
*/
Render(*Responder) *GophorError
}
type GophermapTextSection struct {
Contents []byte /* Text contents */
Contents []byte
}
func (s *GophermapTextSection) Render(responder *Responder) *GophorError {
@ -100,30 +110,26 @@ func (s *GophermapTextSection) Render(responder *Responder) *GophorError {
}
type GophermapDirectorySection struct {
Request *Request /* Stored filesystem request */
Hidden map[string]bool /* Hidden files map parsed from gophermap */
/* Holds onto a directory path, and a list of files
* to hide from the client when rendering.
*/
Path *RequestPath
Hidden map[string]bool
}
func (g *GophermapDirectorySection) Render(responder *Responder) *GophorError {
/* Create new filesystem request from mixture of stored + supplied */
return listDir(
&Responder{
responder.Host,
responder.Client,
responder.Writer,
g.Request,
},
g.Hidden,
false,
)
/* Create new responder from supplied and using stored path */
return listDir(responder.CloneWithRequest(&Request{ g.Path, nil }), g.Hidden)
}
type GophermapFileSection struct {
Request *Request
/* Holds onto a file path to be read and rendered when requested */
Path *RequestPath
}
func (g *GophermapFileSection) Render(responder *Responder) *GophorError {
fileContents, gophorErr := readIntoGophermap(g.Request.AbsPath())
fileContents, gophorErr := readIntoGophermap(g.Path.Absolute())
if gophorErr != nil {
return gophorErr
}
@ -131,12 +137,13 @@ func (g *GophermapFileSection) Render(responder *Responder) *GophorError {
}
type GophermapSubmapSection struct {
Request *Request
/* Holds onto a gophermap path to be read and rendered when requested */
Path *RequestPath
}
func (g *GophermapSubmapSection) Render(responder *Responder) *GophorError {
/* Load the gophermap into memory as gophermap sections */
sections, gophorErr := readGophermap(g.Request)
sections, gophorErr := readGophermap(g.Path)
if gophorErr != nil {
return gophorErr
}
@ -153,58 +160,51 @@ func (g *GophermapSubmapSection) Render(responder *Responder) *GophorError {
}
type GophermapExecCgiSection struct {
Request *Request /* Stored file system request */
/* Holds onto a request with CGI script path and supplied parameters */
Request *Request
}
func (g *GophermapExecCgiSection) Render(responder *Responder) *GophorError {
/* Create new filesystem request from mixture of stored + supplied */
return executeCgi(&Responder{
responder.Host,
responder.Client,
responder.Writer,
g.Request,
})
return executeCgi(responder.CloneWithRequest(g.Request))
}
type GophermapExecFileSection struct {
Request *Request /* Stored file system request */
/* Holds onto a request with executable file path and supplied arguments */
Request *Request
}
func (g *GophermapExecFileSection) Render(responder *Responder) *GophorError {
return executeFile(&Responder{
responder.Host,
responder.Client,
responder.Writer,
g.Request,
})
/* Create new responder from supplied and using stored path */
return executeFile(responder.CloneWithRequest(g.Request))
}
type GophermapExecCommandSection struct {
/* Holds onto a request with shell command and supplied arguments */
Request *Request
}
func (g *GophermapExecCommandSection) Render(responder *Responder) *GophorError {
return executeCommand(&Responder{
responder.Host,
responder.Client,
responder.Writer,
g.Request,
})
/* Create new responder from supplied and using stored path */
return executeCommand(responder.CloneWithRequest(g.Request))
}
func readGophermap(request *Request) ([]GophermapSection, *GophorError) {
/* Read and parse a gophermap into separately cacheable and renderable GophermapSection */
func readGophermap(path *RequestPath) ([]GophermapSection, *GophorError) {
/* Create return slice */
sections := make([]GophermapSection, 0)
/* Create hidden files map now in case dir listing requested */
hidden := make(map[string]bool)
hidden[request.RelPath()] = true
hidden := map[string]bool{
path.Relative(): true, /* Ignore current gophermap */
CgiBinDirStr: true, /* Ignore cgi-bin if found */
}
/* Keep track of whether we've already come across a title line (only 1 allowed!) */
titleAlready := false
/* Perform buffered scan with our supplied splitter and iterators */
gophorErr := bufferedScan(request.AbsPath(),
gophorErr := bufferedScan(path.Absolute(),
func(scanner *bufio.Scanner) bool {
line := scanner.Text()
@ -228,65 +228,61 @@ func readGophermap(request *Request) ([]GophermapSection, *GophorError) {
case TypeHiddenFile:
/* Add to hidden files map */
hidden[request.PathJoinRel(line[1:])] = true
hidden[path.JoinRel(line[1:])] = true
case TypeSubGophermap:
/* Parse new requestPath and parameters (this automatically sanitizes requestPath) */
subRelPath, subParameters := parseLineRequestString(request.Path, line[1:])
subRequest := &Request{ subRelPath, subParameters }
/* Parse new RequestPath and parameters */
subPath, parameters := parseLineRequestString(path, line[1:])
if !subRequest.PathHasAbsPrefix("/") {
/* Special case here where command must be in path, return GophermapExecCommand */
sections = append(sections, &GophermapExecCommandSection{ subRequest })
if !subPath.HasAbsPrefix("/") {
/* Special case here where command assumed in path, return GophermapExecCommandSection */
sections = append(sections, &GophermapExecCommandSection{ &Request{ subPath, parameters } })
break
} else if subRequest.RelPath() == "" {
/* path cleaning failed */
break
} else if subRequest.RelPath() == request.RelPath() {
/* Same as current gophermap. Recursion bad! */
} else if subPath.Relative() == "" || subPath.Relative() == path.Relative() {
/* Either path parsing failed, or we've been supplied same gophermap, and recursion is
* recursion is recursion is bad kids!
*/
break
}
/* Perform file stat */
stat, err := os.Stat(subRequest.AbsPath())
stat, err := os.Stat(subPath.Absolute())
if (err != nil) || (stat.Mode() & os.ModeDir != 0) {
/* File read error or is directory */
break
}
/* Check if we've been supplied subgophermap or regular file */
if subRequest.PathHasAbsSuffix(GophermapFileStr) {
/* If executable, store as GophermapExecutable, else readGophermap() */
if subPath.HasAbsSuffix(GophermapFileStr) {
/* If executable, store as GophermapExecFileSection, else GophermapSubmapSection */
if stat.Mode().Perm() & 0100 != 0 {
sections = append(sections, &GophermapExecFileSection { subRequest })
sections = append(sections, &GophermapExecFileSection { &Request{ subPath, parameters } })
} else {
/* Treat as any other gophermap! */
sections = append(sections, &GophermapSubmapSection{ subRequest })
sections = append(sections, &GophermapSubmapSection{ subPath })
}
} else {
/* If stored in cgi-bin store as GophermapExecutable, else read into GophermapText */
if subRequest.PathHasRelPrefix(CgiBinDirStr) {
sections = append(sections, &GophermapExecCgiSection{ subRequest })
/* If stored in cgi-bin store as GophermapExecCgiSection, else GophermapFileSection */
if subPath.HasRelPrefix(CgiBinDirStr) {
sections = append(sections, &GophermapExecCgiSection{ &Request{ subPath, parameters} })
} else {
sections = append(sections, &GophermapFileSection{ subRequest })
sections = append(sections, &GophermapFileSection{ subPath })
}
}
case TypeEnd:
/* Lastline, break out at end of loop. Interface method Contents()
* will append a last line at the end so we don't have to worry about
* that here, only stopping the loop.
/* Lastline, break out at end of loop. GophermapContents.Render() will
* append a LastLine string so we don't have to worry about that here.
*/
return false
case TypeEndBeginList:
/* Create GophermapDirListing object then break out at end of loop */
dirRequest := &Request{ NewRequestPath(request.RootDir(), request.PathTrimRelSuffix(GophermapFileStr)), request.Parameters }
/* Append GophermapDirectorySection object then break, as with TypeEnd. */
dirRequest := NewRequestPath(path.RootDir(), path.TrimRelSuffix(GophermapFileStr))
sections = append(sections, &GophermapDirectorySection{ dirRequest, hidden })
return false
default:
/* Just append to sections slice as gophermap text */
/* Default is appending to sections slice as GopherMapTextSection */
sections = append(sections, &GophermapTextSection{ []byte(line+DOSLineEnd) })
}
@ -302,11 +298,12 @@ func readGophermap(request *Request) ([]GophermapSection, *GophorError) {
return sections, nil
}
/* Read a text file into a gophermap as text sections */
func readIntoGophermap(path string) ([]byte, *GophorError) {
/* Create return slice */
fileContents := make([]byte, 0)
/* Perform buffered scan with our supplied splitter and iterators */
/* Perform buffered scan with our supplied iterator */
gophorErr := bufferedScan(path,
func(scanner *bufio.Scanner) bool {
line := scanner.Text()
@ -316,10 +313,10 @@ func readIntoGophermap(path string) ([]byte, *GophorError) {
return true
}
/* Replace the newline character */
/* Replace the newline characters */
line = replaceNewLines(line)
/* Iterate through returned str, reflowing to new line
/* Iterate through line string, reflowing to new line
* until all lines < PageWidth
*/
for len(line) > 0 {
@ -345,6 +342,7 @@ func readIntoGophermap(path string) ([]byte, *GophorError) {
return fileContents, nil
}
/* Return minimum width out of PageWidth and W */
func minWidth(w int) int {
if w <= Config.PageWidth {
return w

@ -17,13 +17,12 @@ const (
type FileSystem struct {
/* Holds and helps manage our file cache, as well as managing
* access and responding to filesystem requests submitted by
* a worker instance.
* access and responses to requests submitted a worker instance.
*/
CacheMap *FixedMap /* Fixed size cache map */
CacheMutex sync.RWMutex /* RWMutex for safe cachemap access */
CacheFileMax int64 /* Cache file size max */
CacheMap *FixedMap
CacheMutex sync.RWMutex
CacheFileMax int64
Remap map[string]string
ReverseRemap map[string]string
}
@ -51,7 +50,7 @@ func (fs *FileSystem)ReverseRemapRequestPath(requestPath *RequestPath) {
func (fs *FileSystem) HandleRequest(responder *Responder) *GophorError {
/* Check if restricted file */
if isRestrictedFile(responder.Request.RelPath()) {
if isRestrictedFile(responder.Request.Path.Relative()) {
return &GophorError{ IllegalPathErr, nil }
}
@ -59,11 +58,11 @@ func (fs *FileSystem) HandleRequest(responder *Responder) *GophorError {
fs.RemapRequestPath(responder.Request.Path)
/* Get filesystem stat, check it exists! */
stat, err := os.Stat(responder.Request.AbsPath())
stat, err := os.Stat(responder.Request.Path.Absolute())
if err != nil {
/* Check file isn't in cache before throwing in the towel */
fs.CacheMutex.RLock()
file := fs.CacheMap.Get(responder.Request.AbsPath())
file := fs.CacheMap.Get(responder.Request.Path.Absolute())
if file == nil {
fs.CacheMutex.RUnlock()
return &GophorError{ FileStatErr, err }
@ -78,21 +77,20 @@ func (fs *FileSystem) HandleRequest(responder *Responder) *GophorError {
return gophorErr
}
/* Handle file type */
switch {
/* Directory */
case stat.Mode() & os.ModeDir != 0:
/* Ignore anything under cgi-bin directory */
if responder.Request.PathHasRelPrefix(CgiBinDirStr) {
if responder.Request.Path.HasRelPrefix(CgiBinDirStr) {
return &GophorError{ IllegalPathErr, nil }
}
/* Check Gophermap exists */
gophermapPath := NewRequestPath(responder.Request.RootDir(), responder.Request.PathJoinRel(GophermapFileStr))
gophermapPath := NewRequestPath(responder.Request.Path.RootDir(), responder.Request.Path.JoinRel(GophermapFileStr))
stat, err = os.Stat(gophermapPath.Absolute())
if err == nil {
/* Gophermap exists! If executable and CGI enabled execute, else serve. */
/* Gophermap exists! If executable try return executed contents, else serve as regular gophermap. */
gophermapRequest := &Request{ gophermapPath, responder.Request.Parameters }
responder.Request = gophermapRequest
@ -103,13 +101,13 @@ func (fs *FileSystem) HandleRequest(responder *Responder) *GophorError {
}
} else {
/* No gophermap, serve directory listing */
return listDir(responder, map[string]bool{}, true)
return listDirAsGophermap(responder, map[string]bool{ gophermapPath.Relative(): true, CgiBinDirStr: true })
}
/* Regular file */
case stat.Mode() & os.ModeType == 0:
/* If cgi-bin and CGI enabled, return executed contents. Else, fetch */
if responder.Request.PathHasRelPrefix(CgiBinDirStr) {
/* If cgi-bin, try return executed contents. Else, fetch regular file */
if responder.Request.Path.HasRelPrefix(CgiBinDirStr) {
return responder.SafeFlush(executeCgi(responder))
} else {
return fs.FetchFile(responder)
@ -124,7 +122,7 @@ func (fs *FileSystem) HandleRequest(responder *Responder) *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(responder.Request.AbsPath())
file := fs.CacheMap.Get(responder.Request.Path.Absolute())
if file != nil {
/* File in cache -- before doing anything get file read lock */
@ -153,7 +151,7 @@ func (fs *FileSystem) FetchFile(responder *Responder) *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(responder.Request.AbsPath())
fd, err := os.Open(responder.Request.Path.Absolute())
if err != nil {
/* Error stat'ing file, unlock read mutex then return error */
fs.CacheMutex.RUnlock()
@ -177,17 +175,10 @@ func (fs *FileSystem) FetchFile(responder *Responder) *GophorError {
/* Create new file contents */
var contents FileContents
if responder.Request.PathHasAbsSuffix("/"+GophermapFileStr) {
contents = &GophermapContents{ responder.Request, nil }
if responder.Request.Path.HasRelSuffix(GophermapFileStr) {
contents = &GophermapContents{ responder.Request.Path, nil }
} else {
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(responder)
contents = &RegularFileContents{ responder.Request.Path, nil }
}
/* Create new file wrapper around contents */
@ -206,7 +197,7 @@ func (fs *FileSystem) FetchFile(responder *Responder) *GophorError {
fs.CacheMutex.Lock()
/* Put file in the FixedMap */
fs.CacheMap.Put(responder.Request.AbsPath(), file)
fs.CacheMap.Put(responder.Request.Path.Absolute(), file)
/* Before unlocking cache mutex, lock file read for upcoming call to .Contents() */
file.Mutex.RLock()
@ -216,7 +207,7 @@ func (fs *FileSystem) FetchFile(responder *Responder) *GophorError {
fs.CacheMutex.RLock()
}
/* Read file contents into new variable for return, then unlock file read lock */
/* Write file contents via responder */
gophorErr := file.WriteContents(responder)
file.Mutex.RUnlock()
@ -228,8 +219,7 @@ func (fs *FileSystem) FetchFile(responder *Responder) *GophorError {
type File struct {
/* Wraps around the cached contents of a file
* helping with management of this content by
* a FileSystem instance.
* helping with management.
*/
Content FileContents
@ -254,11 +244,11 @@ func (f *File) CacheContents() *GophorError {
/* Update lastRefresh, set fresh, unset deletion (not likely set) */
f.LastRefresh = time.Now().UnixNano()
f.Fresh = true
f.Fresh = true
return nil
}
/* Start the file monitor! */
func startFileMonitor(sleepTime time.Duration) {
go func() {
for {
@ -274,6 +264,7 @@ func startFileMonitor(sleepTime time.Duration) {
}()
}
/* Check file cache for freshness, deleting files not-on disk */
func checkCacheFreshness() {
/* Before anything, get cache write lock (in case we have to delete) */
Config.FileSystem.CacheMutex.Lock()
@ -288,16 +279,18 @@ func checkCacheFreshness() {
continue
}
/* Check file still exists on disk, delete and continue if not */
stat, err := os.Stat(path)
if err != nil {
/* Log file as not in cache, then delete */
Config.SysLog.Error("", "Failed to stat file in cache: %s\n", path)
Config.FileSystem.CacheMap.Remove(path)
continue
}
/* Get file's last modified time */
timeModified := stat.ModTime().UnixNano()
/* If the file is marked as fresh, but file on disk newer, mark as unfresh */
/* If the file is marked as fresh, but file on disk is newer, mark as unfresh */
if file.Fresh && file.LastRefresh < timeModified {
file.Fresh = false
}
@ -307,8 +300,8 @@ func checkCacheFreshness() {
Config.FileSystem.CacheMutex.Unlock()
}
/* Just a helper function to neaten-up checking if file contents is of generated type */
func isGeneratedType(file *File) bool {
/* Just a helper function to neaten-up checking if file contents is of generated type */
switch file.Content.(type) {
case *GeneratedFileContents:
return true

@ -80,6 +80,7 @@ func bufferedScan(path string, scanIterator func(*bufio.Scanner) bool) *GophorEr
return nil
}
/* Split on DOS line end */
func dosLineEndSplitter(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
/* At EOF, no more data */
@ -95,6 +96,7 @@ func dosLineEndSplitter(data []byte, atEOF bool) (advance int, token []byte, err
return 0, nil, nil
}
/* Split on unix line end */
func unixLineEndSplitter(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
/* At EOF, no more data */
@ -110,19 +112,43 @@ func unixLineEndSplitter(data []byte, atEOF bool) (advance int, token []byte, er
return 0, nil, nil
}
/* List the files in directory, hiding those requested, including title and footer */
func listDirAsGophermap(responder *Responder, hidden map[string]bool) *GophorError {
/* Write title */
gophorErr := responder.Write(append(buildLine(TypeInfo, "[ "+responder.Host.Name()+responder.Request.Path.Selector()+" ]", "TITLE", NullHost, NullPort), buildInfoLine("")...))
if gophorErr != nil {
return gophorErr
}
/* Writer a 'back' entry. GoLang Readdir() seems to miss this */
gophorErr = responder.Write(buildLine(TypeDirectory, "..", responder.Request.Path.JoinSelector(".."), responder.Host.Name(), responder.Host.Port()))
if gophorErr != nil {
return gophorErr
}
/* Write the actual directory entry */
gophorErr = listDir(responder, hidden)
if gophorErr != nil {
return gophorErr
}
/* Finally write footer */
return responder.WriteFlush(Config.FooterText)
}
/* List the files in a directory, hiding those requested */
func listDir(responder *Responder, hidden map[string]bool, includeTitleFooter bool) *GophorError {
func listDir(responder *Responder, hidden map[string]bool) *GophorError {
/* Open directory file descriptor */
fd, err := os.Open(responder.Request.AbsPath())
fd, err := os.Open(responder.Request.Path.Absolute())
if err != nil {
Config.SysLog.Error("", "failed to open %s: %s\n", responder.Request.AbsPath(), err.Error())
Config.SysLog.Error("", "failed to open %s: %s\n", responder.Request.Path.Absolute(), 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", responder.Request.AbsPath(), err.Error())
Config.SysLog.Error("", "failed to enumerate dir %s: %s\n", responder.Request.Path.Absolute(), err.Error())
return &GophorError{ DirListErr, err }
}
@ -132,19 +158,10 @@ func listDir(responder *Responder, hidden map[string]bool, includeTitleFooter bo
/* Create directory content slice, ready */
dirContents := make([]byte, 0)
/* First add a title + a space */
if includeTitleFooter {
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, "..", responder.Request.PathJoinSelector(".."), responder.Host.Name(), responder.Host.Port())...)
}
/* Walk through files :D */
var reqPath *RequestPath
for _, file := range files {
reqPath = NewRequestPath(responder.Request.RootDir(), file.Name())
reqPath = NewRequestPath(responder.Request.Path.RootDir(), file.Name())
/* If hidden file, or restricted file, continue! */
if isHiddenFile(hidden, reqPath.Relative()) || isRestrictedFile(reqPath.Relative()) {
@ -171,14 +188,11 @@ func listDir(responder *Responder, hidden map[string]bool, includeTitleFooter bo
}
}
if includeTitleFooter {
dirContents = append(dirContents, Config.FooterText...)
}
/* Append the footer (including lastline), write and flush! */
return responder.WriteFlush(dirContents)
/* Finally write dirContents and return result */
return responder.Write(dirContents)
}
/* Helper function to simple checking in map */
func isHiddenFile(hiddenMap map[string]bool, fileName string) bool {
_, ok := hiddenMap[fileName]
return ok

@ -46,8 +46,7 @@ func (fm *FixedMap) Get(key string) *File {
}
}
/* Put file in map as key, pushing out last file
* if size limit reached */
/* Put file in map as key, pushing out last file if size limit reached */
func (fm *FixedMap) Put(key string, value *File) {
element := fm.List.PushFront(key)
fm.Map[key] = &MapElement{ element, value }

@ -91,6 +91,8 @@ var FileExtMap = map[string]ItemType{
".msi": TypeBin,
".exe": TypeBin,
".gophermap": TypeDirectory,
".lz": TypeBinArchive,
".gz": TypeBinArchive,
".bz2": TypeBinArchive,

@ -187,8 +187,8 @@ func setupServer() []*GophorListener {
/* Compile regex statements */
Config.CmdParseLineRegex = compileCmdParseRegex()
Config.RestrictedFiles = compileUserRestrictedFilesRegex(*restrictedFiles)
Config.RestrictedCommands = compileUserRestrictedCommandsRegex(*restrictedCommands)
Config.RestrictedFiles = compileUserRestrictedRegex(*restrictedFiles)
Config.RestrictedCommands = compileUserRestrictedRegex(*restrictedCommands)
/* Setup file cache */
Config.FileSystem = new(FileSystem)
@ -204,7 +204,7 @@ func setupServer() []*GophorListener {
cachePolicyFiles(*serverRoot, *serverDescription, *serverAdmin, *serverGeoloc)
/* Start file cache freshness checker */
go startFileMonitor(*fileMonitorFreq)
startFileMonitor(*fileMonitorFreq)
Config.SysLog.Info("", "File caching enabled with: maxcount=%d maxsize=%.3fMB checkfreq=%s\n", *cacheSize, *cacheFileSizeMax, *fileMonitorFreq)
} else {
/* File caching disabled, init with zero max size so nothing gets cached */

@ -1,5 +1,6 @@
package main
/* Function does, as function is named */
func generateHtmlRedirect(url string) []byte {
content :=
"<html>\n"+

@ -143,8 +143,8 @@ func NewLoggerToFile(path string, logFlags int) *log.Logger {
return log.New(writer, "", logFlags)
}
/* Set the default logger flags before printing version */
func printVersionExit() {
/* Set the default logger flags before printing version */
log.SetFlags(0)
log.Printf("%s\n", GophorVersion)
os.Exit(0)

@ -99,7 +99,7 @@ func parseLineRequestString(requestPath *RequestPath, lineStr string) (*RequestP
if strings.HasPrefix(lineStr, "/") {
/* We are dealing with a file input of some kind. Figure out if CGI-bin */
if strings.HasPrefix(lineStr[1:], CgiBinDirStr) {
/* CGI-bind script, parse requestPath and parameters as standard URL encoding */
/* CGI-bin script, parse requestPath and parameters as standard URL encoding */
relPath, parameters := parseRequestString(lineStr)
return NewRequestPath(requestPath.RootDir(), relPath), parameters
} else {

@ -31,10 +31,10 @@ func cachePolicyFiles(rootDir, description, admin, geoloc string) {
Config.SysLog.Info("", "Generated policy file: %s\n", rootDir+"/"+CapsTxtStr)
}
/* See if caps txt exists, if not generate */
/* See if robots txt exists, if not generate */
_, err = os.Stat(rootDir+"/"+RobotsTxtStr)
if err != nil {
/* We need to generate the caps txt and manually load into cache */
/* We need to generate the robots txt and manually load into cache */
content := generateRobotsTxt()
/* Create new file object from generated file contents */

@ -3,51 +3,36 @@ package main
import (
"regexp"
"strings"
"log"
)
func compileCmdParseRegex() *regexp.Regexp {
return regexp.MustCompile(` `)
}
func compileUserRestrictedFilesRegex(restrictedFiles string) []*regexp.Regexp {
/* Compile a user supplied new line separated list of regex statements */
func compileUserRestrictedRegex(restrictions string) []*regexp.Regexp {
/* Return slice */
restrictedFilesRegex := make([]*regexp.Regexp, 0)
restricted := make([]*regexp.Regexp, 0)
/* Split the user supplied RestrictedFiles string by new-line */
for _, expr := range strings.Split(restrictedFiles, "\n") {
/* Split the user supplied regex statements by new line */
for _, expr := range strings.Split(restrictions, "\n") {
/* Empty expression, skip */
if len(expr) == 0 {
continue
}
regex, err := regexp.Compile(expr)
if err != nil {
Config.SysLog.Fatal("Failed compiling user restricted files regex: %s\n", expr)
}
restrictedFilesRegex = append(restrictedFilesRegex, regex)
}
return restrictedFilesRegex
}
func compileUserRestrictedCommandsRegex(restrictedCommands string) []*regexp.Regexp {
/* Return slice */
restrictedCommandsRegex := make([]*regexp.Regexp, 0)
/* Split the user supplied RestrictedFiles string by new-line */
for _, expr := range strings.Split(restrictedCommands, "\n") {
if len(expr) == 0 {
continue
}
/* Try compile regex then append */
regex, err := regexp.Compile(expr)
if err != nil {
Config.SysLog.Fatal("Failed compiling user restricted commands regex: %s\n", expr)
log.Fatalf("Failed compiling user supplied regex: %s\n", expr)
}
restrictedCommandsRegex = append(restrictedCommandsRegex, regex)
restricted = append(restricted, regex)
}
return restrictedCommandsRegex
return restricted
}
/* Iterate through restricted file expressions, check if file _is_ restricted */
func isRestrictedFile(name string) bool {
for _, regex := range Config.RestrictedFiles {
@ -58,6 +43,7 @@ func isRestrictedFile(name string) bool {
return false
}
/* Iterate through restricted command expressions, check if command _is_ restricted */
func isRestrictedCommand(name string) bool {
for _, regex := range Config.RestrictedCommands {
if regex.MatchString(name) {

@ -8,8 +8,7 @@ import (
type RequestPath struct {
/* Path structure to allow hosts at
* different roots while maintaining relative
* and absolute path names for returned values
* and filesystem reading
* and absolute path names for filesystem reading
*/
Root string
@ -51,65 +50,54 @@ func (rp *RequestPath) Selector() string {
}
}
type Request struct {
Path *RequestPath
Parameters []string
}
func (r *Request) RootDir() string {
return r.Path.RootDir()
}
func (r *Request) AbsPath() string {
return r.Path.Absolute()
func (rp *RequestPath) JoinRel(extPath string) string {
return path.Join(rp.Relative(), extPath)
}
func (r *Request) RelPath() string {
return r.Path.Relative()
func (rp *RequestPath) JoinAbs(extPath string) string {
return path.Join(rp.Absolute(), extPath)
}
func (r *Request) SelectorPath() string {
return r.Path.Selector()
func (rp *RequestPath) JoinSelector(extPath string) string {
return path.Join(rp.Selector(), extPath)
}
func (r *Request) PathJoinSelector(extPath string) string {
return path.Join(r.SelectorPath(), extPath)
func (rp *RequestPath) HasAbsPrefix(prefix string) bool {
return strings.HasPrefix(rp.Absolute(), prefix)
}
func (r *Request) PathJoinAbs(extPath string) string {
return path.Join(r.AbsPath(), extPath)
func (rp *RequestPath) HasRelPrefix(prefix string) bool {
return strings.HasPrefix(rp.Relative(), prefix)
}
func (r *Request) PathJoinRel(extPath string) string {
return path.Join(r.RelPath(), extPath)
func (rp *RequestPath) HasRelSuffix(suffix string) bool {
return strings.HasSuffix(rp.Relative(), suffix)
}
func (r *Request) PathHasAbsPrefix(prefix string) bool {
return strings.HasPrefix(r.AbsPath(), prefix)
func (rp *RequestPath) HasAbsSuffix(suffix string) bool {
return strings.HasSuffix(rp.Absolute(), suffix)
}
func (r *Request) PathHasRelPrefix(prefix string) bool {
return strings.HasPrefix(r.RelPath(), prefix)
func (rp *RequestPath) TrimRelSuffix(suffix string) string {
return strings.TrimSuffix(strings.TrimSuffix(rp.Relative(), suffix), "/")
}
func (r *Request) PathHasRelSuffix(suffix string) bool {
return strings.HasSuffix(r.RelPath(), suffix)
func (rp *RequestPath) TrimAbsSuffix(suffix string) string {
return strings.TrimSuffix(strings.TrimSuffix(rp.Absolute(), suffix), "/")
}
func (r *Request) PathHasAbsSuffix(suffix string) bool {
return strings.HasSuffix(r.AbsPath(), suffix)
func (rp *RequestPath) JoinRootDir(extPath string) string {
return path.Join(rp.RootDir(), extPath)
}
func (r *Request) PathTrimRelSuffix(suffix string) string {
return strings.TrimSuffix(strings.TrimSuffix(r.RelPath(), suffix), "/")
}
func (r *Request) PathTrimAbsSuffix(suffix string) string {
return strings.TrimSuffix(strings.TrimSuffix(r.AbsPath(), suffix), "/")
}
type Request struct {
/* Holds onto a request path to the filesystem and
* a string slice of parsed parameters (usually nil
* or length 1)
*/
func (r *Request) PathJoinRootDir(extPath string) string {
return path.Join(r.Path.RootDir(), extPath)
Path *RequestPath
Parameters []string
}
/* Sanitize a request path string */

@ -33,6 +33,7 @@ func (r *Responder) AccessLogError(format string, args ...interface{}) {
}
func (r *Responder) Write(data []byte) *GophorError {
/* Try write all supplied data */
_, err := r.Writer.Write(data)
if err != nil {
return &GophorError{ BufferedWriteErr, err }
@ -41,6 +42,7 @@ func (r *Responder) Write(data []byte) *GophorError {
}
func (r *Responder) WriteFlush(data []byte) *GophorError {
/* Try write all supplied data, followed by flush */
_, err := r.Writer.Write(data)
if err != nil {
return &GophorError{ BufferedWriteErr, err }
@ -57,6 +59,7 @@ func (r *Responder) Flush() *GophorError {
}
func (r *Responder) SafeFlush(gophorErr *GophorError) *GophorError {
/* Flush only if supplied error is nil */
if gophorErr != nil {
return gophorErr
} else {
@ -65,6 +68,7 @@ func (r *Responder) SafeFlush(gophorErr *GophorError) *GophorError {
}
func (r *Responder) WriteRaw(reader io.Reader) *GophorError {
/* Write directly from reader to bufio writer */
_, err := r.Writer.ReadFrom(reader)
if err != nil {
return &GophorError{ BufferedWriteReadErr, err }
@ -72,88 +76,135 @@ func (r *Responder) WriteRaw(reader io.Reader) *GophorError {
return r.Flush()
}
func (r *Responder) CloneWithRequest(request *Request) *Responder {
/* Create new copy of Responder only with request differring */
return &Responder{
r.Host,
r.Client,
r.Writer,
request,
}
}
type SkipPrefixWriter struct {
/* Wrapper to bufio writer that allows read up to
* some predefined prefix into a buffer, then continuing
* write to expected writer destination either after prefix
* reached, or skip buffer filled (whichever comes first).
*/
Writer *bufio.Writer
/* This allows us to specify the write function so that after
* having performed the skip we can modify the write function used
* and not have to use an if-case EVERY SINGLE TIME.
*/
WriteFunc func([]byte) (int, error)
SkipUpTo []byte
SkipBuffer []byte
HasSkipped bool
Available int
ShouldContinue func([]byte) bool
ShouldWriteSkipped func([]byte) bool
}
func NewSkipPrefixWriter(writer *bufio.Writer, skipUpTo []byte, shouldContinue func(data []byte) bool) *SkipPrefixWriter {
return &SkipPrefixWriter{ writer, skipUpTo, make([]byte, Config.SkipPrefixBufSize), false, shouldContinue }
func NewSkipPrefixWriter(writer *bufio.Writer, skipUpTo []byte, shouldWriteSkipped func(data []byte) bool) *SkipPrefixWriter {
w := &SkipPrefixWriter{}
w.Writer = writer
w.WriteFunc = w.WriteCheckSkip
w.SkipUpTo = skipUpTo
w.SkipBuffer = make([]byte, Config.SkipPrefixBufSize)
w.ShouldWriteSkipped = shouldWriteSkipped
w.Available = Config.SkipPrefixBufSize
return w
}
func (w *SkipPrefixWriter) AddToSkipBuffer(data []byte) int {
i := 0
withinBounds := len(w.SkipBuffer) < cap(w.SkipBuffer)
for i < len(data) {
if !withinBounds {
w.HasSkipped = true
break
}
w.SkipBuffer = append(w.SkipBuffer, data[i])
withinBounds = len(w.SkipBuffer) < cap(w.SkipBuffer)
i += 1
/* Add as much data as we can to the skip buffer */
if len(data) >= w.Available {
toAdd := w.Available
w.SkipBuffer = append(w.SkipBuffer, data[:toAdd]...)
w.Available = 0
return toAdd
} else {
w.SkipBuffer = append(w.SkipBuffer, data...)
w.Available -= len(data)
return len(data)
}
return i
}
func (w *SkipPrefixWriter) Write(data []byte) (int, error) {
if !w.HasSkipped {
split := bytes.Split(data, w.SkipUpTo)
if len(split) == 1 {
/* Try add these to skip buffer */
added := w.AddToSkipBuffer(data)
if added != len(data) {
/* We've hit the skip buffer max. Write skip buffer */
_, err := w.Writer.Write(w.SkipBuffer)
if err != nil {
/* We return 0 here as if failed here our count is massively off anyways */
return 0, err
}
/* Write remaining data not added to skip buffer */
_, err = w.Writer.Write(data[added:])
if err != nil {
/* We return 0 here as if failed here our count is massively off anyways */
return 0, err
}
/* Clear the skip buffer */
w.SkipBuffer = nil
return w.WriteFunc(data)
}
return len(data), nil
func (w *SkipPrefixWriter) WriteRegular(data []byte) (int, error) {
return w.Writer.Write(data)
}
func (w *SkipPrefixWriter) WriteCheckSkip(data []byte) (int, error) {
split := bytes.Split(data, w.SkipUpTo)
if len(split) == 1 {
/* Try add these to skip buffer */
added := w.AddToSkipBuffer(data)
if added < len(data) {
defer func() {
w.WriteFunc = w.WriteRegular
}()
/* Write contents of skip buffer */
_, err := w.Writer.Write(w.SkipBuffer)
if err != nil {
/* We return 0 here as if failed here our count is massively off anyways */
return 0, err
}
return len(data), nil
} else {
/* Set us as skipped! */
w.HasSkipped = true
/* Write remaining data not added to skip buffer */
_, err = w.Writer.Write(data[added:])
if err != nil {
/* We return 0 here as if failed here our count is massively off anyways */
return 0, err
}
}
/* Get length of the byte slice to save */
savedLen := len(split[0])+len(w.SkipUpTo)
return len(data), nil
} else {
defer func() {
w.WriteFunc = w.WriteRegular
}()
/* We only want up to first SkipUpTo, so take first element */
w.SkipBuffer = append(w.SkipBuffer, split[0]...)
/* Try add what we can to skip buffer */
added := w.AddToSkipBuffer(append(split[0], w.SkipUpTo...))
/* Check if we should continue */
if !w.ShouldContinue(w.SkipBuffer) {
return savedLen, io.ErrUnexpectedEOF
/* Check if we should write contents of skip buffer */
if !w.ShouldWriteSkipped(w.SkipBuffer) {
/* Skip empty data remaining */
if added >= len(data)-1 {
return len(data), nil
}
/* Create return slice from remaining data */
ret := bytes.Join(split[1:], w.SkipUpTo)
count, err := w.Writer.Write(ret)
return count+savedLen, err
/* Write from index = added */
count, err := w.Writer.Write(data[added:])
if err != nil {
/* Failed, return added+count as write count*/
return added+count, err
}
} else {
/* We write skip buffer contents */
count, err := w.Writer.Write(w.SkipBuffer)
if err != nil {
/* Failed, assume write up to added */
return added, err
}
/* Now write remaining */
count, err = w.Writer.Write(data[added:])
if err != nil {
/* Failed, return count from added */
return added+count, err
}
}
} else {
return w.Writer.Write(data)
return len(data), nil
}
}

@ -16,11 +16,7 @@ type Worker struct {
}
func (worker *Worker) Serve() {
defer func() {
/* Close-up shop */
worker.Conn.Conn.Close()
}()
defer worker.Conn.Close()
/* Read buffer + final result */
buf := make([]byte, SocketReadBufSize)
@ -84,18 +80,18 @@ func (worker *Worker) Serve() {
/* Do nothing */
}
/* Create new request from dataStr */
/* Create new request from received */
request := NewSanitizedRequest(worker.Conn, received)
/* Create new responder from request */
responder := NewResponder(worker.Conn, request)
/* Handle request */
/* Handle request with supplied responder */
gophorErr := Config.FileSystem.HandleRequest(responder)
/* Handle any error */
if gophorErr != nil {
/* Log serve failure to access, error to system */
/* Log serve failure to error to system */
Config.SysLog.Error("", gophorErr.Error())
/* Generate response bytes from error code */
@ -107,9 +103,10 @@ func (worker *Worker) Serve() {
responder.WriteFlush(errResponse)
}
responder.AccessLogError("Failed to serve: %s\n", request.AbsPath())
/* Log failure to access */
responder.AccessLogError("Failed to serve: %s\n", request.Path.Absolute())
} else {
/* Log served */
responder.AccessLogInfo("Served: %s\n", request.AbsPath())
/* Log served to access */
responder.AccessLogInfo("Served: %s\n", request.Path.Absolute())
}
}

Loading…
Cancel
Save