more big changes
- Current Request structure renamed to Responder - Request's RequestPath and Parameters separated into new Request structure - Responder sent on calls to render to filecontents, but creation / caching of filecontents stores onto a a copy of Request instead (as we don't need the extra host or client info held by Responder) - Cached gophermaps now do not cache regular files within them if inserted, as these files could be over the cache file size limit Signed-off-by: kim (grufwub) <grufwub@gmail.com>
This commit is contained in:
parent
10c9f02eb3
commit
c7ae3fd151
29
exec.go
29
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
|
||||
|
108
filecontents.go
108
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
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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. */
|
||||
|
147
request.go
147
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 */
|
||||
|
15
worker.go
15
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())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user