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