Inititial commit
This commit is contained in:
32
core/communications.go
Normal file
32
core/communications.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SendObject sends a object through the writer
|
||||||
|
func SendObject(writer *http.ResponseWriter, data interface{}) {
|
||||||
|
js := ToJSON(data)
|
||||||
|
if js == "" {
|
||||||
|
SendJSON(writer, "{\"server_message\":\"internal_sending_error\"")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
SendJSON(writer, js)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendJSON sends JSON string through the writer
|
||||||
|
func SendJSON(writer *http.ResponseWriter, data string) {
|
||||||
|
(*writer).Header().Set("Content-Type", "application/json")
|
||||||
|
fmt.Fprintf((*writer), data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToJSON converts an object to a JSON string
|
||||||
|
func ToJSON(in interface{}) string {
|
||||||
|
data, err := json.Marshal(in)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
92
gamemanagment.go
Normal file
92
gamemanagment.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"werwolf/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
var games []JSON_Game
|
||||||
|
|
||||||
|
type JSON_Game struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Gamename string `json:"gamename"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGame returns a reference to a game-Object identified by token
|
||||||
|
func getGame(token string) (*JSON_Game, bool) {
|
||||||
|
for _, g := range games {
|
||||||
|
if g.Token == token {
|
||||||
|
return &g, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// addGame adds a game-Object game to the list of active games or replaces an inactive game.
|
||||||
|
func addGame(game JSON_Game) bool {
|
||||||
|
a := true
|
||||||
|
for _, g := range games {
|
||||||
|
if g.Token == "" {
|
||||||
|
g = game
|
||||||
|
a = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if a {
|
||||||
|
games = append(games, game)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeGame disables a game-Object by setting its token to none. Object to be edited is identified by token. Returns true if successfull or false if not found.
|
||||||
|
func removeGame(token string) bool {
|
||||||
|
for _, g := range games {
|
||||||
|
if g.Token == token {
|
||||||
|
g.Token = ""
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGame(responseWriter http.ResponseWriter, request *http.Request) {
|
||||||
|
var game = new(JSON_Game)
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.ReadFrom(request.Body)
|
||||||
|
|
||||||
|
if json.Unmarshal(buf.Bytes(), game) == nil {
|
||||||
|
game.Token = randomString(32)
|
||||||
|
if addGame(*game) {
|
||||||
|
core.SendObject(&responseWriter, game)
|
||||||
|
addGame(*game)
|
||||||
|
} else {
|
||||||
|
sendError(&responseWriter, "game_creation")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendError(&responseWriter, "json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinGame(responseWriter http.ResponseWriter, request *http.Request) {
|
||||||
|
var game = new(JSON_Game)
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.ReadFrom(request.Body)
|
||||||
|
|
||||||
|
if json.Unmarshal(buf.Bytes(), game) == nil {
|
||||||
|
game, succ := getGame(game.Token)
|
||||||
|
if succ {
|
||||||
|
core.SendObject(&responseWriter, *game)
|
||||||
|
} else {
|
||||||
|
sendError(&responseWriter, "game_notfound")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendError(&responseWriter, "json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendError(responseWriter *http.ResponseWriter, errorMsg string) {
|
||||||
|
core.SendObject(responseWriter, map[string]string{"state": errorMsg + "_error"})
|
||||||
|
}
|
||||||
31
index.html
Normal file
31
index.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Werwolf Test page</title>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||||
|
<script>
|
||||||
|
function send(input, where)
|
||||||
|
{
|
||||||
|
jQuery.post("http://127.0.0.1:25566/"+where, input, function(data, textStatus, jqXHR ){console.log(data);});
|
||||||
|
}
|
||||||
|
|
||||||
|
$("document").ready(function(){
|
||||||
|
$("#send").click(send($("#in").val(),$("#where").val()));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<form>
|
||||||
|
<input id="in"/>
|
||||||
|
<input id="where"/>
|
||||||
|
<button id="send">SEND</button>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
19
main.go
Normal file
19
main.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/rs/cors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("Starting Werwolf Server")
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
mux.HandleFunc("/werwolf/creategame", http.HandlerFunc(newGame))
|
||||||
|
mux.HandleFunc("/werwolf/joingame", http.HandlerFunc(joinGame))
|
||||||
|
|
||||||
|
corsHandler := cors.Default().Handler(mux)
|
||||||
|
http.ListenAndServe(":25566", corsHandler)
|
||||||
|
}
|
||||||
32
randomstring.go
Normal file
32
randomstring.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
letterIdxBits = 6
|
||||||
|
letterIdxMask = 1<<letterIdxBits - 1
|
||||||
|
letterIdxMax = 63 / letterIdxBits
|
||||||
|
)
|
||||||
|
const letterBytes = "abcdefghijkmnlopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
|
||||||
|
var src = rand.NewSource(time.Now().UnixNano())
|
||||||
|
|
||||||
|
func randomString(n int) string {
|
||||||
|
b := make([]byte, n)
|
||||||
|
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
|
||||||
|
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
|
||||||
|
if remain == 0 {
|
||||||
|
cache, remain = src.Int63(), letterIdxMax
|
||||||
|
}
|
||||||
|
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
|
||||||
|
b[i] = letterBytes[idx]
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
cache >>= letterIdxBits
|
||||||
|
remain--
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
89
realip/realip.go
Normal file
89
realip/realip.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package realip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cidrs []*net.IPNet
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
maxCidrBlocks := []string{
|
||||||
|
"127.0.0.1/8", // localhost
|
||||||
|
"10.0.0.0/8", // 24-bit block
|
||||||
|
"172.16.0.0/12", // 20-bit block
|
||||||
|
"192.168.0.0/16", // 16-bit block
|
||||||
|
"169.254.0.0/16", // link local address
|
||||||
|
"::1/128", // localhost IPv6
|
||||||
|
"fc00::/7", // unique local address IPv6
|
||||||
|
"fe80::/10", // link local address IPv6
|
||||||
|
}
|
||||||
|
|
||||||
|
cidrs = make([]*net.IPNet, len(maxCidrBlocks))
|
||||||
|
for i, maxCidrBlock := range maxCidrBlocks {
|
||||||
|
_, cidr, _ := net.ParseCIDR(maxCidrBlock)
|
||||||
|
cidrs[i] = cidr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isLocalAddress works by checking if the address is under private CIDR blocks.
|
||||||
|
// List of private CIDR blocks can be seen on :
|
||||||
|
//
|
||||||
|
// https://en.wikipedia.org/wiki/Private_network
|
||||||
|
//
|
||||||
|
// https://en.wikipedia.org/wiki/Link-local_address
|
||||||
|
func isPrivateAddress(address string) (bool, error) {
|
||||||
|
ipAddress := net.ParseIP(address)
|
||||||
|
if ipAddress == nil {
|
||||||
|
return false, errors.New("address is not valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range cidrs {
|
||||||
|
if cidrs[i].Contains(ipAddress) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromRequest return client's real public IP address from http request headers.
|
||||||
|
func FromRequest(r *http.Request) string {
|
||||||
|
// Fetch header value
|
||||||
|
xRealIP := r.Header.Get("X-Real-Ip")
|
||||||
|
xForwardedFor := r.Header.Get("X-Forwarded-For")
|
||||||
|
|
||||||
|
// If both empty, return IP from remote address
|
||||||
|
if xRealIP == "" && xForwardedFor == "" {
|
||||||
|
var remoteIP string
|
||||||
|
|
||||||
|
// If there are colon in remote address, remove the port number
|
||||||
|
// otherwise, return remote address as is
|
||||||
|
if strings.ContainsRune(r.RemoteAddr, ':') {
|
||||||
|
remoteIP, _, _ = net.SplitHostPort(r.RemoteAddr)
|
||||||
|
} else {
|
||||||
|
remoteIP = r.RemoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
return remoteIP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check list of IP in X-Forwarded-For and return the first global address
|
||||||
|
for _, address := range strings.Split(xForwardedFor, ",") {
|
||||||
|
address = strings.TrimSpace(address)
|
||||||
|
isPrivate, err := isPrivateAddress(address)
|
||||||
|
if !isPrivate && err == nil {
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If nothing succeed, return X-Real-IP
|
||||||
|
return xRealIP
|
||||||
|
}
|
||||||
|
|
||||||
|
// RealIP is depreciated, use FromRequest instead
|
||||||
|
func RealIP(r *http.Request) string {
|
||||||
|
return FromRequest(r)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user