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:
kim (grufwub) 2020-05-07 08:59:53 +01:00
parent 10c9f02eb3
commit c7ae3fd151
6 changed files with 185 additions and 191 deletions

29
exec.go
View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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. */

View File

@ -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 */

View File

@ -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())
}
}