improve code commenting

Signed-off-by: kim (grufwub) <grufwub@gmail.com>
This commit is contained in:
kim (grufwub) 2020-04-24 12:09:54 +01:00
parent 242fc9895a
commit 0f2a2099f2
12 changed files with 76 additions and 57 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

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

View File

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