transfer local

This commit is contained in:
2025-12-28 14:12:31 +01:00
commit 31e4273903
8 changed files with 283 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.git/*

1
README.md Normal file
View File

@@ -0,0 +1 @@
salut

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module httpRouter
go 1.25.4

55
main.go Normal file
View 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
View 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
View 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
View 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
View 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
}