transfer local
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.git/*
|
||||||
55
main.go
Normal file
55
main.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"httpRouter/src/router"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
//Création du socket d'écoute
|
||||||
|
server, err := net.Listen("tcp", "localhost:4000")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Erreur : %s", err.Error())
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
//Initialisation du routeur
|
||||||
|
router := router.NewRouter()
|
||||||
|
router.Register("GET", "/hello", sendHello)
|
||||||
|
router.Register("GET", "/time", sendTime)
|
||||||
|
router.Register("GET", "/time/{time}", hello)
|
||||||
|
router.Register("GET", "/time/{day}/{hour}/{minute}", hello)
|
||||||
|
|
||||||
|
//Écoute et gestion des réception
|
||||||
|
for {
|
||||||
|
//Accepter une nouvelle connexion
|
||||||
|
conn, err := server.Accept()
|
||||||
|
|
||||||
|
//Gestion des erreurs s'il y en a (stop total)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Erreur : %s\n", err.Error())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Nouvelle connection acceptée !\n")
|
||||||
|
|
||||||
|
go router.HandleClient(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendHello(method, path string, args map[string]string, queries map[string]string) (int, string) {
|
||||||
|
return 200, "Hello World !"
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendTime(method, path string, args map[string]string, queries map[string]string) (int, string) {
|
||||||
|
return 200, fmt.Sprintf("%q", time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
func hello(method, path string, args map[string]string, queries map[string]string) (int, string) {
|
||||||
|
fmt.Printf("%w\n", queries)
|
||||||
|
return 200, fmt.Sprintf("%w", args)
|
||||||
|
}
|
||||||
66
src/parser/main.go
Normal file
66
src/parser/main.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReadRequest(reader *bufio.Reader) ([]string, string, error) {
|
||||||
|
var message []byte
|
||||||
|
|
||||||
|
buffer := make([]byte, 8)
|
||||||
|
|
||||||
|
for {
|
||||||
|
if reader.Size() == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
charsRed, err := reader.Read(buffer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Erreur : %s\n", err.Error())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
message = append(message, buffer[:charsRed]...)
|
||||||
|
|
||||||
|
if charsRed < 8 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(string(message), "\r\n\r\n")
|
||||||
|
|
||||||
|
headerLines := strings.Split(parts[0], "\r\n")
|
||||||
|
body := parts[1]
|
||||||
|
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return nil, "", fmt.Errorf("Wrong")
|
||||||
|
}
|
||||||
|
|
||||||
|
return headerLines, body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseURL(path string) (string, map[string]string, error) {
|
||||||
|
//Décomposition du chemin et des paramètres
|
||||||
|
url := strings.Split(path, "?")
|
||||||
|
|
||||||
|
//Gestion d'une URL invalide
|
||||||
|
if len(url) == 0 || len(url) > 2 || url[0] == "" {
|
||||||
|
return "BAD REQUEST", nil, fmt.Errorf("Mauvaise URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters := make(map[string]string)
|
||||||
|
|
||||||
|
//Gestion des paramètres s'il y en a
|
||||||
|
if len(url) > 1 {
|
||||||
|
slices := strings.Split(url[1], "&")
|
||||||
|
for _, slice := range slices {
|
||||||
|
elements := strings.Split(slice, "=")
|
||||||
|
if len(elements) != 2 || elements[0] == "" || elements[1] == "" {
|
||||||
|
return "BAD REQUEST", nil, fmt.Errorf("Mauvaise gestion des paramètres")
|
||||||
|
}
|
||||||
|
parameters[elements[0]] = elements[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url[0], parameters, nil
|
||||||
|
}
|
||||||
27
src/router/definition.go
Normal file
27
src/router/definition.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface de fonction pour les routes statiques
|
||||||
|
type HandlerFunc func(
|
||||||
|
method string,
|
||||||
|
path string,
|
||||||
|
args map[string]string,
|
||||||
|
queries map[string]string,
|
||||||
|
) (int, string)
|
||||||
|
|
||||||
|
type dynamicRoute struct {
|
||||||
|
method string
|
||||||
|
path string
|
||||||
|
elements []string
|
||||||
|
function HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Définition du routeur
|
||||||
|
type Router struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
staticRoutes map[string]HandlerFunc
|
||||||
|
dynamicRoutes []dynamicRoute
|
||||||
|
}
|
||||||
35
src/router/init.go
Normal file
35
src/router/init.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Création d'un routeur
|
||||||
|
func NewRouter() *Router {
|
||||||
|
return &Router{
|
||||||
|
staticRoutes: make(map[string]HandlerFunc),
|
||||||
|
dynamicRoutes: make([]dynamicRoute, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ajout de nouvelles routes
|
||||||
|
func (router *Router) Register(method, path string, handler HandlerFunc) {
|
||||||
|
//Vérouille en écriture la map
|
||||||
|
router.lock.Lock()
|
||||||
|
defer router.lock.Unlock()
|
||||||
|
|
||||||
|
//On vérifie si le chemin est dynamique, si tel est le cas on push dans dynamicRoutes
|
||||||
|
if strings.Contains(path, "{") && strings.Contains(path, "}") {
|
||||||
|
router.dynamicRoutes = append(router.dynamicRoutes, dynamicRoute{
|
||||||
|
method: method,
|
||||||
|
path: path,
|
||||||
|
elements: strings.Split(path, "/"),
|
||||||
|
function: handler,
|
||||||
|
})
|
||||||
|
//Si le chemin ne contient pas de paramètres, alors on l'ajoute dans staticRoutes
|
||||||
|
} else {
|
||||||
|
router.staticRoutes[fmt.Sprintf("%s %s", method, path)] = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
95
src/router/main.go
Normal file
95
src/router/main.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"httpRouter/src/parser"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (router *Router) HandleClient(conn net.Conn) error {
|
||||||
|
//Création du lecteur
|
||||||
|
reader := bufio.NewReader(conn)
|
||||||
|
|
||||||
|
requestHeader, _, err := parser.ReadRequest(reader)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := buildResponse(router.Handle(strings.Split(requestHeader[0], " ")[0], strings.Split(requestHeader[0], " ")[1]))
|
||||||
|
|
||||||
|
conn.Write([]byte(response))
|
||||||
|
|
||||||
|
conn.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recherche et éxécution des routes sauvegardées
|
||||||
|
func (router *Router) Handle(method, path string) (int, string) {
|
||||||
|
|
||||||
|
endpoint, parameters, err := parser.ParseURL(path)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 400, endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
//Vérouille en lecture la map
|
||||||
|
router.lock.RLock()
|
||||||
|
defer router.lock.RUnlock()
|
||||||
|
|
||||||
|
fmt.Printf("%w\n", router.staticRoutes)
|
||||||
|
fmt.Printf("%s - %s", method, path)
|
||||||
|
//Vérification de l'existence de la route dans les routes statiques enregistrées
|
||||||
|
Handler, exists := router.staticRoutes[fmt.Sprintf("%s %s", method, endpoint)]
|
||||||
|
if exists {
|
||||||
|
return Handler(method, path, make(map[string]string), parameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
elements := strings.Split(endpoint, "/")
|
||||||
|
|
||||||
|
//Vérification de la cohérence du chemin avec chaque route dynamique (O(n))
|
||||||
|
for _, route := range router.dynamicRoutes {
|
||||||
|
if len(elements) == len(route.elements) && route.method == method {
|
||||||
|
//On récupère les sous parties du chemin dynamique pour les comparer avec le chemin découpé
|
||||||
|
dynPath := route.elements
|
||||||
|
args := make(map[string]string)
|
||||||
|
//Pour chacun des sous ensembles on compare un a un les éléments
|
||||||
|
for index, item := range dynPath {
|
||||||
|
//S'il n'y a pas égalité stricte et que l'élément n'est pas un paramètre, on passe directement au prochain chemin dynamique
|
||||||
|
if elements[index] != item && (item[0] != '{' || item[len(item)-1] != '}') {
|
||||||
|
break
|
||||||
|
//Sinon si le chemin est un paramètre alors on ajoute dans le dictionnaire des paramètres la valeur du sous élément d'entrée
|
||||||
|
} else if len(item) > 0 && item[0] == '{' && item[len(item)-1] == '}' {
|
||||||
|
args[item[1:len(item)-1]] = elements[index]
|
||||||
|
}
|
||||||
|
//Si on a pas échappé à la boucle et qu'on est arrivés à la fin, c'est que c'était le bon chemin, on exécute la fonction associée
|
||||||
|
if index == len(elements)-1 {
|
||||||
|
return route.function(method, path, args, parameters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Si aucun chemin n'a été validé, c'est qu'il n'est pas renseigné dans le routeur
|
||||||
|
return 404, "NOT FOUND"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logique de code
|
||||||
|
func buildResponse(code int, message string) string {
|
||||||
|
var codes = map[int]string{
|
||||||
|
200: "OK",
|
||||||
|
400: "WRONG REQUEST",
|
||||||
|
404: "NOT FOUND",
|
||||||
|
}
|
||||||
|
|
||||||
|
response := fmt.Sprintf("HTTP/1.1 %d %s", code, codes[code])
|
||||||
|
response += "Content-Type: text/plain\r\n"
|
||||||
|
response += fmt.Sprintf("Content-Length: %d\r\n", len(message))
|
||||||
|
response += "\r\n"
|
||||||
|
response += message
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user