Forgotten files.

This commit is contained in:
Stanislav Nikitin 2019-09-02 22:24:34 +05:00
parent e1b71e5d44
commit 5452ea5b1a
No known key found for this signature in database
GPG Key ID: 106900B32F8192EE
10 changed files with 291 additions and 0 deletions

12
.golangci.yaml Normal file
View File

@ -0,0 +1,12 @@
run:
deadline: 5m
linters:
enable-all: true
disable:
# Actually not really needed.
- gochecknoglobals
linters-settings:
lll:
line-length: 420
gocyclo:
min-complexity: 40

17
commands/exported.go Normal file
View File

@ -0,0 +1,17 @@
package commands
import (
// stdlib
"log"
// local
"develop.pztrn.name/gonews/gonews/commands/greeting"
"develop.pztrn.name/gonews/gonews/commands/quit"
)
func Initialize() {
log.Println("Initializing commands...")
greeting.Initialize()
quit.Initialize()
}

View File

@ -0,0 +1,23 @@
package greeting
import (
// stdlib
"log"
// local
"develop.pztrn.name/gonews/gonews/eventer"
"develop.pztrn.name/gonews/gonews/networker"
)
func Initialize() {
log.Println("Initializing greeting command...")
eventer.AddEventHandler(&eventer.EventHandler{
Command: "internal/greeting",
Handler: handler,
})
}
func handler(data interface{}) interface{} {
return &networker.Reply{Code: "201", Data: "NNTP server is ready, posting prohibited\r\n"}
}

23
commands/quit/exported.go Normal file
View File

@ -0,0 +1,23 @@
package quit
import (
// stdlib
"log"
// local
"develop.pztrn.name/gonews/gonews/eventer"
"develop.pztrn.name/gonews/gonews/networker"
)
func Initialize() {
log.Println("Initializing quit command...")
eventer.AddEventHandler(&eventer.EventHandler{
Command: "commands/quit",
Handler: handler,
})
}
func handler(data interface{}) interface{} {
return &networker.Reply{Code: "205", Data: "NNTP Service exits normally\r\n"}
}

18
configuration/config.go Normal file
View File

@ -0,0 +1,18 @@
package configuration
// Represents configuration file structure.
type config struct {
// Network represents network stack configuration.
Network []Network `yaml:"network"`
}
type Network struct {
// Address represents address to bing in form of "ip:port".
Address string `yaml:"address"`
// Limit sets maximum simultaneous connections that can be
// processed by worker.
Limit int `yaml:"limit"`
// Type sets connection type. See networker for available
// types.
Type string `yaml:"type"`
}

55
configuration/exported.go Normal file
View File

@ -0,0 +1,55 @@
package configuration
import (
// stdlib
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
// other
"gopkg.in/yaml.v2"
)
var (
Cfg *config
)
// Initialize initializes package and parses configuration into struct.
func Initialize() {
log.Println("Initializing configuration...")
pathRaw, found := os.LookupEnv("GONEWS_CONFIG")
if !found {
log.Fatalln("Failed to read configuration - no GONEWS_CONFIG environment variable defined.")
}
// Normalize path.
if strings.HasPrefix(pathRaw, "~") {
userHomeDir, err := os.UserHomeDir()
if err != nil {
log.Fatalln("Failed to obtain user's home directory path: " + err.Error())
}
pathRaw = strings.Replace(pathRaw, "~", userHomeDir, 1)
}
absPath, err1 := filepath.Abs(pathRaw)
if err1 != nil {
log.Fatalln("Failed to get absolute path for configuration file: " + err1.Error())
}
// Read and parse configuration file.
fileData, err2 := ioutil.ReadFile(absPath)
if err2 != nil {
log.Fatalln("Failed to read configuration file data: " + err2.Error())
}
Cfg = &config{}
err3 := yaml.Unmarshal(fileData, Cfg)
if err3 != nil {
log.Fatalln("Failed to parse configuration file: " + err3.Error())
}
log.Printf("Configuration file parsed: %+v\n", Cfg)
}

94
networker/connection.go Normal file
View File

@ -0,0 +1,94 @@
package networker
import (
// stdlib
"bufio"
"log"
"net"
"strings"
// local
"develop.pztrn.name/gonews/gonews/eventer"
)
// This function is a connection worker.
func connectionWorker(conn net.Conn) {
remoteAddr := conn.RemoteAddr()
log.Printf("accepted connection from %v\n", conn.RemoteAddr())
defer func() {
err := conn.Close()
if err != nil {
log.Println("Failed to close connection from " + remoteAddr.String() + ": " + err.Error())
}
log.Println("Connection from " + remoteAddr.String() + " closed")
}()
// Create buffers.
r := bufio.NewReader(conn)
w := bufio.NewWriter(conn)
scanr := bufio.NewScanner(r)
// Send greeting.
greetingData, _ := eventer.LaunchEvent("internal/greeting", nil)
greetingReply := greetingData.(*Reply)
_, err := w.WriteString(greetingReply.Code + " " + greetingReply.Data)
if err != nil {
log.Println("Failed to write greeting for " + remoteAddr.String() + ": " + err.Error())
return
}
w.Flush()
// Start reading for commands.
// Every command can be represented as slice where first element
// is actual command and all next - parameters.
// By default we read only one line per iteration.
// ToDo: multiline data parser for posting.
for {
dataAppeared := scanr.Scan()
if !dataAppeared {
log.Println("Failed to read data from " + remoteAddr.String() + ": " + scanr.Err().Error())
break
}
log.Println("Got data: " + scanr.Text())
// ToDo: what if we'll upload binary data here?
// Not supported yet.
data := strings.Split(scanr.Text(), " ")
replyRaw, err := eventer.LaunchEvent("commands/"+data[0], data[1:])
if err != nil {
// We won't break here as this is just logging of appeared error.
log.Println("Error appeared while processing command '" + data[0] + "' for " + remoteAddr.String() + ": " + err.Error())
}
// We might have nil in reply, so we'll assume that passed command
// is unknown to us.
if replyRaw == nil {
_, err := w.WriteString(unknownCommandErrorCode + " " + unknownCommandErrorText + "\r\n")
if err != nil {
log.Println("Failed to write string to socket for " + remoteAddr.String() + ": " + err.Error())
break
}
w.Flush()
continue
}
// Every reply will be a reply struct.
reply := replyRaw.(*Reply)
_, err1 := w.WriteString(reply.Code + " " + reply.Data)
if err1 != nil {
log.Println("Failed to write string to socket for " + remoteAddr.String() + ": " + err1.Error())
break
}
w.Flush()
// Check for QUIT command.
if strings.ToLower(data[0]) == "quit" {
log.Println("QUIT command received, closing connection to " + remoteAddr.String())
break
}
}
}

6
networker/errors.go Normal file
View File

@ -0,0 +1,6 @@
package networker
const (
unknownCommandErrorCode = "500"
unknownCommandErrorText = "Unknown command"
)

6
networker/reply.go Normal file
View File

@ -0,0 +1,6 @@
package networker
type Reply struct {
Code string
Data string
}

37
networker/server.go Normal file
View File

@ -0,0 +1,37 @@
package networker
import (
// stdlib
"log"
"net"
// local
"develop.pztrn.name/gonews/gonews/configuration"
)
// This function responsible for accepting incoming connections for
// each address configuration.
func startServer(config configuration.Network) {
log.Println("Starting server on " + config.Address + " (type: " + config.Type + ")")
l, err := net.Listen("tcp", config.Address)
if err != nil {
log.Fatalln("Failed to start TCP server on " + config.Address + ": " + err.Error())
}
defer func() {
err := l.Close()
if err != nil {
log.Println("Failed to close TCP server on " + config.Address + ": " + err.Error())
}
}()
for {
conn, err1 := l.Accept()
if err1 != nil {
log.Println("Failed to accept new incoming connection: " + err1.Error())
continue
}
go connectionWorker(conn)
}
}