improve code commenting
Signed-off-by: kim (grufwub) <grufwub@gmail.com>
This commit is contained in:
parent
242fc9895a
commit
0f2a2099f2
17
cache.go
17
cache.go
@ -49,6 +49,12 @@ func checkCacheFreshness() {
|
||||
Config.FileCache.CacheMutex.Unlock()
|
||||
}
|
||||
|
||||
/* FileCache:
|
||||
* Object to hold and help manage our file cache. Uses a fixed map
|
||||
* as a means of easily collecting files by path, but also being able
|
||||
* to remove cached files in a LRU style. Uses a RW mutex to lock the
|
||||
* cache map for appropriate functions and ensure thread safety.
|
||||
*/
|
||||
type FileCache struct {
|
||||
CacheMap *FixedMap
|
||||
CacheMutex sync.RWMutex
|
||||
@ -62,6 +68,7 @@ func (fc *FileCache) Init(size int, fileSizeMax float64) {
|
||||
}
|
||||
|
||||
func (fc *FileCache) FetchRegular(request *FileSystemRequest) ([]byte, *GophorError) {
|
||||
/* Calls fc.Fetch() but with the filecontents init function for a regular file */
|
||||
return fc.Fetch(request, func(path string) FileContents {
|
||||
contents := new(RegularFileContents)
|
||||
contents.path = path
|
||||
@ -70,6 +77,7 @@ func (fc *FileCache) FetchRegular(request *FileSystemRequest) ([]byte, *GophorEr
|
||||
}
|
||||
|
||||
func (fc *FileCache) FetchGophermap(request *FileSystemRequest) ([]byte, *GophorError) {
|
||||
/* Calls fc.Fetch() but with the filecontents init function for a gophermap */
|
||||
return fc.Fetch(request, func(path string) FileContents {
|
||||
contents := new(GophermapContents)
|
||||
contents.path = path
|
||||
@ -82,7 +90,8 @@ func (fc *FileCache) Fetch(request *FileSystemRequest, newFileContents func(stri
|
||||
fc.CacheMutex.RLock()
|
||||
file := fc.CacheMap.Get(request.Path)
|
||||
|
||||
/* TODO: work on efficiency */
|
||||
/* TODO: work on efficiency. improve use of mutex?? */
|
||||
|
||||
if file != nil {
|
||||
/* File in cache -- before doing anything get file read lock */
|
||||
file.RLock()
|
||||
@ -107,7 +116,9 @@ func (fc *FileCache) Fetch(request *FileSystemRequest, newFileContents func(stri
|
||||
file.RLock()
|
||||
}
|
||||
} else {
|
||||
/* Before we do ANYTHING, we need to check file-size on disk */
|
||||
/* Perform filesystem stat ready for checking file size later.
|
||||
* Doing this now allows us to weed-out non-existent files early
|
||||
*/
|
||||
stat, err := os.Stat(request.Path)
|
||||
if err != nil {
|
||||
/* Error stat'ing file, unlock read mutex then return error */
|
||||
@ -122,7 +133,7 @@ func (fc *FileCache) Fetch(request *FileSystemRequest, newFileContents func(stri
|
||||
file = NewFile(contents)
|
||||
|
||||
/* NOTE: file isn't in cache yet so no need to lock file write mutex
|
||||
* before loading from disk
|
||||
* before loading contents from disk
|
||||
*/
|
||||
gophorErr := file.LoadContents()
|
||||
if gophorErr != nil {
|
||||
|
@ -5,12 +5,15 @@ import (
|
||||
"log"
|
||||
)
|
||||
|
||||
/* ServerConfig:
|
||||
* Holds onto global server configuration details
|
||||
* and any data objects we want to keep in memory
|
||||
* (e.g. loggers, restricted files regular expressions
|
||||
* and file cache)
|
||||
*/
|
||||
type ServerConfig struct {
|
||||
/* Base settings */
|
||||
RootDir string
|
||||
Hostname string
|
||||
Port string
|
||||
TlsPort string
|
||||
|
||||
/* Caps.txt information */
|
||||
Description string
|
||||
|
9
conn.go
9
conn.go
@ -4,16 +4,15 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
/* Data structure to hold specific host details
|
||||
* for passing on later
|
||||
*/
|
||||
/* Data structure to hold specific host details */
|
||||
type ConnHost struct {
|
||||
Name string
|
||||
Port string
|
||||
}
|
||||
|
||||
/* Simple wrapper to Listener that generates
|
||||
* GophorConn instances on each accept
|
||||
/* Simple wrapper to Listener that holds onto virtual
|
||||
* host information and generates GophorConn
|
||||
* instances on each accept
|
||||
*/
|
||||
type GophorListener struct {
|
||||
Listener net.Listener
|
||||
|
12
error.go
12
error.go
@ -4,9 +4,7 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
* Client error data structure
|
||||
*/
|
||||
/* Simple error code type defs */
|
||||
type ErrorCode int
|
||||
type ErrorResponseCode int
|
||||
const (
|
||||
@ -43,11 +41,13 @@ const (
|
||||
NoResponse ErrorResponseCode = iota
|
||||
)
|
||||
|
||||
/* Simple GophorError data structure to wrap another error */
|
||||
type GophorError struct {
|
||||
Code ErrorCode
|
||||
Err error
|
||||
}
|
||||
|
||||
/* Convert error code to string */
|
||||
func (e *GophorError) Error() string {
|
||||
var str string
|
||||
switch e.Code {
|
||||
@ -91,6 +91,7 @@ func (e *GophorError) Error() string {
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert a gophor error code to appropriate error response code */
|
||||
func gophorErrorToResponseCode(code ErrorCode) ErrorResponseCode {
|
||||
switch code {
|
||||
case PathEnumerationErr:
|
||||
@ -109,7 +110,7 @@ func gophorErrorToResponseCode(code ErrorCode) ErrorResponseCode {
|
||||
case DirListErr:
|
||||
return ErrorResponse404
|
||||
|
||||
/* These are errors sending, no point trying to send error */
|
||||
/* These are errors _while_ sending, no point trying to send error */
|
||||
case SocketWriteErr:
|
||||
return NoResponse
|
||||
case SocketWriteCountErr:
|
||||
@ -129,6 +130,7 @@ func gophorErrorToResponseCode(code ErrorCode) ErrorResponseCode {
|
||||
}
|
||||
}
|
||||
|
||||
/* Generates gopher protocol compatible error response from our code */
|
||||
func generateGopherErrorResponseFromCode(code ErrorCode) []byte {
|
||||
responseCode := gophorErrorToResponseCode(code)
|
||||
if responseCode == NoResponse {
|
||||
@ -137,11 +139,13 @@ func generateGopherErrorResponseFromCode(code ErrorCode) []byte {
|
||||
return generateGopherErrorResponse(responseCode)
|
||||
}
|
||||
|
||||
/* Generates gopher protocol compatible error response for response code */
|
||||
func generateGopherErrorResponse(code ErrorResponseCode) []byte {
|
||||
b := buildError(code.String())
|
||||
return append(b, []byte(LastLine)...)
|
||||
}
|
||||
|
||||
/* Error response code to string */
|
||||
func (e ErrorResponseCode) String() string {
|
||||
switch e {
|
||||
case ErrorResponse200:
|
||||
|
@ -17,10 +17,14 @@ type RegularFileContents struct {
|
||||
}
|
||||
|
||||
func (fc *RegularFileContents) Render(request *FileSystemRequest) []byte {
|
||||
/* Here we can ignore the extra data in request.
|
||||
* We are but a simple cache'd file
|
||||
*/
|
||||
return fc.contents
|
||||
}
|
||||
|
||||
func (fc *RegularFileContents) Load() *GophorError {
|
||||
/* Load the file into memory */
|
||||
var gophorErr *GophorError
|
||||
fc.contents, gophorErr = bufferedRead(fc.path)
|
||||
return gophorErr
|
||||
@ -42,8 +46,10 @@ type GophermapContents struct {
|
||||
}
|
||||
|
||||
func (gc *GophermapContents) Render(request *FileSystemRequest) []byte {
|
||||
/* We don't just want to read the contents, but also
|
||||
* execute any included gophermap execute lines
|
||||
/* We don't just want to read the contents, each section
|
||||
* in the sections slice needs a call to render() to
|
||||
* perform their own required actions in producing a
|
||||
* sendable byte slice.
|
||||
*/
|
||||
returnContents := make([]byte, 0)
|
||||
for _, line := range gc.sections {
|
||||
@ -54,10 +60,14 @@ func (gc *GophermapContents) Render(request *FileSystemRequest) []byte {
|
||||
returnContents = append(returnContents, content...)
|
||||
}
|
||||
|
||||
/* Finally we end render with last line */
|
||||
returnContents = append(returnContents, []byte(LastLine)...)
|
||||
|
||||
return returnContents
|
||||
}
|
||||
|
||||
func (gc *GophermapContents) Load() *GophorError {
|
||||
/* Load the gophermap into memory as gophermap sections */
|
||||
var gophorErr *GophorError
|
||||
gc.sections, gophorErr = readGophermap(gc.path)
|
||||
return gophorErr
|
||||
@ -146,7 +156,7 @@ func readGophermap(path string) ([]GophermapSection, *GophorError) {
|
||||
sections = append(sections, NewGophermapText(buildInfoLine(line)))
|
||||
|
||||
case TypeTitle:
|
||||
/* Reformat title line to send as info title */
|
||||
/* Reformat title line to send as info line with appropriate selector */
|
||||
if !titleAlready {
|
||||
sections = append(sections, NewGophermapText(buildLine(TypeInfo, line[1:], "TITLE", NullHost, NullPort)))
|
||||
titleAlready = true
|
||||
@ -207,9 +217,7 @@ func readGophermap(path string) ([]GophermapSection, *GophorError) {
|
||||
return false
|
||||
|
||||
default:
|
||||
/* Replace pre-set strings */
|
||||
line = strings.Replace(line, ReplaceStrHostname, Config.Hostname, -1)
|
||||
line = strings.Replace(line, ReplaceStrPort, Config.Port, -1)
|
||||
/* Just append to sections slice as gophermap text */
|
||||
sections = append(sections, NewGophermapText([]byte(line+DOSLineEnd)))
|
||||
}
|
||||
|
||||
@ -224,7 +232,8 @@ func readGophermap(path string) ([]GophermapSection, *GophorError) {
|
||||
|
||||
/* If dir listing requested, append the hidden files map then add
|
||||
* to sections slice. We can do this here as the TypeEndBeginList item
|
||||
* type ALWAYS comes last, at least in the gophermap handled by this context.
|
||||
* type ALWAYS comes last, at least in the gophermap handled by this call
|
||||
* to readGophermap().
|
||||
*/
|
||||
if dirListing != nil {
|
||||
dirListing.Hidden = hidden
|
||||
|
@ -4,10 +4,12 @@ import (
|
||||
"container/list"
|
||||
)
|
||||
|
||||
/* TODO: work on efficiency. use our own lower level data structure? */
|
||||
|
||||
/* FixedMap:
|
||||
* A fixed size map that pushes the last
|
||||
* used value from the stack if size limit
|
||||
* is reached and user attempts .Put()
|
||||
* is reached.
|
||||
*/
|
||||
type FixedMap struct {
|
||||
Map map[string]*MapElement
|
||||
@ -32,6 +34,7 @@ func NewFixedMap(size int) *FixedMap {
|
||||
return fm
|
||||
}
|
||||
|
||||
/* Get file in map for key, or nil */
|
||||
func (fm *FixedMap) Get(key string) *File {
|
||||
elem, ok := fm.Map[key]
|
||||
if ok {
|
||||
@ -41,6 +44,8 @@ func (fm *FixedMap) Get(key string) *File {
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 }
|
||||
@ -60,6 +65,7 @@ func (fm *FixedMap) Put(key string, value *File) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Try delete element, else do nothing */
|
||||
func (fm *FixedMap) Remove(key string) {
|
||||
elem, ok := fm.Map[key]
|
||||
if !ok {
|
||||
|
12
format.go
12
format.go
@ -72,12 +72,13 @@ func buildError(selector string) []byte {
|
||||
return []byte(ret)
|
||||
}
|
||||
|
||||
/* Build gopher compliant line with supplied information */
|
||||
func buildLine(t ItemType, name, selector, host string, port string) []byte {
|
||||
ret := string(t)
|
||||
|
||||
/* Add name, truncate name if too long */
|
||||
if len(name) > Config.PageWidth {
|
||||
ret += name[:Config.PageWidth-4]+"...\t"
|
||||
ret += name[:Config.PageWidth-5]+"...\t"
|
||||
} else {
|
||||
ret += name+"\t"
|
||||
}
|
||||
@ -96,16 +97,12 @@ func buildLine(t ItemType, name, selector, host string, port string) []byte {
|
||||
return []byte(ret)
|
||||
}
|
||||
|
||||
/* Build gopher compliant info line */
|
||||
func buildInfoLine(content string) []byte {
|
||||
return buildLine(TypeInfo, content, NullSelector, NullHost, NullPort)
|
||||
}
|
||||
|
||||
/* getItemType(name string) ItemType:
|
||||
* Here we use an empty function pointer, and set the correct
|
||||
* function to be used during the restricted files regex parsing.
|
||||
* This negates need to check if RestrictedFilesRegex is nil every
|
||||
* single call.
|
||||
*/
|
||||
/* Get item type for named file on disk */
|
||||
func getItemType(name string) ItemType {
|
||||
/* Split, name MUST be lower */
|
||||
split := strings.Split(strings.ToLower(name), ".")
|
||||
@ -128,6 +125,7 @@ func getItemType(name string) ItemType {
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse line type from contents */
|
||||
func parseLineType(line string) ItemType {
|
||||
lineLen := len(line)
|
||||
|
||||
|
4
fs.go
4
fs.go
@ -107,6 +107,7 @@ type FileContents interface {
|
||||
Clear()
|
||||
}
|
||||
|
||||
/* Perform simple buffered read on a file at path */
|
||||
func bufferedRead(path string) ([]byte, *GophorError) {
|
||||
/* Open file */
|
||||
fd, err := os.Open(path)
|
||||
@ -144,6 +145,7 @@ func bufferedRead(path string) ([]byte, *GophorError) {
|
||||
return contents, nil
|
||||
}
|
||||
|
||||
/* Perform buffered read on file at path, then scan through with supplied iterator func */
|
||||
func bufferedScan(path string, scanIterator func(*bufio.Scanner) bool) *GophorError {
|
||||
/* First, read raw file contents */
|
||||
contents, gophorErr := bufferedRead(path)
|
||||
@ -155,7 +157,7 @@ func bufferedScan(path string, scanIterator func(*bufio.Scanner) bool) *GophorEr
|
||||
reader := bytes.NewReader(contents)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
/* If contains DOS line-endings, use them! Else, Unix line-endings */
|
||||
/* If contains DOS line-endings, split by DOS! Else, split by Unix */
|
||||
if bytes.Contains(contents, []byte(DOSLineEnd)) {
|
||||
scanner.Split(dosLineEndSplitter)
|
||||
} else {
|
||||
|
@ -100,8 +100,6 @@ func setupServer() []*GophorListener {
|
||||
/* Setup the server configuration instance and enter as much as we can right now */
|
||||
Config = new(ServerConfig)
|
||||
Config.RootDir = *serverRoot
|
||||
Config.Hostname = *serverHostname
|
||||
Config.Port = strconv.Itoa(*serverPort)
|
||||
Config.Description = *serverDescription
|
||||
Config.AdminEmail = *serverAdmin
|
||||
Config.Geolocation = *serverGeoloc
|
||||
@ -143,13 +141,15 @@ func setupServer() []*GophorListener {
|
||||
listeners := make([]*GophorListener, 0)
|
||||
|
||||
/* If requested, setup unencrypted listener */
|
||||
if Config.Port != NullPort {
|
||||
l, err := BeginGophorListen(*serverBindAddr, *serverHostname, Config.Port)
|
||||
if *serverPort != 0 {
|
||||
l, err := BeginGophorListen(*serverBindAddr, *serverHostname, strconv.Itoa(*serverPort))
|
||||
if err != nil {
|
||||
Config.LogSystemFatal("Error setting up (unencrypted) listener: %s\n", err.Error())
|
||||
}
|
||||
Config.LogSystem("Listening (unencrypted): gopher://%s\n", l.Addr())
|
||||
listeners = append(listeners, l)
|
||||
} else {
|
||||
Config.LogSystemFatal("No valid port to listen on :(\n")
|
||||
}
|
||||
|
||||
/* Drop privileges to retrieved UID + GID */
|
||||
|
12
html.go
12
html.go
@ -1,17 +1,5 @@
|
||||
package main
|
||||
|
||||
/*
|
||||
func generateHtmlErrorResponse(code ErrorResponseCode) []byte {
|
||||
content :=
|
||||
"<html>\n"+
|
||||
"<body>\n"+
|
||||
code.String()+"\n"+
|
||||
"</body>\n"+
|
||||
"</html>\n"
|
||||
return generateHttpResponse(code, content)
|
||||
}
|
||||
*/
|
||||
|
||||
func generateHtmlRedirect(url string) []byte {
|
||||
content :=
|
||||
"<html>\n"+
|
||||
|
3
regex.go
3
regex.go
@ -6,9 +6,9 @@ import (
|
||||
)
|
||||
|
||||
func compileUserRestrictedFilesRegex(restrictedFiles string) []*regexp.Regexp {
|
||||
/* Try compiling the RestrictedFilesRegex from finalRegex */
|
||||
Config.LogSystem("Compiling restricted file regular expressions\n")
|
||||
|
||||
/* Return slice */
|
||||
restrictedFilesRegex := make([]*regexp.Regexp, 0)
|
||||
|
||||
/* Split the user supplied RestrictedFiles string by new-line */
|
||||
@ -23,6 +23,7 @@ func compileUserRestrictedFilesRegex(restrictedFiles string) []*regexp.Regexp {
|
||||
return restrictedFilesRegex
|
||||
}
|
||||
|
||||
/* Iterate through restricted file expressions, check if file _is_ restricted */
|
||||
func isRestrictedFile(name string) bool {
|
||||
for _, regex := range Config.RestrictedFiles {
|
||||
if regex.MatchString(name) {
|
||||
|
16
worker.go
16
worker.go
@ -42,7 +42,6 @@ func (worker *Worker) Serve() {
|
||||
buf := make([]byte, SocketReadBufSize)
|
||||
received := make([]byte, 0)
|
||||
|
||||
/* Buffered read from listener */
|
||||
iter := 0
|
||||
for {
|
||||
/* Buffered read from listener */
|
||||
@ -78,7 +77,7 @@ func (worker *Worker) Serve() {
|
||||
if gophorErr != nil {
|
||||
Config.LogSystemError("%s\n", gophorErr.Error())
|
||||
|
||||
/* Try generate response bytes from error code */
|
||||
/* Generate response bytes from error code */
|
||||
response := generateGopherErrorResponseFromCode(gophorErr.Code)
|
||||
|
||||
/* If we got response bytes to send? SEND 'EM! */
|
||||
@ -112,7 +111,7 @@ func (worker *Worker) RespondGopher(data []byte) *GophorError {
|
||||
/* According to Gopher spec, only read up to first Tab or Crlf */
|
||||
dataStr := readUpToFirstTabOrCrlf(data)
|
||||
|
||||
/* Handle URL request if so */
|
||||
/* Handle URL request if so. TODO: this is so unelegant... */
|
||||
lenBefore := len(dataStr)
|
||||
dataStr = strings.TrimPrefix(dataStr, "URL:")
|
||||
switch len(dataStr) {
|
||||
@ -127,7 +126,7 @@ func (worker *Worker) RespondGopher(data []byte) *GophorError {
|
||||
/* Sanitize supplied path */
|
||||
requestPath := sanitizePath(dataStr)
|
||||
|
||||
/* Handle policy files */
|
||||
/* Handle policy files. TODO: this is so unelegant... */
|
||||
switch requestPath {
|
||||
case "/"+CapsTxtStr:
|
||||
return worker.SendRaw(generateCapsTxt())
|
||||
@ -142,8 +141,9 @@ func (worker *Worker) RespondGopher(data []byte) *GophorError {
|
||||
return &GophorError{ FileOpenErr, err }
|
||||
}
|
||||
|
||||
|
||||
/* If not empty requestPath, check file type */
|
||||
/* If not empty requestPath, check file type.
|
||||
* Default type is directory.
|
||||
*/
|
||||
fileType := FileTypeDir
|
||||
if requestPath != "." {
|
||||
stat, err := file.Stat()
|
||||
@ -164,9 +164,7 @@ func (worker *Worker) RespondGopher(data []byte) *GophorError {
|
||||
/* Don't need the file handle anymore */
|
||||
file.Close()
|
||||
|
||||
/* TODO: work on efficiency */
|
||||
|
||||
/* Handle file type */
|
||||
/* Handle file type. TODO: work on efficiency */
|
||||
response := make([]byte, 0)
|
||||
switch fileType {
|
||||
/* Directory */
|
||||
|
Loading…
Reference in New Issue
Block a user