mirror of
https://github.com/itzg/mc-router.git
synced 2024-11-24 11:55:24 +01:00
Initial commit
This commit is contained in:
commit
17a4bd6515
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/vendor/
|
||||
|
||||
/.idea/
|
||||
/*.iml
|
63
Gopkg.lock
generated
Normal file
63
Gopkg.lock
generated
Normal file
@ -0,0 +1,63 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/alecthomas/kingpin"
|
||||
packages = ["."]
|
||||
revision = "947dcec5ba9c011838740e680966fd7087a71d0d"
|
||||
version = "v2.2.6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/alecthomas/template"
|
||||
packages = [
|
||||
".",
|
||||
"parse"
|
||||
]
|
||||
revision = "a0175ee3bccc567396460bf5acd36800cb10c49c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/alecthomas/units"
|
||||
packages = ["."]
|
||||
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/context"
|
||||
packages = ["."]
|
||||
revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/mux"
|
||||
packages = ["."]
|
||||
revision = "53c1911da2b537f792e7cafcb446b05ffe33b996"
|
||||
version = "v1.6.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "c155da19408a8799da419ed3eeb0cb5db0ad5dbc"
|
||||
version = "v1.0.5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = ["ssh/terminal"]
|
||||
revision = "4ec37c66abab2c7e02ae775328b2ff001c3f025a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"unix",
|
||||
"windows"
|
||||
]
|
||||
revision = "6f686a352de66814cdd080d970febae7767857a3"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "8d17b95931fea7bbf18743367ccc152c392add293fe7945ca9ce941ca1482b4f"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
38
Gopkg.toml
Normal file
38
Gopkg.toml
Normal file
@ -0,0 +1,38 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/alecthomas/kingpin"
|
||||
version = "2.2.6"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
version = "1.0.5"
|
21
LICENSE.txt
Normal file
21
LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Geoff Bourne
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
30
README.md
Normal file
30
README.md
Normal file
@ -0,0 +1,30 @@
|
||||
Routes Minecraft client connections to backend servers based upon the requested server address.
|
||||
|
||||
## Usage
|
||||
|
||||
```text
|
||||
Flags:
|
||||
--help Show context-sensitive help (also try --help-long
|
||||
and --help-man).
|
||||
--port=25565 The port bound to listen for Minecraft client
|
||||
connections
|
||||
--api-binding=API-BINDING The host:port bound for servicing API requests
|
||||
--mapping=MAPPING ... Mapping of external hostname to internal server
|
||||
host:port
|
||||
```
|
||||
|
||||
## REST API
|
||||
|
||||
* `GET /routes`
|
||||
Retrieves the currently configured routes
|
||||
* `POST /routes`
|
||||
Registers a route given a JSON body structured like:
|
||||
```json
|
||||
{
|
||||
"serverAddress": "CLIENT REQUESTED SERVER ADDRESS",
|
||||
"backend": "HOST:PORT"
|
||||
}
|
||||
```
|
||||
|
||||
* `DELETE /routes/{serverAddress}`
|
||||
Deletes an existing route for the given `serverAddress`
|
42
cmd/mc-router/main.go
Normal file
42
cmd/mc-router/main.go
Normal file
@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"github.com/itzg/mc-router/server"
|
||||
"github.com/alecthomas/kingpin"
|
||||
"strconv"
|
||||
"github.com/sirupsen/logrus"
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
var (
|
||||
port = kingpin.Flag("port", "The port bound to listen for Minecraft client connections").
|
||||
Default("25565").Int()
|
||||
apiBinding = kingpin.Flag("api-binding", "The host:port bound for servicing API requests").
|
||||
String()
|
||||
mappings = kingpin.Flag("mapping", "Mapping of external hostname to internal server host:port").
|
||||
StringMap()
|
||||
)
|
||||
|
||||
func main() {
|
||||
kingpin.Parse()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c)
|
||||
|
||||
server.Routes.RegisterAll(*mappings)
|
||||
|
||||
server.Connector.StartAcceptingConnections(ctx, net.JoinHostPort("", strconv.Itoa(*port)))
|
||||
|
||||
if *apiBinding != "" {
|
||||
server.StartApiServer(*apiBinding)
|
||||
}
|
||||
|
||||
<-c
|
||||
logrus.Info("Stopping")
|
||||
cancel()
|
||||
}
|
168
mcproto/types.go
Normal file
168
mcproto/types.go
Normal file
@ -0,0 +1,168 @@
|
||||
package mcproto
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"bytes"
|
||||
"strings"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Frame struct {
|
||||
Length int
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
type Packet struct {
|
||||
Length int
|
||||
PacketID int
|
||||
Data []byte
|
||||
}
|
||||
|
||||
const PacketIdHandshake = 0x00
|
||||
type Handshake struct {
|
||||
ProtocolVersion int
|
||||
ServerAddress string
|
||||
ServerPort uint16
|
||||
NextState int
|
||||
}
|
||||
|
||||
type ByteReader interface {
|
||||
ReadByte() (byte, error)
|
||||
}
|
||||
|
||||
func ReadVarInt(reader io.Reader) (int, error) {
|
||||
b := make([]byte, 1)
|
||||
var numRead uint = 0
|
||||
result := 0
|
||||
for numRead <= 5 {
|
||||
n, err := reader.Read(b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
value := b[0] & 0x7F
|
||||
result |= int(value) << (7*numRead)
|
||||
|
||||
numRead++
|
||||
|
||||
if b[0] & 0x80 == 0 {
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, errors.New("VarInt is too big")
|
||||
}
|
||||
|
||||
func ReadString(reader io.Reader) (string, error) {
|
||||
length, err := ReadVarInt(reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
b := make([]byte, 1)
|
||||
var strBuilder strings.Builder
|
||||
for i := 0; i < length; i++ {
|
||||
n, err := reader.Read(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
strBuilder.WriteByte(b[0])
|
||||
}
|
||||
|
||||
return strBuilder.String(), nil
|
||||
}
|
||||
|
||||
func ReadUnsignedShort(reader io.Reader) (uint16, error) {
|
||||
upper := make([]byte, 1)
|
||||
_, err := reader.Read(upper)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
lower := make([]byte, 1)
|
||||
_, err = reader.Read(lower)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return (uint16(upper[0])<<8) | uint16(lower[0]), nil
|
||||
}
|
||||
|
||||
func ReadFrame(reader io.Reader) (*Frame, error) {
|
||||
var err error
|
||||
frame := &Frame{}
|
||||
|
||||
frame.Length, err = ReadVarInt(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
frame.Payload = make([]byte, frame.Length)
|
||||
total := 0
|
||||
for total < frame.Length {
|
||||
readIntoThis := frame.Payload[total:]
|
||||
n, err := reader.Read(readIntoThis)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
total += n
|
||||
}
|
||||
|
||||
return frame, nil
|
||||
}
|
||||
|
||||
func ReadPacket(reader io.Reader) (*Packet, error) {
|
||||
|
||||
frame, err := ReadFrame(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
packet := &Packet{Length:frame.Length}
|
||||
|
||||
remainder := bytes.NewBuffer(frame.Payload)
|
||||
|
||||
packet.PacketID, err = ReadVarInt(remainder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
packet.Data = remainder.Bytes()
|
||||
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
func ReadHandshake(data []byte) (*Handshake, error) {
|
||||
|
||||
handshake := &Handshake{}
|
||||
buffer := bytes.NewBuffer(data)
|
||||
var err error
|
||||
|
||||
handshake.ProtocolVersion, err = ReadVarInt(buffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
handshake.ServerAddress, err = ReadString(buffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
handshake.ServerPort, err = ReadUnsignedShort(buffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nextState, err := ReadVarInt(buffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
handshake.NextState = nextState
|
||||
return handshake, nil
|
||||
}
|
17
server/api_server.go
Normal file
17
server/api_server.go
Normal file
@ -0,0 +1,17 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var apiRoutes = mux.NewRouter()
|
||||
|
||||
func StartApiServer(apiBinding string) {
|
||||
logrus.WithField("binding", apiBinding).Info("Serving API requests")
|
||||
go func() {
|
||||
logrus.WithError(
|
||||
http.ListenAndServe(apiBinding, apiRoutes)).Error("API server failed")
|
||||
}()
|
||||
}
|
144
server/connector.go
Normal file
144
server/connector.go
Normal file
@ -0,0 +1,144 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/itzg/mc-router/mcproto"
|
||||
"context"
|
||||
"io"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
type IConnector interface {
|
||||
StartAcceptingConnections(ctx context.Context, listenAddress string) error
|
||||
}
|
||||
|
||||
var Connector IConnector = &connectorImpl{}
|
||||
|
||||
type connectorImpl struct {
|
||||
}
|
||||
|
||||
func (c *connectorImpl) StartAcceptingConnections(ctx context.Context, listenAddress string) error {
|
||||
|
||||
ln, err := net.Listen("tcp", listenAddress)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Fatal("Unable to start listening")
|
||||
return err
|
||||
}
|
||||
logrus.WithField("listenAddress", listenAddress).Info("Listening for Minecraft client connections")
|
||||
|
||||
go c.acceptConnections(ctx, ln)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *connectorImpl) acceptConnections(ctx context.Context, ln net.Listener) {
|
||||
defer ln.Close()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
||||
default:
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("Failed to accept connection")
|
||||
} else {
|
||||
go c.HandleConnection(ctx, conn)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *connectorImpl) HandleConnection(ctx context.Context, frontendConn net.Conn) {
|
||||
defer frontendConn.Close()
|
||||
|
||||
clientAddr := frontendConn.RemoteAddr()
|
||||
logrus.WithFields(logrus.Fields{"clientAddr": clientAddr}).Info("Got connection")
|
||||
|
||||
inspectionBuffer := new(bytes.Buffer)
|
||||
|
||||
inspectionReader := io.TeeReader(frontendConn, inspectionBuffer)
|
||||
|
||||
packet, err := mcproto.ReadPacket(inspectionReader)
|
||||
if err != nil {
|
||||
logrus.WithError(err).WithField("clientAddr", clientAddr).Error("Failed to read packet")
|
||||
return
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{"length": packet.Length, "packetID": packet.PacketID}).Info("Got packet")
|
||||
|
||||
if packet.PacketID == mcproto.PacketIdHandshake {
|
||||
handshake, err := mcproto.ReadHandshake(packet.Data)
|
||||
if err != nil {
|
||||
logrus.WithError(err).WithField("clientAddr", clientAddr).Error("Failed to read handshake")
|
||||
return
|
||||
}
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"protocolVersion": handshake.ProtocolVersion,
|
||||
"server": handshake.ServerAddress,
|
||||
"serverPort": handshake.ServerPort,
|
||||
"nextState": handshake.NextState,
|
||||
}).Info("Got handshake")
|
||||
|
||||
backendHostPort := Routes.FindBackendForServerAddress(handshake.ServerAddress)
|
||||
if backendHostPort == "" {
|
||||
logrus.WithField("serverAddress", handshake.ServerAddress).Warn("Unable to find registered backend")
|
||||
return
|
||||
}
|
||||
|
||||
logrus.WithField("backendHostPort", backendHostPort).Info("Connecting to backend")
|
||||
backendConn, err := net.Dial("tcp", backendHostPort)
|
||||
if err != nil {
|
||||
logrus.WithError(err).WithFields(logrus.Fields{
|
||||
"serverAddress": handshake.ServerAddress,
|
||||
"backend": backendHostPort,
|
||||
}).Warn("Unable to connect to backend")
|
||||
return
|
||||
}
|
||||
|
||||
amount, err := io.Copy(backendConn, inspectionBuffer)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("Failed to write handshake to backend connection")
|
||||
return
|
||||
}
|
||||
logrus.WithField("amount", amount).Debug("Relayed handshake to backend")
|
||||
|
||||
pumpConnections(ctx, frontendConn, backendConn)
|
||||
} else {
|
||||
logrus.WithField("packetID", packet.PacketID).Error("Unexpected packetID, expected handshake")
|
||||
}
|
||||
}
|
||||
|
||||
func pumpConnections(ctx context.Context, frontendConn, backendConn net.Conn) {
|
||||
defer backendConn.Close()
|
||||
|
||||
errors := make(chan error, 2)
|
||||
|
||||
go pumpFrames(backendConn, frontendConn, errors, "backend", "frontend")
|
||||
go pumpFrames(frontendConn, backendConn, errors, "frontend", "backend")
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-errors:
|
||||
if err != io.EOF {
|
||||
logrus.WithError(err).Error("Error observed on connection relay")
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func pumpFrames(incoming io.Reader, outgoing io.Writer, errors chan<- error, from, to string) {
|
||||
amount, err := io.Copy(outgoing, incoming)
|
||||
if err != nil {
|
||||
errors <- err
|
||||
}
|
||||
logrus.WithField("amount", amount).Infof("Finished relay %s->%s", from, to)
|
||||
}
|
131
server/routes.go
Normal file
131
server/routes.go
Normal file
@ -0,0 +1,131 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"net/http"
|
||||
"encoding/json"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func init() {
|
||||
apiRoutes.Path("/routes").Methods("GET").
|
||||
Headers("Accept", "application/json").
|
||||
HandlerFunc(routesListHandler)
|
||||
apiRoutes.Path("/routes").Methods("POST").
|
||||
Headers("Content-Type", "application/json").
|
||||
HandlerFunc(routesCreateHandler)
|
||||
apiRoutes.Path("/routes/{serverAddress}").Methods("DELETE").HandlerFunc(routesDeleteHandler)
|
||||
}
|
||||
|
||||
func routesListHandler(writer http.ResponseWriter, request *http.Request) {
|
||||
mappings := Routes.GetMappings()
|
||||
bytes, err := json.Marshal(mappings)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("Failed to marshal mappings")
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
writer.Write(bytes)
|
||||
}
|
||||
|
||||
func routesDeleteHandler(writer http.ResponseWriter, request *http.Request) {
|
||||
serverAddress := mux.Vars(request)["serverAddress"]
|
||||
if serverAddress != "" {
|
||||
if Routes.DeleteMapping(serverAddress) {
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
} else {
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func routesCreateHandler(writer http.ResponseWriter, request *http.Request) {
|
||||
var definition = struct {
|
||||
ServerAddress string
|
||||
Backend string
|
||||
}{}
|
||||
|
||||
defer request.Body.Close()
|
||||
|
||||
decoder := json.NewDecoder(request.Body)
|
||||
err := decoder.Decode(&definition)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("Unable to get request body")
|
||||
writer.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
Routes.CreateMapping(definition.ServerAddress, definition.Backend)
|
||||
writer.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
|
||||
type IRoutes interface {
|
||||
RegisterAll(mappings map[string]string)
|
||||
// FindBackendForServerAddress returns the host:port for the external server address, if registered.
|
||||
// Otherwise, an empty string is returned
|
||||
FindBackendForServerAddress(serverAddress string) string
|
||||
GetMappings() map[string]string
|
||||
DeleteMapping(serverAddress string) bool
|
||||
CreateMapping(serverAddress string, backend string)
|
||||
}
|
||||
|
||||
var Routes IRoutes = &routesImpl{}
|
||||
|
||||
func (r *routesImpl) RegisterAll(mappings map[string]string) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
r.mappings = mappings
|
||||
}
|
||||
|
||||
type routesImpl struct {
|
||||
sync.RWMutex
|
||||
mappings map[string]string
|
||||
}
|
||||
|
||||
func (r *routesImpl) FindBackendForServerAddress(serverAddress string) string {
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
|
||||
if r.mappings == nil {
|
||||
return ""
|
||||
} else {
|
||||
return r.mappings[serverAddress]
|
||||
}
|
||||
}
|
||||
|
||||
func (r *routesImpl) GetMappings() map[string]string {
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
|
||||
result := make(map[string]string, len(r.mappings))
|
||||
for k, v := range r.mappings {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (r *routesImpl) DeleteMapping(serverAddress string) bool {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
logrus.WithField("serverAddress", serverAddress).Info("Deleting route")
|
||||
|
||||
if _, ok := r.mappings[serverAddress]; ok {
|
||||
delete(r.mappings, serverAddress)
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (r *routesImpl) CreateMapping(serverAddress string, backend string) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"serverAddress": serverAddress,
|
||||
"backend": backend,
|
||||
}).Info("Creating route")
|
||||
r.mappings[serverAddress] = backend
|
||||
}
|
Loading…
Reference in New Issue
Block a user